Commit e075204d authored by Laura, Jason R's avatar Laura, Jason R
Browse files

Adds CSM to the sensor interface.

parent 954adbaa
Loading
Loading
Loading
Loading
+48 −35
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@ try:
except Exception as exception:
    from autocnet.utils.utils import FailedImport
    isis = FailedImport(exception)
from knoten.csm import create_csm

from knoten.csm import create_csm, generate_ground_point, generate_image_coordinate
from csmapi import csmapi

# set up the logger file
log = logging.getLogger(__name__)
@@ -30,8 +32,8 @@ class BaseSensor:
    def __init__(self, data_path, dem):
        self.data_path = data_path
        self.dem = dem
        self.semi_major = dem.a
        self.semi_minor = dem.b
        self.semi_major = self.dem.a
        self.semi_minor = self.dem.c

    def _check_arg(self, x, y, z=None):
        if isinstance(x, collections.abc.Sequence) and isinstance(y, collections.abc.Sequence) and (isinstance(z, collections.abc.Sequence) or z is None):
@@ -79,6 +81,12 @@ class BaseSensor:
        else:
            return x_coords, y_coords
        
    def _flatten_results(self, x, results):
        if isinstance(x, (collections.abc.Sequence, np.ndarray)):
            return results
        else:
            return results[0]
        
    @abc.abstractmethod
    def lonlat2xyz(self, lon, lat, height):
        return 
@@ -99,6 +107,7 @@ class BaseSensor:
    def sampline2lonlat(sample, line):
        return


class ISISSensor(BaseSensor):
    """
    ISIS defaults to ocentric latitudes for everything.
@@ -226,12 +235,6 @@ class ISISSensor(BaseSensor):

        return self._flatten_results(x, results)

    def _flatten_results(self, x, results):
        if isinstance(x, (collections.abc.Sequence, np.ndarray)):
            return results
        else:
            return results[0]

    def _get_value(self, obj):
        """Returns *obj*, unless *obj* is of type pvl.collections.Quantity, in
        which case, the .value component of the object is returned."""
@@ -241,7 +244,8 @@ class ISISSensor(BaseSensor):
            return obj
    
    def xyz2sampline(self, x, y, z):
        lon, lat = xyz2oc(x, y, z, self.semi_major, self.semi_minor)
        # No ocentric conversion here. That conversion is handled in lonlat2sampline
        lon, lat = xyz2og(x, y, z, self.semi_major, self.semi_minor)
        return self.lonlat2sampline(lon, lat)

    def lonlat2sampline(self, lon, lat, allowoutside=False):
@@ -269,7 +273,6 @@ class ISISSensor(BaseSensor):
        # ISIS is expecting ocentric latitudes. Convert from ographic before passing.
        lonoc, latoc = og2oc(lon, lat, self.semi_major, self.semi_minor)
        res = self._point_info(lonoc, latoc, "ground", allowoutside=allowoutside)

        if isinstance(lon, (collections.abc.Sequence, np.ndarray)):
            samples, lines = np.asarray([[r["Sample"], r["Line"]] for r in res]).T
        else:
@@ -375,9 +378,10 @@ class ISISSensor(BaseSensor):

        return lons, lats   

    def lonlat2xyz(self, lon, lat, height=0):
        xyz = og2xyz(lon, lat, height, self.semi_major, self.semi_minor)
        return xyz
    def lonlat2xyz(self, lon, lat):
        sample, line = self.lonlat2sampline(lon, lat)
        return self.sampline2xyz(sample, line)

    
class CSMSensor(BaseSensor):
    """
