Index: branches/eam_branches/ipp-20130419/pswarp/src/Makefile.am
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/Makefile.am	(revision 35422)
+++ branches/eam_branches/ipp-20130419/pswarp/src/Makefile.am	(revision 35424)
@@ -35,9 +35,12 @@
 	pswarpDefine.c			\
 	pswarpDefineBackground.c	\
-	pswarpDefineSkycell.c	\
+	pswarpDefineSkycell.c	        \
+	pswarpDefineLayout.c	        \
 	pswarpErrorCodes.c		\
+	pswarpLoadAstrometry.c          \
 	pswarpMapGrid.c			\
 	pswarpMaskStats.c               \
 	pswarpMatchRange.c		\
+	pswarpOverlaps.c		\
 	pswarpParseCamera.c		\
 	pswarpPixelsLit.c		\
Index: branches/eam_branches/ipp-20130419/pswarp/src/pswarp.c
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/pswarp.c	(revision 35422)
+++ branches/eam_branches/ipp-20130419/pswarp/src/pswarp.c	(revision 35424)
@@ -39,4 +39,15 @@
     }
 
+# if (0)
+    // XXX I'm going to add the new code before removing the old code.
+    // load the input & output astrometry, find the output overlaps, generate the output pixels
+    if (!pswarpDefineLayout(config)) {
+        pswarpCleanup(config, statsFile);
+    }
+
+    pswarpDumpOutput (config);
+    exit (0);
+# endif
+
     // load the skycell layout information:
     //   read the output astrometry description
Index: branches/eam_branches/ipp-20130419/pswarp/src/pswarp.h
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/pswarp.h	(revision 35422)
+++ branches/eam_branches/ipp-20130419/pswarp/src/pswarp.h	(revision 35424)
@@ -172,2 +172,25 @@
 // cleanup memory and exit
 void pswarpCleanup (pmConfig *config, pswarpStatsFile *statsFile) PS_ATTR_NORETURN;
