Index: /trunk/Ohana/src/addstar/Makefile
===================================================================
--- /trunk/Ohana/src/addstar/Makefile	(revision 35262)
+++ /trunk/Ohana/src/addstar/Makefile	(revision 35263)
@@ -90,4 +90,5 @@
 $(SRC)/GetFileMode.$(ARCH).o \
 $(SRC)/ReadImageHeader.$(ARCH).o \
+$(SRC)/ImageIndex.$(ARCH).o \
 $(SRC)/UpdateImageIDs.$(ARCH).o \
 $(SRC)/update_coords.$(ARCH).o \
@@ -207,4 +208,5 @@
 $(SRC)/ReadStarsTEXT.$(ARCH).o \
 $(SRC)/ReadStarsSDSS.$(ARCH).o \
+$(SRC)/ImageIndex.$(ARCH).o \
 $(SRC)/UpdateImageIDs.$(ARCH).o \
 $(SRC)/FilterStars.$(ARCH).o \
Index: /trunk/Ohana/src/addstar/include/addstar.h
===================================================================
--- /trunk/Ohana/src/addstar/include/addstar.h	(revision 35262)
+++ /trunk/Ohana/src/addstar/include/addstar.h	(revision 35263)
@@ -56,4 +56,16 @@
   char *refcat;
 } DVO_DATA;
+
+# define IDTYPE int
+
+typedef struct {
+  IDTYPE Nimage;
+  IDTYPE minID;
+  IDTYPE maxID;
+  IDTYPE range;
+  IDTYPE *imageID;
+  IDTYPE *externID;
+  char *found;
+} ImageIndex;
 
 typedef struct sockaddr_in SockAddress;
@@ -275,4 +287,7 @@
 int UpdateImageIDs (Stars *stars, unsigned int Nstars, Image *images, off_t Nimages);
 
+int CheckDuplicateImageIDs (Image *images, off_t Nimages);
+int ImageIndexFileInit ();
+
 int LoadDataSDSS (FILE *f, char *file, Image **images, off_t *nvalid, Stars **stars, unsigned int *Nstars, Header **headers, off_t *extsize, HeaderSet *headerSets, off_t Nimages);
 int LoadDataPMM (FILE *f, char *file, Image **images, off_t *nvalid, Stars **stars, unsigned int *Nstars);