@@ -392,14 +396,12 @@ class CSMSensor(BaseSensor):
        self.dem = dem

    def xyz2sampline(self, x, y, z):
        x_coords, y_coords, z_coords = self._check_args(x, y, z)
        x_coords, y_coords, z_coords = self._check_arg(x, y, z)
        results = []
        for coord in zip(x_coords, y_coords, z_coords):
            ecef = csmapi.EcefCoord(coord[0], coord[1], coord[2])
            imagept = self.sensor.groundToImage(ecef)
            results+=[imagept.sample, imagept.line]

        return self._flatten_results(results)
            imagept = generate_image_coordinate(coord, self.sensor)
            results+=[imagept.samp, imagept.line]
        return self._flatten_results(x_coords, results)

    def lonlat2sampline(self, lon, lat):
        """
@@ -423,13 +425,15 @@ class CSMSensor(BaseSensor):
        line: int
            lint of point
        """
        semi_major = self.dem.a
        semi_minor = self.dem.b

        x_coords, y_coords = self._check_args(lat, lon)
        heights = [self.get.get_height(i[0], i[1]) for i in zip(x_coords, y_coords)]
        x,y,z = og2xyz(lon, lat, heights, semi_major, semi_minor)
        return self.xyz2linesamp(x,y,z)
        x_coords, y_coords = self._check_arg(lon, lat)
        results = []
        for coord in zip(x_coords, y_coords):
            # get_height needs lat, lon ordering
            height = self.dem.get_height(coord[1], coord[0])
            x,y,z = og2xyz(lon, lat, height, self.semi_major, self.semi_minor)
            results += [x,y,z]
        results = self._flatten_results(lat, results)
        return self.xyz2sampline(x,y,z)
    
    def sampline2xyz(self, sample, line):
        """
@@ -459,18 +463,27 @@ class CSMSensor(BaseSensor):
        x,y,z : int(s)
            x,y,z coordinates of the point
        """
        semi_major = self.dem.a
        semi_minor = self.dem.b
        sample, line = self._check_arg(sample, line)
        results = []
        # CSMAPI using line/sample ordering. Swap here.
        for coord in zip(line, sample):
            # self.dem is an autocnet surface model dem that has a GeoDataset attribute
            bcbf = generate_ground_point(self.dem, coord, self.sensor)
            results += [bcbf.x, bcbf.y, bcbf.z]

        image_coord = csmapi.ImageCoord(sample, line)
        pcoord = self.sensor.imageToGround(image_coord)
        return pcoord.x, pcoord.y, pcoord.z
        return self._flatten_results(sample, results)
    
    def sampline2lonlat(self, sample, line):
        x,y,z = self.sampline2xyz(sample, line)
        lon, lat = xyz2og(x, y, z, self.semi_major, self.semi_minor)
        return lon, lat
    
    def lonlat2xyz(self, lon, lat):
        sample, line = self.lonlat2sampline(lon, lat)
        print(sample, line)
        return self.sampline2xyz(sample, line)
    