+
+// load the astrometry header info and generate the tranformations 
+bool pswarpDefineLayout (pmConfig *config);
+
+// XXX function for testing
+bool pswarpDumpOutput (pmConfig *config);
+
+// structure to describe approximate bounds for each fpa element (eg, chip)
+// P,Q are a locally linear projection
+typedef struct {
+    psVector *Pmin; 
+    psVector *Pmax;
+    psVector *Qmin;
+    psVector *Qmax;
+} pswarpBounds;
+
+bool pswarpFindOverlap (pmFPA *fpa, pswarpBounds *src, pswarpBounds *tgt);
+psProjection *pswarpLocalFrame (pmFPA *fpa);
+pswarpBounds *pswarpMakeBounds (pmFPA *fpa, psProjection *frame);
+bool pswarpBoundsAppend(pswarpBounds *bounds, float Pmin, float Pmax, float Qmin, float Qmax);
+pswarpBounds *pswarpBoundsAlloc();
+
+bool pswarpLoadAstrometry (pmConfig *config, pmFPAfile *astrom, pmFPAfile *target);
Index: branches/eam_branches/ipp-20130419/pswarp/src/pswarpDefineLayout.c
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/pswarpDefineLayout.c	(revision 35424)
+++ branches/eam_branches/ipp-20130419/pswarp/src/pswarpDefineLayout.c	(revision 35424)
@@ -0,0 +1,172 @@
+/** @file pswarpDefineLayout.c
+ *
+ *  @brief load input & output astrometry, determine overlaps
+ *  @ingroup pswarp
+ *
+ *  @author IfA
+ *  @date $Date: 2009-02-05 20:44:04 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "pswarp.h"
+
+// load the astrometry header info and generate the tranformations 
+bool pswarpDefineLayout (pmConfig *config) {
+
+    // place input astrometry transformations in 'input'
+    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PSWARP.INPUT");
+    if (!input) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find input data!\n");
+        return false;
+    }
+    // input astrometry may be embedded in 'input' or supplied separately
+    pmFPAfile *astrom = psMetadataLookupPtr(NULL, config->files, "PSWARP.ASTROM");
+    if (!astrom) {
+        astrom = input;
+    }
+    if (!pswarpLoadAstrometry (config, input, astrom)) {
+        psError(PSWARP_ERR_CONFIG, false, "problem loading input astrometry\n");
+        return false;
+    }
+
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PSWARP.OUTPUT");
+    if (!output) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find output data!\n");
+        return false;
+    }
+    // select the input data sources
+    pmFPAfile *skycell = psMetadataLookupPtr (NULL, config->files, "PSWARP.SKYCELL");
+    if (!skycell) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find skycell data!\n");
+        return false;
+    }
+    if (!pswarpLoadAstrometry (config, output, skycell)) {
+        psError(PSWARP_ERR_CONFIG, false, "problem loading output astrometry\n");
+        return false;
+    }
+
+    // given the input data, determine which output elements should be generated
+    // and generate the appropriate images
+
+    // find the R,D center for the skycell, use for common projection
+    psProjection *frame = pswarpLocalFrame (output->fpa);
+
+    // generate Lmin,max, Mmin,max for both datasets
+    pswarpBounds *srcBounds = pswarpMakeBounds (input->fpa, frame);
+    pswarpBounds *tgtBounds = pswarpMakeBounds (output->fpa, frame);
+    pswarpFindOverlap (output->fpa, srcBounds, tgtBounds);
+
+    // Generate the output chips (pixels on output->fpa, concepts from skycell->fpa)
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    pmChip *chip;
+    while ((chip = pmFPAviewNextChip (view, output->fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { continue; }
+	pmCell *cell;
+	while ((cell = pmFPAviewNextCell (view, output->fpa, 1)) != NULL) {
+	    psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+	    if (!cell->process || !cell->file_exists) { continue; }
+
+	    // we've got the output astrom header
+	    pmHDU *hdu = pmHDUFromCell(cell); ///< HDU for source
+	    if (!hdu || !hdu->header) {
+		psError(PM_ERR_PROG, false, "Unable to find header for sky cell.");
+		psFree(view);
+		return false;
+	    }
+	    int numCols = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); ///< Number of columns
+	    int numRows = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); ///< Number of rows
+	    if ((numCols == 0) || (numRows == 0)) {
+		psError(PSWARP_ERR_DATA, false, "skycell has invalid dimensions %d x %d", numCols, numRows);
+		psFree(view);
+		return false;
+	    }
+		
+	    // generate the pixels for output->fpa
+	    pmReadout *readout = pmReadoutAlloc(cell); ///< output readout
+	    readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+	    psImageInit(readout->image, NAN);
+	    psFree(readout);                // Drop reference
+		
+	    // copy the image concepts from the skycell 
+	    bool status = false;
+	    pmCell *refcell = pmFPAviewThisCell(view, skycell->fpa); ///< Target cell
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.XBIN");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.YBIN");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.XSIZE");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.YSIZE");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.XPARITY");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.YPARITY");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.X0");
+	    psMetadataItemSupplement(&status, cell->concepts, refcell->concepts, "CELL.Y0");
+	}
+    }
+
+    // XXX not sure what this does...
+    pmFPAviewReset (view);
+    pmFPAAddSourceFromView(output->fpa, view, output->format);
+
+    psFree (view);
+    return true;
+}
+
+// Lists of file rules used at different stages
+# include "pswarpFileNames.h"
+
+// XX function for tests
+bool pswarpDumpOutput (pmConfig *config) {
+
+    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PSWARP.OUTPUT");
+    if (!output) {
+        psError(PSWARP_ERR_CONFIG, false, "Can't find output data!\n");
+        return false;
+    }
+
+    pswarpFileActivation(config, detectorFiles, false);
+    pswarpFileActivation(config, photFiles, false);
+    pswarpFileActivation(config, independentFiles, false);
+    pswarpFileActivation(config, skycellFiles, false);
+    pmFPAfileActivate(config->files, true, "PSWARP.OUTPUT");
+
+    pmChip *chip;
+    pmFPAview *view = pmFPAviewAlloc(0);
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+	psError(psErrorCodeLast(), false, "Unable to read files.");
+	goto DONE;
+    }
+    while ((chip = pmFPAviewNextChip (view, output->fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { continue; }
+	if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+	    psError(psErrorCodeLast(), false, "Unable to read files.");
+	    goto DONE;
+	}
+	pmCell *cell;
+	while ((cell = pmFPAviewNextCell (view, output->fpa, 1)) != NULL) {
+	    psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+	    if (!cell->process || !cell->file_exists) { continue; }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE) ||
+		!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		psError(psErrorCodeLast(), false, "Unable to read files.");
+		goto DONE;
+	    }
+	}
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	    psError(psErrorCodeLast(), false, "Unable to write files.");
+	    goto DONE;
+	}
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	psError(psErrorCodeLast(), false, "Unable to write files.");
+	goto DONE;
+    }
+    psFree(view);
+
+DONE:
+
+    pswarpFileActivation(config, detectorFiles, true);
+    pmFPAfileActivate(config->files, false, "PSWARP.ASTROM");
+    return true;
+}    
+
Index: branches/eam_branches/ipp-20130419/pswarp/src/pswarpLoadAstrometry.c
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/pswarpLoadAstrometry.c	(revision 35424)
+++ branches/eam_branches/ipp-20130419/pswarp/src/pswarpLoadAstrometry.c	(revision 35424)
@@ -0,0 +1,134 @@
+/** @file pswarpLoadAstrometry.c
+ *
+ *  @brief load the headers and construct astrometric transformations 
+ *  @ingroup pswarp
+ *
+ *  @author IfA
+ *  @date $Date: 2009-02-05 20:44:04 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "pswarp.h"
+
+// we have a pmFPAfile which defines the astrometry via headers.  we force it to have type
+// WCS and read the headers.  We place the resulting info on the target astrometry
+// containers
+bool pswarpLoadAstrometry (pmConfig *config, pmFPAfile *astrom, pmFPAfile *target) {
+
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+
+    // XXX set the type to be WCS
+    pmFPAfileType saveType = astrom->type;
+    astrom->type = PM_FPA_FILE_WCS;
+
+    // Read the pmFPA headers
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    if (!pmFPAfileRead (astrom, view, config)) {
+	psError(PS_ERR_IO, false, "failed READ at FPA %s", astrom->name);
+	psFree(view);
+	return false;
+    }
+
+    while ((chip = pmFPAviewNextChip (view, astrom->fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { continue; }
+	if (!pmFPAfileRead (astrom, view, config)) {
+	    psError(psErrorCodeLast(), false, "failed READ at CHIP %s", astrom->name);
+	    psFree(view);
+	    return false;
+	}
+	while ((cell = pmFPAviewNextCell (view, astrom->fpa, 1)) != NULL) {
+	    psTrace ("pswarp", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+	    if (!cell->process || !cell->file_exists) { continue; }
+	    if (!pmFPAfileRead (astrom, view, config)) {
+		psError(psErrorCodeLast(), false, "failed READ at CELL %s", astrom->name);
+		psFree(view);
+		return false;
+	    }
+	    if (!pmFPAfileClose(astrom, view)) {
+		psError(psErrorCodeLast(), false, "failed CLOSE at CELL %s", astrom->name);
+		psFree (view);
+		return false;
+	    }
+
+	    // we've got the output astrom header
+	    pmHDU *hdu = pmHDUFromCell(cell); ///< HDU for source
+	    if (!hdu || !hdu->header) {
+		psError(PM_ERR_PROG, false, "Unable to find header for sky cell.");
+		psFree(view);
+		return false;
+	    }
+	}
+	if (!pmFPAfileClose(astrom, view)) {
+	    psError(psErrorCodeLast(), false, "failed CLOSE at CHIP %s", astrom->name);
+	    psFree (view);
+	    return false;
+	}
+    }
+    if (!pmFPAfileClose(astrom, view)) {
+	psError(psErrorCodeLast(), false, "failed CLOSE at FPA %s", astrom->name);
+	psFree (view);
+	return false;
+    }
+
+    // the section below converts the header astrometry info (in astrom) to the pmFPA
+    // astrometry structures, saving them on the target fpa structure.
+
+    pmFPAviewReset (view);
+
+    bool bilevelAstrometry = false;
+    pmHDU *phu = pmFPAviewThisPHU(view, astrom->fpa); ///< Astrometry PHU
+    if (phu) {
+        char *ctype = psMetadataLookupStr(NULL, phu->header, "CTYPE1");
+        if (ctype) {
+            bilevelAstrometry = !strcmp(&ctype[4], "-DIS");
+        }
+    }
+    // apply the bilevel astrometry elements to the target
+    if (bilevelAstrometry) {
+	if (!pmAstromReadBilevelMosaic(target->fpa, phu->header)) {
+	    psError(psErrorCodeLast(), false, "Unable to read bilevel mosaic astrometry for skycell.");
+	    psFree(view);
+	    return false;
+	}
+    }
+
+    while ((chip = pmFPAviewNextChip (view, astrom->fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { continue; }
+
+	// we've got the astrom header
+	pmHDU *hdu = pmHDUFromChip(chip); ///< HDU for source
+	if (!hdu || !hdu->header) {
+	    psError(PM_ERR_PROG, false, "Unable to find header for sky cell.");
+	    psFree(view);
+	    return false;
+	}
+
+	// We read from the astrometry source into the target.
+	pmChip *targetChip = pmFPAviewThisChip(view, target->fpa); ///< Chip in the output
+	if (bilevelAstrometry) {
+	    if (!pmAstromReadBilevelChip(targetChip, hdu->header)) {
+		psError(psErrorCodeLast(), false, "Unable to read bilevel chip astrometry for skycell.");
+		psFree(view);
+		return false;
+	    }
+	} else {
+	    // we use a default FPA pixel scale of 1.0
+	    if (!pmAstromReadWCS(target->fpa, targetChip, hdu->header, 1.0)) {
+		psError(psErrorCodeLast(), false, "Unable to read WCS astrometry for skycell.");
+		psFree(view);
+		return false;
+	    }
+	}
+    }
+
+    // reset the type to the original value
+    astrom->type = saveType;
+
+    psFree(view);
+    return true;
+}
+
Index: branches/eam_branches/ipp-20130419/pswarp/src/pswarpLoop.c
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/pswarpLoop.c	(revision 35422)
+++ branches/eam_branches/ipp-20130419/pswarp/src/pswarpLoop.c	(revision 35424)
@@ -91,5 +91,4 @@
     // }
    
-
     // Read the input astrometry
     // XXX rather than use the activations here, this should just explicitly loop over the desired filerule
Index: branches/eam_branches/ipp-20130419/pswarp/src/pswarpOverlaps.c
===================================================================
--- branches/eam_branches/ipp-20130419/pswarp/src/pswarpOverlaps.c	(revision 35424)
+++ branches/eam_branches/ipp-20130419/pswarp/src/pswarpOverlaps.c	(revision 35424)
@@ -0,0 +1,188 @@
+# include "pswarp.h"
+
+static void pswarpBoundsFree (pswarpBounds *bounds) {
+    psFree (bounds->Pmin);
+    psFree (bounds->Pmax);
+    psFree (bounds->Qmin);
+    psFree (bounds->Qmax);
+}
+
+pswarpBounds *pswarpBoundsAlloc() {
+
+    pswarpBounds *bounds = psAlloc(sizeof(pswarpBounds));
+    psMemSetDeallocator(bounds, (psFreeFunc)pswarpBoundsFree);
+
+    bounds->Pmin = psVectorAllocEmpty(32, PS_DATA_F32);
+    bounds->Pmax = psVectorAllocEmpty(32, PS_DATA_F32);
+    bounds->Qmin = psVectorAllocEmpty(32, PS_DATA_F32);
+    bounds->Qmax = psVectorAllocEmpty(32, PS_DATA_F32);
+    return bounds;
+}
+
+bool pswarpBoundsAppend(pswarpBounds *bounds, float Pmin, float Pmax, float Qmin, float Qmax) {
+    psVectorAppend (bounds->Pmin, Pmin);
+    psVectorAppend (bounds->Pmax, Pmax);
+    psVectorAppend (bounds->Qmin, Qmin);
+    psVectorAppend (bounds->Qmax, Qmax);
+    return true;
+}
+
+pswarpBounds *pswarpMakeBounds (pmFPA *fpa, psProjection *frame) {
+
+    pswarpBounds *bounds = pswarpBoundsAlloc ();
+    
+    psPlane *CH = psPlaneAlloc();
+    psPlane *FP  = psPlaneAlloc();
+    psPlane *TP  = psPlaneAlloc();
+    psPlane *FR  = psPlaneAlloc();
+    psSphere *sky = psSphereAlloc();
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    pmChip *chip = NULL;
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { 
+	    // this ensures one entry per chip (regardless of existence)
+	    // XXX maybe not? pswarpBoundsAppend (bounds, NAN, NAN, NAN, NAN);
+	    continue; 
+	}
+
+	// we've got the output astrom header
+	pmHDU *hdu = pmHDUFromChip(chip); ///< HDU for source
+	assert (hdu);
+	assert (hdu->header);
+	int numCols = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); ///< Number of columns
+	int numRows = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); ///< Number of rows
+	if ((numCols == 0) || (numRows == 0)) {
+	    psError(PSWARP_ERR_DATA, false, "astrom has invalid dimensions %d x %d", numCols, numRows);
+	    psFree(view);
+	    return false;
+	}
+
+	float Pmin = +1e9;
+	float Pmax = -1e9;
+	float Qmin = +1e9;
+	float Qmax = -1e9;
+
+	// find P,Q at corners and center of edges (and center just for ease of code)
+	for (float ix = 0.0; ix <= 1.0; ix += 0.5) {
+	    for (float iy = 0.0; iy <= 1.0; iy += 0.5) {
+
+		CH->x = ix * numCols;
+		CH->y = iy * numRows;
+		psPlaneTransformApply(FP, chip->toFPA, CH); 
+		psPlaneTransformApply(TP, fpa->toTPA, FP); 
+		psDeproject (sky, TP, fpa->toSky); 
+		psProject (FR, sky, frame); 
+
+		Pmin = PS_MIN(Pmin, FR->x);
+		Pmax = PS_MAX(Pmax, FR->x);
+		Qmin = PS_MIN(Qmin, FR->y);
+		Qmax = PS_MAX(Qmax, FR->y);
+	    }
+	}
+	pswarpBoundsAppend (bounds, Pmin, Pmax, Qmin, Qmax);
+    }
+    psFree(view);
+    psFree(CH);
+    psFree(FP);
+    psFree(TP);
+    psFree(FR);
+    psFree(sky);
+
+    return bounds;
+}
+
+// find R,D for the center of the output pmFPA
+psProjection *pswarpLocalFrame (pmFPA *fpa) {
+
+    // find R,D for each corner of each chip and take the average
+    psVector *Rvec = psVectorAllocEmpty (256, PS_DATA_F32);
+    psVector *Dvec = psVectorAllocEmpty (256, PS_DATA_F32);
+    
+    psPlane *CH = psPlaneAlloc();
+    psPlane *FP  = psPlaneAlloc();
+    psPlane *TP  = psPlaneAlloc();
+    psSphere *sky = psSphereAlloc();
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    pmChip *chip = NULL;
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { continue; }
+
+	// we've got the output astrom header
+	pmHDU *hdu = pmHDUFromChip(chip); ///< HDU for source
+	assert (hdu);
+	assert (hdu->header);
+	int numCols = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); ///< Number of columns
+	int numRows = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); ///< Number of rows
+	if ((numCols == 0) || (numRows == 0)) {
+	    psError(PSWARP_ERR_DATA, false, "astrom has invalid dimensions %d x %d", numCols, numRows);
+	    psFree(view);
+	    return false;
+	}
+
+	// get R,D for all corners
+	for (float ix = 0.0; ix <= 1.0; ix += 1.0) {
+	    for (float iy = 0.0; iy <= 1.0; iy += 1.0) {
+		CH->x = ix * numCols;
+		CH->y = iy * numRows;
+		psPlaneTransformApply(FP, chip->toFPA, CH); 
+		psPlaneTransformApply(TP, fpa->toTPA, FP); 
+		psDeproject (sky, TP, fpa->toSky); 
+		psVectorAppend (Rvec, sky->r);
+		psVectorAppend (Dvec, sky->d);
+	    }
+	}
+    }
+    psFree(view);
+    psFree(CH);
+    psFree(FP);
+    psFree(TP);
+    psFree(sky);
+
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psVectorStats (stats, Rvec, NULL, NULL, 0);
+    double Rmid = stats->sampleMean;
+
+    psStatsInit (stats);
+    psVectorStats (stats, Dvec, NULL, NULL, 0);
+    double Dmid = stats->sampleMean;
+
+    psProjection *frame = psProjectionAlloc (Rmid, Dmid, 1.0, 1.0, PS_PROJ_TAN);
+    return frame;
+}
+
+bool pswarpFindOverlap (pmFPA *fpa, pswarpBounds *src, pswarpBounds *tgt) {
+
+    // we have the source and target bounds.  loop over all output elements and check if any of the source
+    // elements overlap it.
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    pmChip *chip = NULL;
+    int Nout = 0;
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+	psTrace ("pswarp", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists) { continue; }
+
+	// we have src bounds
+	float Pmin = tgt->Pmin->data.F32[Nout];
+	float Pmax = tgt->Pmax->data.F32[Nout];
+	float Qmin = tgt->Qmin->data.F32[Nout];
+	float Qmax = tgt->Qmax->data.F32[Nout];
+
+	bool valid = false;
+	for (int i = 0; !valid && (i < src->Pmin->n); i++) {
+	    // overlaps in P?
+	    bool Pvalid = (Pmin < src->Pmax->data.F32[i]) && (Pmax > src->Pmin->data.F32[i]);
+	    bool Qvalid = (Qmin < src->Qmax->data.F32[i]) && (Qmax > src->Qmin->data.F32[i]);
+	    valid = Pvalid && Qvalid;
+	}
+	chip->process = valid;
+    }
+    return true;
+}