Index: /trunk/Ohana/src/addstar/src/ImageIndex.c
===================================================================
--- /trunk/Ohana/src/addstar/src/ImageIndex.c	(revision 35263)
+++ /trunk/Ohana/src/addstar/src/ImageIndex.c	(revision 35263)
@@ -0,0 +1,212 @@
+# include "addstar.h"
+
+ImageIndex *ImageIndexLoad (char *filename) {
+
+  int i, Ncol;
+  off_t Nrow;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  ImageIndex *index = NULL;
+  ALLOCATE (index, ImageIndex, 1);
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image index 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 index header\n");
+    fclose (f);
+    return NULL;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image index 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);
+
+  char type[16];
+  index->externID = gfits_get_bintable_column_data (&theader, &ftable, "EXTERN_ID", type, &Nrow, &Ncol);
+  myAssert (!strcmp(type, "int"), "wrong column type");
+
+  index->imageID  = gfits_get_bintable_column_data (&theader, &ftable, "IMAGE_ID", type, &Nrow, &Ncol);
+  myAssert (!strcmp(type, "int"), "wrong column type");
+
+  index->Nimage = Nrow;
+
+  // find the range of externID values
+  index->minID = -1;
+  index->maxID = -1;
+  for (i = 0; i < index->Nimage; i++) {
+    if (index->minID == -1) index->minID = index->externID[i];
+    index->minID = MIN(index->minID, index->externID[i]);
+    index->maxID = MAX(index->maxID, index->externID[i]);
+  }
+  index->range = index->maxID- index->minID + 1;
+  
+  // init the index
+  ALLOCATE (index->found, char, index->range);
+  for (i = 0; i < index->range; i++) index->found[i] = FALSE;
+
+  // generate the index (set this value to the imageID?)
+  for (i = 0; i < index->Nimage; i++) {
+    IDTYPE N = index->externID[i] - index->minID;
+    index->found[N] = TRUE;
+  }
+
+  return index;
+}
+
+// STATUS is value expected for success
+# define CHECK_STATUS(STATUS,MSG,...)					\
+  if (!(STATUS)) {							\
+    fprintf (stderr, MSG, __VA_ARGS__);					\
+    return FALSE;							\
+  }
+
+int ImageIndexSave (char *filename, ImageIndex *index) {
+
+  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", "EXTERN_ID");
+  gfits_define_bintable_column (&theader, "J", "IMAGE_ID", "image ID", NULL, 1.0, 0);
+  gfits_define_bintable_column (&theader, "J", "EXTERN_ID", "extern ID", NULL, 1.0, 0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "IMAGE_ID",       index->imageID,  index->Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "EXTERN_ID",      index->externID, index->Nimage);
+
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open index 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 index %s\n", filename);
+
+  status = gfits_fwrite_matrix  (f, &matrix);
+  CHECK_STATUS (status, "ERROR: cannot write matrix for image index %s\n", filename);
+
+  status = gfits_fwrite_Theader (f, &theader);
+  CHECK_STATUS (status, "ERROR: cannot write table header for image index %s\n", filename);
+
+  status = gfits_fwrite_table  (f, &ftable);
+  CHECK_STATUS (status, "ERROR: cannot write table data for image index %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 index %s\n", filename);
+
+  status = fsync (fd);
+  CHECK_STATUS (!status, "ERROR: cannot fsync file image index %s\n", filename);
+
+  status = fclose (f);
+  CHECK_STATUS (!status, "ERROR: problem closing image index file %s\n", filename);
+
+  return TRUE;
+}
+
+int CheckDuplicateImageIDs (Image *images, off_t Nimages) {
+
+  IDTYPE i, offset;
+
+  // load the image ID table
+  char filename[DVO_MAX_PATH];
+  snprintf (filename, DVO_MAX_PATH, "%s/ImageIndex.fits", CATDIR);
+
+  ImageIndex *index = ImageIndexLoad (filename);
+
+  if (!index) {
+      fprintf (stderr, "image index file is not found, cannot check for image duplicates\n");
+      exit (2);
+  }
+
+  for (i = 0; i < Nimages; i++) {
+    IDTYPE ID = images[i].externID;
+    if (ID == 0) continue;
+
+    if (ID < index->minID) continue;
+    if (ID > index->maxID) continue;
+
+    offset = ID - index->minID;
+    myAssert (offset >= 0, "offset out of range?");
+    myAssert (offset <= index->range, "offset out of range?");
+
+    if (!index->found[offset]) continue;
+    fprintf (stderr, "duplicate external image ID %lld found, exiting\n", (long long) ID);
+    exit (1);
+  }
+  
+  // extend the storage arrays
+  REALLOCATE (index->imageID,  IDTYPE, index->Nimage + Nimages);
+  REALLOCATE (index->externID, IDTYPE, index->Nimage + Nimages);
+
+  // append the new ext IDs and save
+  for (i = 0; i < Nimages; i++) {
+    if (images[i].externID == 0) continue;
+    index->imageID[index->Nimage] = images[i].imageID;
+    index->externID[index->Nimage] = images[i].externID;
+    index->Nimage ++;
+  }
+
+  // do this as an 'extend' operation
+  ImageIndexSave (filename, index);
+  return TRUE;
+}
+
+int ImageIndexFileInit () {
+
+  // load the image ID table
+  char filename[DVO_MAX_PATH];
+  snprintf (filename, DVO_MAX_PATH, "%s/ImageIndex.fits", CATDIR);
+
+  ImageIndex *index = NULL;
+  ALLOCATE (index, ImageIndex, 1);
+  ALLOCATE (index->imageID,  IDTYPE, 1);
+  ALLOCATE (index->externID, IDTYPE, 1);
+  index->Nimage = 0;
+
+  // do this as an 'extend' operation
+  ImageIndexSave (filename, index);
+  return TRUE;
+}
Index: /trunk/Ohana/src/addstar/src/UpdateImageIDs.c
===================================================================
--- /trunk/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 35262)
+++ /trunk/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 35263)
@@ -18,5 +18,6 @@
     if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
     dvo_image_create (&db, GetZeroPoint());