def create_sensor(sensor_type, cam_path, dem=None):
    sensor_type = sensor_type.lower()
    sensor_classes = {
+136 −42
Original line number Diff line number Diff line
@@ -9,15 +9,19 @@ import numpy as np
import numpy.testing as npt

import autocnet.camera.sensor_model as sm
from autocnet.spatial.surface import GdalDem
from autocnet.examples import get_path
from autocnet.camera import sensor_model as sm
from autocnet.spatial.surface import EllipsoidDem, GdalDem
from knoten.surface import EllipsoidDem, GdalDem

@pytest.fixture
def ctx_path():
def ctx_ellipsoid_path():
    return get_path('G02_019154_1800_XN_00N133W.ellipsoid.crop.cub')

@pytest.fixture
def ctx_dem_path():
    return get_path('G02_019154_1800_XN_00N133W.crop.cub')


@pytest.fixture
def remote_mola_height_dem():
    path = '/vsicurl/https://asc-mars.s3.us-west-2.amazonaws.com/basemaps/Mars_MGS_MOLA_DEM_mosaic_global_463m.tif'
@@ -34,20 +38,30 @@ def isis_mola_radius_dem():

@pytest.fixture
def ellipsoid():
    return EllipsoidDem(semi_major=3396190, semi_minor=3396190)
    return EllipsoidDem(semi_major=3396190, semi_minor=3376200)

@pytest.fixture
def ctx_isis_sensor(ctx_path, isis_mola_radius_dem):
    return sm.ISISSensor(ctx_path, isis_mola_radius_dem)
def ctx_ellipsoid_isis_sensor(ctx_ellipsoid_path, ellipsoid):
    return sm.ISISSensor(ctx_ellipsoid_path, ellipsoid)

@pytest.fixture
def ctx_csm_sensor(ctx_path, isis_mola_radius_dem):
    return sm.CSMSensor(ctx_path, isis_mola_radius_dem)
def ctx_dem_isis_sensor(ctx_dem_path, isis_mola_radius_dem):
    return sm.ISISSensor(ctx_dem_path, isis_mola_radius_dem)

@pytest.fixture
def ctx_ellipsoid_csm_sensor(ctx_ellipsoid_path, ellipsoid):
    return sm.CSMSensor(ctx_ellipsoid_path, ellipsoid)

@pytest.fixture
def base_sensor(ellipsoid):
    return sm.BaseSensor(None,ellipsoid)

@pytest.fixture
def ctx_dtm_csm_sensor(ctx_dem_path, isis_mola_radius_dem):
    return sm.CSMSensor(ctx_dem_path, isis_mola_radius_dem)

# @pytest.fixture
# def isis_dtm_csm_sensor(ctx_path)
class TestBaseSensor():

    def test__check_args(self, base_sensor):
@@ -100,43 +114,80 @@ class TestBaseSensor():
            base_sensor._check_arg({10, 20}, {10, 20})


class TestIsisSensor():
class TestIsisSensor_DTM():

    def test_raise_bad_coord_type(self, ctx_isis_sensor):
    def test_raise_bad_coord_type(self, ctx_dem_isis_sensor):
        with pytest.raises(ValueError):
            ctx_isis_sensor._point_info(10, 10, point_type='bogus')
            ctx_dem_isis_sensor._point_info(10, 10, point_type='bogus')

    def test_sampline2lonlat(self, ctx_isis_sensor):
        lon, lat = ctx_isis_sensor.sampline2lonlat(10.0, 10.0)
    def test_sampline2lonlat(self, ctx_dem_isis_sensor):
        lon, lat = ctx_dem_isis_sensor.sampline2lonlat(10.0, 10.0)
        assert lon == 226.76892358441
        assert lat == -0.31770729411217

    def test_sampline2xyz(self, ctx_isis_sensor):
        x, y, z = ctx_isis_sensor.sampline2xyz(10.0, 10.0)
    def test_sampline2xyz(self, ctx_dem_isis_sensor):
        x, y, z = ctx_dem_isis_sensor.sampline2xyz(10.0, 10.0)
        assert x == pytest.approx(-2327023.0983832)
        assert y == pytest.approx(-2475336.0552312)
        assert z == pytest.approx(-18838.904973497)

    def test_lonlat2sampline(self, ctx_isis_sensor):
        samp, line = ctx_isis_sensor.lonlat2sampline(226.8, -0.25)
    def test_lonlat2sampline(self, ctx_dem_isis_sensor):
        samp, line = ctx_dem_isis_sensor.lonlat2sampline(226.8, -0.25)
        assert samp == pytest.approx(450.47864761698)
        assert line == pytest.approx(638.5458457207)

    def test_xyz2sampline(self, ctx_isis_sensor):
    def test_xyz2sampline(self, ctx_dem_isis_sensor):
        x = -2327023.0983832
        y = -2475336.0552312
        z = -18838.904973497
        samp, line = ctx_isis_sensor.xyz2sampline(x,y,z)
        samp, line = ctx_dem_isis_sensor.xyz2sampline(x,y,z)
        assert samp == pytest.approx(10.0,6)
        assert line == pytest.approx(10.0,6)

    def test_lonlat2xyz(self, ctx_isis_sensor):
        x, y, z = ctx_isis_sensor.lonlat2xyz(226.76892358441, -0.31770729411217)
    def test_lonlat2xyz(self, ctx_dem_isis_sensor):
        x, y, z = ctx_dem_isis_sensor.lonlat2xyz(226.76892358441, -0.31770729411217)
        assert x == pytest.approx(-2327023.0983832)
        assert y == pytest.approx(-2475336.0552312)
        assert z == pytest.approx(-18838.904973497)


class TestIsisSensor_Ellipsoid():

    def test_raise_bad_coord_type(self, ctx_ellipsoid_isis_sensor):
        with pytest.raises(ValueError):
            ctx_ellipsoid_isis_sensor._point_info(10, 10, point_type='bogus')

    def test_sampline2lonlat(self, ctx_ellipsoid_isis_sensor):
        lon, lat = ctx_ellipsoid_isis_sensor.sampline2lonlat(10.0, 10.0)
        assert lon == 226.76918706968
        assert lat == -0.32147903205638

    def test_sampline2xyz(self, ctx_ellipsoid_isis_sensor):
        x, y, z = ctx_ellipsoid_isis_sensor.sampline2xyz(10.0, 10.0)
        assert x == pytest.approx(-2326146.9211099)
        assert y == pytest.approx(-2474426.8363236)
        assert z == pytest.approx(-18831.814749997)

    def test_lonlat2sampline(self, ctx_ellipsoid_isis_sensor):
        samp, line = ctx_ellipsoid_isis_sensor.lonlat2sampline(226.76918706968,-0.32147903205638)
        assert samp == pytest.approx(10.0, abs=0.001)
        assert line == pytest.approx(10.0, abs=0.001)

    def test_xyz2sampline(self, ctx_ellipsoid_isis_sensor):
        x = -2326146.9211099
        y = -2474426.8363236
        z = -18831.814749997
        samp, line = ctx_ellipsoid_isis_sensor.xyz2sampline(x,y,z)
        assert samp == pytest.approx(10.0, abs=0.001)
        assert line == pytest.approx(10.0, abs=0.001)

    def test_lonlat2xyz(self, ctx_ellipsoid_isis_sensor):
        x, y, z = ctx_ellipsoid_isis_sensor.lonlat2xyz(226.76918706968,-0.32147903205638)
        assert x == pytest.approx(-2326146.9211099)
        assert y == pytest.approx(-2474426.8363236)
        assert z == pytest.approx(-18831.814749997)


class TestISIS(unittest.TestCase):

    def setUp(self) -> None:
@@ -251,28 +302,71 @@ class TestISIS(unittest.TestCase):
        npt.assert_allclose(np.array([goal_samp, 961.03569217]), samples)
        npt.assert_allclose(np.array([goal_line, 20.50009032]), lines)

class TestCsmSensor():
    def test_sampline2lonlat(self, ctx_csm_sensor):
        lon, lat = ctx_csm_sensor.sampline2lonlat(10.0, 10.0)
        assert lon == 226.76892358441
        assert lat == -0.31770729411217

    def test_sampline2xyz(self, ctx_csm_sensor):
        x, y, z = ctx_csm_sensor.sampline2xyz(10.0, 10.0)
        assert x == pytest.approx(-2327023.0983832)
        assert y == pytest.approx(-2475336.0552312)
        assert z == pytest.approx(-18838.904973497)

    def test_lonlat2sampline(self, ctx_csm_sensor):
        samp, line = ctx_csm_sensor.lonlat2sampline(226.8, -0.25)
        assert samp == pytest.approx(450.47864761698)
        assert line == pytest.approx(638.5458457207)

    def test_xyz2sampline(self, ctx_csm_sensor):
class TestCsmSensor_Ellipsoid():
    def test_sampline2lonlat(self, ctx_ellipsoid_csm_sensor):
        # No tracking of a crop occurs, so manually offset x,y
        lon, lat = ctx_ellipsoid_csm_sensor.sampline2lonlat(710.0, 310.0)
        assert lon == pytest.approx(226.76918706968, abs=0.01)
        assert lat == pytest.approx(-0.32147903205638, abs=0.01)

    def test_sampline2xyz(self, ctx_ellipsoid_csm_sensor):
        # No tracking of a crop occurs, so manually offset x,y
        x, y, z = ctx_ellipsoid_csm_sensor.sampline2xyz(710.0, 310.0)
        assert x == pytest.approx(-2326146.9211099, abs=5)
        assert y == pytest.approx(-2474426.8363236, abs=5)
        assert z == pytest.approx(-18831.814749997, abs=7)
    
    # How is this point off by a whole pixel?!?!?! 
    def test_lonlat2sampline(self, ctx_ellipsoid_csm_sensor):
        samp, line = ctx_ellipsoid_csm_sensor.lonlat2sampline(226.76918706968, 
                                                              -0.32147903205638)
        assert samp == pytest.approx(710.0, abs=1.0)
        assert line == pytest.approx(310.0, abs=1.0)

    def test_xyz2sampline(self, ctx_ellipsoid_csm_sensor):
        x = -2326146.9211099
        y = -2474426.8363236
        z = -18831.814749997
        samp, line = ctx_ellipsoid_csm_sensor.xyz2sampline(x,y,z)
        assert samp == pytest.approx(710.0,6)
        assert line == pytest.approx(310.0,6)


class TestCsmSensor_DTM():
    def test_sampline2lonlat(self, ctx_dtm_csm_sensor):
        lon, lat = ctx_dtm_csm_sensor.sampline2lonlat(710, 310)
        assert lon == pytest.approx(226.76892358441, abs=0.001)
        assert lat == pytest.approx(-0.31770729411217, abs=0.001)
        
    def test_sampline2xyz(self, ctx_dtm_csm_sensor):
        # No tracking of a crop occurs, so manually offset x,y
        x, y, z = ctx_dtm_csm_sensor.sampline2xyz(710.0, 310.0)
        assert x == pytest.approx(-2327023.0983832, abs=4)
        assert y == pytest.approx(-2475336.0552312, abs=4)
        assert z == pytest.approx(-18838.904973497, abs=7)

    def test_lonlat2sampline(self, ctx_dtm_csm_sensor):
        # CSM Radius: 3397503 
        # ISIS Radius: 3397506.7524735
        # CSM XYZ:  -2325728.70656223 -2476649.5224272553 -14824.356384854329
        # ISIS XYZ: -2325731.2752827, -2476652.2578367, -14824.372758057
        samp, line = ctx_dtm_csm_sensor.lonlat2sampline(226.76892358441, 
                                                        -0.31770729411217)
        assert samp == pytest.approx(710.0, abs=1.0)
        assert line == pytest.approx(310.0, abs=1.0)

    def test_xyz2sampline(self, ctx_dtm_csm_sensor):
        x = -2327023.0983832
        y = -2475336.0552312
        z = -18838.904973497
        samp, line = ctx_csm_sensor.xyz2sampline(x,y,z)
        print(samp, line)
        assert samp == pytest.approx(10.0,6)
        assert line == pytest.approx(10.0,6)
        # Why is this a whole pixel?
        samp, line = ctx_dtm_csm_sensor.xyz2sampline(x,y,z)
        assert samp == pytest.approx(710.0,abs=1.0)
        assert line == pytest.approx(310.0,abs=1.0)

    def test_lonlat2xyz(self, ctx_dtm_csm_sensor):
        x, y, z = ctx_dtm_csm_sensor.lonlat2xyz(226.76892358441, -0.31770729411217)
        assert x == pytest.approx(-2327023.0983832)
        assert y == pytest.approx(-2475336.0552312)
        assert z == pytest.approx(-18838.904973497)
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ def compute_covariance(df, dem, latsigma, lonsigma, radsigma):
    df : pd.DataFrame
         with columns pointtype, adjustedY, and adjustedX
    
    dem : ~autocnet.spatial.surface.EllipsoidDem or ~autocnet.spatial.surface.GdalDem
    dem : knoten.surface.EllipsoidDem or ~autocnet.knoten.surface.GdalDem
          Digital Elevation Model (DEM) object described the target body

    latsigma : int/float
+1 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ from shapely.geometry import Polygon

import pytest
from autocnet.control import control
from autocnet.spatial.surface import EllipsoidDem
from knoten.surface import EllipsoidDem

def test_identify_potential_overlaps(controlnetwork, candidategraph):
    res = control.identify_potential_overlaps(candidategraph,
+4.55 MiB

File added.

Preview size limit exceeded, changes collapsed.

Loading