Index: /trunk/Ohana/src/addstar/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/addstar/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/ConfigInit.c	(revision 25757)
@@ -180,5 +180,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/addstar/src/ConfigInit_skycells.c
===================================================================
--- /trunk/Ohana/src/addstar/src/ConfigInit_skycells.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/ConfigInit_skycells.c	(revision 25757)
@@ -55,5 +55,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/addstar/src/addstar.c
===================================================================
--- /trunk/Ohana/src/addstar/src/addstar.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/addstar.c	(revision 25757)
@@ -25,5 +25,5 @@
   options = args (argc, argv, options);
 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   if (sky == NULL) {
       fprintf (stderr, "ERROR: unable to load sky table data\n");
Index: /trunk/Ohana/src/addstar/src/addstard.c
===================================================================
--- /trunk/Ohana/src/addstar/src/addstard.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/addstard.c	(revision 25757)
@@ -12,5 +12,5 @@
 
   /* store the sky table in a global for internal use */
-  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (ServerSky, CATDIR, "cpt");
 
Index: /trunk/Ohana/src/addstar/src/addstart.c
===================================================================
--- /trunk/Ohana/src/addstar/src/addstart.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/addstart.c	(revision 25757)
@@ -12,5 +12,5 @@
 
   /* store the sky table in a global for internal use */
-  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (ServerSky, CATDIR, "cpt");
 
Index: /trunk/Ohana/src/addstar/src/load2mass.c
===================================================================
--- /trunk/Ohana/src/addstar/src/load2mass.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/load2mass.c	(revision 25757)
@@ -16,5 +16,5 @@
 
   // load the full sky description table:
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   
Index: /trunk/Ohana/src/addstar/src/sedstar.c
===================================================================
--- /trunk/Ohana/src/addstar/src/sedstar.c	(revision 25756)
+++ /trunk/Ohana/src/addstar/src/sedstar.c	(revision 25757)
@@ -15,5 +15,5 @@
   options = args_sedstar (argc, argv, options);
 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   
Index: /trunk/Ohana/src/delstar/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/delstar/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/delstar/src/ConfigInit.c	(revision 25757)
@@ -45,5 +45,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/delstar/src/delete_imagefile.c
===================================================================
--- /trunk/Ohana/src/delstar/src/delete_imagefile.c	(revision 25756)
+++ /trunk/Ohana/src/delstar/src/delete_imagefile.c	(revision 25757)
@@ -12,5 +12,5 @@
 
   /* load sky from correct table */
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
 
Index: /trunk/Ohana/src/delstar/src/delete_imagename.c
===================================================================
--- /trunk/Ohana/src/delstar/src/delete_imagename.c	(revision 25756)
+++ /trunk/Ohana/src/delstar/src/delete_imagename.c	(revision 25757)
@@ -15,5 +15,5 @@
 
   /* load sky from correct table */
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
 
Index: /trunk/Ohana/src/delstar/src/delete_times.c
===================================================================
--- /trunk/Ohana/src/delstar/src/delete_times.c	(revision 25756)
+++ /trunk/Ohana/src/delstar/src/delete_times.c	(revision 25757)
@@ -15,5 +15,5 @@
 
   /* load sky from correct table */
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
 
Index: /trunk/Ohana/src/dvomerge/src/dvomerge.c
===================================================================
--- /trunk/Ohana/src/dvomerge/src/dvomerge.c	(revision 25756)
+++ /trunk/Ohana/src/dvomerge/src/dvomerge.c	(revision 25757)
@@ -24,5 +24,5 @@
   // the first input define the photcode table & db layout
   sprintf (filename, "%s/Photcodes.dat", input1);
-  if (!LoadPhotcodes (filename, NULL)) {
+  if (!LoadPhotcodes (filename, NULL, FALSE)) {
     fprintf (stderr, "error loading photcode table %s\n", filename);
     exit (1);
@@ -30,5 +30,5 @@
   // save the photcodes in the output catdir
   sprintf (filename, "%s/Photcodes.dat", output);
-  if (!check_file_access (filename, TRUE, TRUE)) {
+  if (!check_file_access (filename, TRUE, TRUE, TRUE)) {
     fprintf (stderr, "error creating output catdir %s\n", output);
     exit (1);
@@ -40,12 +40,12 @@
 
   // load the sky table for the existing database
-  insky1 = SkyTableLoadOptimal (input1, NULL, NULL, SKY_DEPTH_HST, VERBOSE);
+  insky1 = SkyTableLoadOptimal (input1, NULL, NULL, FALSE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (insky1, input1, "cpt");
 
-  insky2 = SkyTableLoadOptimal (input2, NULL, NULL, SKY_DEPTH_HST, VERBOSE);
+  insky2 = SkyTableLoadOptimal (input2, NULL, NULL, FALSE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (insky2, input2, "cpt");
 
   // generate an output table populated at the desired depth
-  outsky = SkyTableLoadOptimal (output, NULL, GSCFILE, SKY_DEPTH, VERBOSE);
+  outsky = SkyTableLoadOptimal (output, NULL, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (outsky, output, "cpt");
 
@@ -117,5 +117,5 @@
   // save the output sky table copy
   sprintf (filename, "%s/SkyTable.fits", output);
-  check_file_access (filename, TRUE, VERBOSE);
+  check_file_access (filename, TRUE, TRUE, VERBOSE);
   if (!SkyTableSave (outsky, filename)) {
     fprintf (stderr, "ERROR: failed to save sky table for %s\n", output);
Index: /trunk/Ohana/src/dvosplit/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/dvosplit/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/dvosplit/src/ConfigInit.c	(revision 25757)
@@ -33,5 +33,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/dvosplit/src/dvosplit.c
===================================================================
--- /trunk/Ohana/src/dvosplit/src/dvosplit.c	(revision 25756)
+++ /trunk/Ohana/src/dvosplit/src/dvosplit.c	(revision 25757)
@@ -18,5 +18,5 @@
 
   // load the sky table for the existing database
-  sky = SkyTableLoadOptimal (CATDIR, NULL, NULL, SKY_DEPTH_HST, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, NULL, NULL, TRUE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
 
@@ -79,5 +79,5 @@
   // save sky table copy
   sprintf (filename, "%s/SkyTable.fits", CATDIR);
-  check_file_access (filename, TRUE, VERBOSE);
+  check_file_access (filename, TRUE, TRUE, VERBOSE);
   if (!SkyTableSave (sky, filename)) {
     fprintf (stderr, "ERROR: failed to save sky table for %s\n", CATDIR);
Index: /trunk/Ohana/src/gastro/src/getptolemy.c
===================================================================
--- /trunk/Ohana/src/gastro/src/getptolemy.c	(revision 25756)
+++ /trunk/Ohana/src/gastro/src/getptolemy.c	(revision 25757)
@@ -19,5 +19,5 @@
 
   /* load regions from GSC table, restrict to patch */
-  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, FALSE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   skylist = SkyListByPatch (sky, -1, &patch);
Index: /trunk/Ohana/src/gastro2/src/getptolemy.c
===================================================================
--- /trunk/Ohana/src/gastro2/src/getptolemy.c	(revision 25756)
+++ /trunk/Ohana/src/gastro2/src/getptolemy.c	(revision 25757)
@@ -21,5 +21,5 @@
 
   /* load regions from GSC table, restrict to patch */
-  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, FALSE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   skylist = SkyListByPatch (sky, -1, &patch);
Index: /trunk/Ohana/src/getstar/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/getstar/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/getstar/src/ConfigInit.c	(revision 25757)
@@ -34,5 +34,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/getstar/src/ConfigInit_coords.c
===================================================================
--- /trunk/Ohana/src/getstar/src/ConfigInit_coords.c	(revision 25756)
+++ /trunk/Ohana/src/getstar/src/ConfigInit_coords.c	(revision 25757)
@@ -35,5 +35,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/getstar/src/ConfigInit_extract.c
===================================================================
--- /trunk/Ohana/src/getstar/src/ConfigInit_extract.c	(revision 25756)
+++ /trunk/Ohana/src/getstar/src/ConfigInit_extract.c	(revision 25757)
@@ -35,5 +35,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/getstar/src/ConfigInit_overlaps.c
===================================================================
--- /trunk/Ohana/src/getstar/src/ConfigInit_overlaps.c	(revision 25756)
+++ /trunk/Ohana/src/getstar/src/ConfigInit_overlaps.c	(revision 25757)
@@ -35,5 +35,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, FALSE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/getstar/src/getstar.c
===================================================================
--- /trunk/Ohana/src/getstar/src/getstar.c	(revision 25756)
+++ /trunk/Ohana/src/getstar/src/getstar.c	(revision 25757)
@@ -13,5 +13,5 @@
   set_db (&db);
 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   if (!sky) exit (1);
     
Index: /trunk/Ohana/src/imregister/base/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/imregister/base/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/imregister/base/ConfigInit.c	(revision 25757)
@@ -105,5 +105,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", catdir);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/imregister/imphot/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/imregister/imphot/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/imregister/imphot/ConfigInit.c	(revision 25757)
@@ -101,5 +101,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/kapa2/Makefile
===================================================================
--- /trunk/Ohana/src/kapa2/Makefile	(revision 25756)
+++ /trunk/Ohana/src/kapa2/Makefile	(revision 25757)
@@ -48,7 +48,8 @@
 $(SRC)/Layout.$(ARCH).o		  	  $(SRC)/Sections.$(ARCH).o	      \
 $(SRC)/Graphs.$(ARCH).o                   $(SRC)/SetGraphSize.$(ARCH).o       \
-$(SRC)/Resize.$(ARCH).o                   $(SRC)/ErasePlots.$(ARCH).o         \
-$(SRC)/EraseImage.$(ARCH).o         	  $(SRC)/SetToolbox.$(ARCH).o         \
+$(SRC)/Resize.$(ARCH).o                   $(SRC)/Relocate.$(ARCH).o           \
+$(SRC)/ErasePlots.$(ARCH).o               $(SRC)/EraseImage.$(ARCH).o         \
 $(SRC)/EraseCurrentPlot.$(ARCH).o         $(SRC)/EraseSections.$(ARCH).o      \
+$(SRC)/SetToolbox.$(ARCH).o                                                   \
 $(SRC)/SetSection.$(ARCH).o		  $(SRC)/DefineSection.$(ARCH).o      \
 $(SRC)/SetLimits.$(ARCH).o                $(SRC)/SetFont.$(ARCH).o            \
@@ -79,7 +80,4 @@
 $(SRC)/ColorHistogram.$(ARCH).o           $(SRC)/CreateWide.$(ARCH).o
 
-#$(SRC)/CreateZoom8.$(ARCH).o              $(SRC)/CreateZoom16.$(ARCH).o       \
-#$(SRC)/CreateZoom24.$(ARCH).o             $(SRC)/CreateZoom32.$(ARCH).o       \
-
 OBJ  =  $(KAPA)
 
Index: /trunk/Ohana/src/kapa2/include/constants.h
===================================================================
--- /trunk/Ohana/src/kapa2/include/constants.h	(revision 25756)
+++ /trunk/Ohana/src/kapa2/include/constants.h	(revision 25757)
@@ -11,5 +11,5 @@
 
 # define NCHANNELS 3
-# define NPIXELS_DYNAMIC 128
+# define NPIXELS_DYNAMIC 4600
 
 // XXX for the moment, this is set to match the values in SetColorScale3D_CC
Index: /trunk/Ohana/src/kapa2/include/prototypes.h
===================================================================
--- /trunk/Ohana/src/kapa2/include/prototypes.h	(revision 25756)
+++ /trunk/Ohana/src/kapa2/include/prototypes.h	(revision 25757)
@@ -55,4 +55,5 @@
 int           LoadTextlines       PROTO((int sock));
 int           Resize              PROTO((int sock));
+int           Relocate            PROTO((int sock));
 int           GetLimits           PROTO((int sock));
 int           SetLimits           PROTO((int sock));
@@ -178,6 +179,6 @@
 void          Image_to_Picture    PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
 void          Image_to_Screen     PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
-void          Picture_Lower 	  PROTO((int *i_start, int *j_start, Matrix *matrix, Picture *picture));
-void          Picture_Upper 	  PROTO((int *i_end, int *j_end, Matrix *matrix, Picture *picture));
+void          Picture_Lower 	  PROTO((int *i_start, int *j_start, int *I_start, int *J_start, Matrix *matrix, Picture *picture));
+void          Picture_Upper 	  PROTO((int *i_end, int *j_end, int i_start, int j_start, Matrix *matrix, Picture *picture));
 
 void          DragColorbar        PROTO((Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event));
Index: /trunk/Ohana/src/kapa2/include/structures.h
===================================================================
--- /trunk/Ohana/src/kapa2/include/structures.h	(revision 25756)
+++ /trunk/Ohana/src/kapa2/include/structures.h	(revision 25757)
@@ -21,5 +21,5 @@
   Window         window;
   Visual        *visual;
-  int            visualclass; // is visual dynamic? (XXX change name?)
+  int            dynamicColors; // is visual dynamic?
   int            Nbits;	      // pixel depth in bits (8, 16, 24, 32)
   GC             gc;
@@ -94,5 +94,5 @@
   int      DX, DY;	      // size of displayed picture (must be updated with new images...)
   int      expand;	      // zoomscale
-  double   X,  Y;	      // center of image in picture
+  double   Xc,  Yc;	      // center of image in picture
   char     flipx, flipy;      // parity (0 = +; 1 = -)
   XImage  *pix;
Index: /trunk/Ohana/src/kapa2/src/ButtonFunctions.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 25757)
@@ -44,6 +44,6 @@
 int Recenter (Graphic *graphic, KapaImageWidget *image) {
 
-  image[0].picture.X = 0.5*image[0].image[0].matrix.Naxis[0];
-  image[0].picture.Y = 0.5*image[0].image[0].matrix.Naxis[1];
+  image[0].picture.Xc = 0.5*image[0].image[0].matrix.Naxis[0];
+  image[0].picture.Yc = 0.5*image[0].image[0].matrix.Naxis[1];
  
   Remap (graphic, image);
@@ -56,4 +56,5 @@
 
   image[0].picture.expand = 1;
+  image[0].zoom.expand = 5;
   Remap (graphic, image);
   Refresh ();
@@ -64,7 +65,8 @@
 int RecenterRescale (Graphic *graphic, KapaImageWidget *image) {
 
-  image[0].picture.X = 0.5*image[0].image[0].matrix.Naxis[0];
-  image[0].picture.Y = 0.5*image[0].image[0].matrix.Naxis[1];
+  image[0].picture.Xc = 0.5*image[0].image[0].matrix.Naxis[0];
+  image[0].picture.Yc = 0.5*image[0].image[0].matrix.Naxis[1];
   image[0].picture.expand = 1;
+  image[0].zoom.expand = 5;
  
   Remap (graphic, image);
Index: /trunk/Ohana/src/kapa2/src/Center.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Center.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Center.c	(revision 25757)
@@ -16,8 +16,10 @@
   if (image == NULL) return (TRUE);
 
-  image[0].picture.X = X;
-  image[0].picture.Y = Y;
+  // enforce integer center here?
+  image[0].picture.Xc = X;
+  image[0].picture.Yc = Y;
   if ((zoom != 0) && (zoom != -1)) {
     image[0].picture.expand = zoom;
+    image[0].zoom.expand = MIN(image[0].zoom.dx / 5.5, MAX(5, 2*zoom));
   }
 
Index: /trunk/Ohana/src/kapa2/src/CheckPipe.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CheckPipe.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/CheckPipe.c	(revision 25757)
@@ -131,4 +131,10 @@
   }
 
+  if (!strcmp (word, "MOVE")) {
+    status = Relocate (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
   if (!strcmp (word, "GLIM")) {
     GetLimits (sock);
Index: /trunk/Ohana/src/kapa2/src/CheckVisual.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CheckVisual.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/CheckVisual.c	(revision 25757)
@@ -10,5 +10,5 @@
 
   int i, Nfound, N;
-  int isColor, isDefault, isDynamic;
+  int isColor, isDefault;
   XVisualInfo *visual_list, visual_temp;
   unsigned long planes[3];
@@ -34,29 +34,8 @@
   }
   
-// skip the default and the PseudoColor visuals, go for TrueColor first
-# if (0)
-  /* check default visual first */ 
-  for (i = 0; (i < Nfound) && (graphic[0].visual != visual_list[i].visual); i++);
-  if (i == Nfound) {
-    fprintf (stderr, "default visual not found??\n");
-    exit (0);
-  }
-# endif
-
   // set these based on selected visual
-  isColor = isDefault = isDynamic = FALSE;
-
-# if (0)
-  // attempt to select the most desirable type of visual: Default as PseudoColor (XXX is it still true?)
-  if (DEBUG) fprintf (stderr, "default visual class is %d\n", visual_list[i].class);
-  if (visual_list[i].class == PseudoColor) {
-    isColor = isDefault = isDynamic = TRUE;
-    graphic[0].visual = visual_list[i].visual;
-    graphic[0].visualclass = TRUE;
-    goto test_pixels;
-  }
-# endif
-
-  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  isColor = isDefault = FALSE;
+
+  // attempt to select the most desirable type of visual: TrueColor
   for (i = 0; (i < Nfound) && (visual_list[i].class != TrueColor); i++);
   if (i != Nfound) {
@@ -65,5 +44,18 @@
     isDefault = (graphic[0].visual == visual_list[i].visual);
     graphic[0].visual = visual_list[i].visual;
-    graphic[0].visualclass = FALSE;
+    graphic[0].dynamicColors = FALSE;
+    if (DEBUG) fprintf (stderr, "got TrueColor visual\n");
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: DirectColor
+  for (i = 0; (i < Nfound) && (visual_list[i].class != DirectColor); i++);
+  if (i != Nfound) {
+    isColor = TRUE;
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].dynamicColors = TRUE;
+    if (DEBUG) fprintf (stderr, "got DirectColor visual\n");
     goto test_pixels;
   }
@@ -72,9 +64,10 @@
   for (i = 0; (i < Nfound) && (visual_list[i].class != PseudoColor); i++);
   if (i != Nfound) {
-    isColor = isDynamic = TRUE;
+    isColor = TRUE;
     if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
     isDefault = (graphic[0].visual == visual_list[i].visual);
     graphic[0].visual = visual_list[i].visual;
-    graphic[0].visualclass = TRUE;
+    graphic[0].dynamicColors = TRUE;
+    if (DEBUG) fprintf (stderr, "got PseudoColor visual\n");
     goto test_pixels;
   }
@@ -83,9 +76,9 @@
   for (i = 0; (i < Nfound) && (visual_list[i].class != GrayScale); i++);
   if (i != Nfound) {
-    isDynamic = TRUE;
     if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
     isDefault = (graphic[0].visual == visual_list[i].visual);
     graphic[0].visual = visual_list[i].visual;
-    graphic[0].visualclass = TRUE;
+    graphic[0].dynamicColors = TRUE;
+    if (DEBUG) fprintf (stderr, "got GrayScale visual\n");
     goto test_pixels;
   }
@@ -97,5 +90,6 @@
     isDefault = (graphic[0].visual == visual_list[i].visual);
     graphic[0].visual = visual_list[i].visual;
-    graphic[0].visualclass = FALSE;
+    graphic[0].dynamicColors = FALSE;
+    if (DEBUG) fprintf (stderr, "got StaticColor visual\n");
     goto test_pixels;
   }
@@ -107,5 +101,6 @@
     isDefault = (graphic[0].visual == visual_list[i].visual);
     graphic[0].visual = visual_list[i].visual;
-    graphic[0].visualclass = FALSE;
+    graphic[0].dynamicColors = FALSE;
+    if (DEBUG) fprintf (stderr, "got StaticGray visual\n");
     goto test_pixels;
   }
@@ -115,4 +110,5 @@
   /* need to make a colormap if 
      1) the selected visual is not the default 
+        XXX not sure if I need to / am allowed to alloc a colormap if I've grabbed the default.
      2) there are not enough colors available */
 
@@ -125,11 +121,15 @@
   } 
 
-  // allocate a private colormap, if needed
+  // dynamic visual classes can accept AllocAll and can use XAllocColorCells while AllocNone must use XAllocColor
+  // int allocMode = graphic[0].dynamicColors ? AllocAll : AllocNone;
+  int allocMode = AllocNone;
+
+  // allocate a private colormap, if desired or needed
   if (!isDefault) {
     if (DEBUG) fprintf (stderr, "allocated private colormap\n");
-    graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
-  }
-
-  if (isDynamic) {
+    graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, allocMode);
+  }
+
+  if (graphic[0].dynamicColors) {
     Npixels = NPIXELS_DYNAMIC;
     if ((N = get_argument (*argc, argv, "-ncolors"))) {
@@ -144,6 +144,9 @@
     for (graphic[0].Npixels = Npixels; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
       if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
-      if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels)) {
+      if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 0, graphic[0].pixels, graphic[0].Npixels)) {
 	break;
+      }
+      for (i = 0; i < graphic[0].Npixels; i++) {
+	graphic[0].cmap[i].pixel = graphic[0].pixels[i];
       }
     }
@@ -152,9 +155,10 @@
     if (graphic[0].Npixels < 16) {
       if (!isDefault) {
+	// We've already tried to allocate a colormap above...
 	fprintf (stderr, "can't allocate enough cells in private colormap\n");
 	exit (0);
       }
       if (DEBUG) fprintf (stderr, "can't allocate enough cells, using private colormap\n");
-      graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+      graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, allocMode);
 
       for (graphic[0].Npixels = NPIXELS_DYNAMIC; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
@@ -163,6 +167,8 @@
 	  break;
 	}
-      }
-
+	for (i = 0; i < graphic[0].Npixels; i++) {
+	  graphic[0].cmap[i].pixel = graphic[0].pixels[i];
+	}
+      }
       if ((N = get_argument (*argc, argv, "-colorcount"))) {
 	fprintf (stderr, "kapa can grab %d colors\n", graphic[0].Npixels);
Index: /trunk/Ohana/src/kapa2/src/CheckVisual.test.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CheckVisual.test.c	(revision 25757)
+++ /trunk/Ohana/src/kapa2/src/CheckVisual.test.c	(revision 25757)
@@ -0,0 +1,174 @@
+# include "Ximage.h"
+
+/* DirectColor doesn't seem to work, even though it is available:
+   I cannot use XAllocColorCells to get pixels under DirectColor
+*/
+
+/* static int try_visual[] = {5, 3, 1, 4, 2, 0}; */
+
+int TryVisualType (int *isColor, int *isDefault, Graphic *graphic, int Nvisual, XVisualInfo *visual_list, int VisualType, char *name) {
+
+    int i;
+
+    for (i = 0; i < Nvisual; i++) {
+	if (visual_list[i].class != VisualType) continue;
+
+	*isColor = FALSE;
+	if (VisualType == DirectColor) *isColor = TRUE;
+	if (VisualType == PseudoColor) *isColor = TRUE;
+	if (VisualType == TrueColor)   *isColor = TRUE;
+
+	if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+	*isDefault = (graphic[0].visual == visual_list[i].visual);
+
+	graphic[0].dynamicColors = FALSE;
+	if (VisualType == DirectColor) graphic[0].dynamicColors = TRUE;
+	if (VisualType == PseudoColor) graphic[0].dynamicColors = TRUE;
+	if (VisualType == GrayScale) graphic[0].dynamicColors = TRUE;
+
+	if (DEBUG) fprintf (stderr, "got %s visual\n", name);
+	return (TRUE);
+    }
+    return (FALSE);
+}
+
+void CheckVisual (Graphic *graphic, int *argc, char **argv) {
+
+  int i, Nvisual, N;
+  int isColor, isDefault;
+  XVisualInfo *visual_list, visual_temp;
+  unsigned long planes[3];
+  XPixmapFormatValues *pixmaps;
+  int Npixmaps, Npixels;
+
+  if (DEBUG) {
+    fprintf (stderr, "DirectColor: %d\n", DirectColor);
+    fprintf (stderr, "PseudoColor: %d\n", PseudoColor);
+    fprintf (stderr, "TrueColor:   %d\n", TrueColor);
+    fprintf (stderr, "GrayScale:   %d\n", GrayScale);
+    fprintf (stderr, "StaticColor: %d\n", StaticColor);
+    fprintf (stderr, "StaticGray:  %d\n", StaticGray);
+  }
+
+  visual_temp.screen = graphic[0].screen;
+  
+  /* find available visuals */
+  visual_list = XGetVisualInfo (graphic[0].display, VisualScreenMask, &visual_temp, &Nvisual);
+  if (Nvisual == 0) {
+    fprintf (stderr, "error finding useful visual\n");
+    exit (2);
+  }
+  
+  // set these based on selected visual
+  isColor = isDefault = FALSE;
+
+  if (TryVisualType (&isColor, &isDefault, graphic, Nvisual, visual_list, TrueColor,   "TrueColor"))   goto test_pixels;
+  if (TryVisualType (&isColor, &isDefault, graphic, Nvisual, visual_list, DirectColor, "DirectColor")) goto test_pixels;
+  if (TryVisualType (&isColor, &isDefault, graphic, Nvisual, visual_list, PseudoColor, "PseudoColor")) goto test_pixels;
+  if (TryVisualType (&isColor, &isDefault, graphic, Nvisual, visual_list, GrayScale,   "GrayScale"))   goto test_pixels;
+  if (TryVisualType (&isColor, &isDefault, graphic, Nvisual, visual_list, StaticColor, "StaticColor")) goto test_pixels;
+  if (TryVisualType (&isColor, &isDefault, graphic, Nvisual, visual_list, StaticGray,  "StaticGray"))  goto test_pixels;
+
+  fprintf (stderr, "error finding valid visual\n");
+  exit (2);
+
+  test_pixels:
+
+  /* need to make a colormap if 
+     1) the selected visual is not the default 
+        XXX not sure if I need to / am allowed to alloc a colormap if I've grabbed the default.
+     2) there are not enough colors available */
+
+  /* NEED TO ADD A COUPLE LINES DEFINING THE VARIOUS HARD COLORS (BLACK, WHITE, AND THE OVERLAYS) */
+
+  // allow user to force a private colormap
+  if ((N = get_argument (*argc, argv, "-private"))) {
+    remove_argument(N, argc, argv);
+    isDefault = FALSE;
+  } 
+
+  // dynamic visual classes can accept AllocAll and can use XAllocColorCells while AllocNone must use XAllocColor
+  // int allocMode = graphic[0].dynamicColors ? AllocAll : AllocNone;
+  int allocMode = AllocNone;
+
+  // allocate a private colormap, if desired or needed
+  if (!isDefault) {
+    if (DEBUG) fprintf (stderr, "allocated private colormap\n");
+    graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, allocMode);
+  }
+
+  if (graphic[0].dynamicColors) {
+    Npixels = NPIXELS_DYNAMIC;
+    if ((N = get_argument (*argc, argv, "-ncolors"))) {
+      remove_argument(N, argc, argv);
+      Npixels = atoi (argv[N]);
+      remove_argument(N, argc, argv);
+    } 
+
+    /* allocate color cells */
+    ALLOCATE (graphic[0].pixels, unsigned long, Npixels);
+    ALLOCATE (graphic[0].cmap,   XColor,        Npixels);
+    for (graphic[0].Npixels = Npixels; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
+      if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+      if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 0, graphic[0].pixels, graphic[0].Npixels)) {
+	break;
+      }
+      for (i = 0; i < graphic[0].Npixels; i++) {
+	graphic[0].cmap[i].pixel = graphic[0].pixels[i];
+      }
+    }
+
+    /* insufficient cells, can we make a private colormap? */
+    if (graphic[0].Npixels < 16) {
+      if (!isDefault) {
+	// We've already tried to allocate a colormap above...
+	fprintf (stderr, "can't allocate enough cells in private colormap\n");
+	exit (0);
+      }
+      if (DEBUG) fprintf (stderr, "can't allocate enough cells, using private colormap\n");
+      graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, allocMode);
+
+      for (graphic[0].Npixels = NPIXELS_DYNAMIC; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
+	if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+	if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels)) {
+	  break;
+	}
+	for (i = 0; i < graphic[0].Npixels; i++) {
+	  graphic[0].cmap[i].pixel = graphic[0].pixels[i];
+	}
+      }
+      if ((N = get_argument (*argc, argv, "-colorcount"))) {
+	fprintf (stderr, "kapa can grab %d colors\n", graphic[0].Npixels);
+	exit (0);
+      } 
+      if (graphic[0].Npixels < 16) {
+	fprintf (stderr, "can't even allocate enough cells in private colormap\n");
+	exit (0);
+      }
+    }
+  } else {
+    Npixels = NPIXELS_STATIC;
+    if ((N = get_argument (*argc, argv, "-ncolors"))) {
+      remove_argument(N, argc, argv);
+      Npixels = atoi (argv[N]);
+      remove_argument(N, argc, argv);
+    } 
+
+    // XXX allocate the unsigned long *pixels here and above
+    ALLOCATE (graphic[0].pixels, unsigned long, Npixels);
+    ALLOCATE (graphic[0].cmap,   XColor,        Npixels);
+    graphic[0].Npixels = Npixels;
+  }
+
+  if ((N = get_argument (*argc, argv, "-colorcount"))) {
+    fprintf (stderr, "kii can grab %d colors\n", graphic[0].Npixels);
+    exit (0);
+  } 
+  
+  pixmaps = XListPixmapFormats (graphic[0].display, &Npixmaps);
+  for (i = 0; i < Npixmaps; i++) {
+    if (pixmaps[i].depth == graphic[0].depth) {
+      graphic[0].Nbits = pixmaps[i].bits_per_pixel;
+    }
+  }
+}
Index: unk/Ohana/src/kapa2/src/CreateZoom16.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CreateZoom16.c	(revision 25756)
+++ 	(revision )
@@ -1,144 +1,0 @@
-# include "Ximage.h"
-
-void CreateZoom16 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
-
-# if (0)
-  int i, j, ii, jj;
-  int i_start, i_end, j_start, j_end;
-  int dropback;  /* this is a bit of a kludge... */
-  int dx, dy, DX, DY, pixelN;
-  double expand, zoomscale, Rx, Ry;
-  int expand_in, expand_out;
-  unsigned char *out_pix, *out_pix2, *data;
-  float *imdata, *in_pix,  *in_pix2;
-  unsigned char pixel1[256], pixel2[256];
-  unsigned char pixvalue1, pixvalue2;
-  unsigned long back;
-  unsigned char back1, back2;
-
-  dx = image[0].zoom.dx;
-  dy = image[0].zoom.dy;
-
-  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
-    REALLOCATE (image[0].zoom.data, char, 2*dy*dx);
-    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
-    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 16, 0);
-    return;
-  }
-
-  for (i = 0; i < 256; i++) { /* set up pixel array */
-    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
-    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
-  }
-  back = graphic[0].back;
-  back1 = 0x0000ff & back;
-  back2 = 0x0000ff & (back >> 8);
-  // define the color transform parameters
-  MaxValue = graphic[0].Npixels - 1;
-  if (image[0].image[0].range != 0.0) {
-    slope = graphic[0].Npixels / image[0].image[0].range;
-    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
-  } else {
-    slope = 1.0;
-    start = image[0].image[0].zero;
-  }
-
-  zoomscale = MAX (5, image[0].expand + 5);
-  expand = 1 / zoomscale;
-  expand_out = (int) zoomscale;
-  expand_in  = 1;
-
-  DX = image[0].image[0].matrix.Naxis[0];
-  DY = image[0].image[0].matrix.Naxis[1];
-  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
-  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
-
-  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
-  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
-  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
-  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
-  dropback = expand_out - (i_end - i_start) % expand_out;
-  if ((i_end - i_start) % expand_out == 0) dropback = 0;
-
-  data = out_pix = (unsigned char *) image[0].zoom.data;
-  imdata  = (float *) image[0].image[0].matrix.buffer;
-  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
-
-  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
-
-  if ((i_end < i_start) || (j_end < j_start)) {
-    for (j = 0; j < dx*dy; j++, out_pix++) 
-      *out_pix = back;
-    return;
-  } 
-  
-  /**** fill in bottom area ****/
-  for (j = 0; j < j_start; j++) {
-    for (i = 0; i < dx; i++, out_pix+=2) {
-      out_pix[0] = back1;
-      out_pix[1] = back2;
-    }
-  }
-
-  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
-    out_pix = &data[2*j*dx];
-
-    /**** fill in area to the left of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) { 
-      out_pix2 = out_pix + 2*jj*dx;
-      for (i = 0; i < i_start; i++, out_pix2+=2) {
-	out_pix2[0] = back1;
-	out_pix2[1] = back2;
-      }
-    }
-    out_pix += 2*i_start;
-    
-    /*** fill in the picture region ***/
-    in_pix2 = in_pix;
-    if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=2) {
-	pixelN = PixelLookup(*in_pix2);
-	out_pix[0] = pixel1[pixelN];
-	out_pix[1] = pixel2[pixelN];
-      }
-    } else {
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 2*expand_out) { 
-	pixelN = PixelLookup(*in_pix2);
-	pixvalue1 = pixel1[pixelN];
-	pixvalue2 = pixel2[pixelN];
-	out_pix2 = out_pix;
-	for (jj = 0; jj < expand_out; jj++, out_pix2+=2*(dx-expand_out)) {
-	  for (ii = 0; ii < expand_out; ii++, out_pix2+=2) {
-	    out_pix2[0] = pixvalue1; 
-	    out_pix2[1] = pixvalue2; 
-	  }
-	}
-      }
-    }
-    out_pix -= 2*dropback;
-    
-    /**** fill in area to the right of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) {
-      out_pix2 = out_pix + 2*jj*dx;
-      for (i = i_end; i < dx; i++, out_pix2+=2) {
-	out_pix2[0] = back1;
-	out_pix2[1] = back2;
-      }
-    }
-  } 
-  
-  out_pix = &data[2*j_end*dx];
-  /**** fill in top area ****/
-  for (j = 0; j < (dy - j_end); j++) {
-    for (i = 0; i < dx; i++, out_pix+=2) { 
-      out_pix[0] = back1;
-      out_pix[1] = back2;
-    }
-  }
-
-  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 16, 0);
-  
-# endif
-}
Index: unk/Ohana/src/kapa2/src/CreateZoom24.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CreateZoom24.c	(revision 25756)
+++ 	(revision )
@@ -1,178 +1,0 @@
-# include "Ximage.h"
-
-void CreateZoom24 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
-
-# if (0)
-  int i, j, ii, jj, extra;
-  int i_start, i_end, j_start, j_end;
-  int dropback;  /* this is a bit of a kludge... */
-  int dx, dy, DX, DY, pixelN;
-  double expand, zoomscale, Rx, Ry;
-  int expand_in, expand_out;
-  unsigned char *out_pix, *out_pix2, *data;
-  float *imdata, *in_pix, *in_pix2;
-  unsigned char pixel1[256], pixel2[256], pixel3[256];
-  unsigned char pixvalue1, pixvalue2, pixvalue3;
-  unsigned long back;
-  unsigned char back1, back2, back3;
-
-  dx = image[0].zoom.dx;
-  dy = image[0].zoom.dy;
-  extra = 4 - (dx * 3) % 4;
-
-  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
-    REALLOCATE (image[0].zoom.data, char, dy*(3*dx+extra));
-    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
-    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
-    return;
-  }
-
-  for (i = 0; i < 256; i++) { /* set up pixel array */
-    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
-    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
-    pixel3[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 16);
-  }
-  back = graphic[0].back;
-  back1 = 0x0000ff & back;
-  back2 = 0x0000ff & (back >> 8);
-  back3 = 0x0000ff & (back >> 16);
-
-  // define the color transform parameters
-  MaxValue = graphic[0].Npixels - 1;
-  if (image[0].image[0].range != 0.0) {
-    slope = graphic[0].Npixels / image[0].image[0].range;
-    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
-  } else {
-    slope = 1.0;
-    start = image[0].image[0].zero;
-  }
-
-  zoomscale = MAX (5, image[0].expand + 5);
-  expand = 1 / zoomscale;
-  expand_out = (int) zoomscale;
-  expand_in  = 1;
-
-  DX = image[0].image[0].matrix.Naxis[0];
-  DY = image[0].image[0].matrix.Naxis[1];
-  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
-  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
-
-  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
-  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
-  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
-  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
-  dropback = expand_out - (i_end - i_start) % expand_out;
-  if ((i_end - i_start) % expand_out == 0) dropback = 0;
-
-  data = out_pix = (unsigned char *) image[0].zoom.data;
-  imdata  = (float *) image[0].image[0].matrix.buffer;
-  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
-
-  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
-
-  if ((i_end < i_start) || (j_end < j_start)) {
-    for (j = 0; j < dx*dy; j++, out_pix++) 
-      *out_pix = back;
-    return;
-  } 
-  
-  /**** fill in bottom area ****/
-  for (j = 0; j < j_start; j++) {
-    for (i = 0; i < dx; i++, out_pix+=3) {
-      out_pix[0] = back1;
-      out_pix[1] = back2;
-      out_pix[2] = back3;
-    }
-    out_pix += extra;
-  }
-
-  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
-    out_pix = &data[j*(3*dx+extra)];
-
-    /**** fill in area to the left of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) { 
-      out_pix2 = out_pix + jj*(3*dx + extra);
-      for (i = 0; i < i_start; i++, out_pix2+=3) {
-	out_pix2[0] = back1;
-	out_pix2[1] = back2;
-	out_pix2[2] = back3;
-      }
-    }
-    out_pix += 3*i_start;
-    
-    /*** fill in the picture region ***/
-    in_pix2 = in_pix;
-    if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
-	pixelN = PixelLookup(*in_pix2);
-	out_pix[0] = pixel1[pixelN];
-	out_pix[1] = pixel2[pixelN];
-	out_pix[2] = pixel3[pixelN];
-      }
-    } else {
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
-	pixelN   = PixelLookup(*in_pix2);
-	pixvalue1 = pixel1[pixelN];
-	pixvalue2 = pixel2[pixelN];
-	pixvalue3 = pixel3[pixelN];
-	out_pix2 = out_pix;
-	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
-	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
-	    out_pix2[0] = pixvalue1; 
-	    out_pix2[1] = pixvalue2; 
-	    out_pix2[2] = pixvalue3; 
-	  }
-	}
-      }
-    }
-    out_pix -= 3*dropback;
-    
-    /**** fill in area to the right of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) {
-      out_pix2 = out_pix + jj*(3*dx+extra);
-      for (i = i_end; i < dx; i++, out_pix2+=3) {
-	out_pix2[0] = back1;
-	out_pix2[1] = back2;
-	out_pix2[2] = back3;
-      }
-    }
-  } 
-  
-  /*
-  if ((j_end - j_start) % expand_out > 0)
-    out_pix -= (expand_out - (j_end - j_start) % expand_out);
-  */
-
-  out_pix = &data[j_end*(3*dx+extra)];
-  /**** fill in top area ****/
-  for (j = 0; j < (dy - j_end); j++) {
-    for (i = 0; i < dx; i++, out_pix+=3) { 
-      out_pix[0] = back1;
-      out_pix[1] = back2;
-      out_pix[2] = back3;
-    }
-    out_pix+=extra;
-  }
-
-  /*
-  out_pix = (unsigned char *) image[0].zoom.data;
-  for (i = 0; i < dx*dy; i++) {
-    out_pix[3*i + 0] = back1;
-    out_pix[3*i + 1] = back2;
-    out_pix[3*i + 2] = back3;
-  }
-  for (j = 0; j < dy/2; j++) {
-    for (i = 0; i < dx; i++) {
-      out_pix[j*(extra + 3*dx) + 3*i + 0] = pixel1[10];
-      out_pix[j*(extra + 3*dx) + 3*i + 1] = pixel2[10];
-      out_pix[j*(extra + 3*dx) + 3*i + 2] = pixel3[10];
-    }
-
-  }
-  */
-  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, 24, ZPixmap, 0, 
-					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
-  
-# endif
-}
Index: unk/Ohana/src/kapa2/src/CreateZoom32.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CreateZoom32.c	(revision 25756)
+++ 	(revision )
@@ -1,138 +1,0 @@
-# include "Ximage.h"
-
-void CreateZoom32 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
-
-  int i, j, ii, jj;
-  int i_start, i_end, j_start, j_end;
-  int dropback;  /* this is a bit of a kludge... */
-  int dx, dy, DX, DY;
-  double expand, Ix, Iy;
-  int expand_in, expand_out;
-  unsigned int *out_pix, *out_pix2;
-  unsigned short *in_pix, *in_pix2;
-  unsigned long *pixel, pixvalue;
-  unsigned long back;
-
-  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
-    REALLOCATE (image[0].zoom.data, char, 4*image[0].zoom.dx*image[0].zoom.dy);
-    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
-    return;
-  }
-
-  ALLOCATE (pixel, unsigned long, graphic[0].Npixels);
-
-  // local array for pixel values
-  for (i = 0; i < graphic[0].Npixels; i++) { 
-    pixel[i] = graphic[0].cmap[i].pixel;
-  }
-  back = graphic[0].back;
-
-  // set up expansions
-  assert ((image[0].zoom.expand >= 1) || (image[0].zoom.expand <= -2));
-  expand = expand_in = expand_out = 1.0;
-  if (image[0].zoom.expand > 0) {
-    expand = 1 / (1.0*image[0].zoom.expand);
-    expand_out = image[0].zoom.expand;
-    expand_in  = 1;
-  }
-  if (image[0].zoom.expand < 0) {
-    expand = fabs((double)image[0].zoom.expand);
-    expand_out = 1;
-    expand_in  = -image[0].zoom.expand;
-  }
-
-  // define the image sizes
-  dx = image[0].zoom.dx;
-  dy = image[0].zoom.dy;
-  DX = image[0].image[0].matrix.Naxis[0];
-  DY = image[0].image[0].matrix.Naxis[1];
-  
-  // x, y are the closest lit screen pixel to 0,0
-  Picture_Lower (&i_start, &j_start, &image[0].image[0].matrix, &image[0].zoom);
-
-  // x, y are the closest lit screen pixel to dx, dy
-  Picture_Upper (&i_end, &j_end, &image[0].image[0].matrix, &image[0].zoom);
-
-  // Ix,Iy are the first displayed image pixel
-  Picture_to_Image (&Ix, &Iy, i_start, j_start, &image[0].zoom);
-
-  dropback = expand_out - (i_end - i_start) % expand_out;
-  if ((i_end - i_start) % expand_out == 0) dropback = 0;
-
-  out_pix = (unsigned int *) image[0].zoom.data;
-  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
-
-  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
-
-  // XXX can we just drop this?
-  if ((i_end < i_start) || (j_end < j_start)) {
-    for (j = 0; j < dx*dy; j++, out_pix++) 
-      *out_pix = back;
-    return;
-  } 
-
-  /**** fill in bottom area ****/
-  for (j = 0; j < dx*j_start; j++, out_pix++) {
-    *out_pix = back;
-  }
-
-  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
-
-    /**** fill in area to the left of the picture ****/
-    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
-      out_pix2 = out_pix + jj*dx;
-      for (i = 0; i < i_start; i++, out_pix2++) {
-	*out_pix2 = back;
-      }
-    }
-    out_pix += i_start;
-    
-    /*** fill in the picture region ***/
-    in_pix2 = in_pix;
-    if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
-	*out_pix = pixel[*in_pix2];
-      }
-    } else {
-      // equiv to : out_pix += (i_end - i_start) + [expand_out - (i_end - i_start) % expand_out]
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
-	pixvalue = pixel[*in_pix2];
-	out_pix2 = out_pix;
-	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
-	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
-	    *out_pix2 = pixvalue; 
-	  }
-	}
-      }
-    }
-    out_pix -= dropback;
-    
-    /**** fill in area to the right of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) {
-      out_pix2 = out_pix + jj*dx;
-      for (i = i_end; i < dx; i++, out_pix2++) {
-	*out_pix2 = back;
-      }
-    }
-    out_pix += (dx - i_end);
-    assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
-  } 
-  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
-  
-  if ((j_end - j_start) % expand_out > 0)
-    out_pix -= (expand_out - (j_end - j_start) % expand_out);
-  
-  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
-
-  /**** fill in top area ****/
-  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)image[0].zoom.data < dx*dy); j++, out_pix++) { 
-    *out_pix = back;
-  }
-  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
-
-  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
-  
-  free (pixel);
-}
Index: unk/Ohana/src/kapa2/src/CreateZoom8.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CreateZoom8.c	(revision 25756)
+++ 	(revision )
@@ -1,130 +1,0 @@
-# include "Ximage.h"
-
-void CreateZoom8 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
-
-# if (0)
-  int i, j, ii, jj;
-  int i_start, i_end, j_start, j_end;
-  int dropback;  /* this is a bit of a kludge... */
-  int dx, dy, DX, DY, pixelN;
-  double expand, zoomscale, Rx, Ry;
-  int expand_in, expand_out;
-  unsigned char *out_pix, *out_pix2;
-  float *imdata, *in_pix, *in_pix2;
-  unsigned long pixel[256], pixvalue;
-  unsigned long back;
-
-  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
-    REALLOCATE (image[0].zoom.data, char, image[0].zoom.dx*image[0].zoom.dy);
-    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
-    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 8, 0);
-    return;
-  }
-
-  for (i = 0; i < 256; i++) { /* set up pixel array */
-    pixel[i] = graphic[0].cmap[i].pixel;
-  }
-  back = graphic[0].back;
-
-  // define the color transform parameters
-  MaxValue = graphic[0].Npixels - 1;
-  if (image[0].image[0].range != 0.0) {
-    slope = graphic[0].Npixels / image[0].image[0].range;
-    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
-  } else {
-    slope = 1.0;
-    start = image[0].image[0].zero;
-  }
-
-  zoomscale = MAX (5, image[0].expand + 5);
-  expand = 1 / zoomscale;
-  expand_out = (int) zoomscale;
-  expand_in  = 1;
-
-  dx = image[0].zoom.dx;
-  dy = image[0].zoom.dy;
-  DX = image[0].image[0].matrix.Naxis[0];
-  DY = image[0].image[0].matrix.Naxis[1];
-  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
-  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
-
-  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
-  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
-  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
-  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
-  dropback = expand_out - (i_end - i_start) % expand_out;
-  if ((i_end - i_start) % expand_out == 0) dropback = 0;
-
-  out_pix = (unsigned char *) image[0].zoom.data;
-  imdata  = (float *) image[0].image[0].matrix.buffer;
-  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
-
-  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
-
-  if ((i_end < i_start) || (j_end < j_start)) {
-    for (j = 0; j < dx*dy; j++, out_pix++) 
-      *out_pix = back;
-    return;
-  } 
-  
-  /**** fill in bottom area ****/
-  for (j = 0; j < dx*j_start; j++, out_pix++) 
-    *out_pix = back;
-  
-  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
-
-    /**** fill in area to the left of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) { 
-      out_pix2 = out_pix + jj*dx;
-      for (i = 0; i < i_start; i++, out_pix2++) {
-	*out_pix2 = back;
-      }
-    }
-    out_pix += i_start;
-    
-    /*** fill in the picture region ***/
-    in_pix2 = in_pix;
-    if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
-	pixelN = PixelLookup(*in_pix2);
-	*out_pix = pixel[pixelN];
-      }
-    } else {
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
-	pixelN   = PixelLookup(*in_pix2);
-	pixvalue = pixel[pixelN];
-	out_pix2 = out_pix;
-	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
-	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
-	    *out_pix2 = pixvalue;
-	  }
-	}
-      }
-    }
-    out_pix -= dropback;
-    
-    /**** fill in area to the right of the picture ****/
-    for (jj = 0; jj < expand_out; jj++) {
-      out_pix2 = out_pix + jj*dx;
-      for (i = i_end; i < dx; i++, out_pix2++) {
-	*out_pix2 = back;
-      }
-    }
-    out_pix += (dx - i_end);
-    
-  } 
-  
-  if ((j_end - j_start) % expand_out > 0)
-    out_pix -= expand_out - (j_end - j_start) % expand_out;
-  
-  /**** fill in top area ****/
-  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *) image[0].zoom.data < dx*dy); j++, out_pix++) { 
-    *out_pix = back;
-  }
-
-  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 8, 0);
-  
-# endif
-}
Index: /trunk/Ohana/src/kapa2/src/CursorOps.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/CursorOps.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/CursorOps.c	(revision 25757)
@@ -2,4 +2,5 @@
 
 // input coordinates are relative to the picture bounding box
