diff --git a/.travis.yml b/.travis.yml index 9fce481a544bacae0bdfe49eaf0ff598e9d5bce4..f1d26c72f7c5d90006194c0a0409ab86638aa2f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,12 +45,11 @@ install: - conda config --add channels jlaura - conda install python=$PYTHON_VERSION - conda install -c conda-forge gdal h5py - - conda install pandas sqlalchemy pyyaml networkx + - conda install pandas sqlalchemy pyyaml networkx affine - conda install -c jlaura pvl protobuf # Development installation - - conda install nose coverage sh anaconda-client - - pip install coveralls + - conda install pytest pytest-cov sh anaconda-client # Straight from the menpo team - if [["$TRAVIS_OS_NAME" == "osx"]]; then @@ -62,7 +61,7 @@ install: script: - - nosetests --with-coverage --cover-package=plio + - pytest --cov=plio # After test success, package and upload to Anaconda - ~/miniconda/bin/python condaci.py build ./conda diff --git a/appveyor.yml b/appveyor.yml index 58fa4f2ff020fce49d20478182eb8fd5aa0e33d0..33c1b9993c7c0969e82a690c9b30c4037462256b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,7 +40,7 @@ install: - cmd: set PYTHONUNBUFFERED=1 - cmd: conda config --set show_channel_urls true - - cmd: conda install --yes python=3.5 + - cmd: conda install --yes python=3.5 - cmd: conda install -c pelson/channel/development --yes --quiet obvious-ci - cmd: conda config --add channels conda-forge - cmd: conda info @@ -54,18 +54,18 @@ install: - cmd: conda config --add channels conda-forge - cmd: conda config --add channels jlaura - cmd: conda install --yes -c conda-forge gdal h5py - - cmd: conda install --yes pandas sqlalchemy pyyaml networkx + - cmd: conda install --yes pandas sqlalchemy pyyaml networkx affine - cmd: conda install --yes -c jlaura protobuf pvl # Development installation - - cmd: conda install --yes nose coverage + - cmd: conda install --yes pytest pytest-cov - cmd: pip install coveralls # Skip .NET project specific build phase. build: off test_script: - - cmd: nosetests --with-coverage --cover-package=plio + - cmd: pytest --cov=plio --ignore=plio/examples - "%CMD_IN_ENV% conda build conda --quiet" deploy_script: diff --git a/conda/meta.yaml b/conda/meta.yaml index 18fbf121a501256f19dd85b24621c628de4ef3d7..ecfc71b3219c9793c5f46f3e7d59bbb1abec9b17 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -23,6 +23,8 @@ requirements: - pandas - sqlalchemy - pyyaml + - affine + - networkx run: - python - setuptools @@ -35,6 +37,8 @@ requirements: - pandas - sqlalchemy - pyyaml + - affine + - networkx test: imports: diff --git a/plio/__init__.py b/plio/__init__.py index d3f342b7d48cd4d52201ac799c685be5755e2823..3e4efb6ce20e714db61902d878bc680fb51b0b27 100755 --- a/plio/__init__.py +++ b/plio/__init__.py @@ -5,5 +5,5 @@ from . import io from . import date from . import data from . import examples +from . import geofuncs from . import utils - diff --git a/plio/date/__init__.py b/plio/date/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5c7c1bcf9b5235263cee4cffdb2e198a98f0b341 100644 --- a/plio/date/__init__.py +++ b/plio/date/__init__.py @@ -0,0 +1,2 @@ +from . import julian2ls +from . import julian2season diff --git a/plio/geofuncs/__init__.py b/plio/geofuncs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/plio/geofuncs/geofuncs.py b/plio/geofuncs/geofuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..9db0f00da7fe4c683d3d420ed72f3d2a794a30b5 --- /dev/null +++ b/plio/geofuncs/geofuncs.py @@ -0,0 +1,163 @@ +import json +import numpy as np + +def intersection_to_pixels(inverse_affine, ul, ur, lr, ll): + """ + Given an inverse affine transformation, take four bounding coordinates + in lat/lon space and convert them to a minimum bounding rectangle in pixel + space. + + inverse_affine : object + An Affine transformation object + + ul : tuple + Upper Left coordinate in the form (x,y) + + ur : tuple + Upper Right coordinate in the form (x,y) + + lr : tuple + Lower Right coordinate in the form (x,y) + + ll : tuple + Lower Left coordinate in the form (x,y) + """ + minx = np.inf + maxx = -np.inf + miny = np.inf + maxy = -np.inf + + for c in [ul, ur, lr, ll]: + px, py = map(int, inverse_affine * (c[0], c[1])) + + if px < minx: + minx = px + if px > maxx: + maxx = px + + if py < miny: + miny = py + if py > maxy: + maxy = py + + if minx < 0: + minx = 0 + if miny < 0: + miny = 0 + + return minx, maxx, miny, maxy + +def compute_overlap(geodata_a, geodata_b): + p1 = geodata_a.footprint + p2 = geodata_b.footprint + intersection = json.loads(p1.Intersection(p2).ExportToJson())['coordinates'][0] + + ul, ur, lr, ll = find_four_corners(intersection) + + a_intersection = intersection_to_pixels(geodata_a.inverse_affine, ul, ur, lr, ll) + b_intersection = intersection_to_pixels(geodata_b.inverse_affine, ul, ur, lr, ll) + + return (ul, ur, lr, ll), a_intersection, b_intersection + +def estimate_mbr(geodata_a, geodata_b, bufferd=50): + p1 = geodata_a.footprint + p2 = geodata_b.footprint + minx, maxx, miny, maxy = p1.Intersection(p2).GetEnvelope() + ul = (minx, maxy) + lr = (maxx, miny) + a_slice = intersection_to_pixels(geodata_a.latlon_to_pixel, + ul, lr, *geodata_a.xy_extent[1], bufferd) + b_slice = intersection_to_pixels(geodata_b.latlon_to_pixel, + ul, lr, *geodata_b.xy_extent[1], bufferd) + + return a_slice, b_slice + + +def find_corners(coords, threshold=120): + """ + Given a list of coordinates in the form [(x, y), (x1, y1), ..., (xn, yn)], + attempt to find corners by identifying all angles < the threshold. For a + line segment composed of 3 points, the angle between ab and ac is 180 if the + segments are colinear. If they are less than threshold, the line segments + are considered to be a corner. + + Parameters + ---------- + coords : list + of coordinates + + threshold : numeric + Angle under which a corner is identified, Default: 120 + """ + corners = [coords[0]] + for i, a in enumerate(coords[:-2]): + a = np.asarray(a) + b = np.asarray(coords[i+1]) + c = np.asarray(coords[i+2]) + ba = a - b + bc = c - b + angle = np.arccos(np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))) + if np.degrees(angle) < threshold: + corners.append(b.tolist()) + + return corners + +def find_four_corners(coords, threshold=120): + """ + Find four corners in a polygon by making a call to the find_corners + function and using the first four corners. + + Parameters + ---------- + coords: list + of coordinates + + threshold : numeric + Anfle under which a corner is identified, Default: 120 + + See Also + -------- + plio.geofuncs.geofuncs.find_corners + """ + corners = find_corners(coords, threshold) + + corners.sort(key = lambda x:x[1]) + upper = corners[2:] + lower = corners[:2] + + + key = lambda x:x[0] + ul = min(upper, key=key) + ur = max(upper, key=key) + lr = max(lower, key=key) + ll = min(lower, key=key) + + return ul, ur, lr, ll + +def is_clockwise(vertices): + """ + Returns whether a list of points describing a polygon are clockwise or counterclockwise. + is_clockwise(Point list) -> bool + Parameters + ---------- + vertices : a list of points that form a single ring + Examples + -------- + >>> is_clockwise([Point((0, 0)), Point((10, 0)), Point((0, 10))]) + False + >>> is_clockwise([Point((0, 0)), Point((0, 10)), Point((10, 0))]) + True + >>> v = [(-106.57798, 35.174143999999998), (-106.583412, 35.174141999999996), (-106.58417999999999, 35.174143000000001), (-106.58377999999999, 35.175542999999998), (-106.58287999999999, 35.180543), (-106.58263099999999, 35.181455), (-106.58257999999999, 35.181643000000001), (-106.58198299999999, 35.184615000000001), (-106.58148, 35.187242999999995), (-106.58127999999999, 35.188243), (-106.58138, 35.188243), (-106.58108, 35.189442999999997), (-106.58104, 35.189644000000001), (-106.58028, 35.193442999999995), (-106.580029, 35.194541000000001), (-106.57974399999999, 35.195785999999998), (-106.579475, 35.196961999999999), (-106.57922699999999, 35.198042999999998), (-106.578397, 35.201665999999996), (-106.57827999999999, 35.201642999999997), (-106.57737999999999, 35.201642999999997), (-106.57697999999999, 35.201543000000001), (-106.56436599999999, 35.200311999999997), (-106.56058, 35.199942999999998), (-106.56048, 35.197342999999996), (-106.56048, 35.195842999999996), (-106.56048, 35.194342999999996), (-106.56048, 35.193142999999999), (-106.56048, 35.191873999999999), (-106.56048, 35.191742999999995), (-106.56048, 35.190242999999995), (-106.56037999999999, 35.188642999999999), (-106.56037999999999, 35.187242999999995), (-106.56037999999999, 35.186842999999996), (-106.56037999999999, 35.186552999999996), (-106.56037999999999, 35.185842999999998), (-106.56037999999999, 35.184443000000002), (-106.56037999999999, 35.182943000000002), (-106.56037999999999, 35.181342999999998), (-106.56037999999999, 35.180433000000001), (-106.56037999999999, 35.179943000000002), (-106.56037999999999, 35.178542999999998), (-106.56037999999999, 35.177790999999999), (-106.56037999999999, 35.177143999999998), (-106.56037999999999, 35.175643999999998), (-106.56037999999999, 35.174444000000001), (-106.56037999999999, 35.174043999999995), (-106.560526, 35.174043999999995), (-106.56478, 35.174043999999995), (-106.56627999999999, 35.174143999999998), (-106.566541, 35.174144999999996), (-106.569023, 35.174157000000001), (-106.56917199999999, 35.174157999999998), (-106.56938, 35.174143999999998), (-106.57061499999999, 35.174143999999998), (-106.57097999999999, 35.174143999999998), (-106.57679999999999, 35.174143999999998), (-106.57798, 35.174143999999998)] + >>> is_clockwise(v) + True + """ + if len(vertices) < 3: + return True + area = 0.0 + ax, ay = vertices[0] + for bx, by in vertices[1:]: + area += ax * by - ay * bx + ax, ay = bx, by + bx, by = vertices[0] + area += ax * by - ay * bx + return area < 0.0 diff --git a/plio/geofuncs/tests/test_geofuncs.py b/plio/geofuncs/tests/test_geofuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..26a0cbdeb8275028ee2a7cc43589323a31bcbb01 --- /dev/null +++ b/plio/geofuncs/tests/test_geofuncs.py @@ -0,0 +1,24 @@ +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +import pytest + +from plio.geofuncs import geofuncs + +@pytest.fixture +def nevada(): + return [[-114.04392,40.68928],[-114.04558,40.4958],[-114.04619,40.30302],[-114.04644,40.09896],[-114.04658,39.99994],[-114.04727,39.75817],[-114.04757,39.61018],[-114.0473,39.45715],[-114.04779,39.36296],[-114.04841,39.23851],[-114.04885,39.08777],[-114.04833,38.90545],[-114.04916,38.75165],[-114.04992,38.55049],[-114.04997,38.20495],[-114.05013,37.95499],[-114.04939,37.77873],[-114.05198,37.70735],[-114.05264,37.47222],[-114.05187,37.13439],[-114.0506,37.0004],[-114.0506,36.99997],[-114.05014,36.817],[-114.04736,36.60322],[-114.04338,36.37619],[-114.04404,36.21464],[-114.1139,36.09833],[-114.22646,36.01461],[-114.32346,36.10119],[-114.51122,36.15058],[-114.6729,36.11546],[-114.73513,36.05493],[-114.74365,35.98542],[-114.70883,35.9167],[-114.67489,35.86436],[-114.70415,35.81412],[-114.69704,35.73579],[-114.68702,35.66942],[-114.65449,35.60517],[-114.66076,35.5417],[-114.6768,35.49125],[-114.61121,35.37012],[-114.58031,35.21811],[-114.57354,35.14231],[-114.63064,35.11791],[-114.60899,35.07971],[-114.63423,35.00332],[-114.63349,35.00186],[-114.63361,35.00195],[-114.82052,35.15341],[-115.11622,35.38796],[-115.36992,35.59033],[-115.65233,35.81231],[-115.89512,36.0018],[-116.08072,36.14577],[-116.37528,36.37205],[-116.87227,36.75057],[-117.31883,37.08441],[-117.79563,37.43715],[-118.04392,37.6185],[-118.22972,37.75309],[-118.51722,37.96065],[-119.00097,38.30368],[-119.43506,38.60904],[-119.76041,38.83427],[-119.9748,38.98156],[-120.00608,39.37557],[-120.0015,39.57782],[-120.00049,39.79567],[-119.99733,40.08934],[-119.99567,40.39719],[-119.99926,40.86934],[-120.00002,41.26742],[-119.99919,41.97905],[-119.99917,41.99454],[-119.99917,41.99484],[-119.90622,41.9972],[-119.80128,41.99746],[-119.70479,41.99621],[-119.61469,41.99594],[-119.48157,41.99572],[-119.36302,41.99428],[-119.25103,41.99384],[-119.0022,41.99375],[-118.7824,41.99259],[-118.54194,41.99478],[-118.42657,41.99586],[-118.3477,41.99629],[-118.19842,41.99699],[-118.09918,41.9975],[-118.06386,41.99767],[-117.99278,41.99804],[-117.90359,41.99817],[-117.79003,41.99842],[-117.71943,41.99819],[-117.62441,41.99836],[-117.51839,41.99915],[-117.46483,41.9994],[-117.33055,41.9996],[-117.18204,42.00038],[-117.02916,42.00018],[-117.02622,42.00025],[-117.02234,41.99989],[-116.861,41.99876],[-116.70972,41.99812],[-116.55307,41.99766],[-116.48554,41.99686],[-116.37594,41.99632],[-116.20623,41.99768],[-116.12593,41.99765],[-116.01199,41.99804],[-115.98839,41.99855],[-115.84911,41.99677],[-115.69522,41.99701],[-115.52677,41.99676],[-115.3968,41.99648],[-115.31388,41.9961],[-115.25015,41.99615],[-115.12254,41.99607],[-114.91817,41.99977],[-114.76097,41.99991],[-114.6492,41.9962],[-114.61271,41.99503],[-114.49854,41.9946],[-114.39727,41.99501],[-114.30408,41.99437],[-114.18492,41.99407],[-114.04537,41.99372],[-114.04172,41.99372],[-114.0398,41.89425],[-114.04055,41.59062],[-114.04061,41.36],[-114.04195,41.05548],[-114.04375,40.76026],[-114.04391,40.68985]] + +def test_find_four_corners(nevada): + corners = geofuncs.find_four_corners(nevada) + assert corners[0] == [-119.99917, 41.99484] + assert corners[3] == [-114.63349, 35.00186] + assert len(corners) == 4 + +def test_find_corners(nevada): + corners = geofuncs.find_corners(nevada) + assert len(corners) == 8 + + corners = geofuncs.find_corners(nevada, threshold=100) + assert len(corners) == 5 diff --git a/plio/io/__init__.py b/plio/io/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f040a7cbca8ec352cc7739a9978e72fe01dafa30 100644 --- a/plio/io/__init__.py +++ b/plio/io/__init__.py @@ -0,0 +1,12 @@ +from . import io_autocnetgraph +from . import io_controlnetwork +from . import io_db +from . import io_gdal +from . import io_hdf +from . import io_json +from . import io_krc +from . import io_pvl +from . import io_spectral_profiler +from . import io_tes +from . import io_yaml +from . import isis_serial_number diff --git a/plio/io/io_gdal.py b/plio/io/io_gdal.py index b794d5d292fee90e9b309f5b3f25874e331e0d51..44a54913251eb6b41579d4ae0c63ed1221cc9584 100644 --- a/plio/io/io_gdal.py +++ b/plio/io/io_gdal.py @@ -1,6 +1,9 @@ +import json +import math import os import warnings +import affine import gdal import numpy as np import osr @@ -8,6 +11,7 @@ import pvl from osgeo import ogr from plio.io import extract_metadata +from plio.geofuncs import geofuncs from plio.utils.utils import find_in_dict gdal.UseExceptions() @@ -27,7 +31,8 @@ NP2GDAL_CONVERSION = { GDAL2NP_CONVERSION = {} -DEFAULT_PROJECTIONS = {'mars':'GEOGCS["Mars 2000",DATUM["D_Mars_2000",SPHEROID["Mars_2000_IAU_IAG",3396190.0,169.89444722361179]],PRIMEM["Greenwich",0],UNIT["Decimal_Degree",0.0174532925199433]]'} +DEFAULT_PROJECTIONS = {'mars':'GEOGCS["Mars 2000",DATUM["D_Mars_2000",SPHEROID["Mars_2000_IAU_IAG",3396190.0,169.89444722361179]],PRIMEM["Greenwich",0],UNIT["Decimal_Degree",0.0174532925199433]]', + 'moon':'GEOGCS["Moon 2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Greenwich",0],UNIT["Decimal_Degree",0.0174532925199433]]'} DEFAULT_RADII = {'mars': 3396190.0} for k, v in iter(NP2GDAL_CONVERSION.items()): @@ -173,22 +178,31 @@ class GeoDataset(object): """ if not getattr(self, '_geotransform', None): if self.footprint: - # This is an ISIS3 cube that does not report a valid geotransform - ul, ll, lr, ur = self.latlon_extent - xs, ys = self.raster_size - xres = abs(ul[0] - ur[0]) / xs - yres = abs(ul[1] - ll[1]) / ys - self._geotransform = [ul[0], xres, 0, ul[1], 0, -yres] - - if ul[1] > ll[1]: - # Image is South-North instead of North-South - self._geotransform[-1] *= -1 # Lat increases with image length - self._geotransform[3] = ll[1] # Origin is at the LL - + coords = json.loads(self.footprint.ExportToJson())['coordinates'][0][0] + ul, ur, lr, ll = geofuncs.find_four_corners(coords) + + xsize, ysize = self.raster_size + xstart = ul[0] + xscale = (ur[0] - xstart) / xsize + xskew = (ll[0] - xstart) / ysize + ystart = ul[1] + yskew = (ur[1] - ystart) / xsize + yscale = (ll[1] - ystart) / ysize + self._geotransform = [xstart, xscale, xskew, ystart, yskew, yscale] else: self._geotransform = self.dataset.GetGeoTransform() return self._geotransform + @property + def forward_affine(self): + self._fa = affine.Affine.from_gdal(*self.geotransform) + return self._fa + + @property + def inverse_affine(self): + self._ia = ~self.forward_affine + return self._ia + @property def standard_parallels(self): if not getattr(self, '_standard_parallels', None): @@ -201,6 +215,13 @@ class GeoDataset(object): self._unit_type = self.dataset.GetRasterBand(1).GetUnitType() return self._unit_type + @property + def north_up(self): + if self.footprint: + return geofuncs.is_clockwise(json.loads(self.footprint.ExportToJson())['coordinates'][0][0]) + else: + return True + @property def spatial_reference(self): if not getattr(self, '_srs', None): @@ -396,16 +417,8 @@ class GeoDataset(object): (Latitude, Longitude) corresponding to the given (x,y). """ - try: - geotransform = self.geotransform - x = geotransform[0] + (x * geotransform[1]) + (y * geotransform[2]) - y = geotransform[3] + (x * geotransform[4]) + (y * geotransform[5]) - lon, lat, _ = self.coordinate_transformation.TransformPoint(x, y) - except: - lat = lon = None - warnings.warn('Unable to compute pixel to geographic conversion without ' - 'projection information for {}'.format(self.base_name)) - + lon, lat = self.forward_affine * (x,y) + lon, lat, _ = self.coordinate_transformation.TransformPoint(lon, lat) return lat, lon def latlon_to_pixel(self, lat, lon): @@ -424,11 +437,9 @@ class GeoDataset(object): (Sample, line) position corresponding to the given (latitude, longitude). """ - geotransform = self.geotransform - upperlat, upperlon, _ = self.inverse_coordinate_transformation.TransformPoint(lon, lat) - x = (upperlat - geotransform[0]) / geotransform[1] - y = (upperlon - geotransform[3]) / geotransform[5] - return int(x), int(y) + lon, lat, _ = self.inverse_coordinate_transformation.TransformPoint(lon, lat) + px, py = map(int, self.inverse_affine * (lon, lat)) + return px, py def read_array(self, band=1, pixels=None, dtype='float32'): """ @@ -458,25 +469,34 @@ class GeoDataset(object): if not pixels: array = band.ReadAsArray().astype(dtype) + if self.north_up == False: + array = np.flipud(array) else: # Check that the read start is not outside of the image - xstart, ystart, xextent, yextent = pixels + xstart, ystart, xcount, ycount = pixels + xmax, ymax = map(int, self.xy_extent[1]) + + # If the image is south up, flip the roi + if self.north_up == False: + ystart = ymax - ystart - ycount if xstart < 0: xstart = 0 if ystart < 0: ystart = 0 - xmax, ymax = map(int, self.xy_extent[1]) - if xstart + pixels[2] > xmax: - xextent = xmax - xstart + if xstart + xcount > xmax: + xcount = xmax - xstart - if ystart + pixels[3] > ymax: - yextent = ymax - ystart + if ystart + ycount > ymax: + ycount = ymax - ystart - array = band.ReadAsArray(xstart, ystart, xextent, yextent).astype(dtype) + array = band.ReadAsArray(xstart, ystart, xcount, ycount).astype(dtype) return array + def compute_overlap(self, geodata, **kwargs): + return geofuncs.compute_overlap(self, geodata, **kwargs) + def array_to_raster(array, file_name, projection=None, geotransform=None, outformat='GTiff', @@ -549,7 +569,7 @@ def array_to_raster(array, file_name, projection=None, def match_rasters(match_to, match_from, destination, - resampling_method='GRA_Bilinear'): + resampling_method='GRA_Bilinear', ndv=0): """ Match a source raster to a match raster, including resolution and extent. @@ -590,7 +610,9 @@ def match_rasters(match_to, match_from, destination, dst = gdal.GetDriverByName('GTiff').Create(destination, width, height, 1, gdalconst.GDT_Float64) + dst.SetGeoTransform(match_to_gt) dst.SetProjection(match_to_srs) + dst.GetRasterBand(1).SetNoDataValue(ndv) gdal.ReprojectImage(match_from.dataset, dst, None, None, getattr(gdalconst, resampling_method)) diff --git a/plio/io/io_pvl.py b/plio/io/io_pvl.py new file mode 100644 index 0000000000000000000000000000000000000000..79c4574be08cd8554a98bf4b080b4275d50ce709 --- /dev/null +++ b/plio/io/io_pvl.py @@ -0,0 +1,20 @@ +import pvl +from plio.utils.utils import find_in_dict + +def extract_keywords(header, *args): + """ + For a given header, find all of the keys and return an unnested dict. + """ + try: + header = pvl.load(header) + except: + header = pvl.loads(header) + + res = {} + # Iterate through all of the requested keys + for a in args: + try: + res[a] = find_in_dict(a) + except: + res[a] = None + return res diff --git a/plio/io/tests/test_io_gdal.py b/plio/io/tests/test_io_gdal.py index b7e79fdd0beae49ef0397b981094589418fe5875..0221a0871c8c53b54d186948bf7af4779eb8f68b 100644 --- a/plio/io/tests/test_io_gdal.py +++ b/plio/io/tests/test_io_gdal.py @@ -189,7 +189,7 @@ class TestWriter(unittest.TestCase): no_data_value = 0.0 io_gdal.array_to_raster(self.arr, 'test.tif', ndv=no_data_value) dataset = io_gdal.GeoDataset('test.tif') - self.assertEqual(dataset.no_data_value, no_data_value) + self.assertEqual(dataset.no_data_value, no_data_value) def test_with_projection(self): wktsrs = """PROJCS["Moon2000_Mercator180", @@ -226,6 +226,3 @@ class TestWriter(unittest.TestCase): os.remove('test.tif') except: pass - - - diff --git a/plio/utils/utils.py b/plio/utils/utils.py index 1b97f2407fdb156262c44b39d2454890d793d4ea..761f70d28416f338b896d10f1daff6c4d7eb3ad2 100644 --- a/plio/utils/utils.py +++ b/plio/utils/utils.py @@ -18,6 +18,16 @@ def create_dir(basedir=''): """ return tempfile.mkdtemp(dir=basedir) +def check_file_exists(fpath): + #Ensure that the file exists at the PATH specified + if os.path.isfile(fpath) == False: + error_msg = "Unable to find file: {}\n".format(fpath) + try: + logging.error(error_msg) + except: + print(error_msg) + return + return True def delete_dir(dir): """ @@ -153,4 +163,4 @@ def xstr(s): """ if s is None: return '' - return str(s) \ No newline at end of file + return str(s)