Question #4376

How to make maps of distant targets?

Added by Brian Burns about 1 year ago. Updated about 1 year ago.

Target version:
Software Version:
Test Reviewer:



I'm working on a project to make movies of the Voyager flybys - what I'd like to do is start from scratch for each target and build up a simple cylindrical map (or sinusoidal if that's better) as the target gets closer, using cam2map and adding it into a map for each channel. Then for the closeup images (which are often just one or two filters), it could pull the missing information from the map using map2cam and colorize all the images.

The pointing information for the Voyagers is not very accurate (~100 pixels in an 800x800 image), so I'd made a routine to center the circular targets on the screen, to handle the case where the whole target is in view. From this you could get the deltas for the two angles, which I'd like to use to adjust the pointing angles. As the targets get closer and fill the frame, it could use jigsaw to fit the pieces into place and update the camera pointing.

But for the distant images, is there a way to directly adjust the pointing angles using ISIS? I've tried using qtie to go through the process manually but it thought I was selecting a point outside of the target, due to the pointing errors, and deltack didn't seem like the right way to add angle deltas.

If necessary, I could try to read and update the angles directly using Python and SpiceyPy (a Python interface for SPICE), but wanted to see if I was missing something, or if you knew of a better approach.

Any pointers are much appreciated - thank you!


#1 Updated by Tammy Becker about 1 year ago

  • Category set to Applications
  • Status changed from New to Acknowledged

#2 Updated by Tammy Becker about 1 year ago

  • Assignee set to Tammy Becker

We are working through a suggested procedure.

One application to check out is 'center'. This application is used for centering disk images and creating a movie. Voyager data might offer a challenge due to a noisy background even after radiometric calibration and reseau removal.

I'm networking with Kris Becker on details with how to use the 'center' output information and apply deltack in order to adjust the camera pointing for the image.

Stay tuned.

#3 Updated by Brian Burns about 1 year ago


I'd been working on this for a while before discovering ISIS, so it's in a bit of a transition at the moment, but the project is here, with a few movies -

I'd gotten as far as centering targets with gaps and noise, and aligning channels in composite images, then had started working on the map projections when I found ISIS, and realized I would be reinventing several wheels...

So I started making a program to rotate the camera matrix by specified angles, but the results have been incorrect so far - it winds up rotating the camera too much and putting the target 10-200x outside of the image frame. I must be misunderstanding how rotations work, unless there's another problem somewhere.

A good view of Jupiter with 4 channels I've been experimenting with is C1465333-C1465339, e.g.

$ wget\
$ voy2isis from=C1465335.IMQ to=blue.cub
$ spiceinit from=blue.cub

# horizontal and vertical are in degrees - 
# the narrow angle camera FOV is 0.424 deg
$ camrotate from=blue.cub horizontal=-0.0196905395541 vertical=0.106166548884

# old quaternion

# new quaternion

$ cam2map from=blue.cub to=map.cub
# looks bad

I've also been testing camrotate by parsing the new quaternion and converting it to a rotation matrix, then multiplying it with the actual target location in ECLIPB1950 and projecting to pixelspace, which should end up right on top of the actual target, but it's far outside of the screen.

Anyway, here's the code for camrotate -

Thanks for looking into this :)

#include "Isis.h"

#include <iostream>

#include "Cube.h"
#include "Camera.h"
#include "CameraFactory.h"
#include "Table.h"
#include "History.h"

#include <SpiceUsr.h>

using namespace std;
using namespace Isis;