+// XXX pre-calculate fexpand, Xc, Yc when expand is set?
 void Picture_to_Image (double *x1, double *y1, double x2, double y2, Picture *picture) {
 
@@ -14,10 +15,14 @@
   
   // pixel coordinates in picture frame
-  dx = expand*(x2 - 0.5*picture[0].dx);
-  dy = expand*(y2 - 0.5*picture[0].dy);
+  dx = expand*(x2 - (int)(0.5*picture[0].dx));
+  dy = expand*(y2 - (int)(0.5*picture[0].dy));
+
+  // set the true center (image and screen pixel boundaries must be aligned)
+  // Xc = (int)(picture[0].Xc / expand) * expand;
+  // Yc = (int)(picture[0].Yc / expand) * expand;
 
   // picture[0].X,Y is the image coordinate in the center of the picture
-  *x1 = picture[0].flipx ? picture[0].X - dx : picture[0].X + dx;
-  *y1 = picture[0].flipy ? picture[0].Y - dy : picture[0].Y + dy;
+  *x1 = picture[0].flipx ? picture[0].Xc - dx : picture[0].Xc + dx;
+  *y1 = picture[0].flipy ? picture[0].Yc - dy : picture[0].Yc + dy;
 }
 
@@ -46,10 +51,14 @@
   }
   
