Index: trunk/Ohana/src/libdvo/Makefile
===================================================================
--- trunk/Ohana/src/libdvo/Makefile	(revision 35260)
+++ trunk/Ohana/src/libdvo/Makefile	(revision 35263)
@@ -98,4 +98,6 @@
 $(SRC)/LoadImages.$(ARCH).o		\
 $(SRC)/ImageSelection.$(ARCH).o		\
+$(SRC)/ImageMetadataSelection.$(ARCH).o \
+$(SRC)/ImageMetadata.$(ARCH).o \
 $(SRC)/ImageOps.$(ARCH).o		\
 $(SRC)/match_image.$(ARCH).o		\
Index: trunk/Ohana/src/libdvo/doc/image-searches.txt
===================================================================
--- trunk/Ohana/src/libdvo/doc/image-searches.txt	(revision 35263)
+++ trunk/Ohana/src/libdvo/doc/image-searches.txt	(revision 35263)
@@ -0,0 +1,19 @@
+
+I am making some fixes to the code to select an image from the full
+table (specifically for mextract).
+
+here is how things work currently: 
+
+* mextract detects that a query needs data from an image field
+* mextract calls SetImageSelection()
+  * SetImageSelection calls:
+    * LoadImagesDVO(), which reads the image table into its static cache
+    * BuildChipMatch to generate the index for chip->mosaic 
+    * image_subset which generates a local (to ImageSelection.c) static subset
+    * sort_image_subset sorts the subset selection to be ordered by time
+* mextract calls MatchImageDVO to find a specific image
+  * match_image_subset is ok because subset is sorted by time
+
+* for the image metadata, I need to do the following:
+
+* load the image subset table
Index: trunk/Ohana/src/libdvo/include/dvodb.h
===================================================================
--- trunk/Ohana/src/libdvo/include/dvodb.h	(revision 35260)
+++ trunk/Ohana/src/libdvo/include/dvodb.h	(revision 35263)
@@ -298,4 +298,16 @@
 } dbValue;
 
+typedef struct {
+  double crval1;
+  double crval2;
+  unsigned int imageID;
+  unsigned int externID;
+  unsigned int expname;
+  float Mcal;
+  float secz;
+  float Xcenter;
+  float Ycenter;
+} ImageMetadata;
+
 Image        *LoadImagesDVO         PROTO((off_t *Nimage));
 void          FreeImagesDVO         PROTO((Image *images));
@@ -346,5 +358,5 @@
 int dbExtractMeasuresInitAve (void);
 int dbExtractMeasuresInitMeas (void);
-int dbExtractMeasuresInit (void);
+int dbExtractMeasuresInit (int isRemoteClient);
 
 int dbExtractAveragesInitTransform (CoordTransformSystem target);
@@ -363,3 +375,13 @@
 #include "get_graphdata.h"
 
+ImageMetadata *ImageMetadataLoad(char *filename, off_t *nimage);
+int ImageMetadataSave(char *filename, Image *image, off_t Nimage);
+
+int SetImageMetadataSelection (char *filename);
+void FreeImageMetadataSelection ();
+ImageMetadata *MatchImageMetadataDVO (unsigned int imageID);
+Coords *MatchMosaicMetadata (unsigned int imageID);
+off_t match_image_by_ID (ImageMetadata *image, off_t Nimage, unsigned int ID);
+void sort_image_metadata (ImageMetadata *image, off_t Nimage);
+
 # endif