-    isEmpty = TRUE;
+    ImageIndexFileInit ();
+  isEmpty = TRUE;
   } else {
     /* position to start of file */
@@ -51,4 +52,8 @@
   }
 
+  // set and update the imageID sequence
+  // the file holding the index is created above if this is an empty db
+  CheckDuplicateImageIDs (images, Nimages);
+
   imageID += Nimages;
   status = gfits_modify (&db.header, "IMAGEID", "%u", 1, imageID);
Index: /trunk/Ohana/src/addstar/test/simple.dvo
===================================================================
--- /trunk/Ohana/src/addstar/test/simple.dvo	(revision 35262)
+++ /trunk/Ohana/src/addstar/test/simple.dvo	(revision 35263)
@@ -52,4 +52,5 @@
   exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 01:00:00 -radec $RA $DEC -type $1
   if ($TAP_VERBOSE)
+    echo exec addstar -D CATDIR catdir.test -D CAMERA simtest test.cmf -D CATFORMAT $2 -quick-airmass
     exec addstar -D CATDIR catdir.test -D CAMERA simtest test.cmf -D CATFORMAT $2 -quick-airmass
   else
@@ -68,4 +69,5 @@
 
   for i 0 $testfields:n
+    if ($TAP_VERBOSE) echo $testfields:$i
     list name -split $testfields:$i
     if ("$name:0" == "SKIP") continue
@@ -337,5 +339,5 @@
   PSF_INST_FLUX     : SKIP # not ingested into DVO
   PSF_INST_FLUX_SIG : SKIP # not ingested into DVO
-  AP_MAG_STANDARD   : mag:aperinst # FAIL
+  AP_MAG            : mag:aperinst # FAIL
   AP_MAG_RAW        : SKIP # not ingested into DVO
   AP_MAG_RADIUS     : SKIP # not ingested into DVO
@@ -346,5 +348,5 @@
   PEAK_FLUX_AS_MAG  : SKIP # not ingested into DVO
   SKY               : sky	
-  SKY_SIG           : sky_err	
+  SKY_SIGMA         : sky_err	
   PSF_CHISQ         : psf_chisq	
   CR_NSIGMA         : cr_nsigma	
Index: /trunk/Ohana/src/dvomerge/test/HostTable.dat
===================================================================
--- /trunk/Ohana/src/dvomerge/test/HostTable.dat	(revision 35262)
+++ /trunk/Ohana/src/dvomerge/test/HostTable.dat	(revision 35263)
@@ -1,5 +1,5 @@
 # Host Table for parallel DVO
 # ID  Hostname  Catdir
-   1  pikake    catdir.merge.p1
-   2  pikake    catdir.merge.p2
-   3  pikake    catdir.merge.p3
+   1  localhost    catdir.merge.p1
+   2  localhost    catdir.merge.p2
+   3  localhost    catdir.merge.p3
Index: /trunk/Ohana/src/libdvo/Makefile
===================================================================
--- /trunk/Ohana/src/libdvo/Makefile	(revision 35262)
+++ /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 35262)
+++ /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 35262)
+++ /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 35262)
+++ /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 35262)
+++ /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
Index: /trunk/Ohana/src/opihi/dvo/dvo_client.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/dvo_client.c	(revision 35262)
+++ /trunk/Ohana/src/opihi/dvo/dvo_client.c	(revision 35263)
@@ -39,4 +39,8 @@
     remove_argument (N, argc, argv);
   }
+  if (!HOST_ID) {
+    fprintf (stderr, "ERROR: dvo_client requires a -hostID value\n");
+    exit (3);
+  }
 
   HOSTDIR = NULL;
@@ -45,4 +49,8 @@
     HOSTDIR = strcreate (argv[N]);;
     remove_argument (N, argc, argv);
+  }
+  if (!HOSTDIR) {
+    fprintf (stderr, "ERROR: dvo_client requires a -hostdir value\n");
+    exit (3);
   }
 
Index: /trunk/Ohana/src/opihi/dvo/mextract.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/mextract.c	(revision 35262)
+++ /trunk/Ohana/src/opihi/dvo/mextract.c	(revision 35263)
@@ -1,3 +1,22 @@
 # include "dvoshell.h"