+  // set the true center (image and screen pixel boundaries must be aligned)
+  // Xc = ((int)(picture[0].Xc * expand)) / expand;
+  // Yc = ((int)(picture[0].Yc * expand)) / expand;
+
   // pixel coordinates in picture frame
-  dx = picture[0].flipx ? picture[0].X - x2 : x2 - picture[0].X;
-  dy = picture[0].flipy ? picture[0].Y - y2 : y2 - picture[0].Y;
+  dx = picture[0].flipx ? picture[0].Xc - x2 : x2 - picture[0].Xc;
+  dy = picture[0].flipy ? picture[0].Yc - y2 : y2 - picture[0].Yc;
     
-  *x1 = expand*dx + 0.5*picture[0].dx;
-  *y1 = expand*dy + 0.5*picture[0].dy;
+  *x1 = expand*dx + (int)(0.5*picture[0].dx);
+  *y1 = expand*dy + (int)(0.5*picture[0].dy);
 }
 
@@ -66,5 +75,5 @@
 
 // input coordinates are relative to the picture bounding box
-void Picture_Lower (int *i_start, int *j_start, Matrix *matrix, Picture *picture) {
+void Picture_Lower (int *i_start, int *j_start, int *I_start, int *J_start, Matrix *matrix, Picture *picture) {
 
   double Ix, Iy, Sx, Sy;
@@ -72,8 +81,4 @@
   // Ix, Iy are the image coordinates of the specified screen pixel
   Picture_to_Image (&Ix, &Iy, 0.0, 0.0, picture);
-
-  // Ix, Iy are now limited to valid image coordinates
-  Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
-  Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
 
   // round up (down) to nearest pixel boundary
@@ -84,4 +89,12 @@
     Iy = picture[0].flipy ? (int)(Iy) : (int)(Iy) + 1;
   }
+
+  // Ix, Iy are now limited to valid image coordinates
+  Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
+  Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
+
+  // I_start, J_start are the first displayed image pixel 
+  *I_start = Ix;
+  *J_start = Iy;
 
   // Sx, Sy are the screen coordinates of the Ix,Iy pixel
@@ -94,6 +107,7 @@
   
 // input coordinates are relative to the picture bounding box
-void Picture_Upper (int *i_end, int *j_end, Matrix *matrix, Picture *picture) {
+void Picture_Upper (int *i_end, int *j_end, int i_start, int j_start, Matrix *matrix, Picture *picture) {
 
+  int nExtra;
   double Ix, Iy, Sx, Sy;
 
@@ -101,15 +115,19 @@
   Picture_to_Image (&Ix, &Iy, picture[0].dx, picture[0].dy, picture);
 
+  // round down (up) to nearest pixel boundary
+  if (Ix > (int)(Ix)) {
+    Ix = picture[0].flipx ? (int)(Ix) + 1: (int)(Ix);
+  }
+  if (Iy > (int)(Iy)) {
+    Iy = picture[0].flipy ? (int)(Iy) + 1: (int)(Iy);
+  }
+
   // Ix, Iy are now limited to valid image coordinates
   Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
   Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
 
-  // round down (up) to nearest pixel boundary
-  if (Ix > (int)(Ix)) {
-    Ix = picture[0].flipx ? (int)(Ix) + 1 : (int)(Ix);
-  }
-  if (Iy > (int)(Iy)) {
-    Iy = picture[0].flipy ? (int)(Iy) + 1: (int)(Iy);
-  }
+  // I_end, J_end are the last displayed image pixel 
+  // *I_end = Ix;
+  // *J_end = Iy;
 
   // Sx, Sy are the screen coordinates of the Ix,Iy pixel
@@ -119,3 +137,11 @@
   *i_end = MIN (MAX (Sx, 0), picture[0].dx);
   *j_end = MIN (MAX (Sy, 0), picture[0].dy);
+
+  // round off error can leave us with a small number of extra pixels here.
+  if (picture[0].expand > 1) {
+    nExtra = (*i_end - i_start) % picture[0].expand;
+    *i_end -= nExtra;
+    nExtra = (*j_end - j_start) % picture[0].expand;
+    *j_end -= nExtra;
+  }
 }
Index: /trunk/Ohana/src/kapa2/src/DragColorbar.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/DragColorbar.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/DragColorbar.c	(revision 25757)
@@ -10,5 +10,5 @@
   int             xstatus, Npix, center;
 
-  if (!graphic[0].visualclass) return;
+  if (!graphic[0].dynamicColors) return;
 
   X = mouse_event[0].x;
@@ -79,5 +79,5 @@
   XColor cmap[256];
 
-  if (!graphic[0].visualclass) return;
+  if (!graphic[0].dynamicColors) return;
 
   for (i = 0; i < graphic[0].Npixels; i++) {
@@ -88,4 +88,5 @@
       cmap[i].blue = graphic[0].cmap[0].blue;
       cmap[i].green = graphic[0].cmap[0].green;
+      cmap[i].flags = DoRed | DoGreen | DoBlue;
     }
     else {
@@ -94,4 +95,5 @@
 	cmap[i].blue = graphic[0].cmap[graphic[0].Npixels-1].blue;
 	cmap[i].green = graphic[0].cmap[graphic[0].Npixels-1].green;
+	cmap[i].flags = DoRed | DoGreen | DoBlue;
       }
       else {
@@ -99,4 +101,5 @@
 	cmap[i].blue = graphic[0].cmap[j].blue;
 	cmap[i].green = graphic[0].cmap[j].green;
+	cmap[i].flags = DoRed | DoGreen | DoBlue;
       }
     }
Index: /trunk/Ohana/src/kapa2/src/Image.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Image.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Image.c	(revision 25757)
@@ -48,18 +48,18 @@
 
   // set the center and expansion for the pictures:
-  image[0].picture.X      = 0.0;
-  image[0].picture.Y      = 0.0;
+  image[0].picture.Xc     = 0.0;
+  image[0].picture.Yc     = 0.0;
   image[0].picture.expand = 1;
   image[0].picture.flipx  = FALSE;
   image[0].picture.flipy  = FALSE;
 
-  image[0].zoom.X      	  = 0.0;
-  image[0].zoom.Y      	  = 0.0;
-  image[0].zoom.expand 	  = +5;
+  image[0].zoom.Xc     	  = 0.0;
+  image[0].zoom.Yc     	  = 0.0;
+  image[0].zoom.expand 	  = 5;
   image[0].zoom.flipx  	  = FALSE;
   image[0].zoom.flipy  	  = FALSE;
 
-  image[0].wide.X      	  = 0.0;
-  image[0].wide.Y      	  = 0.0;
+  image[0].wide.Xc     	  = 0.0;
+  image[0].wide.Yc     	  = 0.0;
   image[0].wide.expand 	  = -5;
   image[0].wide.flipx  	  = FALSE;
@@ -144,8 +144,10 @@
 		  image[0].picture.dx+1, image[0].picture.dy+1);
   
-  XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
-	     image[0].picture.pix, 0, 0, 
-	     image[0].picture.x + 1, image[0].picture.y + 1, 
-	     image[0].picture.dx, image[0].picture.dy);
+  if (image[0].picture.pix) {
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].picture.pix, 0, 0, 
+	       image[0].picture.x + 1, image[0].picture.y + 1, 
+	       image[0].picture.dx, image[0].picture.dy);
+  }
 
   for (i = 0; i < NOVERLAYS; i++) {
@@ -156,13 +158,16 @@
 
   if (image[0].location) {
-    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
-	       image[0].cmapbar.pix, 0, 0, 
-	       image[0].cmapbar.x, image[0].cmapbar.y, 
-	       image[0].cmapbar.dx, image[0].cmapbar.dy);
-
-    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
-	       image[0].wide.pix, 0, 0, 
-	       image[0].wide.x, image[0].wide.y, 
-	       image[0].wide.dx, image[0].wide.dy);
+    if (image[0].cmapbar.pix) {
+      XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+		 image[0].cmapbar.pix, 0, 0, 
+		 image[0].cmapbar.x, image[0].cmapbar.y, 
+		 image[0].cmapbar.dx, image[0].cmapbar.dy);
+    }
+    if (image[0].wide.pix) {
+	XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+		   image[0].wide.pix, 0, 0, 
+		   image[0].wide.x, image[0].wide.y, 
+		   image[0].wide.dx, image[0].wide.dy);
+    }
 
     CrossHairs (graphic, &image[0].zoom);
Index: /trunk/Ohana/src/kapa2/src/InterpretKeys.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/InterpretKeys.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/InterpretKeys.c	(revision 25757)
@@ -93,5 +93,5 @@
       SetColorScale (graphic, image);
       Remap (graphic, image);
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, 0);
       break;
 
@@ -100,5 +100,5 @@
       SetColorScale (graphic, image);
       Remap (graphic, image);
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, 0);
       break;
 
@@ -107,5 +107,5 @@
       SetColorScale (graphic, image);
       Remap (graphic, image);
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, 0);
       break;
 
@@ -113,9 +113,11 @@
     case XK_Home:
       image[0].picture.expand = 1;
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      image[0].zoom.expand = 5;
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, 0);
       break;
     case XK_KP_End:
     case XK_End:
       image[0].picture.expand = 1;
+      image[0].zoom.expand = 5;
       Reorient (graphic, image, 0.5*image[0].image[0].matrix.Naxis[0], 0.5*image[0].image[0].matrix.Naxis[1], 0);
       break;
@@ -128,25 +130,25 @@
     case XK_Prior:
     case XK_KP_Prior:
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, +1);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, +1);
       break;
     case XK_Next:
     case XK_KP_Next:
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, -1);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, -1);
       break;
     case XK_Up:
     case XK_KP_Up:
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y + offset, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc + offset, 0);
       break;
     case XK_Down:
     case XK_KP_Down:
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y - offset, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc - offset, 0);
       break;
     case XK_Left:
     case XK_KP_Left:
-      Reorient (graphic, image, image[0].picture.X + offset, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc + offset, image[0].picture.Yc, 0);
       break;
     case XK_Right:
     case XK_KP_Right:
-      Reorient (graphic, image, image[0].picture.X - offset, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc - offset, image[0].picture.Yc, 0);
       break;
 
@@ -160,5 +162,5 @@
       SetColorScale (graphic, image);
       Remap (graphic, image);
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, 0);
       break;
     case XK_KP_Subtract:
@@ -171,5 +173,5 @@
       SetColorScale (graphic, image);
       Remap (graphic, image);
-      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      Reorient (graphic, image, image[0].picture.Xc, image[0].picture.Yc, 0);
       break;
 
Index: /trunk/Ohana/src/kapa2/src/JPEGit24.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/JPEGit24.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/JPEGit24.c	(revision 25757)
@@ -22,4 +22,5 @@
   int ii, i, j;
   int i_start, i_end, j_start, j_end;
+  int I_start, J_start;
   int dropback;  /* this is a bit of a kludge... */
   int dx, dy, DX, DY, inDX, inDY;
@@ -95,17 +96,17 @@
   DY = image[0].image[0].matrix.Naxis[1];
 
-  // x, y are the closest lit screen pixel to 0,0
-  Picture_Lower (&i_start, &j_start, &image[0].image[0].matrix, &image[0].picture);
-
-  // x, y are the closest lit screen pixel to dx, dy
-  Picture_Upper (&i_end, &j_end, &image[0].image[0].matrix, &image[0].picture);
-
-  if (i_end < i_start) MY_SWAP_INT (i_start, i_end);
-  if (j_end < j_start) MY_SWAP_INT (j_start, j_end);
-
-  // Ix,Iy are the first displayed image pixel
-  Picture_to_Image (&Ix, &Iy, i_start, j_start, &image[0].picture);
-  Ix = MIN (MAX (0, Ix), DX - 1);
-  Iy = MIN (MAX (0, Iy), DY - 1);
+  // i_start, j_start are the closest lit screen pixel to 0,0
+  // I_start, J_start are the image pixel corresponding to i_start, j_start
+  Picture_Lower (&i_start, &j_start, &I_start, &J_start, &image[0].image[0].matrix, &image[0].picture);
+
+  // i_end, j_end are the closest lit screen pixel to dx, dy
+  // I_end, J_end are the image pixel corresponding to i_end, j_end
+  Picture_Upper (&i_end, &j_end, i_start, j_start, &image[0].image[0].matrix, &image[0].picture);
+
+  assert (i_start <= i_end);
+  assert (j_start <= j_end);
+
+  Ix = image[0].picture.flipx ? I_start - 1 : I_start;
+  Iy = image[0].picture.flipy ? J_start - 1 : J_start;
 
   inDX = image[0].picture.flipx ? -1 : +1;
Index: /trunk/Ohana/src/kapa2/src/LoadPicture.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/LoadPicture.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/LoadPicture.c	(revision 25757)
@@ -25,6 +25,7 @@
   Yoffset = 0.0;
   if (image[0].image[0].matrix.size) {
-    Xoffset = image[0].picture.X - 0.5*image[0].image[0].matrix.Naxis[0];
-    Yoffset = image[0].picture.Y - 0.5*image[0].image[0].matrix.Naxis[1];
+    // XXX enforce int center here?
+    Xoffset = image[0].picture.Xc - 0.5*image[0].image[0].matrix.Naxis[0];
+    Yoffset = image[0].picture.Yc - 0.5*image[0].image[0].matrix.Naxis[1];
   }
 
@@ -49,6 +50,6 @@
 
   // reference point for image is the center pixel
-  image[0].picture.X = 0.5*header.Naxis[0] + Xoffset;
-  image[0].picture.Y = 0.5*header.Naxis[1] + Yoffset;
+  image[0].picture.Xc = 0.5*header.Naxis[0] + Xoffset;
+  image[0].picture.Yc = 0.5*header.Naxis[1] + Yoffset;
 
   // choose expand for wide to guarantee we fit:
@@ -59,6 +60,6 @@
     image[0].wide.expand = 1.0 / wx;
   }    
-  image[0].wide.X = 0.5*header.Naxis[0];
-  image[0].wide.Y = 0.5*header.Naxis[1];
+  image[0].wide.Xc = 0.5*header.Naxis[0];
+  image[0].wide.Yc = 0.5*header.Naxis[1];
 
   fcntl (sock, F_SETFL, O_NONBLOCK);  
Index: /trunk/Ohana/src/kapa2/src/MakeColormap.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/MakeColormap.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/MakeColormap.c	(revision 25757)
@@ -2,4 +2,5 @@
 
 static char default_cmap[] = "grayscale";
