diff --git a/.gitignore b/.gitignore index a007feab071f496a016f150e1acd8fae57669cce..e5cc0d85013408f24b1e13a352e68896d480664a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ build/* +*.pyc +__pycache__/* +.pytest_cache/* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..492658052283c6cb92c487c36eda253eedce3ebb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.10) + +find_package(SWIG REQUIRED) +include(${SWIG_USE_FILE}) + +add_subdirectory(python) \ No newline at end of file diff --git a/csm.i b/csm.i new file mode 100644 index 0000000000000000000000000000000000000000..227014a0a20fe01dbf02cd7d7d9512e010e41c4f --- /dev/null +++ b/csm.i @@ -0,0 +1,25 @@ +%module(package="csmapi") csm +%{ + #include "csm.h" +%} + +%ignore CSM_UNKNOWN; +%ignore CSM_SENSOR_TYPE_UNKNOWN; +%ignore CSM_SENSOR_TYPE_EO; +%ignore CSM_SENSOR_TYPE_IR; +%ignore CSM_SENSOR_TYPE_MWIR; +%ignore CSM_SENSOR_TYPE_LWIR; +%ignore CSM_SENSOR_TYPE_SAR; +%ignore CSM_SENSOR_TYPE_EOIRSC; +%ignore CSM_SENSOR_MODE_UNKNOWN; +%ignore CSM_SENSOR_MODE_FRAME; +%ignore CSM_SENSOR_MODE_PULSE; +%ignore CSM_SENSOR_MODE_PB; +%ignore CSM_SENSOR_MODE_WB; +%ignore CSM_SENSOR_MODE_SPOT; +%ignore CSM_SENSOR_MODE_STRIP; +%ignore CSM_SENSOR_MODE_SCAN; +%ignore CSM_SENSOR_MODE_VIDEO; +%ignore CSM_SENSOR_MODE_BODY_POINTING; + +%include "csm.h" diff --git a/csmapi.i b/csmapi.i new file mode 100644 index 0000000000000000000000000000000000000000..1cec5ae97687a418fda404134a81e1a3e5d8cf6e --- /dev/null +++ b/csmapi.i @@ -0,0 +1,9 @@ +%module csmapi +%include "csm.i" +%include "version.i" +%include "ellipsoid.i" +%include "warning.i" +%include "isd.i" +%include "rastergm.i" +%include "plugin.i" +%include "model.i" \ No newline at end of file diff --git a/ellipsoid.i b/ellipsoid.i new file mode 100644 index 0000000000000000000000000000000000000000..b428582ae54f4ba1394405396a8b160965b3b8d2 --- /dev/null +++ b/ellipsoid.i @@ -0,0 +1,9 @@ +%module(package="csmapi") ellipsoid +%{ + #include "Ellipsoid.h" +%} + +%ignore CSM_WGS84_SEMI_MAJOR_AXIS; +%ignore CSM_WGS84_SEMI_MINOR_AXIS; + +%include "Ellipsoid.h" diff --git a/isd.i b/isd.i new file mode 100644 index 0000000000000000000000000000000000000000..f2e88869406042f612af630e1acff5d54071cdcf --- /dev/null +++ b/isd.i @@ -0,0 +1,38 @@ +%module(package="csmapi") isd +%{ + #include "Isd.h" +%} + +%ignore IMAGE_ID_PARAM; +%ignore IMAGE_ID_PARAM; +%ignore IMAGE_INDEX_PARAM; +%ignore LOGICAL_INDEX_PARAM; +%ignore MODEL_NAME_PARAM; + +%include <std_string.i> + +%include "Isd.h" +%pythoncode %{ + import json + import numpy as np + @classmethod + def loads(cls, stream): + isd = cls() + if not isinstance(stream, dict): + stream = json.loads(stream) + for k, v in stream.items(): + if isinstance(v, np.ndarray): + v = v.tolist() + if isinstance(v, list): + for i in v: + isd.addParam(k, str(i)) + isd.addParam(k, str(v)) + return isd + + @classmethod + def load(cls, fp): + return cls.loads(fp.read()) + + Isd.load = load + Isd.loads = loads +%} \ No newline at end of file diff --git a/model.i b/model.i new file mode 100644 index 0000000000000000000000000000000000000000..cdd9305071cc244ffd864b3ec2b497b0fabc0292 --- /dev/null +++ b/model.i @@ -0,0 +1,7 @@ +%module(package="csmapi") model +%{ + #include "Model.h" +%} + +%include Model.h + diff --git a/plugin.i b/plugin.i new file mode 100644 index 0000000000000000000000000000000000000000..590fe179b05e0cff560ad7395a249fa6dc56b4ce --- /dev/null +++ b/plugin.i @@ -0,0 +1,29 @@ +%module(package="csmapi") plugin +%{ + #include "Plugin.h" +%} + +%include <std_string.i> +%include <std_list.i> +%include typemaps.i +%include Plugin.h + + + + +%template(PluginList) std::list<const csm::Plugin*>; + +%{ + namespace swig { + template <> struct traits<csm::Plugin> + { + typedef pointer_category category; + static const char* type_name() + { + return "Plugin"; + } + }; + } +%} + + diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4addffdca46767a7ab5ef66abe84f5fa8d488697 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,41 @@ +# Include python +find_package(PythonLibs REQUIRED) +include_directories(${PYTHON_INCLUDE_PATH}) + + +set(CMAKE_SWIG_FLAGS "") +set_source_files_properties(../csmapi.i + PROPERTIES CPLUSPLUS ON) + +find_path(CSM_INCLUDE_DIR NAMES csm.h PATH_SUFFIXES csm) +find_library(CSM_LIBRARY NAMES csmapi) + +include_directories(${CSM_INCLUDE_DIR}) + +# Add swig module +swig_add_library(csmapi + LANGUAGE python + SOURCES ../csmapi.i) + + +swig_link_libraries(csmapi ${CSM_LIBRARY} ${PYTHON_LIBRARIES}) + +# Files to install with Python +set(PYTHON_INSTALL_FILES + ${CMAKE_CURRENT_BINARY_DIR}/csmapi.py + ${CMAKE_CURRENT_BINARY_DIR}/_csmapi.so) + +# Configure setup.py and copy to output directory +set(SETUP_PY_IN ${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in) +set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) +configure_file(${SETUP_PY_IN} ${SETUP_PY_OUT}) + +# Declare install target for python +#install(TARGETS swig_example +# COMMAND "${PYTHON_EXECUTABLE} setup.py" +# COMPONENT swig-python) + +# Install target to call setup.py +add_custom_target(install-python + DEPENDS _csmapi +COMMAND python ${SETUP_PY_OUT} install) diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000000000000000000000000000000000000..1494b0d525a07891e713c8e265f7e343dd27aeee --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,14 @@ +import setuptools.command.install +import shutil +from distutils.sysconfig import get_python_lib + +if __name__ == '__main__': + setuptools.setup( + name='csmapi', + version='0.1.0', + py_modules=['csmapi'], + package_data={'csmapi':['_csmapi.so']}, + license='UnLicense', + author='jlaura', + author_email='jlaura@usgs.gov' +) diff --git a/rastergm.i b/rastergm.i new file mode 100644 index 0000000000000000000000000000000000000000..64b658f189b0f1afcd828e901b99a5a6e816cc99 --- /dev/null +++ b/rastergm.i @@ -0,0 +1,8 @@ +%module(package="csmapi") rastergm +%{ + #include "RasterGM.h" +%} + +%ignore CSM_RASTER_FAMILY; + +%include RasterGM.h \ No newline at end of file diff --git a/tests/test_functional.py b/tests/test_functional.py new file mode 100644 index 0000000000000000000000000000000000000000..0746b1f646cb93438af391b9daf7cbe3b8dda0f0 --- /dev/null +++ b/tests/test_functional.py @@ -0,0 +1,48 @@ +import ctypes +from distutils import dir_util +import json +import os + +import pytest + +import csmapi + +# Load a plugin with CSM compliant sensors +lib = ctypes.CDLL('/data/big/github/CSM-CameraModel/build/libusgscsm.so') + +@pytest.fixture +def datadir(tmpdir, request): + ''' + Fixture responsible for searching a folder with the same name of test + module and, if available, moving all contents to a temporary directory so + tests can use them freely. + ''' + filename = request.module.__file__ + test_dir, _ = os.path.splitext(filename) + + if os.path.isdir(test_dir): + dir_util.copy_tree(test_dir, str(tmpdir)) + return tmpdir + +@pytest.fixture +def isd(datadir): + with open(datadir.join('simpleFramerISD.json')) as f: + i = csmapi.Isd.load(f) + return i + + +@pytest.fixture +def plugin(): + plugin = csmapi.Plugin.findPlugin('UsgsAstroFramePluginCSM') + return plugin + +def test_isd_to_model_to_ground(isd, plugin): + model_name = "USGS_ASTRO_FRAME_SENSOR_MODEL" + assert plugin.canModelBeConstructedFromISD(isd, model_name) + assert plugin.canISDBeConvertedToModelState(isd, model_name) + + model = plugin.constructModelFromISD(isd, model_name) + assert model.getVersion().version() == '0.1.0' + print(dir(model)) + assert False + \ No newline at end of file diff --git a/tests/test_functional/simpleFramerISD.json b/tests/test_functional/simpleFramerISD.json new file mode 100644 index 0000000000000000000000000000000000000000..8e4f7ebf41509e3650ba9fee69708d594f0d7ede --- /dev/null +++ b/tests/test_functional/simpleFramerISD.json @@ -0,0 +1,96 @@ +{ + "boresight": [ + 0.0, + 0.0, + 1.0 + ], + "ccd_center": [ + 7.5, + 7.5 + ], + "ephemeris_time": 100.0, + "focal_length": 500, + "focal_length_epsilon": 1.0, + "ifov": 6.0, + "model_name": "UsgsAstroFramePluginCSM", + "spacecraft_name": "TEST_CRAFT", + "instrument_id": "TEST_SENSOR", + "target_name": "TEST_BALL", + "pixel_pitch": 0.1, + "itrans_line": [ + 0.0, + 0.0, + 10 + ], + "itrans_sample": [ + 0.0, + 10, + 0.0 + ], + "transx": [ + 0.0, + 0.1, + 0.0 + ], + "transy": [ + 0.0, + 0.0, + 0.1 + ], + "min_elevation": -1, + "max_elevation": 1, + "nlines": 16, + "nsamples": 16, + "original_half_lines": 8.0, + "original_half_samples": 8.0, + "omega": 0, + "phi": 0, + "kappa": 0, + "semi_major_axis": 10, + "semi_minor_axis": 10, + "transx": [ + 0.0, + 0.1, + 0.0 + ], + "transy": [ + 0.0, + 0.0, + 0.1 + ], + "x_sensor_origin": 1000, + "y_sensor_origin": 0, + "z_sensor_origin": 0, + "x_sensor_velocity": 1, + "y_sensor_velocity": 0, + "z_sensor_velocity": 0, + "x_sun_position": 100, + "y_sun_position": 100, + "z_sun_position": 0, + "odt_x": [ + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "odt_y": [ + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "starting_detector_line": 0.0, + "starting_detector_sample": 0.0 +} diff --git a/tests/test_plugin.py b/tests/test_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..e3424316b511e414bca63a2dc7f876bcfb2edd37 --- /dev/null +++ b/tests/test_plugin.py @@ -0,0 +1,34 @@ +import pytest + +import csmapi +import ctypes + +# Load a plugin with CSM compliant sensors +lib = ctypes.CDLL('/data/big/github/CSM-CameraModel/build/libusgscsm.so') + +@pytest.fixture +def plugin(): + return csmapi.Plugin + +@pytest.fixture +def model(plugin): + pl = plugin.findPlugin('UsgsAstroFramePluginCSM') + return pl + +def test_plugin_size(plugin): + plugin_list = plugin.getList() + assert len(plugin_list) == 2 + +@pytest.mark.parametrize("plugin, index, expected_name", + [(plugin(), 0, 'UsgsAstroFramePluginCSM'), + (plugin(), 1, 'USGS_ASTRO_LINE_SCANNER_PLUGIN')]) +def test_plugin_name(plugin, index, expected_name): + pl = plugin.getList()[index] + plugin_name = pl.getPluginName() + assert plugin_name == expected_name + +def test_model_getNumModels(model): + assert model.getNumModels() == 1 + +def test_model_getModelName(model): + assert model.getModelName(0) == 'USGS_ASTRO_FRAME_SENSOR_MODEL' \ No newline at end of file diff --git a/usgs.i b/usgs.i new file mode 100644 index 0000000000000000000000000000000000000000..4520657b544dcdad16def98abfcf87f07fdb91b6 --- /dev/null +++ b/usgs.i @@ -0,0 +1,6 @@ +%module(package="csmapi") usgs +%{ + #include "UsgsAstroLsPlugin.h" +%} + +%import "UsgsAstroLsPlugin.h" \ No newline at end of file diff --git a/version.i b/version.i new file mode 100644 index 0000000000000000000000000000000000000000..e775da1e5755d60d2c8833b2b17fbce2c6d31a48 --- /dev/null +++ b/version.i @@ -0,0 +1,7 @@ +%module(package="csmapi") version +%{ + #include "Version.h" +%} + +%include <std_string.i> +%include "Version.h" diff --git a/warning.i b/warning.i new file mode 100644 index 0000000000000000000000000000000000000000..04e85d4cc0b520b15ff4cf90fff77b58bccc6fd0 --- /dev/null +++ b/warning.i @@ -0,0 +1,6 @@ +%module(package="csmapi") warning +%{ + #include "Warning.h" +%} + +%include Warning.h \ No newline at end of file