diff --git a/.travis.yml b/.travis.yml index f4d69908f..618e0fac1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,10 @@ os: - osx env: - - PYTHON_VERSION=3.5 - - PYTHON_VERSION=3.6 + - PYTHON_VERSION=3.5 HAS_GDAL=true + - PYTHON_VERSION=3.6 HAS_GDAL=true + - PYTHON_VERSION=3.5 HAS_GDAL=false + - PYTHON_VERSION=3.6 HAS_GDAL=false before_install: # We do this conditionally because it saves us some downloading if the @@ -36,8 +38,12 @@ before_install: install: - conda config --add channels usgs-astrogeology - - conda config --add channels conda-forge - - conda install libgdal gdal numpy pyproj h5py pvl scipy protobuf affine jinja2 networkx pandas sqlalchemy pyyaml ncurses usgscam pytest pytest-cov sh coveralls nbsphinx + - conda config --add channels conda-forge + - if $HAS_GDAL; then + conda install libgdal gdal numpy pyproj h5py pvl scipy protobuf affine jinja2 networkx pandas sqlalchemy pyyaml ncurses pytest pytest-cov sh coveralls nbsphinx; + else + conda install numpy pyproj h5py pvl scipy protobuf affine jinja2 networkx pandas sqlalchemy pyyaml ncurses pytest pytest-cov sh coveralls nbsphinx; + fi script: - pytest --cov=plio diff --git a/plio/camera/__init__.py b/plio/camera/__init__.py index e69de29bb..3f4336cbf 100644 --- a/plio/camera/__init__.py +++ b/plio/camera/__init__.py @@ -0,0 +1,20 @@ +import importlib +import warnings + +cam = importlib.find_loader('usgscam') +cycsm_isd = importlib.find_loader('cycsm.isd') + +if cam: + cam = cam.load_module() + +if cycsm_isd: + cycsm_isd = cycsm_isd.load_module() + +def conditional_cameras(func): + def cam_check(*args, **kwargs): + if cam: + return func(*args, **kwargs) + else: + warning.warn('Trying to call a camera method, but usgscam is not installed.') + return None + return cam_check \ No newline at end of file diff --git a/plio/camera/csm.py b/plio/camera/csm.py index b555efc6f..6e20e79f0 100644 --- a/plio/camera/csm.py +++ b/plio/camera/csm.py @@ -1,17 +1,11 @@ import datetime import json -try: - import usgscam as cam - from cycsm.isd import Isd - camera_support = True -except: - camera_support = False import requests from plio.utils.utils import find_in_dict from plio.io.io_json import NumpyEncoder - +from plio.camera import conditional_cameras, cam, cycsm_isd def data_from_cube(header): data = {} @@ -28,16 +22,15 @@ def data_from_cube(header): data['SPACECRAFT_CLOCK_START_COUNT'] = find_in_dict(header, 'SpacecraftClockCount') return data +@conditional_cameras def create_camera(obj, url='http://smalls:8002/api/1.0/missions/mars_reconnaissance_orbiter/csm_isd'): - if not camera_support: - print("Usgscam library not installed. Camera capabilities are disabled") data = json.dumps(data_from_cube(obj.metadata), cls=NumpyEncoder) r = requests.post(url, data=data) # Get the ISD back and instantiate a local ISD for the image isd = r.json()['data']['isd'] - i = Isd.loads(isd) + i = cycsm_isd.Isd.loads(isd) # Create the plugin and camera as usual plugin = cam.genericls.Plugin() diff --git a/plio/camera/tests/test_csm.py b/plio/camera/tests/test_csm.py index ce6cf12db..7f2cd90ae 100644 --- a/plio/camera/tests/test_csm.py +++ b/plio/camera/tests/test_csm.py @@ -5,9 +5,7 @@ import pytest import pvl -import usgscam - -from plio.camera import csm +from plio.camera import csm, cam, conditional_cameras from plio.examples import get_path def mock_requests_post(*args, **kwargs): @@ -35,11 +33,13 @@ def header(): def req_obj(): return +@pytest.mark.skipif(cam is None, reason="Cameras not installed") def test_data_from_cube(header): data = csm.data_from_cube(header) assert data['START_TIME'] == datetime.datetime(2008, 9, 17, 5, 8, 10, 820000) +@pytest.mark.skipif(cam is None, reason="Cameras not installed") @mock.patch('requests.post', side_effect=mock_requests_post) def test_create_camera(header): - cam = csm.create_camera(header) - assert isinstance(cam, usgscam.genericls.SensorModel) + created_camera = csm.create_camera(header) + assert isinstance(create_camera, cam.genericls.SensorModel) diff --git a/plio/io/__init__.py b/plio/io/__init__.py index f040a7cbc..ce170f1a6 100644 --- a/plio/io/__init__.py +++ b/plio/io/__init__.py @@ -1,3 +1,26 @@ +# Conditional imports for GDAL +import importlib +import warnings + +gdal = importlib.find_loader('gdal') +ogr = importlib.find_loader('osgeo.ogr') +osr = importlib.find_loader('osr') + +if gdal: + gdal = gdal.load_module() + ogr = ogr.load_module() + osr = osr.load_module() + gdal.UseExceptions() + +def conditional_gdal(func): + def has_gdal(*args, **kwargs): + if gdal: + return func(*args, **kwargs) + else: + warning.warn('Trying to call a GDAL method, but GDAL is not installed.') + return None + return has_gdal + from . import io_autocnetgraph from . import io_controlnetwork from . import io_db diff --git a/plio/io/extract_metadata.py b/plio/io/extract_metadata.py index 547b578e8..6d94c2a4b 100644 --- a/plio/io/extract_metadata.py +++ b/plio/io/extract_metadata.py @@ -1,14 +1,10 @@ -try: - from osgeo import osr - hasosr = True -except: - hasosr = False - +from plio.io import osr, conditional_gdal import_options = ['ImportFromWkt', 'ImportFromProj4', 'ImportFromEPSG', 'ImportFromUSGS', 'ImportFromXML'] +@conditional_gdal def extract_projstring(proj_string): """ Import an OSR supported projection string into @@ -25,10 +21,7 @@ def extract_projstring(proj_string): OSR spatial reference object """ - if hasosr: - srs = osr.SpatialReference() - else: - return + srs = osr.SpatialReference() for import_option in import_options: try: func = getattr(srs, import_option) @@ -42,6 +35,7 @@ def extract_projstring(proj_string): srs.MorphFromESRI() return srs +@conditional_gdal def get_standard_parallels(srs): """ Get all standard parallels for a given map projection @@ -62,6 +56,7 @@ def get_standard_parallels(srs): parallels[i] = srs.GetProjParm('Standard_Parallel_{}'.format(i+1), 0.0) return parallels +@conditional_gdal def get_central_meridian(srs): """ Get the central meridian of the projection @@ -79,6 +74,7 @@ def get_central_meridian(srs): return srs.GetProjParm('central_meridian', 0.0) +@conditional_gdal def get_spheroid(srs): """ Get the semi-major, semi-minor, and inverse flattening @@ -100,6 +96,7 @@ def get_spheroid(srs): invflattening = srs.GetInvFlattening() return semimajor, semiminor, invflattening +@conditional_gdal def get_projection_name(srs): """ Extract the projection name from a @@ -118,6 +115,7 @@ def get_projection_name(srs): proj_name = srs.GetAttrValue("PROJECTION", 0) return proj_name +@conditional_gdal def get_false_easting(srs): """ Extract the false easting parameter from a @@ -136,6 +134,7 @@ def get_false_easting(srs): return srs.GetProjParm('False_Easting', 0) +@conditional_gdal def get_false_northing(srs): """ Extract the false northing parameter from a @@ -154,6 +153,7 @@ def get_false_northing(srs): return srs.GetProjParm('False_Northing', 0) +@conditional_gdal def get_scale_factor(srs): """ Extract the scale factor, k, from a spatial reference system (if present) @@ -171,6 +171,7 @@ def get_scale_factor(srs): return srs.GetProjParm('scale_factor', 1.0) +@conditional_gdal def get_latitude_of_origin(srs): """ Extract the latitude of origin from diff --git a/plio/io/io_gdal.py b/plio/io/io_gdal.py index 249511d20..a8900153d 100644 --- a/plio/io/io_gdal.py +++ b/plio/io/io_gdal.py @@ -6,20 +6,12 @@ import affine import numpy as np import pvl -try: - # Try the full GDAL stack - import gdal - from osgeo import ogr - import osr - gdal.UseExceptions() - has_gdal = True -except: - has_gdal = False - -from plio.io import extract_metadata + + +from plio.io import extract_metadata, conditional_gdal from plio.geofuncs import geofuncs from plio.utils.utils import find_in_dict - +from plio.io import gdal, ogr, osr NP2GDAL_CONVERSION = { "uint8": 1, @@ -158,7 +150,7 @@ def __init__(self, file_name): """ self.file_name = file_name - if not has_gdal: + if not gdal: raise ImportError('No module name gdal.') self.dataset = gdal.Open(file_name) if self.dataset is None: @@ -581,7 +573,7 @@ def array_to_raster(array, file_name, projection=None, A GDAL supported bittype, e.g. GDT_Int32 Default: GDT_Float64 """ - if not has_gdal: + if not gdal: raise ImportError('No module named gdal.') driver = gdal.GetDriverByName(outformat) try: @@ -617,7 +609,7 @@ def array_to_raster(array, file_name, projection=None, bnd.WriteArray(array[:,:,i - 1]) dataset.FlushCache() - +@conditional_gdal def match_rasters(match_to, match_from, destination, resampling_method='GRA_Bilinear', ndv=0): """ @@ -658,9 +650,6 @@ def match_rasters(match_to, match_from, destination, match_from__srs = match_from.dataset.GetProjection() match_from__gt = match_from.geotransform - if not has_gdal: - raise ImportError('No module named gdal.') - dst = gdal.GetDriverByName('GTiff').Create(destination, width, height, 1, gdalconst.GDT_Float64) diff --git a/plio/io/io_spectral_profiler.py b/plio/io/io_spectral_profiler.py index a82db1367..dc31aa02c 100755 --- a/plio/io/io_spectral_profiler.py +++ b/plio/io/io_spectral_profiler.py @@ -4,7 +4,6 @@ import numpy as np from plio.utils.utils import find_in_dict -from plio.io.io_gdal import GeoDataset class Spectral_Profiler(object): @@ -169,5 +168,7 @@ def open_browse(self, extension='.jpg'): ------- """ + + from plio.io.io_gdal import GeoDataset path, ext = os.path.splitext(self.input_data) self.browse = GeoDataset(path + extension) diff --git a/plio/io/tests/test_io_gdal.py b/plio/io/tests/test_io_gdal.py index f3d52553d..38ef04f61 100644 --- a/plio/io/tests/test_io_gdal.py +++ b/plio/io/tests/test_io_gdal.py @@ -3,14 +3,17 @@ import unittest import numpy as np +import pytest from plio.examples import get_path sys.path.insert(0, os.path.abspath('..')) from plio.io import io_gdal +from plio.io import gdal +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") class TestMercator(unittest.TestCase): def setUp(self): self.dataset = io_gdal.GeoDataset(get_path('Mars_MGS_MOLA_ClrShade_MAP2_0.0N0.0_MERC.tif')) @@ -101,7 +104,7 @@ def test_read_array_set_dtype(self): self.assertEqual(arr.dtype, np.int8) self.assertAlmostEqual(np.mean(arr), 10.10353227, 6) - +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") class TestLambert(unittest.TestCase): def setUp(self): self.dataset = io_gdal.GeoDataset(get_path('Lunar_LRO_LOLA_Shade_MAP2_90.0N20.0_LAMB.tif')) @@ -143,6 +146,7 @@ def test_standard_parallels(self): self.assertEqual(sp, [73.0, 42.0]) +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") class TestPolar(unittest.TestCase): def setUp(self): self.dataset = io_gdal.GeoDataset(get_path('Mars_MGS_MOLA_ClrShade_MAP2_90.0N0.0_POLA.tif')) @@ -171,6 +175,7 @@ def test_latlon_to_pixel(self): self.assertAlmostEqual(pixel[0], 0.0, 6) self.assertAlmostEqual(pixel[1], 0.0, 6) +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") class TestWriter(unittest.TestCase): def setUp(self): self.arr = np.random.random((100,100)) @@ -240,11 +245,3 @@ def tearDown(self): except: pass -class TestWithoutGdal(unittest.TestCase): - def test_without_gdal(self): - io_gdal.has_gdal = False - with self.assertRaises(ImportError): - io_gdal.GeoDataset('foo') - - def tearDown(self): - io_gdal.has_gdal = True diff --git a/plio/io/tests/test_io_spectral_profiler.py b/plio/io/tests/test_io_spectral_profiler.py index 0446607ee..6b5b39056 100644 --- a/plio/io/tests/test_io_spectral_profiler.py +++ b/plio/io/tests/test_io_spectral_profiler.py @@ -3,12 +3,15 @@ import unittest import pandas as pd +import pytest sys.path.insert(0, os.path.abspath('..')) from plio.examples import get_path from plio.io import io_spectral_profiler from plio.io.io_gdal import GeoDataset +from plio.io import gdal + class Test_Spectral_Profiler_IO(unittest.TestCase): @@ -19,7 +22,8 @@ def test_openspc(self): ds = io_spectral_profiler.Spectral_Profiler(self.examplefile) self.assertEqual(ds.nspectra, 38) self.assertEqual(ds.spectra[0].columns.tolist(), ['RAW', 'REF1', 'REF2', 'QA', 'RAD']) - + + @pytest.mark.skipif(gdal is None, reason="GDAL not installed") def test_read_browse(self): ds = io_spectral_profiler.Spectral_Profiler(self.examplefile) ds.open_browse() diff --git a/plio/io/tests/test_metadata.py b/plio/io/tests/test_metadata.py index 5b28ff56b..df63a0096 100644 --- a/plio/io/tests/test_metadata.py +++ b/plio/io/tests/test_metadata.py @@ -1,60 +1,65 @@ -import unittest - -from osgeo import osr - -osr.UseExceptions() +import pytest from plio.io import extract_metadata as em - - -class TestSRSProjectionExtraction(unittest.TestCase): - - def setUp(self): - self.wktsrs = 'PROJCS["Moon2000_Mercator180",GEOGCS["GCS_Moon_2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",180.0],PARAMETER["Standard_Parallel_1",0.0],UNIT["Meter",1.0]]' - self.wktsrs = 'PROJCS["Mercator",GEOGCS["GCS_Moon_2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",180.0],PARAMETER["Standard_Parallel_1",0.0],UNIT["Meter",1.0]]' - self.srs = em.extract_projstring(self.wktsrs) - - def test_generate_srs(self): - self.srs = em.extract_projstring(self.wktsrs) - - def test_false_easting(self): - e = em.get_false_easting(self.srs) - self.assertEqual(e, 0.0) - - def test_false_northing(self): - n = em.get_false_northing(self.srs) - self.assertEqual(n, 0.0) - - def test_projection_name(self): - name = em.get_projection_name(self.srs) - self.assertEqual(name, 'Mercator_2SP') - - def test_axes_extract(self): - smajor, sminor, invflattening = em.get_spheroid(self.srs) - self.assertEqual(smajor, 1737400.0) - self.assertEqual(sminor, 1737400.0) - self.assertEqual(invflattening, 0.0) - - def test_get_standard_parallels(self): - parallels = em.get_standard_parallels(self.srs) - self.assertEqual(parallels[0], 0.0) - self.assertEqual(parallels[1], 0.0) - - def test_get_central_meridian(self): - clon = em.get_central_meridian(self.srs) - self.assertEqual(clon, 180.0) - - def test_export_to_proj4(self): - """ - Check that proj4 is not supporting Moon2000_Mercator - """ - proj4 = self.srs.ExportToProj4() - self.assertEqual(proj4, '+proj=merc +lon_0=180 +lat_ts=0 +x_0=0 +y_0=0 +a=1737400 +b=1737400 +units=m +no_defs ') - - def test_scale_factor(self): - k = em.get_scale_factor(self.srs) - self.assertEqual(k, 1.0) - - def test_latitude_of_origin(self): - lo = em.get_latitude_of_origin(self.srs) - self.assertEqual(lo, 90.0) +from plio.io import gdal + +@pytest.fixture +def wkt_moon(): + wktsrs = 'PROJCS["Moon2000_Mercator180",GEOGCS["GCS_Moon_2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",180.0],PARAMETER["Standard_Parallel_1",0.0],UNIT["Meter",1.0]]' + return em.extract_projstring(wktsrs) + +@pytest.fixture +def srs_mars(): + wktsrs = 'PROJCS["Mercator",GEOGCS["GCS_Moon_2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",180.0],PARAMETER["Standard_Parallel_1",0.0],UNIT["Meter",1.0]]' + return em.extract_projstring(wktsrs) + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_false_easting(srs_mars): + e = em.get_false_easting(srs_mars) + assert e == 0.0 + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_false_northing(srs_mars): + n = em.get_false_northing(srs_mars) + assert n == 0.0 + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_projection_name(srs_mars): + name = em.get_projection_name(srs_mars) + assert name == 'Mercator_2SP' + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_axes_extract(srs_mars): + smajor, sminor, invflattening = em.get_spheroid(srs_mars) + assert smajor == 1737400.0 + assert sminor == 1737400.0 + assert invflattening == 0.0 + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_get_standard_parallels(srs_mars): + parallels = em.get_standard_parallels(srs_mars) + assert parallels[0] == 0.0 + assert parallels[1] == 0.0 + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_get_central_meridian(srs_mars): + clon = em.get_central_meridian(srs_mars) + assert clon == 180.0 + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_export_to_proj4(srs_mars): + """ + Check that proj4 is not supporting Moon2000_Mercator + """ + proj4 = srs_mars.ExportToProj4() + assert proj4 == '+proj=merc +lon_0=180 +lat_ts=0 +x_0=0 +y_0=0 +a=1737400 +b=1737400 +units=m +no_defs ' + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_scale_factor(srs_mars): + k = em.get_scale_factor(srs_mars) + assert k == 1.0 + +@pytest.mark.skipif(gdal is None, reason="GDAL not installed") +def test_latitude_of_origin(srs_mars): + lo = em.get_latitude_of_origin(srs_mars) + assert lo == 90.0