+// static char default_cmap[] = "heat";
 
 void MakeColormap (int argc, char **argv) {
Index: /trunk/Ohana/src/kapa2/src/PSOverlay.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/PSOverlay.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/PSOverlay.c	(revision 25757)
@@ -80,11 +80,11 @@
 	  double sn = sin(angle);
 	  x0 = X + pX*dX*cs;
-	  y0 = Y + pY*dX*sn;
+	  y0 = Y - pY*dX*sn;
 	  // XXX dt should be based on the size of the ellipse...
 	  // 0.10 -> 60 segments on the ellipse
 	  # define DT 0.1
 	  for (t = DT; t < 2*M_PI + DT; t+=DT) {
-	    x1 = X + pX*dX*cos(t)*cs - pX*dY*sin(t)*sn;
-	    y1 = Y + pY*dX*cos(t)*sn + pY*dY*sin(t)*cs;
+	    x1 = X + pX*dX*cos(t)*cs + pX*dY*sin(t)*sn;
+	    y1 = Y - pY*dX*cos(t)*sn + pY*dY*sin(t)*cs;
 	    fprintf (f, " %6.1f %6.1f %6.1f %6.1f L\n", x0 + extra, y0 + extra, x1 + extra, y1 + extra);
 	    x0 = x1;
Index: /trunk/Ohana/src/kapa2/src/PaintOverlay.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/PaintOverlay.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/PaintOverlay.c	(revision 25757)
@@ -9,5 +9,5 @@
  
   XSetForeground (graphic[0].display, graphic[0].gc, image[0].overlay[N].color);
-  XSetLineAttributes (graphic->display, graphic->gc, 2, LineSolid, CapNotLast, JoinMiter);
+  XSetLineAttributes (graphic->display, graphic->gc, 1, LineSolid, CapNotLast, JoinMiter);
   
   expand = 1.0;
@@ -72,11 +72,11 @@
 	  double sn = sin(angle);
 	  x0 = X + pX*dx*cs;
-	  y0 = Y - pY*dx*sn;
+	  y0 = Y + pY*dx*sn;
 	  // XXX dt should be based on the size of the ellipse...
 	  // 0.10 -> 60 segments on the ellipse
 	  # define DT 0.1
 	  for (t = DT; t < 2*M_PI + DT; t+=DT) {
-	    x1 = X + pX*dx*cos(t)*cs + pX*dy*sin(t)*sn;
-	    y1 = Y - pY*dx*cos(t)*sn + pY*dy*sin(t)*cs;
+	    x1 = X + pX*dx*cos(t)*cs - pX*dy*sin(t)*sn;
+	    y1 = Y + pY*dx*cos(t)*sn + pY*dy*sin(t)*cs;
 	    XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, x0, y0, x1, y1);
 	    x0 = x1;
Index: /trunk/Ohana/src/kapa2/src/Relocate.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Relocate.c	(revision 25757)
+++ /trunk/Ohana/src/kapa2/src/Relocate.c	(revision 25757)
@@ -0,0 +1,30 @@
+# include "Ximage.h"
+
+// XXX Should there be a base command + KiiMessage command?
+int Relocate (int sock) {
+ 
+  int i, Nsection;
+  int x, y, dX, dY;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  KiiScanMessage (sock, "%d %d", &x, &y);
+
+  if (!USE_XWINDOW) return (TRUE);
+
+  dX = DisplayWidth  (graphic->display, graphic->screen);
+  dY = DisplayHeight (graphic->display, graphic->screen);
+
+  // let's not lose the window
+  x = MAX(0, MIN(x, dX - 10)); 
+  y = MAX(0, MIN(y, dY - 10)); 
+
+  XMoveWindow (graphic->display, graphic->window, x, y);
+
+  // if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  // Refresh ();
+
+  return (TRUE);
+}
Index: /trunk/Ohana/src/kapa2/src/Remap16.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Remap16.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Remap16.c	(revision 25757)
@@ -1,3 +1,4 @@
 # include "Ximage.h"
+# define OUT_TYPE unsigned short
 
 # define MY_SWAP_BYTE(W) { \
@@ -10,15 +11,21 @@
   int i, j, ii, jj;
   int i_start, i_end, j_start, j_end;
-  int dropback;
+  int I_start, J_start;
+  int dropback, inDX, inDY;
   int dx, dy, DX, DY;
   double expand, Ix, Iy;
   int expand_in, expand_out;
-  unsigned short *out_pix, *out_pix2, *data;
+  OUT_TYPE *out_pix, *out_pix2, *data;
   unsigned short *in_pix, *in_pix2;
-  unsigned short *pixel, pixvalue;
-  unsigned short back;
+  OUT_TYPE *pixel, pixvalue;
+  OUT_TYPE back;
   int swap_client, swap_server, swap_bytes;
 
-  ALLOCATE (pixel, unsigned short, graphic[0].Npixels);
+  // just skip if there is no data
+  if (matrix[0].Naxes == 0) return;
+  if (matrix[0].Naxis[0] == 0) return;
+  if (matrix[0].Naxis[1] == 0) return;
+
+  ALLOCATE (pixel, OUT_TYPE, graphic[0].Npixels);
 
 # ifdef BYTE_SWAP
@@ -38,5 +45,4 @@
   back = 0xffff & graphic[0].back;
   if (swap_bytes) MY_SWAP_BYTE (back);
-  // XXX not certain this is the correct solution...
 
   // set up expansions
@@ -60,17 +66,25 @@
   DY = matrix[0].Naxis[1];
 
-  // x, y are the closest lit screen pixel to 0,0
-  Picture_Lower (&i_start, &j_start, matrix, picture);
+  // i_start, j_start are the closest lit screen pixel to 0,0
+  // I_start, J_start are the image pixel corresponding to i_start, j_start
+  Picture_Lower (&i_start, &j_start, &I_start, &J_start, matrix, picture);
 
-  // x, y are the closest lit screen pixel to dx, dy
-  Picture_Upper (&i_end, &j_end, matrix, picture);
+  // i_end, j_end are the closest lit screen pixel to dx, dy
+  // I_end, J_end are the image pixel corresponding to i_end, j_end
+  Picture_Upper (&i_end, &j_end, i_start, j_start, matrix, picture);
 
-  // Ix,Iy are the first displayed image pixel
-  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+  assert (i_start <= i_end);
+  assert (j_start <= j_end);
+
+  Ix = picture[0].flipx ? I_start - 1 : I_start;
+  Iy = picture[0].flipy ? J_start - 1 : J_start;
+
+  inDX = picture[0].flipx ? -1 : +1;
+  inDY = picture[0].flipy ? -1 : +1;
 
   dropback = expand_out - (i_end - i_start) % expand_out;
   if ((i_end - i_start) % expand_out == 0) dropback = 0;
 
-  data = out_pix = (unsigned short *) picture[0].data;
+  out_pix = data = (OUT_TYPE *) picture[0].data;
   in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
 
@@ -82,5 +96,5 @@
   }
   
-  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += inDY*expand_in*DX) {
     out_pix = &data[j*dx];
 
@@ -97,9 +111,9 @@
     in_pix2 = in_pix;
     if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+      for (i = i_start; i < i_end; i++, in_pix2 += inDX*expand_in, out_pix++) {
 	*out_pix = pixel[*in_pix2];
       }
     } else {
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2 += inDX, out_pix+= expand_out) { 
 	pixvalue = pixel[*in_pix2];
 	out_pix2 = out_pix;
@@ -124,5 +138,5 @@
   /**** fill in top area ****/
   out_pix = &data[j_end*dx];
-  for (j = 0; j < (dy - j_end); j++) {
+  for (j = 0; j < dy - j_end; j++) {
     for (i = 0; i < dx; i++, out_pix++) { 
       *out_pix = back;
Index: /trunk/Ohana/src/kapa2/src/Remap24.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Remap24.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Remap24.c	(revision 25757)
@@ -5,5 +5,6 @@
   int i, j, ii, jj;
   int i_start, i_end, j_start, j_end;
-  int dropback, extra;
+  int I_start, J_start;
+  int dropback, extra, inDX, inDY;
   int dx, dy, DX, DY;
   double expand, Ix, Iy;
@@ -53,12 +54,20 @@
   extra = 4 - (dx * 3) % 4;
 
-  // x, y are the closest lit screen pixel to 0,0
-  Picture_Lower (&i_start, &j_start, matrix, picture);
+  // i_start, j_start are the closest lit screen pixel to 0,0
+  // I_start, J_start are the image pixel corresponding to i_start, j_start
+  Picture_Lower (&i_start, &j_start, &I_start, &J_start, matrix, picture);
 
-  // x, y are the closest lit screen pixel to dx, dy
-  Picture_Upper (&i_end, &j_end, matrix, picture);
+  // i_end, j_end are the closest lit screen pixel to dx, dy
+  // I_end, J_end are the image pixel corresponding to i_end, j_end
+  Picture_Upper (&i_end, &j_end, i_start, j_start, matrix, picture);
 
-  // Ix,Iy are the first displayed image pixel
-  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+  assert (i_start <= i_end);
+  assert (j_start <= j_end);
+
+  Ix = picture[0].flipx ? I_start - 1 : I_start;
+  Iy = picture[0].flipy ? J_start - 1 : J_start;
+
+  inDX = picture[0].flipx ? -1 : +1;
+  inDY = picture[0].flipy ? -1 : +1;
 
   dropback = expand_out - (i_end - i_start) % expand_out;
@@ -80,5 +89,5 @@
   }
   
-  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += inDY*expand_in*DX) {
     out_pix = &data[j*(3*dx+extra)];
 
@@ -97,5 +106,5 @@
     in_pix2 = in_pix;
     if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+      for (i = i_start; i < i_end; i++, in_pix2+= inDX*expand_in, out_pix+=3) {
 	out_pix[0] = pixel1[*in_pix2];
 	out_pix[1] = pixel2[*in_pix2];
@@ -103,5 +112,5 @@
       }
     } else {
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2 += inDX, out_pix+= 3*expand_out) { 
 	pixvalue1 = pixel1[*in_pix2];
 	pixvalue2 = pixel2[*in_pix2];
@@ -121,5 +130,4 @@
     /**** fill in area to the right of the picture ****/
     for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++) {
-      //    for (jj = 0; jj < expand_out; jj++) {
       out_pix2 = out_pix + jj*(3*dx+extra);
       for (i = i_end; i < dx; i++, out_pix2+=3) {
Index: /trunk/Ohana/src/kapa2/src/Remap32.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Remap32.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Remap32.c	(revision 25757)
@@ -1,11 +1,12 @@
 # include "Ximage.h"
+# define OUT_TYPE unsigned int
 
 # define MY_SWAP_INT(A,B) { int tmp; tmp = A; A = B; B = tmp; }
 
-# define MY_SWAP_WORD(W) { \
-  char tmp, *X; \
-  X = (char *) &W; \
-  tmp = X[0]; X[0] = X[3]; X[3] = tmp; \
-  tmp = X[1]; X[1] = X[2]; X[2] = tmp; }
+# define MY_SWAP_WORD(W) {			\
+    char tmp, *X;				\
+    X = (char *) &W;				\
+    tmp = X[0]; X[0] = X[3]; X[3] = tmp;	\
+    tmp = X[1]; X[1] = X[2]; X[2] = tmp; }
 
 void Remap32 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
@@ -13,15 +14,21 @@
   int i, j, ii, jj;
   int i_start, i_end, j_start, j_end;
+  int I_start, J_start;
   int dropback, inDX, inDY;
   int dx, dy, DX, DY;
   double expand, Ix, Iy;
   int expand_in, expand_out;
-  unsigned int *out_pix, *out_pix2;
+  OUT_TYPE *out_pix, *out_pix2, *data;
   unsigned short *in_pix, *in_pix2;
-  unsigned long *pixel, pixvalue;
-  unsigned long back;
+  OUT_TYPE *pixel, pixvalue;
+  OUT_TYPE back;
   int swap_client, swap_server, swap_bytes;
 
-  ALLOCATE (pixel, unsigned long, graphic[0].Npixels);
+  // just skip if there is no data
+  if (matrix[0].Naxes == 0) return;
+  if (matrix[0].Naxis[0] == 0) return;
+  if (matrix[0].Naxis[1] == 0) return;
+
+  ALLOCATE (pixel, OUT_TYPE, graphic[0].Npixels);
 
 # ifdef BYTE_SWAP
@@ -43,5 +50,5 @@
 
   // set up expansions
-  assert ((picture[0].expand >= 1) || (picture[0].expand <= -1));
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
   expand = expand_in = expand_out = 1.0;
   if (picture[0].expand > 0) {
@@ -62,22 +69,17 @@
   DY = matrix[0].Naxis[1];
 
-  // x, y are the closest lit screen pixel to 0,0
-  Picture_Lower (&i_start, &j_start, matrix, picture);
+  // i_start, j_start are the closest lit screen pixel to 0,0
+  // I_start, J_start are the image pixel corresponding to i_start, j_start
+  Picture_Lower (&i_start, &j_start, &I_start, &J_start, matrix, picture);
 
-  // x, y are the closest lit screen pixel to dx, dy
-  Picture_Upper (&i_end, &j_end, matrix, picture);
+  // i_end, j_end are the closest lit screen pixel to dx, dy
+  // I_end, J_end are the image pixel corresponding to i_end, j_end
+  Picture_Upper (&i_end, &j_end, i_start, j_start, matrix, picture);
 
-  if (i_end < i_start) MY_SWAP_INT (i_start, i_end);
-  if (j_end < j_start) MY_SWAP_INT (j_start, j_end);
+  assert (i_start <= i_end);
+  assert (j_start <= j_end);
 
-  // Ix,Iy are the first displayed image pixel
-  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
-  Ix = MIN (MAX (0, Ix), DX - 1);
-  Iy = MIN (MAX (0, Iy), DY - 1);
-  // XXX not completely consistent with the i_start, i_end range...
-
-  // we need to offset because i_start points to the bottom edge of Ix
-  // if (picture[0].flipx) Ix -= 1.0;
-  // if (picture[0].flipy) Iy -= 1.0;
+  Ix = picture[0].flipx ? I_start - 1 : I_start;
+  Iy = picture[0].flipy ? J_start - 1 : J_start;
 
   inDX = picture[0].flipx ? -1 : +1;
@@ -87,5 +89,5 @@
   if ((i_end - i_start) % expand_out == 0) dropback = 0;
 
-  out_pix = (unsigned int *) picture[0].data;
+  out_pix = data = (OUT_TYPE *) picture[0].data;
   in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
 
@@ -97,5 +99,6 @@
   }
   
-  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += inDY*expand_in*DX) {
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += inDY*expand_in*DX) {
+    out_pix = &data[j*dx];
 
     /**** fill in area to the left of the picture ****/
@@ -126,9 +129,4 @@
     }
     out_pix -= dropback;
-    
-    // assert (in_pix2 - image[0].pixmap <= DX*DY);
-    // assert (in_pix2 - image[0].pixmap >= 0);
-    // assert (in_pix - image[0].pixmap <= DX*DY);
-    // assert (in_pix - image[0].pixmap >= 0);
 
     /**** fill in area to the right of the picture ****/
@@ -139,22 +137,16 @@
       }
     }
-    out_pix += (dx - i_end);
-    // assert (out_pix - (unsigned int *)picture[0].data <= dx*dy);
-    // assert (out_pix - (unsigned int *)picture[0].data >= 0);
   } 
-  
-  if ((j_end - j_start) % expand_out > 0)
-    out_pix -= expand_out - (j_end - j_start) % expand_out;
-  
-  // assert (out_pix - (unsigned int *)picture[0].data <= dx*dy);
-  // assert (out_pix - (unsigned int *)picture[0].data >= 0);
 
   /**** fill in top area ****/
-  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)picture[0].data < dx*dy); j++, out_pix++) { 
-    *out_pix = back;
+  out_pix = &data[j_end*dx];
+  for (j = 0; j < dy - j_end; j++) {
+    for (i = 0; i < dx; i++, out_pix ++) {
+      *out_pix = back;
+    }
   }
 
   picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
-					picture[0].data, picture[0].dx, picture[0].dy, 32, 0);
+				 picture[0].data, picture[0].dx, picture[0].dy, 32, 0);
 
   free (pixel);
Index: /trunk/Ohana/src/kapa2/src/Remap8.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Remap8.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Remap8.c	(revision 25757)
@@ -1,3 +1,4 @@
 # include "Ximage.h"
+# define OUT_TYPE unsigned char
 
 void Remap8 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
@@ -5,14 +6,20 @@
   int i, j, ii, jj;
   int i_start, i_end, j_start, j_end;
-  int dropback;
+  int I_start, J_start;
+  int dropback, inDX, inDY;
   int dx, dy, DX, DY;
   double expand, Ix, Iy;
   int expand_in, expand_out;
-  unsigned char *out_pix, *out_pix2;
-  unsigned short *in_pix,  *in_pix2;
-  unsigned char *pixel, pixvalue;
-  unsigned char back;
+  OUT_TYPE *out_pix, *out_pix2, *data;
+  unsigned short *in_pix, *in_pix2;
+  OUT_TYPE *pixel, pixvalue;
+  OUT_TYPE back;
 
-  ALLOCATE (pixel, unsigned char, graphic[0].Npixels);
+  // just skip if there is no data
+  if (matrix[0].Naxes == 0) return;
+  if (matrix[0].Naxis[0] == 0) return;
+  if (matrix[0].Naxis[1] == 0) return;
+
+  ALLOCATE (pixel, OUT_TYPE, graphic[0].Npixels);
 
   // local array for pixel values
@@ -44,17 +51,25 @@
   DY = matrix[0].Naxis[1];
 
-  // x, y are the closest lit screen pixel to 0,0
-  Picture_Lower (&i_start, &j_start, matrix, picture);
+  // i_start, j_start are the closest lit screen pixel to 0,0
+  // I_start, J_start are the image pixel corresponding to i_start, j_start
+  Picture_Lower (&i_start, &j_start, &I_start, &J_start, matrix, picture);
 
-  // x, y are the closest lit screen pixel to dx, dy
-  Picture_Upper (&i_end, &j_end, matrix, picture);
+  // i_end, j_end are the closest lit screen pixel to dx, dy
+  // I_end, J_end are the image pixel corresponding to i_end, j_end
+  Picture_Upper (&i_end, &j_end, i_start, j_start, matrix, picture);
 
-  // Ix,Iy are the first displayed image pixel
-  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+  assert (i_start <= i_end);
+  assert (j_start <= j_end);
+
+  Ix = picture[0].flipx ? I_start - 1 : I_start;
+  Iy = picture[0].flipy ? J_start - 1 : J_start;
+
+  inDX = picture[0].flipx ? -1 : +1;
+  inDY = picture[0].flipy ? -1 : +1;
 
   dropback = expand_out - (i_end - i_start) % expand_out;
   if ((i_end - i_start) % expand_out == 0) dropback = 0;
 
-  out_pix = (unsigned char *) picture[0].data;
+  out_pix = data = (OUT_TYPE *) picture[0].data;
   in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
 
@@ -66,5 +81,6 @@
   }
   
-  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += inDY*expand_in*DX) {
+    out_pix = &data[j*dx];
 
     /**** fill in area to the left of the picture ****/
@@ -80,12 +96,12 @@
     in_pix2 = in_pix;
     if (expand_out == 1) {
-      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+      for (i = i_start; i < i_end; i++, in_pix2 += inDX*expand_in, out_pix++) {
 	*out_pix = pixel[*in_pix2];
       }
     } else {
-      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2 += inDX, out_pix+= expand_out) { 
 	pixvalue = pixel[*in_pix2];
 	out_pix2 = out_pix;
-	for (jj = 0; (jj < expand_out) & (j + jj < dy); jj++, out_pix2+=(dx-expand_out)) {
+	for (jj = 0; (jj < expand_out) && (j + jj < dy); jj++, out_pix2+=(dx-expand_out)) {
 	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
 	    *out_pix2 = pixvalue;
@@ -103,14 +119,12 @@
       }
     }
-    out_pix += (dx - i_end);
-    
   } 
   
-  if ((j_end - j_start) % expand_out > 0)
-    out_pix -= expand_out - (j_end - j_start) % expand_out;
-  
   /**** fill in top area ****/
-  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *)picture[0].data < dx*dy); j++, out_pix++) { 
-    *out_pix = back;
+  out_pix = &data[j_end*dx];
+  for (j = 0; j < dy - j_end; j++) {
+    for (i = 0; i < dx; i++, out_pix++) {
+      *out_pix = back;
+    }
   }
   picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
Index: /trunk/Ohana/src/kapa2/src/Reorient.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/Reorient.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/Reorient.c	(revision 25757)
@@ -7,7 +7,10 @@
   picture = &image[0].picture;
 
-  if (picture[0].expand == 0) picture[0].expand = 1;
+  if (picture[0].expand == 0) {
+      picture[0].expand = 1;
+      image[0].zoom.expand = 5;
+  }
 
-  if ((picture[0].X == X) && (picture[0].Y == Y) && (mode == 0)) {
+  if ((picture[0].Xc == X) && (picture[0].Yc == Y) && (mode == 0)) {
     Refresh ();
     XFlush (graphic[0].display);
@@ -17,7 +20,7 @@
   switch (mode) {
   case 0:
-    if ((picture[0].X != X) || (picture[0].Y != Y)) {
-      picture[0].X = X;
-      picture[0].Y = Y;
+    if ((picture[0].Xc != X) || (picture[0].Yc != Y)) {
+      picture[0].Xc = X;
+      picture[0].Yc = Y;
     }
     break;
@@ -25,14 +28,15 @@
     picture[0].expand--;
     if ((picture[0].expand == 0) || (picture[0].expand == -1)) picture[0].expand = -2;
-    picture[0].X = X;
-    picture[0].Y = Y;
+    picture[0].Xc = X;
+    picture[0].Yc = Y;
     break;
   case +1:
     picture[0].expand++;
     if ((picture[0].expand == 0) || (picture[0].expand == -1)) picture[0].expand = 1;
-    picture[0].X = X;
-    picture[0].Y = Y;
+    picture[0].Xc = X;
+    picture[0].Yc = Y;
     break;
   }
+  image[0].zoom.expand = MIN(image[0].zoom.dx / 5.5, MAX(5, 2*picture[0].expand));
 
   Remap (graphic, image);
Index: /trunk/Ohana/src/kapa2/src/SetColorScale.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/SetColorScale.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/SetColorScale.c	(revision 25757)
@@ -218,5 +218,5 @@
   // store the colors
   if (USE_XWINDOW) {
-    if (graphic[0].visualclass) {
+    if (graphic[0].dynamicColors) {
       XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, Npixels);
     } else {
Index: /trunk/Ohana/src/kapa2/src/SetColormap.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/SetColormap.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/SetColormap.c	(revision 25757)
@@ -146,9 +146,11 @@
  store_colors:
   if (!USE_XWINDOW) return (TRUE);
-  if (graphic[0].visualclass) {
-    XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, graphic[0].Npixels);
+  if (graphic[0].dynamicColors) {
+    if (!XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, graphic[0].Npixels)) {
+	fprintf (stderr, "error storing colors\n");
+    }
   } else {
     for (i = 0; i < graphic[0].Npixels; i++) {
-      if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
+      if (!XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i])) {
 	fprintf (stderr, "error on %d\n", i);
       }
Index: /trunk/Ohana/src/kapa2/src/SetUpGraphic.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 25757)
@@ -60,4 +60,5 @@
   graphic->color = KapaX11colors (graphic->display, graphic->colormap, graphic->fore, &Ncolors);
   if (MAP_WINDOW) MapWindow (graphic);
+
   return;
 }
Index: /trunk/Ohana/src/kapa2/src/StatusBox.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/StatusBox.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/StatusBox.c	(revision 25757)
@@ -11,10 +11,10 @@
     y = 0.5*image[0].image[0].matrix.Naxis[1];
     // z = -1;
-    image[0].zoom.X = x;
-    image[0].zoom.Y = y;
+    image[0].zoom.Xc = x;
+    image[0].zoom.Yc = y;
     // image[0].z = z;
   } else {
-    x = image[0].zoom.X;
-    y = image[0].zoom.Y;
+    x = image[0].zoom.Xc;
+    y = image[0].zoom.Yc;
     // z = image[0].z;
   }
Index: /trunk/Ohana/src/kapa2/src/UpdatePointer.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/UpdatePointer.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/UpdatePointer.c	(revision 25757)
@@ -29,6 +29,6 @@
 
   skip:
-    image[0].zoom.X = x;
-    image[0].zoom.Y = y;
+    image[0].zoom.Xc = x;
+    image[0].zoom.Yc = y;
     
     UpdateStatusBox (graphic, image, x, y, z, 0);
@@ -36,8 +36,10 @@
       
     CreateZoom (graphic, image);  
-    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
-	       image[0].zoom.pix, 0, 0, 
-	       image[0].zoom.x, image[0].zoom.y, 
-	       image[0].zoom.dx, image[0].zoom.dy);
+    if (image[0].zoom.pix) {
+	XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+		   image[0].zoom.pix, 0, 0, 
+		   image[0].zoom.x, image[0].zoom.y, 
+		   image[0].zoom.dx, image[0].zoom.dy);
+    }
     CrossHairs (graphic, &image[0].zoom);
     XFlush (graphic[0].display);
Index: /trunk/Ohana/src/kapa2/src/UpdateStatusBox.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/UpdateStatusBox.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/UpdateStatusBox.c	(revision 25757)
@@ -48,5 +48,5 @@
   
   bzero (line, 100);
-  sprintf (line, "%10.1f %10.1f", x, y);
+  sprintf (line, "%10.2f %10.2f", x, y);
   XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
 	       image[0].text_x + PAD1, image[0].text_y + 2*textpad + 2*PAD1, line, strlen(line));
Index: /trunk/Ohana/src/kapa2/src/bDrawOverlay.c
===================================================================
--- /trunk/Ohana/src/kapa2/src/bDrawOverlay.c	(revision 25756)
+++ /trunk/Ohana/src/kapa2/src/bDrawOverlay.c	(revision 25757)
@@ -81,11 +81,11 @@
 	  double sn = sin(angle);
 	  x0 = X + pX*dx*cs;