+
+int field_needs_images (dbField *field) {
+  // the image subset table requires imageID for all fields
+
+    if (!MEASURE_HAS_XCCD) {
+      // I'm keeping this code because it gives a way of handling dvo dbs that don't have
+      // measure.xccd if we need it
+      if (field->ID == MEAS_XCCD) return TRUE; // full astrometry per chip (120 bytes!)
+      if (field->ID == MEAS_YCCD) return TRUE; // full astrometry per chip (120 bytes!)
+    }
+    if (field->ID == MEAS_XMOSAIC) 	  return TRUE; // crval1,2 only
+    if (field->ID == MEAS_YMOSAIC) 	  return TRUE; // crval1,2 only
+    if (field->ID == MEAS_EXTERN_ID)      return TRUE; // externID
+    if (field->ID == MEAS_FLAT)           return TRUE; // Mcal
+    if (field->ID == MEAS_CENTER_OFFSET)  return TRUE; // 0.5*NX, 0.5*NY
+    if (field->ID == MEAS_EXPNAME_AS_INT) return TRUE; // expname (or as int)
+    if (field->ID == MEAS_AIRMASS)        return TRUE; // airmass
+    return FALSE;
+}
 
 int mextract (int argc, char **argv) {
@@ -5,5 +24,5 @@
   off_t i, j, k, m; // used for counter averages and measures
   int n, N, Npts, NPTS, last, next, state, Nfields, Nreturn, Ncstack, Nstack;
-  int Nsecfilt, VERBOSE, loadImages, mosaicMode;
+  int Nsecfilt, VERBOSE, loadImages;
   char **cstack, name[1024];
   dbValue *values;
@@ -33,4 +52,11 @@
     remove_argument (N, &argc, argv);
     VERBOSE = TRUE;
+  }
+
+  char *imageMetadataFile = FALSE;
+  if ((N = get_argument (argc, argv, "-image-metadata"))) {
+    remove_argument (N, &argc, argv);
+    imageMetadataFile = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
   }
 
@@ -66,5 +92,6 @@
 
   // init locally static variables (time refs)
-  dbExtractMeasuresInit();
+  // HOST_ID tells library if operation is on remote client or not
+  dbExtractMeasuresInit(HOST_ID);
 
   // command-line is of the form: mextract field,field, field [where (field op value)...]
@@ -106,7 +133,43 @@
   if ((skylist = SelectRegions (selection)) == NULL) goto escape;
 
+  // load image data if needed (for fields listed below)
+  loadImages = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    loadImages = field_needs_images (&fields[i]);
+  }
+    
   // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
   if (PARALLEL && !HOST_ID) {
-    int status = HostTableParallelOps (argc, argv, RESULT_FILE, TRUE, 0, VERBOSE);
+
+    // Image Metadata for remote queries:
+    // 1) figure out if we need any image metadata
+    // 2) load the images and generate a subset table with just the fields of interest
+    // 3) add the input subset filename to the dvo_client command
+
+    // allocate the temp array and copy all but (RA) (DEC)
+    int targc = 0;
+    char **targv = NULL;
+    ALLOCATE (targv, char *, argc);
+    for (i = 0; i < argc; i++) {
+      targv[targc] = strcreate (argv[i]);
+      targc ++;
+    }
+
+    if (loadImages) {
+      Image *image;
+      off_t Nimage;
+      if ((image = LoadImagesDVO (&Nimage)) == NULL) goto escape;
+
+      char *filename = abspath("image.metadata.fits", DVO_MAX_PATH);
+      ImageMetadataSave (filename, image, Nimage);
+
+      REALLOCATE (targv, char *, targc + 2);
+      targv[targc+0] = strcreate ("-image-metadata");
+      targv[targc+1] = strcreate (filename);
+      targc += 2;
+    }
+
+    // call the remote client
+    int status = HostTableParallelOps (targc, targv, RESULT_FILE, TRUE, 0, VERBOSE);
 
     dbFreeFields (fields, Nfields);
@@ -116,24 +179,20 @@
     dvo_catalog_free (&catalog);
 
+    // free up targv
+    for (i = 0; i < targc; i++) {
+      free (targv[i]);
+    }
+    free (targv);
+
     return status;
   }
 
