Commit 56f1b361 authored by Jesse Mapel's avatar Jesse Mapel Committed by jlaura
Browse files

To ISIS formatter (#172)

* stub to_isis formatter

* ISIS formatter test

* Non-working isis formatter

* in progress commit

* More in progress

* Moved find child frame to FrameNode

* Moved find child frame to FrameNode

* Moved last time dependent frame into transformation

* Removed old identity matrix

* To ISIS formatter now properly handling numpy

* Cleaned up rotation construction slightly

* Clean up from code review

* Added doc strings to formatters

* More docs clean up
parent e76d251a
Loading
Loading
Loading
Loading

ale/encoders.py

0 → 100644
+15 −0
Original line number Diff line number Diff line
import json
import numpy as np
import datetime

class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        elif isinstance(obj, np.ndarray):
            return obj.tolist()
        elif isinstance(obj, datetime.date):
            return obj.isoformat()
        return json.JSONEncoder.default(self, obj)
+1 −1
Original line number Diff line number Diff line
from . import usgscsm_formatter
from . import usgscsm_formatter, isis_formatter
+95 −0
Original line number Diff line number Diff line
import json

from ale.transformation import FrameNode
from ale.rotation import ConstantRotation, TimeDependentRotation
from ale.encoders import NumpyEncoder

def to_isis(driver):
    """
    Formatter to create ISIS sensor model meta data from a driver.

    Parameters
    ----------
    driver : Driver
        Concrete driver for the image that meta data is being generated for.

    Returns
    -------
    string
        The ISIS compatible meta data as a JSON encoded string.
    """
    meta_data = {}

    meta_data['CameraVersion'] = driver.sensor_model_version

    meta_data['NaifKeywords'] = driver.isis_naif_keywords

    j2000 = driver.frame_chain

    instrument_pointing = {}
    sensor_frame = j2000.find_child_frame(driver.sensor_frame_id)
    time_dependent_sensor_frame = j2000.last_time_dependent_frame_between(sensor_frame)
    if time_dependent_sensor_frame != j2000:
        forward_path, reverse_path = j2000.path_to(time_dependent_sensor_frame)
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        instrument_pointing['TimeDependentFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        time_dependent_rotation = j2000.rotation_to(time_dependent_sensor_frame)
        instrument_pointing['CkTableStartTime'] = time_dependent_rotation.times[0]
        instrument_pointing['CkTableEndTime'] = time_dependent_rotation.times[-1]
        instrument_pointing['CkTableOriginalSize'] = len(time_dependent_rotation.times)
        instrument_pointing['EphemerisTimes'] = time_dependent_rotation.times
        instrument_pointing['Quaternions'] = time_dependent_rotation.quats
    if time_dependent_sensor_frame != sensor_frame:
        forward_path, reverse_path = time_dependent_sensor_frame.path_to(sensor_frame)
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        instrument_pointing['ConstantFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        constant_rotation = time_dependent_sensor_frame.rotation_to(sensor_frame)
        instrument_pointing['ConstantRotation'] = constant_rotation.rotation_matrix()
    meta_data['InstrumentPointing'] = instrument_pointing

    body_rotation = {}
    target_frame = j2000.find_child_frame(driver.target_frame_id)
    time_dependent_target_frame = j2000.last_time_dependent_frame_between(target_frame)
    if time_dependent_target_frame != j2000:
        forward_path, reverse_path = j2000.path_to(time_dependent_target_frame)
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        body_rotation['TimeDependentFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        time_dependent_rotation = j2000.rotation_to(time_dependent_target_frame)
        body_rotation['CkTableStartTime'] = time_dependent_rotation.times[0]
        body_rotation['CkTableEndTime'] = time_dependent_rotation.times[-1]
        body_rotation['CkTableOriginalSize'] = len(time_dependent_rotation.times)
        body_rotation['EphemerisTimes'] = time_dependent_rotation.times
        body_rotation['Quaternions'] = time_dependent_rotation.quats
    if time_dependent_target_frame != target_frame:
        forward_path, reverse_path = time_dependent_target_frame.path_to(target_frame)
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        body_rotation['ConstantFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        constant_rotation = time_dependent_target_frame.rotation_to(target_frame)
        body_rotation['ConstantRotation'] = constant_rotation.rotation_matrix()
    meta_data['BodyRotation'] = body_rotation

    instrument_position = {}
    positions, velocities, times = driver.sensor_position
    instrument_position['SpkTableStartTime'] = times[0]
    instrument_position['SpkTableEndTime'] = times[-1]
    instrument_position['SpkTableOriginalSize'] = len(times)
    instrument_position['EphemerisTimes'] = times
    instrument_position['Positions'] = positions
    instrument_position['Velocities'] = velocities
    meta_data['InstrumentPosition'] = instrument_position

    sun_position = {}
    positions, velocities, times = driver.sun_position
    sun_position['SpkTableStartTime'] = times[0]
    sun_position['SpkTableEndTime'] = times[-1]
    sun_position['SpkTableOriginalSize'] = len(times)
    sun_position['EphemerisTimes'] = times
    sun_position['Positions'] = positions
    sun_position['Velocities'] = velocities
    meta_data['SunPosition'] = sun_position

    return json.dumps(meta_data, cls=NumpyEncoder)
+15 −1
Original line number Diff line number Diff line
import json
from ale.base.type_sensor import LineScanner, Framer
from ale.encoders import NumpyEncoder

def to_usgscsm(driver):
    """
    Formatter to create USGSCSM meta data from a driver.

    Parameters
    ----------
    driver : Driver
        Concrete driver for the image that meta data is being generated for.

    Returns
    -------
    string
        The USGSCSM compatible meta data as a JSON encoded string.
    """
    isd_data = {}

    # exterior orientation
@@ -88,4 +102,4 @@ def to_usgscsm(driver):
        raise Exception('No CSM sensor model name found!')

    # Convert to JSON object
    return json.dumps(isd_data)
    return json.dumps(isd_data, cls=NumpyEncoder)
+13 −7
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ class ConstantRotation:
        """
        self.source = source
        self.dest = dest
        self.quat = quat
        self.quat = np.asarray(quat)

    @property
    def quat(self):
@@ -54,7 +54,13 @@ class ConstantRotation:
                   The new quaternion as an array.
                   The quaternion must be in scalar last format (x, y, z, w).
        """
        self._rot = Rotation.from_quat(new_quat)
        self._rot = Rotation.from_quat(np.asarray(new_quat))

    def rotation_matrix(self):
        """
        The rotation matrix representation of the constant rotation
        """
        return self._rot.as_dcm()

    def inverse(self):
        """
@@ -77,7 +83,7 @@ class ConstantRotation:
                Another rotation object, it can be constant or time dependent.
        """
        if self.source != other.dest:
            raise ValueError("Destination frame of first rotation is not the same as source frame of second rotation.")
            raise ValueError("Destination frame of first rotation {} is not the same as source frame of second rotation {}.".format(other.dest, self.source))
        if isinstance(other, ConstantRotation):
            new_rot = self._rot * other._rot
            return ConstantRotation(new_rot.as_quat(), other.source, self.dest)
@@ -119,8 +125,8 @@ class TimeDependentRotation:
        """
        self.source = source
        self.dest = dest
        self.quats = quats
        self.times = times
        self.quats = np.asarray(quats)
        self.times = np.asarray(times)

    @property
    def quats(self):
@@ -142,7 +148,7 @@ class TimeDependentRotation:
                    The new quaternions as a 2d array. The quaternions must be
                    in scalar last format (x, y, z, w).
        """
        self._rots = Rotation.from_quat(new_quats)
        self._rots = Rotation.from_quat(np.asarray(new_quats))

    def inverse(self):
        """
@@ -169,7 +175,7 @@ class TimeDependentRotation:
                Another rotation object, it can be constant or time dependent.
        """
        if self.source != other.dest:
            raise ValueError("Destination frame of first rotation is not the same as source frame of second rotation.")
            raise ValueError("Destination frame of first rotation {} is not the same as source frame of second rotation {}.".format(other.dest, self.source))
        if isinstance(other, ConstantRotation):
            return TimeDependentRotation((self._rots * other._rot).as_quat(), self.times, other.source, self.dest)
        elif isinstance(other, TimeDependentRotation):
Loading