Index: trunk/Ohana/src/libdvo/src/ImageMetadata.c
===================================================================
--- trunk/Ohana/src/libdvo/src/ImageMetadata.c	(revision 35263)
+++ trunk/Ohana/src/libdvo/src/ImageMetadata.c	(revision 35263)
@@ -0,0 +1,225 @@
+# include "dvo.h"
+# define VERBOSE 1
+
+# define GET_COLUMN(OUT,NAME,TYPE) \
+  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+// this is nearly identical to the one in 'uniphot' and 'relphot'
+ImageMetadata *ImageMetadataLoad(char *filename, off_t *nimage) {
+
+  int i, Ncol;
+  off_t Nrow;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  *nimage = 0;
+  ImageMetadata *image = NULL;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset header\n");
+    fclose (f);
+    return NULL;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return NULL;
+  }
+
+  ftable.header = &theader;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) {
+    fclose (f);
+    return NULL;
+  }
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftable, FALSE)) {
+    fclose (f);
+    return (NULL);
+  }
+  fclose (f);
+
+  // a bit annoying : we read the entire block of data, then extract the columns, then set the image structure values.
+  // this means I need 3 copies in memory at some point.  ugh.
+
+  char type[16];
+
+  GET_COLUMN (imageID,  "IMAGE_ID",       int);
+  GET_COLUMN (externID, "EXTERN_ID",      int);
+  GET_COLUMN (expname,  "EXPNAME_AS_INT", int);
+  GET_COLUMN (crval1,   "CRVAL1",         double);
+  GET_COLUMN (crval2,   "CRVAL2",         double);
+  GET_COLUMN (Mcal,     "MCAL",           float);
+  GET_COLUMN (secz,     "SECZ",           float);
+  GET_COLUMN (Xcenter,  "X_CENTER",       float);
+  GET_COLUMN (Ycenter,  "Y_CENTER",       float);
+
+  ALLOCATE (image, ImageMetadata, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    image[i].imageID  = imageID[i] ;
+    image[i].externID = externID[i];
+    image[i].expname  = expname[i] ;
+    image[i].crval1   = crval1[i]  ;
+    image[i].crval2   = crval2[i]  ;
+    image[i].Mcal     = Mcal[i]    ;
+    image[i].secz     = secz[i]    ;
+    image[i].Xcenter  = Xcenter[i] ;
+    image[i].Ycenter  = Ycenter[i] ;
+  }
+  fprintf (stderr, "loaded data for %lld images\n", (long long) Nrow);
+
+  free (imageID);
+  free (externID);
+  free (expname);
+  free (crval1);
+  free (crval2);
+  free (Mcal);
+  free (secz);
+  free (Xcenter);
+  free (Ycenter);
+
+  *nimage = Nrow;
+  return image;
+}
+
+// STATUS is value expected for success
+# define CHECK_STATUS(STATUS,MSG,...)					\
+  if (!(STATUS)) {							\
+    fprintf (stderr, MSG, __VA_ARGS__);					\
+    return FALSE;							\
+  }
+
+// save a minimal set of metadata needed to mextract joins
+int ImageMetadataSave(char *filename, Image *image, off_t Nimage) {
+
+  int i;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_create_table_header (&theader, "BINTABLE", "IMAGE_SUBSET");
+
+  gfits_define_bintable_column (&theader, "J", "IMAGE_ID", "image ID", NULL, 1.0, 1.0*0x8000);
+  gfits_define_bintable_column (&theader, "J", "EXTERN_ID", "extern ID", NULL, 1.0, 1.0*0x8000);
+  gfits_define_bintable_column (&theader, "J", "EXPNAME_AS_INT", "expname as integer", NULL, 1.0, 1.0*0x8000);
+  gfits_define_bintable_column (&theader, "D", "CRVAL1", "ra at center", "degrees", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "CRVAL2", "dec at center", "degrees", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "MCAL", "zero point offset", "magnitudes", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "SECZ", "airmass", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "X_CENTER", "chip center", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "Y_CENTER", "chip center", "none", 1.0, 0.0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  unsigned int *imageID, *externID, *expname;
+  double *crval1, *crval2;
+  float *Mcal, *Xcenter, *Ycenter, *secz;
+
+  // create intermediate storage arrays
+  ALLOCATE (imageID,  unsigned int,   Nimage);
+  ALLOCATE (externID, unsigned int,   Nimage);
+  ALLOCATE (expname,  unsigned int,   Nimage);
+  ALLOCATE (crval1,   double,  	      Nimage);
+  ALLOCATE (crval2,   double, 	      Nimage);
+  ALLOCATE (Mcal,     float, 	      Nimage);
+  ALLOCATE (secz,     float, 	      Nimage);
+  ALLOCATE (Xcenter,  float, 	      Nimage);
+  ALLOCATE (Ycenter,  float, 	      Nimage);
+
+  // assign the storage arrays
+  for (i = 0; i < Nimage; i++) {
+    imageID[i]  = image[i].imageID;
+    externID[i] = image[i].externID;
+    crval1[i]   = image[i].coords.crval1;
+    crval2[i]   = image[i].coords.crval2;
+    Mcal[i]     = image[i].Mcal;
+    secz[i]     = image[i].secz;
+    Xcenter[i]  = 0.5*image[i].NX;
+    Ycenter[i]  = 0.5*image[i].NY;
+
+    expname[i]  = 0;
+    if ((image[i].name[0] == 'o') && (image[i].name[5] == 'g') && (image[i].name[10] == 'o')) {
+      int mjd  = atoi(&image[i].name[1]);
+      int Nexp = atoi(&image[i].name[6]);
+      expname[i] = mjd * 10000 + Nexp;
+    }
+  }
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "IMAGE_ID",       imageID,  Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "EXTERN_ID",      externID, Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "EXPNAME_AS_INT", expname,  Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "CRVAL1",         crval1,  Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "CRVAL2",         crval2,  Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "MCAL",           Mcal,    Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "SECZ",           secz,    Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "X_CENTER",       Xcenter, Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "Y_CENTER",       Ycenter, Nimage);
+
+  free (imageID);
+  free (externID);
+  free (expname);
+  free (crval1);
+  free (crval2);
+  free (Mcal);
+  free (secz);
+  free (Xcenter);
+  free (Ycenter);
+
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file for output %s\n", filename);
+    return FALSE;
+  }
+
+  int status;
+  status = gfits_fwrite_header  (f, &header);
+  CHECK_STATUS (status, "ERROR: cannot write header for image subset %s\n", filename);
+
+  status = gfits_fwrite_matrix  (f, &matrix);
+  CHECK_STATUS (status, "ERROR: cannot write matrix for image subset %s\n", filename);
+
+  status = gfits_fwrite_Theader (f, &theader);
+  CHECK_STATUS (status, "ERROR: cannot write table header for image subset %s\n", filename);
+
+  status = gfits_fwrite_table  (f, &ftable);
+  CHECK_STATUS (status, "ERROR: cannot write table data for image subset %s\n", filename);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  int fd = fileno (f);
+
+  status = fflush (f);
+  CHECK_STATUS (!status, "ERROR: cannot flush file image subset %s\n", filename);
+
+  status = fsync (fd);
+  CHECK_STATUS (!status, "ERROR: cannot flush file image subset %s\n", filename);
+
+  status = fclose (f);
+  CHECK_STATUS (!status, "ERROR: problem closing image subset file %s\n", filename);
+
+  return TRUE;
+}
Index: trunk/Ohana/src/libdvo/src/ImageMetadataSelection.c
===================================================================
--- trunk/Ohana/src/libdvo/src/ImageMetadataSelection.c	(revision 35263)
+++ trunk/Ohana/src/libdvo/src/ImageMetadataSelection.c	(revision 35263)
@@ -0,0 +1,87 @@
+# include "dvo.h"
+
+/* db image table */
+static ImageMetadata *image = NULL;
+static off_t Nimage = 0;
+static Coords mosaic;
+
+/* load images based on parameters and region, etc */
+int SetImageMetadataSelection (char *filename) {
+
+  image = NULL;
+  
+  /* mosaic defines a frame with 0,0 at the mosaic center, and 1 arcsec / pixel */
+  mosaic.crpix1 = mosaic.crpix2 = 0.0;
+  mosaic.cdelt1 = mosaic.cdelt2 = 1.0 / 3600;
+  mosaic.pc1_1  = mosaic.pc2_2  = 1.0;
+  mosaic.pc1_2  = mosaic.pc2_1  = 0.0;
+  mosaic.Npolyterms = 0;
+  strcpy (mosaic.ctype, "RA---SIN");
+
+  if ((image = ImageMetadataLoad (filename, &Nimage)) == NULL) return (FALSE);
+
+  // sort the images by their imageID for fast searches
+  // XXX is this too slow?  do I need to sort an array of pointers?
+  sort_image_metadata (image, Nimage);
+  return (TRUE);
+}
+
+/* free loaded images */
+void FreeImageMetadataSelection () {
+  if (image != NULL) free(image);
+  image = NULL;
+  return;
+}
+
+ImageMetadata *MatchImageMetadataDVO (unsigned int imageID) { 
+
+  off_t m = match_image_by_ID (image, Nimage, imageID);
+  if (m == -1) return (NULL);
+
+  return (&image[m]);
+}
+
+Coords *MatchMosaicMetadata (unsigned int imageID) { 
+
+  int m;
+
+  m = match_image_by_ID (image, Nimage, imageID);
+  if (m == -1) return (NULL);
+  mosaic.crval1 = image[m].crval1;
+  mosaic.crval2 = image[m].crval2;
+  return (&mosaic);
+}
+
+off_t match_image_by_ID (ImageMetadata *image, off_t Nimage, unsigned int ID) {
+
+  off_t N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].imageID < ID) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].imageID > ID) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if (image[N].imageID == ID) return (N);
+  }
+  return (-1);
+}
Index: trunk/Ohana/src/libdvo/src/ImageSelection.c
===================================================================
--- trunk/Ohana/src/libdvo/src/ImageSelection.c	(revision 35260)
+++ trunk/Ohana/src/libdvo/src/ImageSelection.c	(revision 35263)
@@ -68,4 +68,25 @@
 }
 