-  // load image data if needed (for fields listed below)
-  loadImages = FALSE;
-  mosaicMode = FALSE;
-  for (i = 0; !loadImages && (i < Nfields); i++) {
-    if (!MEASURE_HAS_XCCD) {
-      // I'm keeping this code because it gives a way of handling dvo dbs that don't have
-      // measure.xccd if we need it
-      if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
-      if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
-    }
-    if (fields[i].ID == MEAS_XMOSAIC) 	    loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_YMOSAIC) 	    loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_EXTERN_ID)     loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_FLAT)          loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_CENTER_OFFSET) loadImages = mosaicMode = TRUE;
-  }
-  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+  if (loadImages) {
+    if (HOST_ID) {
+      if (!SetImageMetadataSelection (imageMetadataFile)) goto escape;
+    } else {
+      if (!SetImageSelection (TRUE, selection)) goto escape;
+    }
+  }
 
   /* create storage vector */
@@ -251,4 +310,5 @@
   free (stack);
   FreeImageSelection ();
+  FreeImageMetadataSelection ();
   SkyListFree (skylist);
   FreeSkyRegionSelection (selection);
@@ -264,4 +324,5 @@
   free (stack);
   FreeImageSelection ();
+  FreeImageMetadataSelection ();
   SkyListFree (skylist);
   FreeSkyRegionSelection (selection);
Index: /trunk/Ohana/src/opihi/dvo/mmatch.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/mmatch.c	(revision 35262)
+++ /trunk/Ohana/src/opihi/dvo/mmatch.c	(revision 35263)
@@ -1,3 +1,4 @@
 # include "dvoshell.h"
+int field_needs_images (dbField *field);
 
 /* This function uses the 'find_match' algorithm to select the objects of interest.
@@ -48,4 +49,12 @@
   }
 
+  // load info about the images from a reduced-size file
+  char *imageMetadataFile = FALSE;
+  if ((N = get_argument (argc, argv, "-image-metadata"))) {
+    remove_argument (N, &argc, argv);
+    imageMetadataFile = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   int PARALLEL = FALSE;
   if ((N = get_argument (argc, argv, "-parallel"))) {
@@ -72,5 +81,25 @@
 
   // init locally static variables (time refs)
-  dbExtractMeasuresInit();
+  // HOST_ID tells library if operation is on remote client or not
+  dbExtractMeasuresInit(HOST_ID);
+
+  // parse the fields to be extracted and returned
+  int first = 3;
+  if (CoordsFile) {
+    first = 1;
+  }
+  fields = dbCmdlineFields (argc-first, &argv[first], DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) goto help;
+  if ((Nfields == 0) || (last != argc - first)) {
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // load image data if needed (for fields listed below)
+  int loadImages = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    loadImages = field_needs_images (&fields[i]);
+  }
 
   // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
@@ -122,4 +151,18 @@
       targc += 2;
     }      
+
+    if (loadImages) {
+      Image *image;
+      off_t Nimage;
+      if ((image = LoadImagesDVO (&Nimage)) == NULL) goto escape;
+
+      char *filename = abspath("image.metadata.fits", DVO_MAX_PATH);
+      ImageMetadataSave (filename, image, Nimage);
+
+      REALLOCATE (targv, char *, targc + 2);
+      targv[targc+0] = strcreate ("-image-metadata");
+      targv[targc+1] = strcreate (filename);
+      targc += 2;
+    }
 
     // call the remote client
@@ -153,35 +196,16 @@
   remove_argument (1, &argc, argv);
 
-  // parse the fields to be extracted and returned
-  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
-  if (fields == NULL) goto help;
-  if ((Nfields == 0) || (last != argc)) {
-    dbFreeFields (fields, Nfields);
-    dvo_catalog_free (&catalog);
-    goto help;
-  }
-
-  // load image data if needed (for fields listed below)
-  int loadImages = FALSE;
-  int mosaicMode = FALSE;
-  for (i = 0; !loadImages && (i < Nfields); i++) {
-    if (!MEASURE_HAS_XCCD) {
-      // I'm keeping this code because it gives a way of handling dvo dbs that don't have
-      // measure.xccd if we need it
-      if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
-      if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
-    }
-    if (fields[i].ID == MEAS_XMOSAIC) 	    loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_YMOSAIC) 	    loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_EXTERN_ID)     loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_FLAT)          loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_CENTER_OFFSET) loadImages = mosaicMode = TRUE;
-  }
-
   // use the whole sky (since we select random points around the sky)
   SkyRegionSelection selection;
   selection.useDisplay = FALSE;
   selection.useSkyregion = FALSE;
-  if (loadImages && !SetImageSelection (mosaicMode, &selection)) goto escape;
+
+  if (loadImages) {
+    if (HOST_ID) {
+      if (!SetImageMetadataSelection (imageMetadataFile)) goto escape;
+    } else {
+      if (!SetImageSelection (TRUE, &selection)) goto escape;
+    }
+  }
 
   /* load regions which contain all supplied RA,DEC coordinates */