-	  y0 = Y - pY*dx*sn;
+	  y0 = Y + pY*dx*sn;
 	  // XXX dt should be based on the size of the ellipse...
 	  // 0.10 -> 60 segments on the ellipse
 	  # define DT 0.1
 	  for (t = DT; t < 2*M_PI + DT; t+=DT) {
-	    x1 = X + pX*dx*cos(t)*cs + pX*dy*sin(t)*sn;
-	    y1 = Y - pY*dx*cos(t)*sn + pY*dy*sin(t)*cs;
+	    x1 = X + pX*dx*cos(t)*cs - pX*dy*sin(t)*sn;
+	    y1 = Y + pY*dx*cos(t)*sn + pY*dy*sin(t)*cs;
 	    bDrawLine (x0, y0, x1, y1);
 	    x0 = x1;
Index: /trunk/Ohana/src/kapa2/test/overlay.sh
===================================================================
--- /trunk/Ohana/src/kapa2/test/overlay.sh	(revision 25757)
+++ /trunk/Ohana/src/kapa2/test/overlay.sh	(revision 25757)
@@ -0,0 +1,10 @@
+
+macro checker
+  mcreate a 8 8
+  for ix 0 8
+    for iy 0 8
+      if (($ix + $iy) % 2) continue
+      a[$ix][$iy] = $ix + $iy
+    end
+  end
+end
Index: /trunk/Ohana/src/libdvo/doc/locking.txt
===================================================================
--- /trunk/Ohana/src/libdvo/doc/locking.txt	(revision 25757)
+++ /trunk/Ohana/src/libdvo/doc/locking.txt	(revision 25757)
@@ -0,0 +1,13 @@
+
+The DVO locking policy is a little bit broken.  DVO currently
+(2009.08.10) requires that users have write access to files because
+the code does not distinguish well cases where read-only (soft locks)
+are used vs cases of read/write (hard locks) are needed.
+
+locks set in DVO:
+
+dvo_catalog_lock -- on catalog open & catalog create (all files for
+		    split mode)
+
+gfits_db_lock -- on photcode table read/write and dvo_image_lock
+
Index: /trunk/Ohana/src/libdvo/include/dvo.h
===================================================================
--- /trunk/Ohana/src/libdvo/include/dvo.h	(revision 25756)
+++ /trunk/Ohana/src/libdvo/include/dvo.h	(revision 25757)
@@ -325,5 +325,5 @@
 PhotCodeData *GetPhotcodeTable (void);
 
-int LoadPhotcodes (char *catdir_file, char *master_file);
+int LoadPhotcodes (char *catdir_file, char *master_file, int readwrite);
 int LoadPhotcodesText (char *filename);
 int LoadPhotcodesFITS (char *filename);
@@ -432,5 +432,5 @@
 SkyTable  *SkyTableLoad        	   PROTO((char *filename, int VERBOSE));
 SkyTable  *SkyTableFromGSC     	   PROTO((char *filename, int depth, int VERBOSE));
-SkyTable  *SkyTableLoadOptimal 	   PROTO((char *catdir, char *SKYFILE, char *GSCFILE, int depth, int VERBOSE));
+SkyTable  *SkyTableLoadOptimal 	   PROTO((char *catdir, char *SKYFILE, char *GSCFILE, int readwrite, int depth, int VERBOSE));
 int        SkyTableSetDepth    	   PROTO((SkyTable *sky, int depth));
 SkyList   *SkyRegionByPoint    	   PROTO((SkyTable *table, int depth, double ra, double dec));
Index: /trunk/Ohana/src/libdvo/src/LoadPhotcodes.c
===================================================================
--- /trunk/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 25756)
+++ /trunk/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 25757)
@@ -1,15 +1,20 @@
 # include <dvo.h>
 