+Image *MatchImageDVO_old (unsigned int time, short int source, unsigned int imageID) { 
+
+  int m = -1;
+
+  if ((imageID != 0) && (imageID < Nimage)) {
+    // imageID is in range for the array of images. If the table is still in order and
+    // no images have been deleted the index of the image we are looking for will be imageID - 1
+    // If this is the case, we have it. Otherwise we'll have to go search for it below
+    int guess = (int) imageID - 1;
+    if (image[guess].imageID == imageID) {
+        m = guess;
+    }
+  } 
+  if (m == -1) {
+    m = match_image_subset (image, subset, Nsubset, time, source);
+  }
+  if (m == -1) return (NULL);
+  if (!FindMosaicForImage (image, Nimage, m)) return (NULL);
+  return (&image[m]);
+}
+
 Coords *MatchMosaic (unsigned int time, short int source) { 
 
Index: trunk/Ohana/src/libdvo/src/dbExtractMeasures.c
===================================================================
--- trunk/Ohana/src/libdvo/src/dbExtractMeasures.c	(revision 35260)
+++ trunk/Ohana/src/libdvo/src/dbExtractMeasures.c	(revision 35263)
@@ -14,4 +14,6 @@
 static CoordTransform *celestial_to_ecliptic = NULL;
 
+static int REMOTE_CLIENT = FALSE;
+
 static int haveGalacticAve = FALSE;
 static double GLON_AVE = 0.0;
@@ -30,5 +32,6 @@
 static double ELAT_MEAS = 0.0;
 
-int dbExtractMeasuresInit () {
+int dbExtractMeasuresInit (int isRemoteClient) {
+  REMOTE_CLIENT = isRemoteClient;
   GetTimeFormat (&TimeReference, &TimeFormat);
   return (TRUE);
@@ -335,10 +338,14 @@
       break;
     case MEAS_MEAN_AIRMASS: /* OK */
-      {
+      if (REMOTE_CLIENT) {
+	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
+	if (image == NULL) break;
+	value.Flt = image[0].secz;
+      } else {
 	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
 	if (image == NULL) break;
 	value.Flt = image[0].secz;
-	break;
-      }
+      }
+      break;
     case MEAS_AZ: /* OK */
       value.Flt = measure[0].az;
@@ -461,5 +468,5 @@
       ra  = average[0].R - measure[0].dR / 3600.0;
       dec = average[0].D - measure[0].dD / 3600.0;
-      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      mosaic = MatchMosaicMetadata (measure[0].imageID);
       if (mosaic == NULL) break;
       RD_to_XY (&x, &y, ra, dec, mosaic);
@@ -515,16 +522,22 @@
       break;
     case MEAS_EXTERN_ID: /* OK */
-      {
-	Image *image;
-	image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+      if (REMOTE_CLIENT) {
+	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
 	if (image == NULL) break;
 	value.Int = image->externID;
+      } else {
+	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+	if (image == NULL) break;
+	value.Int = image[0].externID;
       }
       break;
 
     case MEAS_EXPNAME_AS_INT:
-      {
-	Image *image;
-	image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+      if (REMOTE_CLIENT) {
+	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
+	if (image == NULL) break;
+	value.Int = image->expname;
+      } else {
+	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
 	if (image == NULL) break;
 	// XXX very crude: if this matches oNNNNgNNNNo, then convert to an int
@@ -544,20 +557,27 @@
     case MEAS_FLAT: /* OK */
       // flat = measure.Mcal - image.Mcal
-      { 
-	Image *image;
-	image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+      if (REMOTE_CLIENT) {
+	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
 	if (image == NULL) break;
 	value.Flt = measure[0].Mcal - image[0].Mcal;
-      }
-      break;
-
+      } else {
+	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+	if (image == NULL) break;
+	value.Flt = measure[0].Mcal - image[0].Mcal;
+      }
+      break;
+
+      // we have measure[0].Xccd,Yccd and image[0].NX,NY.  Find the distance to the center
     case MEAS_CENTER_OFFSET: /* OK */
-      { 
-	Image *image;
-	image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
-	if (image == NULL) break;
-	
-	// we have measure[0].Xccd,Yccd and image[0].NX,NY.  Find the distance to the center
-
+      if (REMOTE_CLIENT) {
+	ImageMetadata *image = MatchImageMetadataDVO (measure[0].imageID);
+	if (image == NULL) break;
+	float Xcenter = image[0].Xcenter;
+	float Ycenter = image[0].Ycenter;
+	float distance = hypot (measure[0].Xccd - Xcenter, measure[0].Yccd - Ycenter);
+	value.Flt = distance;
+      } else {
+	Image *image = MatchImageDVO (measure[0].t, measure[0].photcode, measure[0].imageID);
+	if (image == NULL) break;
 	// XXX we may hypotetically have images with -NX to +NX here (eg, projection center), but 
 	// we do not get a detection from that type of image
Index: trunk/Ohana/src/libdvo/src/dvosorts.c
===================================================================
--- trunk/Ohana/src/libdvo/src/dvosorts.c	(revision 35260)
+++ trunk/Ohana/src/libdvo/src/dvosorts.c	(revision 35263)
@@ -23,4 +23,17 @@
 
   OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort ImageMetadata array by image[i].imageID */
+void sort_image_metadata (ImageMetadata *image, off_t Nimage) {
+
+# define SWAPFUNC(A,B){ ImageMetadata tmp; tmp = image[A]; image[A] = image[B]; image[B] = tmp; }
+# define COMPARE(A,B)(image[A].imageID < image[B].imageID)
+
+  OHANA_SORT (Nimage, COMPARE, SWAPFUNC);
 
 # undef SWAPFUNC