@@ -318,4 +342,6 @@
   if (invec) FreeVectorArray (invec, Ninvec);
   dbFreeFields (fields, Nfields);
+  FreeImageSelection ();
+  FreeImageMetadataSelection ();
   SkyListFree (skylist);
   return (TRUE);
@@ -326,4 +352,6 @@
   if (invec) FreeVectorArray (invec, Ninvec);
   dbFreeFields (fields, Nfields);
+  FreeImageSelection ();
+  FreeImageMetadataSelection ();
   SkyListFree (skylist);
   return (FALSE);
Index: /trunk/Ohana/src/opihi/dvo/mmextract.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/mmextract.c	(revision 35262)
+++ /trunk/Ohana/src/opihi/dvo/mmextract.c	(revision 35263)
@@ -1,3 +1,4 @@
 # include "dvoshell.h"
+int field_needs_images (dbField *field);
 
 int mmextract (int argc, char **argv) {
@@ -7,5 +8,5 @@
   int Nfields, Nreturn, Nreturn_base, Ncstack1, Ncstack2, Nstack1, Nstack2;
   int Nwhere, Iwhere, Nmatch, Imatch, NTABLE, Nt1, Nt2, n1, n2;
-  int Nsecfilt, VERBOSE, loadImages, mosaicMode;
+  int Nsecfilt, VERBOSE, loadImages;
   char **cstack1, **cstack2, name1[1024], name2[1024];
   dbValue *values, **table1, **table2;
@@ -45,5 +46,5 @@
   
   // init locally static variables (time refs)
-  dbExtractMeasuresInit();
+  dbExtractMeasuresInit(HOST_ID);
 
   // parse skyregion options
@@ -137,12 +138,8 @@
   // load image data if needed (for fields listed below)
   loadImages = FALSE;
-  mosaicMode = FALSE;
   for (i = 0; !loadImages && (i < Nfields); i++) {
-    if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
-    if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
-    if (fields[i].ID == MEAS_XMOSAIC) loadImages = mosaicMode = TRUE;
-    if (fields[i].ID == MEAS_YMOSAIC) loadImages = mosaicMode = TRUE;
-  }
-  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+    loadImages = field_needs_images (&fields[i]);
+  }
+  if (loadImages && !SetImageSelection (TRUE, selection)) goto escape;
 
   /* create storage vector */
Index: /trunk/Ohana/src/opihi/dvo/test/mmatch.sh
===================================================================
--- /trunk/Ohana/src/opihi/dvo/test/mmatch.sh	(revision 35262)
+++ /trunk/Ohana/src/opihi/dvo/test/mmatch.sh	(revision 35263)
@@ -10,5 +10,5 @@
   $Ro = 2.59
   $Do = 1.23
-  catdir /data/pikake.0/eugene/src/ipp-dev/Ohana/src/dvomerge/test/catdir.merge
+  catdir catdir.merge
   $Gname = g
   $Rname = r
@@ -21,5 +21,7 @@
   subset r_ave = $Rname  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
   vectors 
-  mmatch -v -parallel R D 1.0 RA DEC MAG PHOTCODE -index index
+
+  # mmatch -v -parallel R D 1.0 RA DEC MAG PHOTCODE -index index
+  mmatch -v -parallel R D 1.0 RA DEC MAG PHOTCODE externID mean_airmass -index index
 
   reindex g_ave_match = g_ave using index
