diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b34bd9d0a9a19288262c15eea8ee491ebe27ea..ab23f36f0fd60aa7f94bc0511818a1f3a90414fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ release. ## [Unreleased] ### Fixed +- Tests for gdal > 3 and pvl > 1.0. This includes fixing the `k` value on the MOLA polar stereographic test data and updating the proj string for GDAL > 3 (new fields are included). +- Conditional GDAL import to support gdal > 3.0 +- `generate_isis_serial` to work on cubes that have been run through `jigsaw` by removing the custom `SerialNumberDecoder`. Fixes [#194](https://github.com/DOI-USGS/plio/issues/194) - Updated `create_pvl_header()` to add a newline (`\n`) character to the end of the pvl string. This ensures that the control networks can be written, then read back in using pvl 1.3.0 [#193](https://github.com/USGS-Astrogeology/plio/pull/193) ## [1.5.3]() diff --git a/environment.yml b/environment.yml index 37ea027dbec1bc9c58c16bdee3854cfb6a0c4b49..233b9ccab294e21cdb5acfbfb05c83e43aa36581 100644 --- a/environment.yml +++ b/environment.yml @@ -1,10 +1,8 @@ name: plio channels: - conda-forge - - usgs-astrogeology dependencies: - - libgdal < 3 - - gdal < 3 + - gdal - numpy - pyproj - h5py diff --git a/plio/examples/CTX/ctx.pvl b/plio/examples/CTX/ctx.pvl index b062798ad7043009e8c98e607ea39122b6b80c18..a44e44c7fe911faead77e4570cb190b9c7d8d4d3 100644 --- a/plio/examples/CTX/ctx.pvl +++ b/plio/examples/CTX/ctx.pvl @@ -67,6 +67,8 @@ Object = IsisCube End_Group End_Object +# Jigged + Object = Label Bytes = 65536 End_Object diff --git a/plio/examples/Projections/Mars_MGS_MOLA_ClrShade_MAP2_90.0N0.0_POLA.tif b/plio/examples/Projections/Mars_MGS_MOLA_ClrShade_MAP2_90.0N0.0_POLA.tif index e47b318148f7c268ebf1aa4e20ee7051695bf24c..eed8e641775cca2fcef4ab8dba81782982a2d73c 100644 Binary files a/plio/examples/Projections/Mars_MGS_MOLA_ClrShade_MAP2_90.0N0.0_POLA.tif and b/plio/examples/Projections/Mars_MGS_MOLA_ClrShade_MAP2_90.0N0.0_POLA.tif differ diff --git a/plio/io/__init__.py b/plio/io/__init__.py index 7c0f093a632bf35ac26b4a177185132733ec5727..b67fa436d6680e82b006dbb2b58874de3be9cbcf 100644 --- a/plio/io/__init__.py +++ b/plio/io/__init__.py @@ -1,25 +1,13 @@ # Conditional imports for GDAL import importlib import warnings -import sys try: - gdal = importlib.util.find_spec('gdal') - ogr = importlib.util.find_spec('osgeo.ogr') - osr = importlib.util.find_spec('osr') - - gdal = gdal.loader.load_module() - ogr = ogr.loader.load_module() - osr = osr.loader.load_module() - gdal.UseExceptions() + gdal = importlib.import_module('osgeo.gdal') + osr = importlib.import_module('osgeo.osr') + ogr = importlib.import_module('osgeo.ogr') except: - try: - gdal = importlib.util.find_spec('osgeo.gdal') - gdal = gdal.loader.load_module() - except: - gdal = None - ogr = None - osr = None + gdal = osr = ogr = None def conditional_gdal(func): def has_gdal(*args, **kwargs): diff --git a/plio/io/extract_metadata.py b/plio/io/extract_metadata.py index 6d94c2a4b2744c0dffacd7dfc6c484959a90877c..ea1e3706e385149828af42bc90a6363931909f08 100644 --- a/plio/io/extract_metadata.py +++ b/plio/io/extract_metadata.py @@ -1,5 +1,6 @@ -from plio.io import osr, conditional_gdal - + +from plio.io import conditional_gdal, osr + import_options = ['ImportFromWkt', 'ImportFromProj4', 'ImportFromEPSG', 'ImportFromUSGS', 'ImportFromXML'] diff --git a/plio/io/io_db.py b/plio/io/io_db.py index 988ec589fed803328d139a578fff260b7fc972d1..5903484017356fa4127e75c9911779b44ad1cb91 100644 --- a/plio/io/io_db.py +++ b/plio/io/io_db.py @@ -1,9 +1,10 @@ from sqlalchemy import Column, Integer, String, create_engine, orm -from sqlalchemy.ext import declarative +from sqlalchemy.orm import declarative_base +from sqlalchemy import MetaData from plio.sqlalchemy_json.alchemy import NestedJsonObject -Base = declarative.declarative_base() +Base = declarative_base() def setup_db_session(db): @@ -21,8 +22,8 @@ def setup_db_session(db): A SQLAlchemy session object """ engine = create_engine('sqlite:///{}'.format(db)) - Base.metadata.bind = engine - Base.metadata.create_all() + meta = MetaData() + meta.create_all(engine) return orm.sessionmaker(bind=engine)() diff --git a/plio/io/io_gdal.py b/plio/io/io_gdal.py index fc4222a7c52f8821c66db9d8b68e034653c19fb0..749fa405ad5f2d7d7d2f4286053cc7ee61046d0e 100644 --- a/plio/io/io_gdal.py +++ b/plio/io/io_gdal.py @@ -7,11 +7,9 @@ import affine import numpy as np import pvl - -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 +from plio.io import gdal, ogr, osr, extract_metadata, conditional_gdal NP2GDAL_CONVERSION = { "byte": 1, diff --git a/plio/io/isis_serial_number.py b/plio/io/isis_serial_number.py index faba3bf52d9cd7b4b7e6293969f2199e61bb933e..725e9a184ce3f31f5eafcd87e77a9c4e136ff56e 100644 --- a/plio/io/isis_serial_number.py +++ b/plio/io/isis_serial_number.py @@ -1,3 +1,4 @@ +import datetime import warnings import pvl @@ -71,7 +72,7 @@ def generate_serial_number(label): The ISIS compatible serial number """ if not isinstance(label, PVLModule): - label = pvl.load(label, decoder=SerialNumberDecoder()) + label = pvl.load(label) # Get the translation information translation = get_isis_translation(label) @@ -97,82 +98,11 @@ def generate_serial_number(label): serial_entry = search_translation[serial_entry] elif '*' in search_translation.keys() and search_translation['*'] != '*': serial_entry = search_translation['*'] + if isinstance(serial_entry, datetime.datetime): + # PVL returns datetime objects now. Convert these to string and strip trailing zeros on microseconds. + serial_entry = serial_entry.strftime('%Y-%m-%dT%H:%M:%S.%f').rstrip('0') serial_number.append(serial_entry) except: pass - + return '/'.join(serial_number) - - -class SerialNumberDecoder(pvl.decoder.PVLDecoder): - """ - A PVL Decoder class to handle cube label parsing for the purpose of creating a valid ISIS - serial number. Inherits from the PVLDecoder in planetarypy's pvl module. - """ - - def decode_simple_value(self, value: str): - """Returns a Python object based on *value*, assuming - that *value* can be decoded as a PVL Simple Value:: - - <Simple-Value> ::= (<Numeric> | <String>) - - Modified from https://pvl.readthedocs.io/en/stable/_modules/pvl/decoder.html#PVLDecoder.decode_simple_value - Modification entails stripping datetime from list of functions. - """ - for d in ( - self.decode_quoted_string, - self.decode_non_decimal, - self.decode_decimal, - ): - try: - return d(value) - except ValueError: - pass - - if value.casefold() == self.grammar.none_keyword.casefold(): - return None - - if value.casefold() == self.grammar.true_keyword.casefold(): - return True - - if value.casefold() == self.grammar.false_keyword.casefold(): - return False - - return self.decode_unquoted_string(value) - - def decode_unquoted_string(self, value: str) -> str: - """Returns a Python ``str`` if *value* can be decoded - as an unquoted string, based on this decoder's grammar. - Raises a ValueError otherwise. - - Modified from: https://pvl.readthedocs.io/en/stable/_modules/pvl/decoder.html#PVLDecoder.decode_unquoted_string - Modification entails removal of decode_datetime call - """ - for coll in ( - ("a comment", chain.from_iterable(self.grammar.comments)), - ("some whitespace", self.grammar.whitespace), - ("a special character", self.grammar.reserved_characters), - ): - for item in coll[1]: - if item in value: - raise ValueError( - "Expected a Simple Value, but encountered " - f'{coll[0]} in "{self}": "{item}".' - ) - - agg_keywords = self.grammar.aggregation_keywords.items() - for kw in chain.from_iterable(agg_keywords): - if kw.casefold() == value.casefold(): - raise ValueError( - "Expected a Simple Value, but encountered " - f'an aggregation keyword: "{value}".' - ) - - for es in self.grammar.end_statements: - if es.casefold() == value.casefold(): - raise ValueError( - "Expected a Simple Value, but encountered " - f'an End-Statement: "{value}".' - ) - - return str(value) diff --git a/plio/io/tests/test_io_bae.py b/plio/io/tests/test_io_bae.py index 4031703f49552e2799db35ed82fbad5bb0b9a9a6..11bffd98e5516ded0683824abb5879fedddb49d7 100644 --- a/plio/io/tests/test_io_bae.py +++ b/plio/io/tests/test_io_bae.py @@ -3,7 +3,7 @@ import os import numpy as np import pandas as pd -from pandas.util.testing import assert_frame_equal +from pandas.testing import assert_frame_equal from plio.io.io_bae import socetset_keywords_to_dict, read_gpf, save_gpf, read_ipf, save_ipf from plio.examples import get_path diff --git a/plio/io/tests/test_io_gdal.py b/plio/io/tests/test_io_gdal.py index 0423ca3f74d26d502c12162755f521eef5496200..a8bc23d04ad5934effd1c934ada79163e7b4e26a 100644 --- a/plio/io/tests/test_io_gdal.py +++ b/plio/io/tests/test_io_gdal.py @@ -39,7 +39,7 @@ class TestMercator(unittest.TestCase): """ def test_scale(self): - self.assertEqual(self.dataset.scale, ('Meter', 1.0)) + self.assertEqual(self.dataset.scale, ('metre', 1.0)) def test_xy_extent(self): xy_extent = self.dataset.xy_extent @@ -231,13 +231,17 @@ class TestWriter(unittest.TestCase): DATUM["Moon_2000", SPHEROID["Moon_2000_IAU_IAG",1737400,0]], PRIMEM["Reference_Meridian",0], - UNIT["Degree",0.017453292519943295]], - PROJECTION["Mercator_2SP"], + UNIT["degree",0.0174532925199433, + AUTHORITY["EPSG","9122"]]], + PROJECTION["Mercator_1SP"], PARAMETER["central_meridian",180], + PARAMETER["scale_factor",1], PARAMETER["false_easting",0], PARAMETER["false_northing",0], - PARAMETER["standard_parallel_1",0], - UNIT["Meter",1]]""" + UNIT["metre",1, + AUTHORITY["EPSG","9001"]], + AXIS["Easting",EAST], + AXIS["Northing",NORTH]]""" dataset = io_gdal.GeoDataset('test.tif') test_srs = dataset.spatial_reference.__str__() self.assertEqual(test_srs.split(), expected_srs.split()) diff --git a/plio/io/tests/test_metadata.py b/plio/io/tests/test_metadata.py index 6eef483cd94b475aeb771033fd3c316731b4aa67..08d41a650dab7e8f9290d9752630a17372fb8f2e 100644 --- a/plio/io/tests/test_metadata.py +++ b/plio/io/tests/test_metadata.py @@ -1,7 +1,7 @@ import pytest from plio.io import extract_metadata as em -from plio.io import gdal +from plio.io import gdal @pytest.fixture def wkt_moon(): @@ -52,7 +52,7 @@ def test_export_to_proj4(srs_mars): Check that proj4 is not supporting Moon2000_Mercator """ proj4 = srs_mars.ExportToProj4() - for element in '+proj=merc +lon_0=180 +lat_ts=0 +x_0=0 +y_0=0 +a=1737400 +b=1737400 +units=m +no_defs'.split(): + for element in '+proj=merc +lon_0=180 +lat_ts=0 +x_0=0 +y_0=0 +R=1737400 +units=m +no_defs'.split(): assert element in proj4 @pytest.mark.skipif(gdal is None, reason="GDAL not installed")