Source code for pyroSAR.ancillary

##############################################################
# ancillary routines for software pyroSAR
# John Truckenbrodt 2014-2019
##############################################################
"""
This script gathers central functions and classes for general pyroSAR applications.
"""
import os
import re
import math
import inspect
from datetime import datetime
from ._dev_config import product_pattern


from spatialist.ancillary import finder


[docs]def groupby(images, attribute): """ group a list of images by a metadata attribute Parameters ---------- images: list of str the names of the images to be sorted attribute: str the name of the attribute used for sorting; see :func:`parse_datasetname` for options Returns ------- list of lists a list containing a list with image names ofr each group """ images_sort = sorted(images, key=lambda x: re.search(product_pattern, x).group(attribute)) out_meta = [[parse_datasetname(images_sort.pop(0))]] while len(images_sort) > 0: filename = images_sort.pop(0) meta = parse_datasetname(filename) if out_meta[-1][0][attribute] == meta[attribute]: out_meta[-1].append(meta) else: out_meta.append([meta]) out = [[x['filename'] for x in y] for y in out_meta] return out
[docs]def groupbyTime(images, function, time): """ function to group images by their acquisition time difference Parameters ---------- images: list of str a list of image names function: function a function to derive the time from the image names; see e.g. :func:`seconds` time: int or float a time difference in seconds by which to group the images Returns ------- list a list of sub-lists containing the grouped images """ # sort images by time stamp srcfiles = sorted(images, key=function) groups = [[srcfiles[0]]] group = groups[0] for i in range(1, len(srcfiles)): item = srcfiles[i] timediff = abs(function(item) - function(group[-1])) if timediff <= time: group.append(item) else: groups.append([item]) group = groups[-1] return [x[0] if len(x) == 1 else x for x in groups]
[docs]def multilook_factors(sp_rg, sp_az, tr_rg, tr_az, geometry, incidence): """ compute multilooking factors to approximate a target ground range resolution Parameters ---------- sp_rg: int or float the range pixel spacing sp_az: int or float the azimuth pixel spacing tr_rg: int or float the range target resolution tr_az: int or float the azimuth target resolution geometry: str the imaging geometry; either 'SLANT_RANGE' or 'GROUND_RANGE' incidence: int or float the angle of incidence Returns ------- tuple the multilooking factors as (rangelooks, azimuthlooks) """ if geometry == 'SLANT_RANGE': # compute the ground range resolution groundRangePS = sp_rg / (math.sin(math.radians(incidence))) rlks = int(math.floor(float(tr_rg) / groundRangePS)) elif geometry == 'GROUND_RANGE': rlks = int(math.floor(float(tr_rg) / sp_rg)) else: raise ValueError("parameter 'geometry' must be either 'SLANT_RANGE' or 'GROUND_RANGE'") # compute the azimuth looks azlks = int(math.floor(float(tr_az) / sp_az)) # set the look factors to 1 if they were computed to be 0 rlks = rlks if rlks > 0 else 1 azlks = azlks if azlks > 0 else 1 return rlks, azlks
[docs]def seconds(filename): """ function to extract time in seconds from a file name. the format must follow a fixed pattern: YYYYmmddTHHMMSS Images processed with pyroSAR functionalities via module snap or gamma will contain this information. Parameters ---------- filename: str the name of a file from which to extract the time from Returns ------- float the difference between the time stamp in filename and Jan 01 1900 in seconds """ # return mktime(strptime(re.findall('[0-9T]{15}', filename)[0], '%Y%m%dT%H%M%S')) td = datetime.strptime(re.findall('[0-9T]{15}', filename)[0], '%Y%m%dT%H%M%S') - datetime(1900, 1, 1) return td.total_seconds()
[docs]def parse_datasetname(name, parse_date=False): """ Parse the name of a pyroSAR processing product and extract its metadata components as dictionary Parameters ---------- name: str the name of the file to be parsed parse_date: bool parse the start date to a :class:`~datetime.datetime` object or just return the string? Returns ------- dict the metadata attributes Examples -------- >>> meta = parse_datasetname('S1A__IW___A_20150309T173017_VV_grd_mli_geo_norm_db.tif') >>> print(sorted(meta.keys())) ['acquisition_mode', 'extensions', 'filename', 'orbit', 'outname_base', 'polarization', 'proc_steps', 'sensor', 'start'] """ filename = os.path.abspath(name) if os.path.isfile(name) else name match = re.match(re.compile(product_pattern), filename) if not match: return out = match.groupdict() if out['extensions'] == '': out['extensions'] = None if out['proc_steps'] is not None: out['proc_steps'] = out['proc_steps'].split('_') if parse_date: out['start'] = datetime.strptime(out['start'], '%Y%m%dT%H%M%S') out['filename'] = filename out['outname_base'] = out['outname_base'].strip('_') return out
[docs]def find_datasets(directory, recursive=False, **kwargs): """ find pyroSAR datasets in a directory based on their metadata Parameters ---------- directory: str the name of the directory to be searched recursive: bool search the directory recursively into subdirectories? kwargs: Metadata attributes for filtering the scene list supplied as `key=value`. e.g. `sensor='S1A'`. Multiple allowed options can be provided in tuples, e.g. `sensor=('S1A', 'S1B')`. Any types other than tuples require an exact match, e.g. `proc_steps=['grd', 'mli', 'geo', 'norm', 'db']` will be matched only if these processing steps are contained in the product name in this exact order. The special attributes `start` and `stop` can be used for time filtering where `start<=value<=stop`. See function :func:`parse_datasetname` for further options. Returns ------- list of str the file names found in the directory and filtered by metadata attributes Examples -------- >>> selection = find_datasets('path/to/files', sensor=('S1A', 'S1B'), polarization='VV') """ files = finder(directory, [product_pattern], regex=True, recursive=recursive) selection = [] for file in files: meta = parse_datasetname(file) matches = [] for key, val in kwargs.items(): if key == 'start': match = val <= meta['start'] elif key == 'stop': match = val >= meta['start'] # only the start time stamp is contained in the filename elif isinstance(val, tuple): match = meta[key] in val else: match = meta[key] == val matches.append(match) if all(matches): selection.append(file) return selection
[docs]def getargs(func): """ get the arguments of a function Parameters ---------- func: function the function to be checked Returns ------- list or str the argument names """ return sorted(inspect.getfullargspec(func).args)
[docs]def hasarg(func, arg): """ simple check whether a function takes a parameter as input Parameters ---------- func: function the function to be checked arg: str the argument name to be found Returns ------- bool does the function take this as argument? """ return arg in getargs(func)