void IsisMain() {

  UserInterface &ui = Application::GetUserInterface();

  // get cube filename
  QString filename = ui.GetFileName("FROM");

  // get rotation amounts about x,y,z axes
  double horizontal = ui.GetDouble("HORIZONTAL"); // y-axis (NOT x-axis)
  double vertical = ui.GetDouble("VERTICAL"); // x-axis (NOT y-axis)
  double twist = ui.GetDouble("TWIST"); // z-axis

  // open cube file
  Cube cube;, "rw");

  // get camera
  Camera *cam = CameraFactory::Create(cube);

  // get instrument pointing table (set of quaternions with times)
  Table table = cam->instrumentRotation()->Cache("InstrumentPointing");

  // print the table
  cout << Table::toString(table);

  // there should just be one record in the table

  // get the record
  TableRecord record = table[0];

  // get the quaternion from the record
  // record[0] etc are TableFields
  double q0 = record[0];
  double q1 = record[1];
  double q2 = record[2];
  double q3 = record[3];

  // translate quaternion to a rotation matrix using SPICE q2m
  ConstSpiceDouble q[4] = {q0,q1,q2,q3};
  SpiceDouble M[3][3];
  q2m_c(q, M);

  // convert angles to radians
  horizontal *= rpd_c();
  vertical   *= rpd_c();
  twist      *= rpd_c();

  // get axes to rotate about
  //. must be a better way to do this
  ConstSpiceDouble axisX[] = {M[0][0],M[0][1],M[0][2]};
  ConstSpiceDouble axisY[] = {M[1][0],M[1][1],M[1][2]};
  ConstSpiceDouble axisZ[] = {M[2][0],M[2][1],M[2][2]};

  // get rotation matrices about the different axes using SPICE axisar
  SpiceDouble Rx[3][3];
  SpiceDouble Ry[3][3];
  SpiceDouble Rz[3][3];
  axisar_c ( axisX, vertical,   Rx );
  axisar_c ( axisY, horizontal, Ry );
  axisar_c ( axisZ, twist,      Rz );

  // rotate camera pointing matrix by rotation matrices
  mxm_c ( M, Rx, M );
  mxm_c ( M, Ry, M );
  mxm_c ( M, Rz, M );

  // translate rotation matrix back to a quaternion using SPICE m2q
  SpiceDouble qnew[4];
  m2q_c(M, qnew);

  // save the new quaternion to the table
  record[0] = qnew[0];
  record[1] = qnew[1];
  record[2] = qnew[2];
  record[3] = qnew[3];
  table.Update(record, 0); // 0 is the record number

  // print the new table
  cout << Table::toString(table);

  // save table to the cube file

  // add a history record for this command and parameters
  History hist = History("IsisCube");
  try {; // read history from cube, if it exists.
  catch (IException &e) {
    // if the history does not exist in the cube, 
    // the cube's write method will add it.

  // close cube file



<?xml version="1.0" encoding="UTF-8"?>

<application name="camrotate" 

      Rotate the camera pointing matrix associated with a cube.

      Camrotate rotates the camera pointing matrix associated with a cube about the x, y, and z axes by specified amounts.


    <change name="Brian Burns" date="2016-09-12">
      Original version

    <group name="Files">
      <parameter name="FROM">
          Input Cube
            This is the cube containing the pointing information table to update.

    <group name="Angles">
      <parameter name="HORIZONTAL">
          Horizontal offset in degrees
            This is the angular offset to apply to the camera pointing matrix by rotating about the y-axis.

      <parameter name="VERTICAL">
          Vertical offset in degrees
            This is the angular offset to apply to the camera pointing matrix by rotating about the x-axis.

      <parameter name="TWIST">
          Twist in degrees
            This is the amount to rotate the camera pointing matrix about the z-axis.



#4 Updated by Brian Burns about 1 year ago

Just an update - I realized I should getting the rotation axes from the rotation matrix as it's rotated:

  // x-axis rotation: vertical (pitch)
  ConstSpiceDouble axisX[] = {M[0][0],M[0][1],M[0][2]}; // get x-axis
  SpiceDouble Rx[3][3];
  axisar_c ( axisX, vertical, Rx ); // get rotation matrix Rx
  mxm_c ( M, Rx, M ); // rotate matrix M by Rx

  // y-axis rotation: horizontal (yaw)
  ConstSpiceDouble axisY[] = {M[1][0],M[1][1],M[1][2]};
  SpiceDouble Ry[3][3];
  axisar_c ( axisY, horizontal, Ry );
  mxm_c ( M, Ry, M );

  // z-axis rotation: twist (roll)
  ConstSpiceDouble axisZ[] = {M[2][0],M[2][1],M[2][2]};
  SpiceDouble Rz[3][3];
  axisar_c ( axisZ, twist, Rz );
  mxm_c ( M, Rz, M );

I think this is the right approach - it's close, but when I draw the grid on the image it's off-center from the target by ~5-20 pixels.

Then I realized I could just translate the image using ISIS translate to get the target to appear where ISIS expected it, then could use cam2map etc - this works great, except that as the target gets closer parts of the image will get cut off.

So, still working on it - will post progress here.

Also available in: Atom PDF