-int LoadPhotcodes (char *catdir_file, char *master_file) {
+int LoadPhotcodes (char *catdir_file, char *master_file, int readwrite) {
 
   /* first try to load the photcodes from the specified CATDIR location */
   if (LoadPhotcodesFITS (catdir_file)) return TRUE;
   
+  if (!readwrite) {
+    fprintf (stderr, "db is missing a photcode table & access is read-only -- create one with photcode-table -import\n");
+    return FALSE;
+  }
+
   /* next try to load the photcodes from the master text photcode file */
   /* automatically (or on demand?) save the text file to the FITS version */
   if (LoadPhotcodesText (master_file)) { 
-      if (!check_file_access (catdir_file, TRUE, TRUE)) return TRUE;
-      SavePhotcodesFITS (catdir_file);
-      return TRUE;
+    if (!check_file_access (catdir_file, TRUE, TRUE, TRUE)) return TRUE;
+    SavePhotcodesFITS (catdir_file);
+    return TRUE;
   }
 
Index: /trunk/Ohana/src/libdvo/src/coordops.c
===================================================================
--- /trunk/Ohana/src/libdvo/src/coordops.c	(revision 25756)
+++ /trunk/Ohana/src/libdvo/src/coordops.c	(revision 25757)
@@ -163,4 +163,5 @@
 	return (FALSE);
     }
+    if (fabs(alpha) >= 180.0) return (FALSE);
     *ra  = alpha + coords[0].crval1;
     *dec = delta + coords[0].crval2;
Index: /trunk/Ohana/src/libdvo/src/dvo_catalog.c
===================================================================
--- /trunk/Ohana/src/libdvo/src/dvo_catalog.c	(revision 25756)
+++ /trunk/Ohana/src/libdvo/src/dvo_catalog.c	(revision 25757)
@@ -180,4 +180,5 @@
 
   int Nsecfilt, mode;
+  int BACKUP, READWRITE;
 
   mode = DVO_OPEN_NONE;
@@ -192,9 +193,17 @@
   dvo_catalog_init (catalog, FALSE);
 
+  // default access control options:
   catalog[0].lockmode  = LCK_XCLD;
-  if (mode == DVO_OPEN_READ) catalog[0].lockmode  = LCK_SOFT;
+  BACKUP = TRUE;
+  READWRITE = TRUE;
+
+  // in read-only mode, do not backup or require write access
+  if (mode == DVO_OPEN_READ) {
+    catalog[0].lockmode  = LCK_SOFT;
+    BACKUP = FALSE;
+    READWRITE = FALSE;
+  }
   
-  // XXX make a backup?  always?
-  if (!check_file_access (catalog[0].filename, TRUE, VERBOSE)) {
+  if (!check_file_access (catalog[0].filename, BACKUP, READWRITE, VERBOSE)) {
     if (VERBOSE) fprintf (stderr, "no permission to access %s\n", catalog[0].filename);
     return (FALSE);
Index: /trunk/Ohana/src/libdvo/src/dvo_image.c
===================================================================
--- /trunk/Ohana/src/libdvo/src/dvo_image.c	(revision 25756)
+++ /trunk/Ohana/src/libdvo/src/dvo_image.c	(revision 25757)
@@ -4,6 +4,16 @@
 int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate) {
 
-  /* lock the image catalog */
-  if (!check_file_access (filename, FALSE, TRUE)) return (FALSE);
+  int READWRITE;
+
+  // default access control options:
+  READWRITE = TRUE;
+
+  // in read-only mode, do not backup or require write access
+  if (lockstate == LCK_SOFT) {
+    READWRITE = FALSE;
+  }
+
+  // do not perform a backup here
+  if (!check_file_access (filename, FALSE, READWRITE, TRUE)) return (FALSE);
 
   db[0].lockstate = lockstate;
Index: /trunk/Ohana/src/libdvo/src/skyregion_io.c
===================================================================
--- /trunk/Ohana/src/libdvo/src/skyregion_io.c	(revision 25756)
+++ /trunk/Ohana/src/libdvo/src/skyregion_io.c	(revision 25757)
@@ -84,5 +84,5 @@
 }
 
-SkyTable *SkyTableLoadOptimal (char *catdir, char *skyfile, char *gscfile, int depth, int verbose) {
+SkyTable *SkyTableLoadOptimal (char *catdir, char *skyfile, char *gscfile, int readwrite, int depth, int verbose) {
 
   char filename[256];
@@ -93,5 +93,5 @@
   sprintf (filename, "%s/SkyTable.fits", catdir);
   if (stat (filename, &filestat)) goto SKYFILE;
-  if (!check_file_access (filename, FALSE, verbose)) goto SKYFILE;
+  if (!check_file_access (filename, FALSE, readwrite, verbose)) goto SKYFILE;
   sky = SkyTableLoad (filename, verbose);
   if (sky == NULL) {
@@ -106,5 +106,5 @@
   if (skyfile[0] != 0) goto GSCFILE;
   if (stat (skyfile, &filestat)) goto GSCFILE;
-  if (!check_file_access (skyfile, FALSE, verbose)) goto GSCFILE;
+  if (!check_file_access (skyfile, FALSE, readwrite, verbose)) goto GSCFILE;
   sky = SkyTableLoad (skyfile, verbose);
   if (sky == NULL) {
@@ -117,5 +117,5 @@
   /* create CATDIR copy */
   sprintf (filename, "%s/SkyTable.fits", catdir);
-  check_file_access (filename, FALSE, verbose);
+  check_file_access (filename, FALSE, readwrite, verbose);
   if (!SkyTableSave (sky, filename)) return NULL;
 
@@ -134,5 +134,5 @@
   /* create CATDIR copy */
   sprintf (filename, "%s/SkyTable.fits", catdir);
-  check_file_access (filename, FALSE, verbose);
+  check_file_access (filename, FALSE, readwrite, verbose);
   if (!SkyTableSave (sky, filename)) return NULL;
 
Index: /trunk/Ohana/src/libkapa/include/kapa.h
===================================================================
--- /trunk/Ohana/src/libkapa/include/kapa.h	(revision 25756)
+++ /trunk/Ohana/src/libkapa/include/kapa.h	(revision 25757)
@@ -154,4 +154,5 @@
 /* KapaWindow.c */
 int KiiResize (int fd, int Nx, int Ny);
+int KiiRelocate (int fd, int x, int y);
 int KiiCenter (int fd, double x, double y, int zoom);
 int KiiParity (int fd, int xflip, int yflip);
Index: /trunk/Ohana/src/libkapa/src/KapaWindow.c
===================================================================
--- /trunk/Ohana/src/libkapa/src/KapaWindow.c	(revision 25756)
+++ /trunk/Ohana/src/libkapa/src/KapaWindow.c	(revision 25757)
@@ -23,4 +23,12 @@
   KiiSendCommand (fd, 4, "RSIZ");
   KiiSendMessage (fd, "%d %d", Nx, Ny); 
+  KiiWaitAnswer (fd, "DONE");
+  return (TRUE);
+}
+
+int KiiRelocate (int fd, int x, int y) {
+
+  KiiSendCommand (fd, 4, "MOVE");
+  KiiSendMessage (fd, "%d %d", x, y); 
   KiiWaitAnswer (fd, "DONE");
   return (TRUE);
Index: /trunk/Ohana/src/libohana/include/ohana.h
===================================================================
--- /trunk/Ohana/src/libohana/include/ohana.h	(revision 25756)
+++ /trunk/Ohana/src/libohana/include/ohana.h	(revision 25757)
@@ -166,5 +166,5 @@
 int     mkdirhier              PROTO((char *path, int mode));
 void    make_backup            PROTO((char *filename));
-int     check_file_access      PROTO((char *basefile, int backup, int verbose));
+int     check_file_access      PROTO((char *basefile, int backup, int readwrite, int verbose));
 int     check_dir_access       PROTO((char *path, int verbose));
 int     check_file_exec        PROTO((char *filename));
Index: /trunk/Ohana/src/libohana/src/findexec.c
===================================================================
--- /trunk/Ohana/src/libohana/src/findexec.c	(revision 25756)
+++ /trunk/Ohana/src/libohana/src/findexec.c	(revision 25757)
@@ -67,5 +67,5 @@
    - file backup permission OK (optional)
 */
-int check_file_access (char *basefile, int BACKUP, int VERBOSE) {
+int check_file_access (char *basefile, int BACKUP, int READWRITE, int VERBOSE) {
   
   char *path, *filename;
@@ -74,7 +74,12 @@
   gid_t gid;
   int status;
+  int valid;
 
   uid = getuid();
   gid = getgid();
+
+  // XXX this function needs to call 'getgroups' to get the full list of the user's
+  // groups.  we would then need to loop over all groups in the gid test below 
+  // to see if any match the file.  test to see how slow this is.
 
   /* check permission to write to directory */
@@ -87,8 +92,18 @@
   status = stat (basefile, &filestat);
   if (status == 0) { /* file exists, are permissions OK? */
-    if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
-	((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
-	((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
-    } else {
+    valid = FALSE;
+    if (!valid && (uid == filestat.st_uid)) {
+      valid = (filestat.st_mode & S_IRUSR) != 0;
+      valid &= !READWRITE || (filestat.st_mode & S_IWUSR);
+    }
+    if (!valid && (gid == filestat.st_gid)) {
+      valid = (filestat.st_mode & S_IRGRP) != 0;
+      valid &= !READWRITE || (filestat.st_mode & S_IWGRP);
+    }
+    if (!valid) {
+      valid = (filestat.st_mode & S_IROTH) != 0;
+      valid &= !READWRITE || (filestat.st_mode & S_IWOTH);
+    }
+    if (!valid) {
       if (VERBOSE) fprintf (stderr, "can't write to %s\n", basefile);
       return (FALSE);
@@ -102,8 +117,18 @@
     status = stat (filename, &filestat);
     if (status == 0) { /* file exists, are permissions OK? */
-      if (((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR) && (filestat.st_mode & S_IWUSR)) ||
-	  ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP) && (filestat.st_mode & S_IWGRP)) || 
-	  ((filestat.st_mode & S_IROTH) && (filestat.st_mode & S_IWOTH))) {
-      } else {
+      valid = FALSE;
+      if (!valid && (uid == filestat.st_uid)) {
+	valid = (filestat.st_mode & S_IRUSR) != 0;
+	valid &= !READWRITE || (filestat.st_mode & S_IWUSR);
+      }
+      if (!valid && (gid == filestat.st_gid)) {
+	valid = (filestat.st_mode & S_IRGRP) != 0;
+	valid &= !READWRITE || (filestat.st_mode & S_IWGRP);
+      }
+      if (!valid) {
+	valid = (filestat.st_mode & S_IROTH) != 0;
+	valid &= !READWRITE || (filestat.st_mode & S_IWOTH);
+      }
+      if (!valid) {
 	if (VERBOSE) fprintf (stderr, "can't write to %s\n", filename);
 	return (FALSE);
Index: /trunk/Ohana/src/markrock/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/markrock/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/markrock/src/ConfigInit.c	(revision 25757)
@@ -44,5 +44,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/markrock/src/markrock.c
===================================================================
--- /trunk/Ohana/src/markrock/src/markrock.c	(revision 25756)
+++ /trunk/Ohana/src/markrock/src/markrock.c	(revision 25757)
@@ -33,6 +33,6 @@
 
   /* if lockfile exists, program will complain and quit */
-  if (!check_file_access (argv[1], TRUE, TRUE)) exit (1);
-  if (!check_file_access (RockCat, TRUE, TRUE)) exit (1);
+  if (!check_file_access (argv[1], TRUE, TRUE, TRUE)) exit (1);
+  if (!check_file_access (RockCat, TRUE, TRUE, TRUE)) exit (1);
 
   catalog.filename = argv[1];
Index: /trunk/Ohana/src/markstar/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/markstar/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/markstar/src/ConfigInit.c	(revision 25757)
@@ -54,5 +54,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/mosastro/src/getptolemy.c
===================================================================
--- /trunk/Ohana/src/mosastro/src/getptolemy.c	(revision 25756)
+++ /trunk/Ohana/src/mosastro/src/getptolemy.c	(revision 25757)
@@ -19,5 +19,5 @@
 
   /* load regions from GSC table, restrict to patch */
-  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, FALSE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   skylist = SkyListByPatch (sky, -1, &patch);
Index: /trunk/Ohana/src/opihi/cmd.astro/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/Makefile	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.astro/Makefile	(revision 25757)
@@ -21,4 +21,5 @@
 $(SRC)/cgrid.$(ARCH).o		   \
 $(SRC)/coords.$(ARCH).o	   \
+$(SRC)/cdot.$(ARCH).o		   \
 $(SRC)/cplot.$(ARCH).o		   \
 $(SRC)/csystem.$(ARCH).o	   \
@@ -36,4 +37,11 @@
 $(SRC)/medianmap.$(ARCH).o	   \
 $(SRC)/mkgauss.$(ARCH).o	   \
+$(SRC)/mksersic.$(ARCH).o	   \
+$(SRC)/galradius.$(ARCH).o	   \
+$(SRC)/galradbins.$(ARCH).o	   \
+$(SRC)/galsectors.$(ARCH).o	   \
+$(SRC)/galprofiles.$(ARCH).o	   \
+$(SRC)/elliprofile.$(ARCH).o	   \
+$(SRC)/petrosian.$(ARCH).o	   \
 $(SRC)/multifit.$(ARCH).o	   \
 $(SRC)/objload.$(ARCH).o	   \
Index: /trunk/Ohana/src/opihi/cmd.astro/cdot.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/cdot.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/cdot.c	(revision 25757)
@@ -0,0 +1,38 @@
+# include "data.h"
+
+int cdot (int argc, char **argv) {
+  
+  int kapa, status;
+  Graphdata graphmode;
+  float x, y;
+  double r, d, Rmin, Rmax;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dot <ra> <dec>\n");
+    return (FALSE);
+  }
+  r = atof(argv[1]);
+  d = atof(argv[2]);
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  // Rmid = 0.5*(Rmin + Rmax);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 2;
+  graphmode.etype = 0;
+
+  r = ohana_normalize_angle (r);
+  while (r < Rmin) r += 360.0;
+  while (r > Rmax) r -= 360.0;
+
+  status = fRD_to_XY (&x, &y, r, d, &graphmode.coords);
+
+  if (!KapaPrepPlot (kapa, 1, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 1, &x, "x");
+  KapaPlotVector (kapa, 1, &y, "y");
+  
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/cgrid.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 25757)
@@ -37,5 +37,5 @@
   /* set spacings for RA */
   minorRA = minorDEC = 0.1;
-  range = MIN (fabs(graphmode.xmax-graphmode.xmin), fabs(graphmode.ymax-graphmode.ymin));
+  range = MIN (fabs(graphmode.coords.cdelt1*(graphmode.xmax-graphmode.xmin)), fabs(graphmode.coords.cdelt2*(graphmode.ymax-graphmode.ymin)));
   if (NorthPole || SouthPole) range = 360;
   lrange = log10(MAX(fabs(range), 1e-30));
@@ -58,6 +58,7 @@
   }
   dR = range / 100.0;
+
   /* set spacings for DEC */
-  range = MIN (fabs(graphmode.xmax-graphmode.xmin), fabs(graphmode.ymax-graphmode.ymin));
+  range = MIN (fabs(graphmode.coords.cdelt1*(graphmode.xmax-graphmode.xmin)), fabs(graphmode.coords.cdelt2*(graphmode.ymax-graphmode.ymin)));
   lrange = log10(MAX(fabs(range), 1e-30));
   factor = (int) (lrange);
Index: /trunk/Ohana/src/opihi/cmd.astro/elliprofile.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/elliprofile.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/elliprofile.c	(revision 25757)
@@ -0,0 +1,64 @@
+# include "astro.h"
+
+int elliprofile (int argc, char **argv) {
+  
+  int i, ix, iy, Nx, Ny, N, center;
+  float *in;
+  double Rmaj, Rmin, phi, alpha, Io, ARatio;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, r, f, Xo, Yo;
+  Buffer *buf;
+  Vector *rvec, *fvec;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: elliprofile (buffer) (rvec) (fvec) (Xo) (Yo) (Rmaj) (Rmin) (phi)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+
+  if ((rvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (rvec, OPIHI_FLT, Nx*Ny);
+  ResetVector (fvec, OPIHI_FLT, Nx*Ny);
+
+  Xo = atof(argv[4]);
+  Yo = atof(argv[5]);
+
+  /* shape parameters */
+  Rmaj = atof (argv[6]);
+  Rmin = atof (argv[7]);
+  phi  = atof (argv[8]);
+
+  /* given Rmaj, Rmin, phi, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Rmaj);
+  root2 = SQ(1.0 / Rmin);
+
+  // XXX check this
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*phi);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*phi);
+  A3 = -R*sin(2*RAD_DEG*phi);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r^alpha), r^2 = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+  in = (float *) buf[0].matrix.buffer;
+  for (i = iy = 0; iy < Ny; iy++) {
+      for (ix = 0; ix < Nx; ix++, in++, i++) {
+      x = ix + 0.5 - Xo;
+      y = iy + 0.5 - Yo;
+      rvec[0].elements.Flt[i] = sqrt(0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy);
+      fvec[0].elements.Flt[i] = *in;
+    }
+  }
+  rvec[0].Nelements = fvec[0].Nelements = i;
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/galcontour.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/galcontour.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/galcontour.c	(revision 25757)
@@ -0,0 +1,89 @@
+# include "astro.h"
+
+XXX probably not a great algorithm...
+
+
+int galcontour (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, Nsec, NELEMENTS, Npt;
+  float *in;
+  double theta, dtheta, x, y, Xo, Yo;
+  Buffer *buf;
+  Vector *xvec, *yvec, *tvec;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galcontour (buffer) (Xo) (Yo) (flux)\n");
+    gprint (GP_ERR, "  generate contour points (Cx) (Cy) (Ct)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  Xo = atof(argv[2]);
+  Yo = atof(argv[3]);
+  flux = atof(argv[4]);
+
+  if ((xvec = SelectVector ("Cx", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector ("Cy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((tvec = SelectVector ("Ct", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = 0;
+  NELEMENTS = 100;
+  ResetVector (xvec, OPIHI_FLT, NELEMENTS);
+  ResetVector (yvec, OPIHI_FLT, NELEMENTS);
+  ResetVector (tvec, OPIHI_FLT, NELEMENTS);
+
+  in = (float *) buf[0].matrix.buffer;
+
+  // find the flux transition for each row until not found
+
+  // first go up:
+  for (j = Yo; j < Ny; j++) {
+    
+    // first go left
+    for (i = Xo; i >= 0; i--) {
+      F = in[i + j*Nx];
+      if (F < flux) {
+	// interpolate to flux value between f[i,j] and f[i+1,j]
+      }
+    }
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i - Xo;
+      y = j - Yo;
+      theta = DEG_RAD*atan2(y, x);
+      if (theta < -0.5*dtheta) theta += 360.0; // need to allow -0.5dtheta for first bin
+      if (theta > 360 - 0.5*dtheta) theta -= 360.0; // need to allow -0.5dtheta for first bin
+      N = (int)(theta / dtheta + 0.5);
+      if (N < 0) {
+	fprintf (stderr, "?");
+	continue;
+      }
+      if (N >= Nsec) {
+	fprintf (stderr, "!");
+	continue;
+      }
+      
+      fvec[N][0].elements.Flt[fvec[N][0].Nelements] = *in;
+      rvec[N][0].elements.Flt[rvec[N][0].Nelements] = hypot(x, y);
+      fvec[N][0].Nelements ++;
+      rvec[N][0].Nelements ++;
+
+      *in = N;
+
+      if (fvec[N][0].Nelements >= NELEMENTS) {
+	NELEMENTS += 1000;
+	for (N = 0; N < Nsec; N++) {
+	  REALLOCATE (fvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	  REALLOCATE (rvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/galprofiles.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/galprofiles.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/galprofiles.c	(revision 25757)
@@ -0,0 +1,187 @@
+# include "astro.h"
+
+double Interpolate (const double x, const double y, float *in, int Nx, int Ny);
+
+int galprofiles (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, Nsec, NELEMENTS;
+  float *in;
+  double theta, dtheta, x, y, Xo, Yo, r, Rmax, value;
+  Buffer *buf;
+  Vector **fvec, **rvec, *tvec;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galprofiles (buffer) (Xo) (Yo) Nsec\n");
+    gprint (GP_ERR, "  generates Nsec profiles around the circle; the first is centered on 0.0 degrees\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  Rmax = 0.5*hypot(Nx, Ny);
+  
+  Xo = atof(argv[2]);
+  Yo = atof(argv[3]);
+  Nsec = atof(argv[4]);
+
+  dtheta = 360.0 / Nsec;
+  // sector i ranges from (i-0.5)*dtheta to (i+0.5)*dtheta
+  // i = theta / dtheta + 0.5;
+
+  ALLOCATE (fvec, Vector *, Nsec);
+  ALLOCATE (rvec, Vector *, Nsec);
+  if ((tvec = SelectVector ("theta", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ResetVector (tvec, OPIHI_FLT, Nsec);
+
+  // dereference pointer
+  in = (float *) buf[0].matrix.buffer;
+
+  for (i = 0; i < Nsec; i++) {
+
+    // generate the output vector names & vectors
+    sprintf (name, "flux_%d", i);
+    if ((fvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    sprintf (name, "rad_%d", i);
+    if ((rvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+    // allocate initial data space
+    N = 0;
+    NELEMENTS = 1000;
+    ResetVector (fvec[i], OPIHI_FLT, NELEMENTS);
+    ResetVector (rvec[i], OPIHI_FLT, NELEMENTS);
+
+    // angle for this profile
+    tvec[0].elements.Flt[i] = i*dtheta;
+    theta = RAD_DEG*i*dtheta;
+
+    // start at Xo,Yo and find the x,y locations for r_i, theta where r_i increments by 1 pixel
+    for (r = 0; r < Rmax; r++) {
+
+      // XXX Xo,Yo are referenced to pixels with bounds i+0.0, i+1.0
+      x = r * cos (theta) + Xo;
+      y = r * sin (theta) + Yo;
+
+      // value is NAN if we run off the image
+      value = Interpolate(x, y, in, Nx, Ny);
+      if (isnan(value)) break;
+
+      rvec[i][0].elements.Flt[N] = r;
+      fvec[i][0].elements.Flt[N] = value;
+      N++;
+
+      if (N >= NELEMENTS) {
+	NELEMENTS += 100;
+	REALLOCATE (rvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	REALLOCATE (fvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+      }
+    }
+    fvec[i][0].Nelements = N;
+    rvec[i][0].Nelements = N;
+  }
+  return (TRUE);
+}
+
+// fast & simple API to interpolate to a subpixel position using bilinear interpolation
+// x,y in parent image coordinates (pixel centers at 0.5, 0.5)
+// stolen from psLib
+double Interpolate (const double x, const double y, float *in, int Nx, int Ny) {
+
+  // allow extrapolation a small distance beyond the edge of valid pixels, but no
+  // further (this allows the nXskip,nYskip boundary areas to be used as well)
+  float nXedge = 0.125*Nx;
+  float nYedge = 0.125*Ny;
+
+  if ((x < -nXedge) || (x > Nx + nXedge) || (y < -nYedge) || (y > Ny + nYedge)) {
+    return NAN;
+  }
+
+  // limiting cases: Nx == 1 and/or Ny == 1
+
+  // if we have a single pixel, there is no spatial information
+  if ((Nx == 1) && (Ny == 1)) {
+    return in[0];
+  }
+
+  // handle edge cases with extrapolation
+  const int ix = x - 0.5; // index of reference pixel
+  const int iy = y - 0.5; // index of reference pixel
+
+  // do numCols,Rows first so we are never < 0
+  const int Xs = MAX (MIN (ix, Nx - 2), 0);
+  const int Ys = MAX (MIN (iy, Ny - 2), 0);
+
+  const int Xe = Xs + 1;
+  const int Ye = Ys + 1;
+
+  // dx,dy range from 0.0 to 1.0 for interpolated pixels, and -0.5 to 1.5 for extrapolation
+  const double dx = x - 0.5 - Xs;
+  const double dy = y - 0.5 - Ys;
+
+  const double rx = 1.0 - dx;
+  const double ry = 1.0 - dy;
+
+  // if Nx == 1, we have no x-dir spatial information
+  if (Nx == 1) {
+    double V0 = in[Ys*Nx + Xs];
+    double V1 = in[Ye*Nx + Xs];
+
+    const double value = V0*ry + V1*dy;
+    return value;
+  }
+
+  // if Ny == 1, we have no y-dir spatial information
+  if (Ny == 1) {
+    double V0 = in[Ys*Nx + Xs];
+    double V1 = in[Ys*Nx + Xe];
+
+    const double value = V0*rx + V1*dx;
+    return value;
+  }
+
+  // Vxy
+  double V00 = in[Ys*Nx + Xs];
+  double V01 = in[Ye*Nx + Xs];
+  double V10 = in[Ys*Nx + Xe];
+  double V11 = in[Ye*Nx + Xe];
+
+  double value;
+
+  // corners
+  if ((dx < 0.0) && (dy < 0.0)) {
+    return V00;
+  }
+  if ((dx > 1.0) && (dy < 0.0)) {
+    return V10;
+  }
+  if ((dx < 0.0) && (dy > 1.0)) {
+    return V01;
+  }
+  if ((dx > 1.0) && (dy > 1.0)) {
+    return V11;
+  }
+
+  // sides
+  if (dx < 0.0) {
+    value = V00*ry + V01*dy;
+    return value;
+  }
+  if (dy < 0.0) {
+    value = V00*rx + V10*dx;
+    return value;
+  }
+  if (dx > 1.0) {
+    value = V10*ry + V11*dy;
+    return value;
+  }
+  if (dy > 1.0) {
+    value = V01*rx + V11*dx;
+    return value;
+  }
+
+  // bilinear interpolation
+  value = V00*rx*ry + V10*dx*ry + V01*rx*dy + V11*dx*dy;
+  return value;
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/galradbins.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/galradbins.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/galradbins.c	(revision 25757)
@@ -0,0 +1,84 @@
+# include "astro.h"
+
+int MeanSurfaceBrightness (Vector *rvec, Vector *fvec, Vector *Rvec, Vector *Fvec, Vector *Avec, float Rmin, float Rmax, int bin);
+
+// given a collection of r, f points sampled at pixels, generate a pair of vectors r, f
+// where f is defined as the mean surface brightness for \alpha r_i < r < \beta r_i.
+// sample r at r_i = i
+
+// this function can be much more efficient if the input vectors are sorted by R and that
+// fact is used in generating the radial bins...
+int galradbins (int argc, char **argv) {
+  
+  int i, Nbin;
+  double Rmin, Rmax;
+  Vector *fvec, *rvec, *Fvec, *Rvec, *Avec;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: galradius (r_in) (f_in) (r_out) (f_out) (area_out)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((rvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Rvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Avec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Rmax = rvec[0].elements.Flt[0];
+  for (i = 0; i < rvec[0].Nelements; i++) {
+    Rmax = MAX(Rmax, rvec[0].elements.Flt[i]);
+  }
+  Nbin = 0.8*Rmax + 2;
+  ResetVector (Rvec, OPIHI_FLT, Nbin);
+  ResetVector (Fvec, OPIHI_FLT, Nbin);
+  ResetVector (Avec, OPIHI_FLT, Nbin);
+
+  // the first three bins are specially defined:
+
+  // 0 : r < 1.0 -- Area is pi
+  MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 0.00, 1.00, 0);
+
+  // area is \int 2 \pi r dr = \pi (r2^2 - r1^2)
+
+  // 1 : 1.0 < r < 1.25 -- Area is \pi (1.25^2 - 1^2)
+  MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 1.00, 1.25, 1);
+
+  // 2 : 1.25 < r < 1.60 -- Area is \pi (1.6^2 - 1.25^2)
+  MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 1.25, 1.60, 2);
+
+  // from bin 3 on out, r = i - 1 
+  for (i = 3; i < Nbin; i++) {
+    MeanSurfaceBrightness (rvec, fvec, Rvec, Fvec, Avec, 0.8*(i-1), 1.25*(i-1), i);
+  }
+
+  return (TRUE);
+}
+
+int MeanSurfaceBrightness (Vector *rvec, Vector *fvec, Vector *Rvec, Vector *Fvec, Vector *Avec, float Rmin, float Rmax, int bin) {
+
+  int i, Npts;
+  double Fsum;
+
+  Fsum = Npts = 0;
+
+  for (i = 0; i < rvec[0].Nelements; i++) {
+    if (rvec[0].elements.Flt[i] < Rmin) continue;
+    if (rvec[0].elements.Flt[i] > Rmax) continue;
+    Fsum += fvec[0].elements.Flt[i];
+    Npts ++;
+  }
+  if (Rmin > 0.0) {
+    Rvec[0].elements.Flt[bin] = sqrt(Rmin * Rmax); // XXX what is the correct mean radius?
+  } else {
+    Rvec[0].elements.Flt[bin] = 0.5 * Rmax; // XXX what is the correct mean radius?
+  }
+  if (Npts > 0) {
+    Fvec[0].elements.Flt[bin] = Fsum / Npts; // XXX what is the correct mean radius?
+  } else {
+    Fvec[0].elements.Flt[bin] = NAN; // XXX what is the correct mean radius?
+  }
+  Avec[0].elements.Flt[bin] = M_PI * (SQ(Rmax) - SQ(Rmin));
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/galradius.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/galradius.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/galradius.c	(revision 25757)
@@ -0,0 +1,123 @@
+# include "astro.h"
+
+// given an image and associated galaxy profile, find:
+// a) a characteristic flux level that defines a useful isophot
+// b) radius at which the profile crosses that isophot
+int galradius (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, Nsec, NELEMENTS, Rbin, Nout, above;
+  double theta, dtheta, x, y, Xo, Yo, r, Rmax, Rmin, value;
+  double Fmin, Fmax, dF, Fm, Fp, Fo, Rsum, Rnpt, Ro;
+  opihi_flt *flux, *radius, *values;
+  Buffer *buf;
+  Vector *fvec, *rvec, *tvec, *Fvec, *Rvec;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galradius (radius) (flux) (min_flux) (max_flux)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((rvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((Rvec = SelectVector ("rg", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fvec = SelectVector ("fg", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Fmin = atof(argv[3]);
+  Fmax = atof(argv[4]);
+
+  // fvec is a noise sample of the galaxy radial profile at points rvec
+  // rebin fvec into samples defined by the isophot 
+
+  // base selections on fluxes defined by the flux range dF
+  dF = Fmax - Fmin;
+
+  // select all points with flux in the range Fmin + 0.25*dF to Fmin + 0.75*dF
+  Fm = Fmin + 0.25*dF;
+  Fp = Fmin + 0.75*dF;
+  Fo = Fmin + 0.50*dF;
+      
+  Rsum = 0;
+  Rnpt = 0;
+  for (i = 0; i < fvec[0].Nelements; i++) {
+    if (fvec[0].elements.Flt[i] < Fm) continue;
+    if (fvec[0].elements.Flt[i] > Fp) continue;
+      
+    Rsum += rvec[0].elements.Flt[i];
+    Rnpt ++;
+  }
+
+  // determine the binned radius values
+  if (Rnpt == 0) {
+    Rbin = 1;
+  } else {
+    Rbin = MAX(1, 0.5*(Rsum / Rnpt));
+  }
+
+  // do not bother rebinning if the bin size is only 2 or less
+  if (Rbin <= 2) {
+    Nout   = fvec[0].Nelements;
+    ResetVector (Rvec, OPIHI_FLT, Nout);
+    ResetVector (Fvec, OPIHI_FLT, Nout);
+    memcpy (Fvec[0].elements.Flt, fvec[0].elements.Flt, Fvec[0].Nelements*sizeof(opihi_flt));
+    memcpy (Rvec[0].elements.Flt, rvec[0].elements.Flt, Rvec[0].Nelements*sizeof(opihi_flt));
+    flux   = fvec[0].elements.Flt;
+    radius = rvec[0].elements.Flt;
+  } else {
+    // rebin the vectors by Rbin values:
+    Nout = fvec[0].Nelements / Rbin + 1;
+    ALLOCATE (values, opihi_flt, fvec[0].Nelements);
+  
+    ResetVector (Rvec, OPIHI_FLT, Nout);
+    ResetVector (Fvec, OPIHI_FLT, Nout);
+    flux   = Fvec[0].elements.Flt;
+    radius = Rvec[0].elements.Flt;
+
+    for (i = 0; i < Nout; i++) {
+      radius[i] = (i + 0.5)*Rbin;
+      Rmin = radius[i] - 0.5*Rbin;
+      Rmax = radius[i] + 0.5*Rbin;
+
+      N = 0;
+      for (j = 0; j < fvec[0].Nelements; j++) {
+	if (rvec[0].elements.Flt[j] < Rmin) continue;
+	if (rvec[0].elements.Flt[j] > Rmax) continue;
+	values[N] = fvec[0].elements.Flt[j];
+	N++;
+      }
+
+      // take the median of the values
+      dsort (values, N);
+      if (N > 1) {
+	flux[i] = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
+      } else {
+	flux[i] = values[0];
+      }
+    }
+    free (values);
+  }
+
+  above = TRUE;
+  for (i = 0; i < Nout; i++) {
+
+    // find the largest radius that matches the flux transition
+    if (above && (flux[i] < Fo)) {
+      if (i == 0) { 
+	// assume Fmax @ R = 0.0
+	Ro = radius[i] * (Fo - Fmax) / (flux[i] - Fmax);
+      } else {
+	Ro = radius[i-1] + (radius[i] - radius[i-1]) * (Fo - flux[i-1]) / (flux[i] - flux[i-1]);
+      }
+      above = FALSE;
+    }
+  
+    if (!above && (flux[i] >= Fo)) {
+      above = TRUE;
+    }
+  }
+
+  set_variable ("G_R50", Ro);
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/galsectors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/galsectors.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/galsectors.c	(revision 25757)
@@ -0,0 +1,107 @@
+# include "astro.h"
+
+int galsectors (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, Nsec, NELEMENTS, Npt;
+  float *in;
+  double theta, dtheta, x, y, Xo, Yo;
+  Buffer *buf;
+  Vector **fvec, **rvec, *tvec, *Rv, *Tv, *Fv;
+  char name[128];
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: galsectors (buffer) (Xo) (Yo) Nsec\n");
+    gprint (GP_ERR, "  generates Nsec sectors around the circle; the first is centered on 0.0 degrees\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  Xo = atof(argv[2]);
+  Yo = atof(argv[3]);
+  Nsec = atof(argv[4]);
+
+  // for a test, generate R, theta vectors for the whole image:
+  if ((Rv = SelectVector ("Rvec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Tv = SelectVector ("Tvec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fv = SelectVector ("Fvec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ResetVector (Rv, OPIHI_FLT, Nx*Ny);
+  ResetVector (Tv, OPIHI_FLT, Nx*Ny);
+  ResetVector (Fv, OPIHI_FLT, Nx*Ny);
+
+  in = (float *) buf[0].matrix.buffer;
+  Npt = 0;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i - Xo;
+      y = j - Yo;
+      Rv[0].elements.Flt[Npt] = hypot(x, y);
+      Tv[0].elements.Flt[Npt] = DEG_RAD*atan2(y, x);
+      Fv[0].elements.Flt[Npt] = *in;
+      Npt ++;
+    }
+  }
+  return (TRUE);
+
+  dtheta = 360.0 / Nsec;
+  // sector i ranges from (i-0.5)*dtheta to (i+0.5)*dtheta
+  // i = theta / dtheta + 0.5;
+
+  ALLOCATE (fvec, Vector *, Nsec);
+  ALLOCATE (rvec, Vector *, Nsec);
+  if ((tvec = SelectVector ("theta", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ResetVector (tvec, OPIHI_FLT, Nsec);
+
+  // generate the output vector names
+  NELEMENTS = 1000;
+  for (i = 0; i < Nsec; i++) {
+    sprintf (name, "flux_%d", i);
+    if ((fvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    sprintf (name, "rad_%d", i);
+    if ((rvec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    ResetVector (fvec[i], OPIHI_FLT, NELEMENTS);
+    ResetVector (rvec[i], OPIHI_FLT, NELEMENTS);
+    fvec[i][0].Nelements = 0;
+    rvec[i][0].Nelements = 0;
+    tvec[0].elements.Flt[i] = i*dtheta;
+  }
+
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i - Xo;
+      y = j - Yo;
+      theta = DEG_RAD*atan2(y, x);
+      if (theta < -0.5*dtheta) theta += 360.0; // need to allow -0.5dtheta for first bin
+      if (theta > 360 - 0.5*dtheta) theta -= 360.0; // need to allow -0.5dtheta for first bin
+      N = (int)(theta / dtheta + 0.5);
+      if (N < 0) {
+	fprintf (stderr, "?");
+	continue;
+      }
+      if (N >= Nsec) {
+	fprintf (stderr, "!");
+	continue;
+      }
+      
+      fvec[N][0].elements.Flt[fvec[N][0].Nelements] = *in;
+      rvec[N][0].elements.Flt[rvec[N][0].Nelements] = hypot(x, y);
+      fvec[N][0].Nelements ++;
+      rvec[N][0].Nelements ++;
+
+      *in = N;
+
+      if (fvec[N][0].Nelements >= NELEMENTS) {
+	NELEMENTS += 1000;
+	for (N = 0; N < Nsec; N++) {
+	  REALLOCATE (fvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	  REALLOCATE (rvec[N][0].elements.Flt, opihi_flt, NELEMENTS);
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/init.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/init.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.astro/init.c	(revision 25757)
@@ -5,4 +5,5 @@
 int cgrid                   PROTO((int, char **));
 int coords                  PROTO((int, char **));
+int cdot                    PROTO((int, char **));
 int cplot                   PROTO((int, char **));
 int csystem                 PROTO((int, char **));
@@ -22,5 +23,12 @@
 int imsub                   PROTO((int, char **));
 int medianmap               PROTO((int, char **));
+int galsectors              PROTO((int, char **));
+int galprofiles             PROTO((int, char **));
+int galradius               PROTO((int, char **));
+int galradbins              PROTO((int, char **));
+int elliprofile             PROTO((int, char **));
+int petrosian               PROTO((int, char **));
 int mkgauss                 PROTO((int, char **));
+int mksersic                PROTO((int, char **));
 int multifit                PROTO((int, char **));
 int objload                 PROTO((int, char **));
@@ -44,4 +52,5 @@
   {1, "cgrid",       cgrid,        "plot sky coordinate grid"},
   {1, "coords",      coords,       "load coordinates for buffer from file"},
+  {1, "cdot",        cdot,         "plot point in sky coordinates"},
   {1, "cplot",       cplot,        "plot vectors in sky coordinates"},
   {1, "csystem",     csystem,      "convert between coordinate systems"},
@@ -61,4 +70,11 @@
   {1, "medianmap",   medianmap,    "small median image"},
   {1, "mkgauss",     mkgauss,      "generate a 2-D gaussian centered in image"},
+  {1, "mksersic",    mksersic,     "generate a 2-D sersic profile"},
+  {1, "galsectors",  galsectors,   "generate radial vectors for sectors of width dtheta"},
+  {1, "galprofiles", galprofiles,  "generate radial vectors with interpolation along paths"},
+  {1, "galradius",   galradius,    "generate radial vectors with interpolation along paths"},
+  {1, "galradbins",  galradbins,   "generate radial vectors with interpolation along paths"},
+  {1, "elliprofile", elliprofile,  "generate radial vectors with interpolation along paths"},
+  {1, "petrosian",   petrosian,    "petrosian parameters given radial bins"},
   {1, "multifit",    multifit,     "fit multi-order spectrum"},
   {1, "objload",     objload,      "plot obj data on Ximage "},
Index: /trunk/Ohana/src/opihi/cmd.astro/mksersic.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/mksersic.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/mksersic.c	(revision 25757)
@@ -0,0 +1,87 @@
+# include "astro.h"
+
+int mksersic (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N, center;
+  float *in;
+  double Rmaj, Rmin, Theta, alpha, Io, ARatio;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, r, f, Xo, Yo;
+  Buffer *buf;
+
+  Xo = Yo = 0;
+  center = TRUE;
+  if ((N = get_argument (argc, argv, "-coord"))) {
+    remove_argument (N, &argc, argv);
+    Xo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    center = FALSE;
+  }
+
+  Theta = 0.0;
+  if ((N = get_argument (argc, argv, "-angle"))) {
+    remove_argument (N, &argc, argv);
+    Theta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ARatio = 1.0;
+  if ((N = get_argument (argc, argv, "-aratio"))) {
+    remove_argument (N, &argc, argv);
+    ARatio = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mksersic (buffer) (Io) (Rmaj) (alpha)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  if (center) {
+    Xo = 0.5*Nx;
+    Yo = 0.5*Ny;
+  }
+  
+  Io = atof (argv[2]);
+
+  /* shape parameters */
+  Rmaj = atof (argv[3]);
+  Rmin = Rmaj / ARatio;
+
+  alpha = atof (argv[4]);
+
+  /* given Rmaj, Rmin, Theta, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Rmaj);
+  root2 = SQ(1.0 / Rmin);
+
+  // XXX check this
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*Theta);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*Theta);
+  A3 = -R*sin(2*RAD_DEG*Theta);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r^alpha), r = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+      x = i + 0.5 - Xo;
+      y = j + 0.5 - Yo;
+      r = pow((0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy), alpha);
+      f = Io*exp (-r);
+      *in += f;
+    }
+  }
+
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/petrosian.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/petrosian.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.astro/petrosian.c	(revision 25757)
@@ -0,0 +1,90 @@
+# include "astro.h"
+
+int petrosian (int argc, char **argv) {
+  
+  int i, above;
+  float *in;
+  double Fsum, Asum, Area, R_90, rad_90, flux_90;
+  Vector *rvec, *avec, *fvec, *Rvec, *Fvec, *Svec, *Avec;
+
+  if (argc != 8) {
+    gprint (GP_ERR, "USAGE: petrosian (radius) (area) (flux) (P_ratio) (P_flux) (P_sb) (P_area)\n");
+    return (FALSE);
+  }
+
+  /* select input vectors */
+  if ((rvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((avec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((fvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Rvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Fvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Svec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Avec = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // difficult work goes into cleaning the input galaxy profile:
+  // a) extract radial profiles applying 180 degree symmetry requirement
+  // b) determine the axial ratio and position angle (at r_50)
+  // c) extract normalized radial profiles applying outlier clipping
+  // 
+  // the resulting vectors r, f represent the mean surface brightness at a radius r_i, 
+  // measured between \alpha*r_i and \beta*r_i: f(r_i) = \sum_\alpha*r_i^\beta*r_i flux(r)
+
+  // given a clean set of vectors representing the f_i, r_i, determine F_P, R_P such that:
+  // f(r_i) / \sum_0^r_i f(r) = f_P 
+
+  // generate a vector representing f_P(r_i):
+
+  ResetVector (Fvec, OPIHI_FLT, fvec[0].Nelements);
+  ResetVector (Rvec, OPIHI_FLT, fvec[0].Nelements);
+  ResetVector (Svec, OPIHI_FLT, fvec[0].Nelements);
+  ResetVector (Avec, OPIHI_FLT, fvec[0].Nelements);
+
+  above = TRUE;
+  Fsum = 0.0;
+  Asum = 0.0;
+  Area = avec[0].elements.Flt[0];
+  for (i = 0; i < fvec[0].Nelements; i++) {
+    // for nan bins, we keep the area for use with the next valid bin
+    if (isnan(fvec[0].elements.Flt[i])) {
+      Area += avec[0].elements.Flt[i];
+      continue;
+    } 
+    Fsum += fvec[0].elements.Flt[i] * Area;
+    Asum += Area;
+    if (i+1 < fvec[0].Nelements) {
+      Area = avec[0].elements.Flt[i+1];
+    }
+
+    Rvec[0].elements.Flt[i] = Asum * fvec[0].elements.Flt[i] / Fsum;
+    Fvec[0].elements.Flt[i] = Fsum;
+    Svec[0].elements.Flt[i] = Fsum / Asum;
+    Avec[0].elements.Flt[i] = Asum;
+
+    // anytime we transition below the petrosian ratio R_90, calculate the radius and flux
+    // we will keep and report the last (largest radius) value
+    if (above && (Rvec[0].elements.Flt[i] < R_90)) {
+      // interpolate Rvec between i-1 and i to R_90 to get flux (Fvec) and radius (rvec)
+
+      if (i == 0) { 
+	// assume Fmax @ R = 0.0
+	rad_90  = rvec[0].elements.Flt[i] * (R_90 - 1.0) / (Rvec[0].elements.Flt[i] - 1.0);
+	flux_90 = Fvec[0].elements.Flt[i] * (R_90 - 1.0) / (Rvec[0].elements.Flt[i] - 1.0);
+      } else {
+	rad_90  = rvec[0].elements.Flt[i-1] + (rvec[0].elements.Flt[i] - rvec[0].elements.Flt[i-1]) * (R_90 - Rvec[0].elements.Flt[i-1]) / (Rvec[0].elements.Flt[i] - Rvec[0].elements.Flt[i-1]);
+	flux_90 = Fvec[0].elements.Flt[i-1] + (Fvec[0].elements.Flt[i] - Fvec[0].elements.Flt[i-1]) * (R_90 - Rvec[0].elements.Flt[i-1]) / (Rvec[0].elements.Flt[i] - Rvec[0].elements.Flt[i-1]);
+      }
+
+      above = FALSE;
+    }
+    
+    // reset on transitions up, but do not re-calculate rad_90, flux_90
+    if (!above && (Rvec[0].elements.Flt[i] >= R_90)) {
+      above = TRUE;
+    }
+  }
+
+  set_variable ("P_R90", rad_90);
+  set_variable ("P_F90", flux_90);
+
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/cmd.astro/region.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/region.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.astro/region.c	(revision 25757)
@@ -6,5 +6,5 @@
   double Ra, Dec, Radius;
   float dx, dy;
-  int N, kapa;
+  int N, kapa, NoClear;
   char *name;
   Graphdata graphmode;
@@ -46,4 +46,10 @@
     remove_argument (N, &argc, argv);
     graphmode.flipnorth = FALSE;
+  }
+
+  NoClear = FALSE;
+  if ((N = get_argument (argc, argv, "-no-clear"))) {
+    remove_argument (N, &argc, argv);
+    NoClear = TRUE;
   }
 
@@ -122,10 +128,10 @@
   graphmode.coords.cdelt1 = graphmode.coords.cdelt2 = 1.0;
 
-  KapaClearSections (kapa);
+  if (!NoClear) KapaClearSections (kapa);
   KapaSetLimits (kapa, &graphmode);
 
   /* drop this? */
-  sprintf (string, "%8.4f %8.4f (%f)", Ra, Dec, Radius);
-  KapaSendLabel (kapa, string, 2);
+  // sprintf (string, "%8.4f %8.4f (%f)", Ra, Dec, Radius);
+  // KapaSendLabel (kapa, string, 2);
 
   // XXX is this the right thing to be doing?
Index: /trunk/Ohana/src/opihi/cmd.basic/substr.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.basic/substr.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.basic/substr.c	(revision 25757)
@@ -16,9 +16,9 @@
   // add a range check here
   if ((N1 < 0) || (N1 >=  strlen(argv[1]))) {
-      gprint (GP_ERR, "ERROR: N1 out of range\n");
+      gprint (GP_ERR, "ERROR: start value out of range in substr command\n");
       return (FALSE);
   }
   if ((N2 < 0) || ((N2+N1) >  strlen(argv[1]))) {
-      gprint (GP_ERR, "ERROR: N2 out of range\n");
+      gprint (GP_ERR, "ERROR: end value out of range in substr command\n");
       return (FALSE);
   }
Index: /trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 25757)
@@ -97,4 +97,5 @@
 $(SRC)/rebin.$(ARCH).o		\
 $(SRC)/resize.$(ARCH).o	\
+$(SRC)/relocate.$(ARCH).o	\
 $(SRC)/roll.$(ARCH).o		\
 $(SRC)/rotate.$(ARCH).o	\
@@ -110,5 +111,5 @@
 $(SRC)/style.$(ARCH).o		   \
 $(SRC)/subraster.$(ARCH).o	   \
-$(SRC)/subset.$(ARCH).o	   \
+$(SRC)/subset.$(ARCH).o	           \
 $(SRC)/svd.$(ARCH).o		   \
 $(SRC)/swapbytes.$(ARCH).o	   \
@@ -118,11 +119,13 @@
 $(SRC)/tvcolors.$(ARCH).o	   \
 $(SRC)/tvcontour.$(ARCH).o	   \
-$(SRC)/tvgrid.$(ARCH).o	   \
+$(SRC)/tvgrid.$(ARCH).o	           \
 $(SRC)/uniq.$(ARCH).o		   \
-$(SRC)/unsign.$(ARCH).o	   \
+$(SRC)/unsign.$(ARCH).o	           \
 $(SRC)/vbin.$(ARCH).o		   \
+$(SRC)/vgroup.$(ARCH).o		   \
 $(SRC)/vclip.$(ARCH).o		   \
-$(SRC)/vgauss.$(ARCH).o           \
-$(SRC)/vmaxwell.$(ARCH).o           \
+$(SRC)/vgauss.$(ARCH).o            \
+$(SRC)/vellipse.$(ARCH).o          \
+$(SRC)/vmaxwell.$(ARCH).o          \
 $(SRC)/vgrid.$(ARCH).o		   \
 $(SRC)/vload.$(ARCH).o		   \
@@ -130,5 +133,5 @@
 $(SRC)/vpop.$(ARCH).o		   \
 $(SRC)/vroll.$(ARCH).o		   \
-$(SRC)/vsmooth.$(ARCH).o	\
+$(SRC)/vsmooth.$(ARCH).o	   \
 $(SRC)/vstats.$(ARCH).o		   \
 $(SRC)/wd.$(ARCH).o		   \
Index: /trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/init.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.data/init.c	(revision 25757)
@@ -85,4 +85,5 @@
 int rebin            PROTO((int, char **));
 int resize           PROTO((int, char **));
+int relocate         PROTO((int, char **));
 int roll             PROTO((int, char **));
 int rotate           PROTO((int, char **));
@@ -110,8 +111,10 @@
 int unsign           PROTO((int, char **));
 int vbin             PROTO((int, char **));
+int vgroup           PROTO((int, char **));
 int vclip            PROTO((int, char **));
 int vect_select      PROTO((int, char **));
 int vgrid            PROTO((int, char **));
 int vgauss           PROTO((int, char **));
+int vellipse         PROTO((int, char **));
 int vmaxwell         PROTO((int, char **));
 int vload            PROTO((int, char **));
@@ -218,4 +221,5 @@
   {1, "rebin",        rebin,            "rebin image data by factor of N"},
   {1, "resize",       resize,           "set graphics/image window size"},
+  {1, "relocate",     relocate,         "set graphics/image window position"},
   {1, "roll",         roll,             "roll image to new start point"},
   {1, "rotate",       rotate,           "rotate image"},
@@ -243,8 +247,10 @@
   {1, "uniq",         uniq,             "create a uniq vector subset from a vector"},
   {1, "unsign",       unsign,           "toggle the UNSIGN status"},
-  {1, "vbin",         vbin,             "rebin vector data by a fector of N"},
+  {1, "vbin",         vbin,             "rebin vector data by a factor of N"},
+  {1, "vgroup",       vgroup,           "group y vector into bins defined by x vector values"},
   {1, "vclip",        vclip,            "clip values in a vector to be within a range"},
   {1, "vectors",      list_vectors,     "list vectors"},
   {1, "vgauss",       vgauss,           "fit a Gaussian to a vector"},
+  {1, "vellipse",     vellipse,         "fit a Ellipse to a vector pair"},
   {1, "vgrid",        vgrid,            "generate an image from a triplet of vectors"},
   {1, "vhistogram",   histogram,        "generate histogram from vector"},
Index: /trunk/Ohana/src/opihi/cmd.data/relocate.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/relocate.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.data/relocate.c	(revision 25757)
@@ -0,0 +1,33 @@
+# include "data.h"
+
+int relocate (int argc, char **argv) {
+
+  char *end;
+  int x, y;
+  int N, kapa;
+  char *name;
+  
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: relocate x y [-n]\n");
+    return (FALSE);
+  }
+
+  /* x & y are pixels for the screen -- this command has no meaning for non-X */
+
+  x = atoi (argv[1]);
+  y = atoi (argv[2]);
+
+  KiiRelocate (kapa, x, y);
+  return (TRUE);
+}
+
Index: /trunk/Ohana/src/opihi/cmd.data/subset.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/subset.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.data/subset.c	(revision 25757)
@@ -6,5 +6,5 @@
   
   char *out;
-  int  i, Npts, size;
+  int  i, Npts, size, valid;
   Vector *ivec, *ovec, *tvec;
 
@@ -12,6 +12,10 @@
   ivec = ovec = tvec = NULL;
 
-  if ((argc < 6) || strcmp(argv[2], "=") || strcmp (argv[4], "if")) {
-    gprint (GP_ERR, "SYNTAX: subset vec = vec if (logic expression)\n");
+  valid = TRUE;
+  valid &= (argc >= 6);
+  valid &= !strcmp(argv[2], "=");
+  valid &= !strcmp(argv[4], "if") || !strcmp (argv[4], "where");
+  if (!valid) {
+    gprint (GP_ERR, "SYNTAX: subset vec = vec [if/where] (logic expression)\n");
     return (FALSE);
   }
@@ -76,5 +80,5 @@
   return (TRUE);
 
- error:
+error:
   DeleteVector (tvec);
   DeleteVector (ovec);
Index: /trunk/Ohana/src/opihi/cmd.data/tvcolors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 25757)
@@ -19,4 +19,5 @@
   if (argc != 2) {
     gprint (GP_ERR, "USAGE: tvcolors (colormap)\n");
+    gprint (GP_ERR, " colormap options : greyscale, -greyscale, rainbow, heat, fullcolor, ruffcolor (also grayscale, -grayscale)\n");
     return (FALSE);
   }
Index: /trunk/Ohana/src/opihi/cmd.data/vellipse.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/vellipse.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.data/vellipse.c	(revision 25757)
@@ -0,0 +1,272 @@
+# include "data.h"
+
+/* local private functions */
+opihi_flt fellipseOD (opihi_flt theta, opihi_flt *par, int Npar, opihi_flt *dpar);
+
+// enum {PAR_X0, PAR_Y0, PAR_RX, PAR_RY, PAR_T0, PAR_P0};
+enum {PAR_RMIN, PAR_EPSILON, PAR_PHI};
+
+# define GET_VAR(V,A) { \
+  char *c; \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c); }
+
+int vellipse (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  opihi_flt par[6], *pos, *dpos, *theta, **covar;
+  opihi_flt chisq, ochisq, dchisq;
+  // opihi_flt Rmaj;
+  opihi_flt Xmin, Xmax, Ymin, Ymax;
+  Vector *xvec, *yvec, *Xvec, *Yvec, *tvec;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: vellipse <xobs> <yobs> <xfit> <yfit> (theta)\n");
+    // gprint (GP_ERR, " uses guesses: E_X0, E_Y0, E_RMAJ, E_RMIN, E_THETA\n");
+    gprint (GP_ERR, " uses guesses: E_RMAJ, E_RMIN, E_PHI\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Xvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Yvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((tvec = SelectVector (argv[5], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (xvec, FALSE); 
+  REQUIRE_VECTOR_FLT (yvec, FALSE); 
+
+  // GET_VAR (par[PAR_X0], "E_X0");
+  // GET_VAR (par[PAR_Y0], "E_Y0");
+  // GET_VAR (par[PAR_RX], "E_RMAJ");
+  // GET_VAR (par[PAR_RY], "E_RMIN");
+  // GET_VAR (par[PAR_T0], "E_THETA");
+  // GET_VAR (par[PAR_P0], "E_PHI");
+  // Npar = 6;
+
+  // we need to generate a vector of alternating x,y values and the independent variable theta:
+  Xmax = Ymax = Xmin = Ymin = 0.0;
+  ALLOCATE (pos,   opihi_flt, 2*xvec[0].Nelements); 
+  ALLOCATE (dpos,  opihi_flt, 2*xvec[0].Nelements); 
+  ALLOCATE (theta, opihi_flt, 2*xvec[0].Nelements); 
+  for (i = 0; i < xvec[0].Nelements; i++) {
+    pos[2*i+0]   = xvec[0].elements.Flt[i];
+    pos[2*i+1]   = yvec[0].elements.Flt[i];
+    Xmin = MIN (Xmin, xvec[0].elements.Flt[i]);
+    Xmax = MAX (Xmax, xvec[0].elements.Flt[i]);
+    Ymin = MIN (Ymin, yvec[0].elements.Flt[i]);
+    Ymax = MAX (Ymax, yvec[0].elements.Flt[i]);
+    dpos[2*i+0]  = 1.0;
+    dpos[2*i+1]  = 1.0;
+    theta[2*i+0] = atan2(yvec[0].elements.Flt[i], xvec[0].elements.Flt[i]);
+    theta[2*i+1] = theta[2*i+0];
+    // fprintf (stderr, "x,y: %f, %f -> %f, %f\n", yvec[0].elements.Flt[i], xvec[0].elements.Flt[i], yvec[0].elements.Flt[i] - par[PAR_Y0], xvec[0].elements.Flt[i] - par[PAR_X0]);
+    // fprintf (stderr, "x,y: %f, %f -> %f, %f\n", yvec[0].elements.Flt[i], xvec[0].elements.Flt[i], yvec[0].elements.Flt[i] - par[PAR_Y0], xvec[0].elements.Flt[i] - par[PAR_X0]);
+    // fprintf (stderr, "theta: %f - %f : %f\n", DEG_RAD*theta[2*i+0], tvec[0].elements.Flt[i], DEG_RAD*theta[2*i+0] - tvec[0].elements.Flt[i]);
+  }
+  Npts = 2*xvec[0].Nelements;
+
+  // basic guess from range, PHI in degrees, converted to radians
+  // GET_VAR (Rmaj,          "E_RMAJ");
+  // GET_VAR (par[PAR_RMIN], "E_RMIN");
+  // GET_VAR (par[PAR_PHI],  "E_PHI");
+  par[PAR_PHI] = 0.0;
+  par[PAR_EPSILON] = 0.5;
+  par[PAR_RMIN] = 0.25*((Xmax - Xmin) + (Ymax - Ymin));
+  Npar = 3;
+
+  ochisq = mrqinit (theta, pos, dpos, Npts, par, Npar, fellipseOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+  chisq  = ochisq + dchisq;
+
+  for (i = 0; (i < 20) && (chisq > 1e-3) && ((dchisq > 0.001*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (theta, pos, dpos, Npts, par, Npar, fellipseOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  ResetVector (Xvec, OPIHI_FLT, xvec[0].Nelements);
+  ResetVector (Yvec, OPIHI_FLT, xvec[0].Nelements);
+  for (i = 0; i < xvec[0].Nelements; i++) {
+    Xvec[0].elements.Flt[i] = fellipseOD (theta[2*i+0], par, Npar, dpos);
+    Yvec[0].elements.Flt[i] = fellipseOD (theta[2*i+1], par, Npar, dpos);
+  }
+  Xvec[0].Nelements = xvec[0].Nelements;
+  Yvec[0].Nelements = xvec[0].Nelements;
+
+  covar = mrqcovar (Npar);
+  //set_variable ("E_X0",   par[PAR_X0]);
+  //set_variable ("E_Y0",   par[PAR_Y0]);
+  //set_variable ("E_RMAJ", par[PAR_RX]);
+  //set_variable ("E_RMIN", par[PAR_RY]);
+  //set_variable ("E_T0",   par[PAR_T0]);
+  //set_variable ("E_P0",   par[PAR_P0]);
+
+  set_variable ("E_RMAJ", par[PAR_RMIN] / par[PAR_EPSILON]);
+  set_variable ("E_RMIN", par[PAR_RMIN]);
+  set_variable ("E_PHI",  DEG_RAD*par[PAR_PHI]);
+
+  // set_variable ("dE_X0",   sqrt(covar[PAR_X0][PAR_X0]));
+  // set_variable ("dE_Y0",   sqrt(covar[PAR_Y0][PAR_Y0]));
+  // set_variable ("dE_RMAJ", sqrt(covar[PAR_RX][PAR_RX]));
+  // set_variable ("dE_RMIN", sqrt(covar[PAR_RY][PAR_RY]));
+  // set_variable ("dE_T0",   sqrt(covar[PAR_T0][PAR_T0]));
+  // set_variable ("dE_P0",   sqrt(covar[PAR_P0][PAR_P0]));
+
+  set_variable ("dE_RMAJ", sqrt(covar[PAR_EPSILON][PAR_EPSILON]*covar[PAR_RMIN][PAR_RMIN]));
+  set_variable ("dE_RMIN", sqrt(covar[PAR_RMIN][PAR_RMIN]));
+  set_variable ("dE_PHI",  DEG_RAD*sqrt(covar[PAR_PHI][PAR_PHI]));
+
+  // if (!Quiet) gprint (GP_ERR, "Xo : %f +/- %f\n", par[PAR_X0], sqrt(covar[PAR_X0][PAR_X0]));
+  // if (!Quiet) gprint (GP_ERR, "Yo : %f +/- %f\n", par[PAR_Y0], sqrt(covar[PAR_Y0][PAR_Y0]));
+  // if (!Quiet) gprint (GP_ERR, "RX : %f +/- %f\n", par[PAR_RX], sqrt(covar[PAR_RX][PAR_RX]));
+  // if (!Quiet) gprint (GP_ERR, "RY : %f +/- %f\n", par[PAR_RY], sqrt(covar[PAR_RY][PAR_RY]));
+  // if (!Quiet) gprint (GP_ERR, "To : %f +/- %f\n", par[PAR_T0], sqrt(covar[PAR_T0][PAR_T0]));
+  // if (!Quiet) gprint (GP_ERR, "Po : %f +/- %f\n", par[PAR_P0], sqrt(covar[PAR_P0][PAR_P0]));
+
+  if (!Quiet) gprint (GP_ERR, "RMAJ : %f +/- %f\n", par[PAR_RMIN] / par[PAR_EPSILON], 0.0);
+  if (!Quiet) gprint (GP_ERR, "RMIN : %f +/- %f\n", par[PAR_RMIN], sqrt(covar[PAR_RMIN][PAR_RMIN]));
+  if (!Quiet) gprint (GP_ERR, "PHI  : %f +/- %f\n", DEG_RAD*par[PAR_PHI],  sqrt(covar[PAR_PHI][PAR_PHI]));
+
+  free (pos);
+  free (dpos);
+  free (theta);
+
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+# if (0)
+/**
+ * the full chisq is built of two associated sums over coordinates:
+ * chisq = sum ((X_obs - X_fit(t))^2 + (Y_obs - Y_fit(t))^2)
+ * 
+ * the independent variable is Theta:
+ * X_fit = X0 + RX*cos(theta)*cos(phi) - RY*sin(theta)*sin(phi)
+ * Y_fit = Y0 + RX*cos(theta)*sin(phi) + RY*sin(theta)*cos(phi)
+ *
+ * alternating calls to fellipseOD refer alternatively to X or Y
+ *
+ * parameters: Xo, Yo (center), RX, RY (axes), To (angle), Po (phase)
+ */
+
+opihi_flt fellipseOD (opihi_flt theta, opihi_flt *par, int Npar, opihi_flt *dpar) {
+  
+  static int pass = 0;
+
+  opihi_flt csphi = cos(par[PAR_P0]);
+  opihi_flt snphi = sin(par[PAR_P0]);
+
+  opihi_flt dtheta = theta - par[PAR_T0];
+  opihi_flt cstht = cos(dtheta);
+  opihi_flt sntht = sin(dtheta);
+
+  // value is X
+  if (pass == 0) {
+    pass = 1;
+
+    opihi_flt value = par[PAR_X0] + par[PAR_RX]*cstht*csphi - par[PAR_RY]*sntht*snphi;
+
+    dpar[PAR_X0] = 1.0;
+    dpar[PAR_Y0] = 0.0;
+    dpar[PAR_RX] = +cstht*csphi;
+    dpar[PAR_RY] = -sntht*snphi;
+    dpar[PAR_P0] = -par[PAR_RX]*cstht*snphi - par[PAR_RY]*sntht*csphi;
+    dpar[PAR_T0] = -par[PAR_RX]*sntht*csphi - par[PAR_RY]*cstht*snphi;
+
+    return (value);
+  }  
+
+  // value is Y
+  if (pass == 1) {
+    pass = 0;
+
+    opihi_flt value = par[PAR_Y0] + par[PAR_RX]*cstht*snphi + par[PAR_RY]*sntht*csphi;
+
+    dpar[PAR_X0]  = 0.0;
+    dpar[PAR_Y0]  = 1.0;
+    dpar[PAR_RX]  = +cstht*snphi;
+    dpar[PAR_RY]  = +sntht*csphi;
+    dpar[PAR_P0]  = +par[PAR_RX]*cstht*csphi - par[PAR_RY]*sntht*snphi;
+    dpar[PAR_T0]  = -par[PAR_RX]*sntht*snphi + par[PAR_RY]*cstht*csphi;
+    return (value);
+  }  
+  abort();
+}
+
+# endif
+
+/**
+ * the full chisq is built of two associated sums over coordinates:
+ * chisq = sum ((X_obs - X_fit(t))^2 + (Y_obs - Y_fit(t))^2)
+ * 
+ * the independent variable is theta (angle from 0,0 to point):
+ * X_fit = RMIN*R*cos(theta)
+ * Y_fit = RMIN*R*sin(theta)
+ * R = 1 / sqrt(sin^2(theta-phi) + epsilon^2 * cos^2(theta-phi))
+ *
+ * alternating calls to fellipseOD refer alternatively to X or Y
+ *
+ * RMIN, EPSILON, PHI
+ */
+
+opihi_flt fellipseOD (opihi_flt alpha, opihi_flt *par, int Npar, opihi_flt *dpar) {
+  
+  static int pass = 0;
+
+  opihi_flt cs_alpha = cos(alpha);
+  opihi_flt sn_alpha = sin(alpha);
+
+  opihi_flt cs_phi = cos(alpha - par[PAR_PHI]);
+  opihi_flt sn_phi = sin(alpha - par[PAR_PHI]);
+
+  opihi_flt r     = 1.0 / sqrt(SQ(sn_phi) + SQ(par[PAR_EPSILON]*cs_phi));
+  opihi_flt r3    = pow(r, 3.0);
+  opihi_flt drdE  = -0.5 * r3 * SQ(cs_phi) * 2.0 * par[PAR_EPSILON];
+  opihi_flt drdP  = -0.5 * r3 * (SQ(par[PAR_EPSILON]) - 1) * 2.0 * cs_phi * sn_phi;
+
+  // value is X
+  if (pass == 0) {
+    pass = 1;
+
+    opihi_flt value = par[PAR_RMIN]*cs_alpha*r;
+
+    dpar[PAR_RMIN]    = r*cs_alpha;
+    dpar[PAR_EPSILON] = par[PAR_RMIN]*cs_alpha*drdE;
+    dpar[PAR_PHI]     = 4.0*par[PAR_RMIN]*cs_alpha*drdP;
+
+    return (value);
+  }  
+
+  // value is Y
+  if (pass == 1) {
+    pass = 0;
+
+    opihi_flt value = par[PAR_RMIN]*sn_alpha*r;
+
+    dpar[PAR_RMIN]    = r*sn_alpha;
+    dpar[PAR_EPSILON] = par[PAR_RMIN]*sn_alpha*drdE;
+    dpar[PAR_PHI]     = 4.0*par[PAR_RMIN]*sn_alpha*drdP;
+
+    return (value);
+  }  
+  abort();
+}
+
Index: /trunk/Ohana/src/opihi/cmd.data/vgroup.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/vgroup.c	(revision 25757)
+++ /trunk/Ohana/src/opihi/cmd.data/vgroup.c	(revision 25757)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int vgroup (int argc, char **argv) {
+  
+  int i, j, N;
+  Vector *xin, *yin, *xout, *yout;
+  opihi_flt xmin, xmax, sum;
+  double *values;
+
+  // Normalize = FALSE;
+  // if ((N = get_argument (argc, argv, "-norm"))) {
+  //   remove_argument (N, &argc, argv);
+  //   Normalize = TRUE;
+  // }
+  // 
+  // Ignore = FALSE;
+  // IgnoreValue = 0.0;
+  // if ((N = get_argument (argc, argv, "-ignore"))) {
+  //   Ignore = TRUE;
+  //   remove_argument (N, &argc, argv);
+  //   IgnoreValue = atof (argv[N]);
+  //   remove_argument (N, &argc, argv);
+  // }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vbin <xin> <yin> <xout> <yout>\n");
+    gprint (GP_ERR, " group x,y values in bins defined by <xout>\n");
+    return (FALSE);
+  }
+
+  if ((xin  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yin  = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((xout = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yout = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // re-binning creates a float vector
+  ResetVector (yout, OPIHI_FLT, xout[0].Nelements);
+
+  // storage vector
+  ALLOCATE (values, double, xin[0].Nelements);
+
+  for (i = 0; i < xout[0].Nelements - 1; i++) {
+    xmin = xout[0].elements.Flt[i];
+    xmax = xout[0].elements.Flt[i+1];
+
+    N = 0;
+    for (j = 0; j < xin[0].Nelements; j++) {
+      if (xin[0].elements.Flt[j] < xmin) continue;
+      if (xin[0].elements.Flt[j] > xmax) continue;
+      values[N] = yin[0].elements.Flt[j];
+      N++;
+    }
+    
+    dsort (values, N);
+    if (N > 1) {
+      sum = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
+    } else {
+      sum = values[0];
+    }
+
+    // measure the stat for this bin
+    // sum = 0.0;
+    // for (j = 0; j < N; j++) {
+    //   sum += values[j];
+    // }
+    // sum /= N;
+    yout[0].elements.Flt[i] = sum;
+  }
+  free (values);
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/dvo/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/dvo/Makefile	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/Makefile	(revision 25757)
@@ -73,5 +73,4 @@
 $(SRC)/imrough.$(ARCH).o	  	\
 $(SRC)/imsearch.$(ARCH).o	  	\
-$(SRC)/imstats.$(ARCH).o	  	\
 $(SRC)/lcat.$(ARCH).o		  	\
 $(SRC)/lcurve.$(ARCH).o	  	\
Index: /trunk/Ohana/src/opihi/dvo/gimages.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/gimages.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/gimages.c	(revision 25757)
@@ -124,5 +124,7 @@
       }
 
+      // find coordinates of image center
       XY_to_RD (&Ro, &Do, Xo, Yo, &image[i].coords);
+      if (fabs(Ro - Ra) > 120.0) continue;
 
       local.crval1 = Ro;
@@ -138,35 +140,22 @@
 	Xs = -0.5*image[i].NX;
 	Ys = -0.5*image[i].NY;
-	Xe = +0.5*image[i].NX;
-	Ye = +0.5*image[i].NY;
       } else {
 	Xs = 0.0;
 	Ys = 0.0;
-	Xe = image[i].NX;
-	Ye = image[i].NY;
       }
       
+      // find coordinates of an image corner
       XY_to_RD (&Ro, &Do, Xs, Ys, &image[i].coords);
-      RD_to_XY (&Xo, &Xo, Ro, Do, &local);
+
+      // find radius of image in arcsec
+      RD_to_XY (&Xo, &Yo, Ro, Do, &local);
       Radius = hypot (Xo, Yo);
       // fprintf (stderr, "%s: %f %f    %f ", image[i].name, local.crval1, local.crval2, Radius);
 
-      XY_to_RD (&Ro, &Do, Xs, Ye, &image[i].coords);
-      RD_to_XY (&Xo, &Xo, Ro, Do, &local);
-      Radius = MAX (Radius, hypot (Xo, Yo));
-      // fprintf (stderr, "%f ", Radius);
-
-      XY_to_RD (&Ro, &Do, Xe, Ys, &image[i].coords);
-      RD_to_XY (&Xo, &Xo, Ro, Do, &local);
-      Radius = MAX (Radius, hypot (Xo, Yo));
-      // fprintf (stderr, "%f ", Radius);
-
-      XY_to_RD (&Ro, &Do, Xe, Ye, &image[i].coords);
-      RD_to_XY (&Xo, &Xo, Ro, Do, &local);
-      Radius = MAX (Radius, hypot (Xo, Yo));
-      // fprintf (stderr, "%f ", Radius);
-
+      // check for distances to coordinates in arcsec
       RD_to_XY (&Xo, &Yo, Ra, Dec, &local);
       // fprintf (stderr, " : %f\n", hypot(Xo,Yo));
+
+      // skip images with center too far from coordinaes
       if (hypot(Xo,Yo) > 1.5*Radius) continue;
       // fprintf (stderr, " ** try me **\n");
Index: /trunk/Ohana/src/opihi/dvo/imbox.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/imbox.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/imbox.c	(revision 25757)
@@ -3,5 +3,5 @@
 int imbox (int argc, char **argv) {
   
-  int j, kapa, Nskip, status, InPic, flipped, N, haveNx, haveNy, Nx, Ny, SOLO_PHU;
+  int j, kapa, Nskip, status, InPic, flipped, N, haveNx, haveNy, Nx, Ny, SOLO_PHU, Npts, NPTS;
   Vector Xvec, Yvec;
   double r, d, x[4], y[4], Rmin, Rmax, Rmid;
@@ -36,6 +36,8 @@
   
   /* project this image to screen display coords */
-  SetVector (&Xvec, OPIHI_FLT, 8);
-  SetVector (&Yvec, OPIHI_FLT, 8);
+  Npts = 0;
+  NPTS = 200; 
+  SetVector (&Xvec, OPIHI_FLT, NPTS);
+  SetVector (&Yvec, OPIHI_FLT, NPTS);
 
   while (gfits_fread_header (f, &header)) {
@@ -82,28 +84,30 @@
 	while (r > Rmid) r-= 360.0;
       }
-      status |= RD_to_XY (&Xvec.elements.Flt[2*j], &Yvec.elements.Flt[2*j], r, d, &graphmode.coords);
+      status |= RD_to_XY (&Xvec.elements.Flt[Npts + 2*j], &Yvec.elements.Flt[Npts + 2*j], r, d, &graphmode.coords);
       if (j > 0) {
-	Xvec.elements.Flt[2*j - 1] = Xvec.elements.Flt[2*j];
-	Yvec.elements.Flt[2*j - 1] = Yvec.elements.Flt[2*j];
+	Xvec.elements.Flt[Npts + 2*j - 1] = Xvec.elements.Flt[Npts + 2*j];
+	Yvec.elements.Flt[Npts + 2*j - 1] = Yvec.elements.Flt[Npts + 2*j];
       }
     }
-    Xvec.elements.Flt[7] = Xvec.elements.Flt[0];
-    Yvec.elements.Flt[7] = Yvec.elements.Flt[0];
+    Xvec.elements.Flt[Npts + 7] = Xvec.elements.Flt[Npts + 0];
+    Yvec.elements.Flt[Npts + 7] = Yvec.elements.Flt[Npts + 0];
+
     InPic = FALSE;
     for (j = 0; j < 8; j+=2) {
-      if ((Xvec.elements.Flt[j] >= graphmode.xmin) && 
-	  (Xvec.elements.Flt[j] <= graphmode.xmax) && 
-	  (Yvec.elements.Flt[j] >= graphmode.ymin) && 
-	  (Yvec.elements.Flt[j] <= graphmode.ymax))
-	InPic = TRUE;
+      if ((Xvec.elements.Flt[Npts + j] >= graphmode.xmin) && 
+    	  (Xvec.elements.Flt[Npts + j] <= graphmode.xmax) && 
+    	  (Yvec.elements.Flt[Npts + j] >= graphmode.ymin) && 
+    	  (Yvec.elements.Flt[Npts + j] <= graphmode.ymax))
+    	InPic = TRUE;
+    }
+    if (!InPic) continue;
+
+    Npts += 8;
+    if (Npts + 8 >= NPTS) {  /* need to leave room for 4 point image */
+      NPTS += 200;
+      REALLOCATE (Xvec.elements.Flt, opihi_flt, NPTS);
+      REALLOCATE (Yvec.elements.Flt, opihi_flt, NPTS);
     }
 
-    Xvec.Nelements = Yvec.Nelements = 8;
-    if (InPic) {
-      graphmode.style = 2; /* points */
-      graphmode.ptype = 100; /* connect pairs of points */
-      graphmode.etype = 0;
-      PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
-    }
   skip:
     Nskip = gfits_data_size (&header);
@@ -111,4 +115,13 @@
     gfits_free_header (&header);
   }
+
+  Xvec.Nelements = Yvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  }
+
   fclose (f);
   free (Xvec.elements.Flt);
Index: /trunk/Ohana/src/opihi/dvo/init.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/init.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/init.c	(revision 25757)
@@ -102,5 +102,4 @@
   {1, "subpix",      subpix,       "get subpixel positions"},
   {1, "version",     version,      "show version information"},
-//{1, "imstats",     imstats,      "plot image statistics"},
 //{1, "addxtra",     addxtra,      "add extra data to object"},
 //{1, "getxtra",     getxtra,      "get extra data from object"},
Index: /trunk/Ohana/src/opihi/dvo/photometry.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/photometry.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/photometry.c	(revision 25757)
@@ -325,5 +325,7 @@
     return (FALSE);
   }
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+
+  // XXX now that DVO does not allow write access, we can drop the MasterPhotcodeFile
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, FALSE)) {
     gprint (GP_ERR, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     return (FALSE);
Index: /trunk/Ohana/src/opihi/dvo/region_list.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/region_list.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/region_list.c	(revision 25757)
@@ -42,5 +42,5 @@
 
   if (sky != NULL) SkyTableFree (sky);
-  sky = SkyTableLoadOptimal (CATDIR, skyfile, gscfile, skydepth, verbose);
+  sky = SkyTableLoadOptimal (CATDIR, skyfile, gscfile, FALSE, skydepth, verbose);
   if (sky == NULL) return FALSE;
 
Index: /trunk/Ohana/src/opihi/dvo/skycoverage.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/skycoverage.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/dvo/skycoverage.c	(revision 25757)
@@ -16,4 +16,6 @@
   Coords coords;
   int typehash;
+  int PhotcodeSelect;
+  PhotCode *PhotcodeValue;
 
   WITH_MOSAIC = FALSE;
@@ -43,4 +45,18 @@
     remove_argument (N, &argc, argv);
     ByName = TRUE;
+  }
+
+  PhotcodeValue = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    if (!InitPhotcodes ()) return (FALSE);
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyName (argv[N]);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
   }
 
@@ -85,5 +101,6 @@
  
   if (argc != 4) {
-    gprint (GP_ERR, "USAGE: skycoverage (buffer) (pixscale) (Npts) [-time start range] [-name name]\n");
+    gprint (GP_ERR, "USAGE: skycoverage (buffer) (pixscale) (Npts)\n");
+    gprint (GP_ERR, "  options: [-time start range] [-trange start stop] [-name name] [-photcode name] [+mosaic] [-mosaic] [-ra-center RA]\n");
     gprint (GP_ERR, "       (buffer) saves bitmapped AIT plot\n");
     gprint (GP_ERR, "       (pixscale) specifies the pixel size in degrees\n");
@@ -128,6 +145,6 @@
     for (xs = 0; xs < Nx; xs++) {
       status = XY_to_RD (&r, &d, (double)(xs), (double)(ys), &coords);
-      status &= (r > 0);
-      status &= (r < 360);
+      status &= (r >= 0);
+      status &= (r <= 360);
       if (status) {
 	V[ys*Nx + xs] = 2;
@@ -141,4 +158,13 @@
     if (ByName && strcmp (name, image[i].name)) continue;
     if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+
+    if (PhotcodeSelect) {
+      if (PhotcodeValue[0].type == PHOT_DEP) {
+	if (PhotcodeValue[0].code != image[i].photcode) continue;
+      } else {
+	if (PhotcodeValue[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+
     if (!FindMosaicForImage (image, Nimage, i)) continue;
 
@@ -167,4 +193,6 @@
 	XY_to_RD (&r, &d, Xi, Yi, &image[i].coords);
 	r = ohana_normalize_angle (r);
+	if (r - RaCenter > +180.0) r -= 360.0;
+	if (r - RaCenter < -180.0) r += 360.0;
 	status = RD_to_XY (&Xs, &Ys, r, d, &coords);
 	if (Xs < 0) continue;
Index: /trunk/Ohana/src/opihi/lib.data/mrqmin.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.data/mrqmin.c	(revision 25756)
+++ /trunk/Ohana/src/opihi/lib.data/mrqmin.c	(revision 25757)
@@ -17,5 +17,5 @@
 
   int k, j, i;
-  opihi_flt ydiff, wt, chisq;
+  opihi_flt ymodel, ydiff, wt, chisq;
 
   for (j = 0; j < Npar; j++) {
@@ -27,6 +27,9 @@
   for (i = 0; i < Npts; i++) {
 
-    ydiff = funcs (x[i], par, Npar, dyda) - y[i];
+    ymodel = funcs (x[i], par, Npar, dyda);
+    ydiff = ymodel - y[i];
     chisq += SQ(ydiff) * dy[i];
+
+    // fprintf (stderr, "%f %f - %f : %f -> %f\n", x[i], y[i], ymodel, dy[i], chisq);
 
     for (j = 0; j < Npar; j++) {
@@ -85,5 +88,5 @@
 
   /* if good, save temp values */
-  if (rho > 0) {
+  if ((chisq > 1e-3) && (rho > -1e-6)) {
     lambda *= 0.1;
     ochisq = chisq;
Index: /trunk/Ohana/src/photdbc/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/photdbc/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/photdbc/src/ConfigInit.c	(revision 25757)
@@ -64,5 +64,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/photdbc/src/photdbc.c
===================================================================
--- /trunk/Ohana/src/photdbc/src/photdbc.c	(revision 25756)
+++ /trunk/Ohana/src/photdbc/src/photdbc.c	(revision 25757)
@@ -17,5 +17,5 @@
 
   // the output catalog needs to inherit the SKY_DEPTH of the input catalog
-  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, SKY_DEPTH_HST, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, NULL, GSCFILE, TRUE, SKY_DEPTH_HST, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   skylist = SkyListByPatch (sky, -1, &REGION);
Index: /trunk/Ohana/src/relastro/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/relastro/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/relastro/src/ConfigInit.c	(revision 25757)
@@ -60,5 +60,5 @@
   /* update master photcode table if not defined */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/relastro/src/load_images.c
===================================================================
--- /trunk/Ohana/src/relastro/src/load_images.c	(revision 25756)
+++ /trunk/Ohana/src/relastro/src/load_images.c	(revision 25757)
@@ -11,5 +11,5 @@
 
   // load the current sky table (layout of all SkyRegions) 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   
Index: /trunk/Ohana/src/relastro/src/relastro_objects.c
===================================================================
--- /trunk/Ohana/src/relastro/src/relastro_objects.c	(revision 25756)
+++ /trunk/Ohana/src/relastro/src/relastro_objects.c	(revision 25757)
@@ -11,5 +11,5 @@
 
   // load the current sky table (layout of all SkyRegions) 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   
Index: /trunk/Ohana/src/relphot/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/relphot/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/relphot/src/ConfigInit.c	(revision 25757)
@@ -58,5 +58,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
Index: /trunk/Ohana/src/relphot/src/load_images.c
===================================================================
--- /trunk/Ohana/src/relphot/src/load_images.c	(revision 25756)
+++ /trunk/Ohana/src/relphot/src/load_images.c	(revision 25757)
@@ -11,5 +11,5 @@
 
   // load the current sky table (layout of all SkyRegions) 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   
Index: /trunk/Ohana/src/relphot/src/relphot_objects.c
===================================================================
--- /trunk/Ohana/src/relphot/src/relphot_objects.c	(revision 25756)
+++ /trunk/Ohana/src/relphot/src/relphot_objects.c	(revision 25757)
@@ -11,5 +11,5 @@
 
   // load the current sky table (layout of all SkyRegions) 
-  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
   SkyTableSetFilenames (sky, CATDIR, "cpt");
   
Index: /trunk/Ohana/src/tools/src/mktemp.c
===================================================================
--- /trunk/Ohana/src/tools/src/mktemp.c	(revision 25756)
+++ /trunk/Ohana/src/tools/src/mktemp.c	(revision 25757)
@@ -1,17 +1,116 @@
 # include <stdio.h>
 # include <stdlib.h>
+# include <string.h>
+
+# define FALSE 0
+# define TRUE 1
 
 int main (int argc, char **argv) {
 
-  if (argc != 2) {
-    fprintf (stderr, "USAGE: %s (template)\n", argv[0]);
-    exit (1);
+  int i, j, Ntotal;
+  char *tmpdir, *template, deftemplate[32], *prefix, defprefix[32], *filename;
+  int make_directory, fail_silently, unsafe_mode, full_path;
+
+  tmpdir = NULL;
+  prefix = NULL;
+  template = NULL;
+  filename = NULL;
+
+  make_directory = FALSE;
+  fail_silently = FALSE;
+  unsafe_mode = FALSE;
+  full_path = TRUE;
+
+  for (i = 1; i < argc; i++) {
+    // -options must be first
+    if (argv[i][0] == '-') {
+      if (!strcmp(argv[i], "-V")) {
+	fprintf (stdout, "mktemp version Ohana $Revision: $\n");
+	exit (0);
+      }
+      if (!strcmp(argv[i], "-p")) {
+	if (argc <= i + 1) usage();
+	i++;
+	prefix = argv[i];
+	full_path = FALSE;
+	continue;
+      }
+      for (j = 1; j < strlen(argv[i]); j++) {
+	if (argv[i][j] == 'q') {
+	  fail_silently = TRUE;
+	  continue;
+	}
+	if (argv[i][j] == 't') {
+	  full_path = FALSE;
+	  continue;
+	}
+	if (argv[i][j] == 'd') {
+	  // make directory
+	  make_directory = TRUE;
+	  continue;
+	}
+	if (argv[i][j] == 'u') {
+	  unsafe_mode = TRUE;
+	  continue;
+	}
+	// unknown option
+	usage();
+      }
+      continue;
+    }
+    // report an error if too many arguments are given
+    if (template) usage();
+    template = argv[i];
   }
 
-  if (mkstemp (argv[1]) == -1) exit (1);
+  if (!full_path) {
+    // prefix = TMPDIR ? TMPDIR : (prefix ? prefix : /tmp)
+    tmpdir = getenv("TMPDIR");
+    if (tmpdir) {
+      prefix = tmpdir;
+    }
+    if (!prefix) {
+      strcpy (defprefix, "/tmp");
+      prefix = defprefix;
+    }
+    if (template && strchr(template, '/')) usage();
+  }
 
-  fprintf (stdout, "%s\n", argv[1]);
+  if (!template) {
+    if (full_path) {
+      strcpy (deftemplate, "/tmp/tmp.XXXXXXXXXX");
+    } else {
+      strcpy (deftemplate, "tmp.XXXXXXXXXX");
+    }
+    template = deftemplate;
+  }
+
+  // filename = full_path ? prefix/template : template;
+  if (!full_path) {
+    Ntotal = strlen(prefix) + strlen(template) + 2;
+    filename = (char *) malloc (Ntotal);
+    snprintf (filename, Ntotal, "%s/%s", prefix, template);
+    template = filename;
+  }
+
+  if (make_directory) {
+    if (mkdtemp (template) == -1) {
+      if (!fail_silently) fprintf (stderr, "failed to make temp file from %s\n", template);
+      exit (1);
+    }
+  } else {
+    if (mkstemp (template) == -1) {
+      if (!fail_silently) fprintf (stderr, "failed to make temp file from %s\n", template);
+      exit (1);
+    }
+  }
+
+  fprintf (stdout, "%s\n", template);
 
   exit (0);
 }
  
+usage() {
+  fprintf (stderr, "Usage: mktemp [-V] | [-dqtu] [-p prefix] [template]\n");
+  exit (1);
+}
Index: /trunk/Ohana/src/uniphot/src/ConfigInit.c
===================================================================
--- /trunk/Ohana/src/uniphot/src/ConfigInit.c	(revision 25756)
+++ /trunk/Ohana/src/uniphot/src/ConfigInit.c	(revision 25757)
@@ -36,5 +36,5 @@
   /* XXX this does not yet write out the master photcode table */
   sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
-  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile, TRUE)) {
     fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
     exit (1);
