# 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 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])