Source code for landsat.utils

# Landsat Util
# License: CC0 1.0 Universal

from __future__ import print_function, division, absolute_import

import os
import sys
import time
import re

try:
    from io import StringIO
except ImportError:
    from cStringIO import StringIO

from datetime import datetime

import geocoder

from .mixins import VerbosityMixin


[docs]class Capturing(list): """ Captures a subprocess stdout. :Usage: >>> with Capturing(): ... subprocess(args) """ def __enter__(self): self._stdout = sys.stdout sys.stdout = self._stringio = StringIO() return self def __exit__(self, *args): self.extend(self._stringio.getvalue().splitlines()) sys.stdout = self._stdout
[docs]class timer(object): """ A timer class. :Usage: >>> with timer(): ... your code """ def __enter__(self): self.start = time.time() def __exit__(self, type, value, traceback): self.end = time.time() print('Time spent : {0:.2f} seconds'.format((self.end - self.start)))
[docs]def exit(message, code=0): """ output a message to stdout and terminates the process. :param message: Message to be outputed. :type message: String :param code: The termination code. Default is 0 :type code: int :returns: void """ v = VerbosityMixin() if code == 0: v.output(message, normal=True, arrow=True) v.output('Done!', normal=True, arrow=True) else: v.output(message, normal=True, error=True) sys.exit(code)
[docs]def create_paired_list(value): """ Create a list of paired items from a string. :param value: the format must be 003,003,004,004 (commas with no space) :type value: String :returns: List :example: >>> create_paired_list('003,003,004,004') [['003','003'], ['004', '004']] """ if isinstance(value, list): value = ",".join(value) array = re.split('\D+', value) # Make sure the elements in the list are even and pairable if len(array) % 2 == 0: new_array = [list(array[i:i + 2]) for i in range(0, len(array), 2)] return new_array else: raise ValueError('The string should include pairs and be formated. ' 'The format must be 003,003,004,004 (commas with ' 'no space)')
[docs]def check_create_folder(folder_path): """ Check whether a folder exists, if not the folder is created. :param folder_path: Path to the folder :type folder_path: String :returns: (String) the path to the folder """ if not os.path.exists(folder_path): os.makedirs(folder_path) return folder_path
[docs]def get_file(path): """ Separate the name of the file or folder from the path and return it. :param path: Path to the folder :type path: String :returns: (String) the filename :example: >>> get_file('/path/to/file.jpg') 'file.jpg' """ return os.path.basename(path)
[docs]def get_filename(path): """ Return the filename without extension. :param path: Path to the folder :type path: String :returns: (String) the filename without extension :example: >>> get_filename('/path/to/file.jpg') 'file' """ return os.path.splitext(get_file(path))[0]
[docs]def three_digit(number): """ Add 0s to inputs that their length is less than 3. :param number: The number to convert :type number: int :returns: String :example: >>> three_digit(1) '001' """ number = str(number) if len(number) == 1: return u'00%s' % number elif len(number) == 2: return u'0%s' % number else: return number
[docs]def georgian_day(date): """ Returns the number of days passed since the start of the year. :param date: The string date with this format %m/%d/%Y :type date: String :returns: int :example: >>> georgian_day('05/1/2015') 121 """ try: fmt = '%m/%d/%Y' return datetime.strptime(date, fmt).timetuple().tm_yday except (ValueError, TypeError): return 0
[docs]def year(date): """ Returns the year. :param date: The string date with this format %m/%d/%Y :type date: String :returns: int :example: >>> year('05/1/2015') 2015 """ try: fmt = '%m/%d/%Y' return datetime.strptime(date, fmt).timetuple().tm_year except ValueError: return 0
[docs]def reformat_date(date, new_fmt='%Y-%m-%d'): """ Returns reformated date. :param date: The string date with this format %m/%d/%Y :type date: String :param new_fmt: date format string. Default is '%Y-%m-%d' :type date: String :returns: int :example: >>> reformat_date('05/1/2015', '%d/%m/%Y') '1/05/2015' """ try: if isinstance(date, datetime): return date.strftime(new_fmt) else: fmt = '%m/%d/%Y' return datetime.strptime(date, fmt).strftime(new_fmt) except ValueError: return date
[docs]def convert_to_integer_list(value): """ Converts a comma separate string to a list :param value: the format must be 003,003,004,004 (commas with no space) :type value: String :returns: List :example: >>> convert_to_integer_list('003,003,004,004') ['003', '003', '004', '004'] """ if isinstance(value, list) or value is None: return value else: s = re.findall('(10|11|QA|[0-9])', value) for k, v in enumerate(s): try: s[k] = int(v) except ValueError: pass return s
# Geocoding confidence scores, from https://github.com/DenisCarriere/geocoder/blob/master/docs/features/Confidence%20Score.md geocode_confidences = { 10: 0.25, 9: 0.5, 8: 1., 7: 5., 6: 7.5, 5: 10., 4: 15., 3: 20., 2: 25., 1: 99999., # 0: unable to locate at all }
[docs]def geocode(address, required_precision_km=1.): """ Identifies the coordinates of an address :param address: the address to be geocoded :type value: String :param required_precision_km: the maximum permissible geographic uncertainty for the geocoding :type required_precision_km: float :returns: dict :example: >>> geocode('1600 Pennsylvania Ave NW, Washington, DC 20500') {'lat': 38.89767579999999, 'lon': -77.0364827} """ geocoded = geocoder.google(address) precision_km = geocode_confidences[geocoded.confidence] if precision_km <= required_precision_km: (lon, lat) = geocoded.geometry['coordinates'] return {'lat': lat, 'lon': lon} else: raise ValueError("Address could not be precisely located")
[docs]def convert_to_float_list(value): """ Converts a comma separate string to a list :param value: the format must be 1.2,-3.5 (commas with no space) :type value: String :returns: List :example: >>> convert_to_integer_list('003,003,004,004') [1.2, -3.5] """ if isinstance(value, list) or value is None: return value else: s = re.findall('([-+]?\d*\.\d+|\d+|[-+]?\d+)', value) for k, v in enumerate(s): try: s[k] = float(v) except ValueError: pass return s
[docs]def adjust_bounding_box(bounds1, bounds2): """ If the bounds 2 corners are outside of bounds1, they will be adjusted to bounds1 corners @params bounds1 - The source bounding box bounds2 - The target bounding box that has to be within bounds1 @return A bounding box tuple in (y1, x1, y2, x2) format """ # out of bound check # If it is completely outside of target bounds, return target bounds if ((bounds2[0] > bounds1[0] and bounds2[2] > bounds1[0]) or (bounds2[2] < bounds1[2] and bounds2[2] < bounds1[0])): return bounds1 if ((bounds2[1] < bounds1[1] and bounds2[3] < bounds1[1]) or (bounds2[3] > bounds1[3] and bounds2[1] > bounds1[3])): return bounds1 new_bounds = list(bounds2) # Adjust Y axis (Longitude) if (bounds2[0] > bounds1[0] or bounds2[0] < bounds1[3]): new_bounds[0] = bounds1[0] if (bounds2[2] < bounds1[2] or bounds2[2] > bounds1[0]): new_bounds[2] = bounds1[2] # Adjust X axis (Latitude) if (bounds2[1] < bounds1[1] or bounds2[1] > bounds1[3]): new_bounds[1] = bounds1[1] if (bounds2[3] > bounds1[3] or bounds2[3] < bounds1[1]): new_bounds[3] = bounds1[3] return tuple(new_bounds)
[docs]def remove_slash(value): assert(isinstance(value, str)) return re.sub('(^\/|\/$)', '', value)
[docs]def url_builder(segments): # Only accept list or tuple assert((isinstance(segments, list) or isinstance(segments, tuple))) return "/".join([remove_slash(s) for s in segments])