VIRCAM Pipeline  2.3.12
casu_match.c
1 /* $Id: casu_match.c,v 1.2 2015/08/07 13:06:54 jim Exp $
2  *
3  * This file is part of the CASU Pipeline utilities
4  * Copyright (C) 2015 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: jim $
23  * $Date: 2015/08/07 13:06:54 $
24  * $Revision: 1.2 $
25  * $Name: $
26  */
27 
28 /* Includes */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include <math.h>
35 
36 #include <cpl.h>
37 #include <string.h>
38 #include <strings.h>
39 
40 #include "casu_mods.h"
41 #include "casu_stats.h"
42 #include "catalogue/casu_utils.h"
43 
44 #define NX 2048
45 #define NY 2048
46 #define NGRIDMAX_XY 61
47 #define NGRIDMAX_STD 31
48 
49 static cpl_table *casu_mkmstd_table(cpl_table *objtab, cpl_table *stdstab);
50 
53 /*---------------------------------------------------------------------------*/
103 /*---------------------------------------------------------------------------*/
104 
105 extern int casu_matchxy(cpl_table *progtab, cpl_table *template, float srad,
106  float *xoffset, float *yoffset, int *nm,
107  cpl_table **outtab, int *status) {
108  cpl_propertylist *p;
109  float *xprog,*yprog,*xtemp,*ytemp,aveden,errlim,xoffbest,yoffbest,xoff;
110  float yoff,x,y,*xoffs,*yoffs;
111  const char *fctid = "casu_matchxy";
112  int nprog,ntemp,ngrid,ngrid2,ibest,ig,jg,nmatch,k,jm;
113 
114  /* Inherited status */
115 
116  *xoffset = 0.0;
117  *yoffset = 0.0;
118  *nm = 0;
119  *outtab = NULL;
120  if (*status != CASU_OK)
121  return(*status);
122 
123  /* Check the size of each of the tables */
124 
125  nprog = (int)cpl_table_get_nrow(progtab);
126  ntemp = (int)cpl_table_get_nrow(template);
127  if (nprog == 0) {
128  cpl_msg_warning(fctid,"Program table has no rows");
129  WARN_RETURN
130  } else if (ntemp == 0) {
131  cpl_msg_warning(fctid,"Template table has no rows");
132  WARN_RETURN
133  }
134 
135  /* First, sort the two tables by the Y coordinate */
136 
137  p = cpl_propertylist_new();
138  cpl_propertylist_append_bool(p,"Y_coordinate",0);
139  if (cpl_table_sort(progtab,p) != CPL_ERROR_NONE) {
140  cpl_propertylist_delete(p);
141  FATAL_ERROR
142  }
143  if (cpl_table_sort(template,p) != CPL_ERROR_NONE) {
144  cpl_propertylist_delete(p);
145  FATAL_ERROR
146  }
147  cpl_propertylist_delete(p);
148 
149  /* Get the x,y coordinates for each table */
150 
151  xprog = cpl_table_get_data_float(progtab,"X_coordinate");
152  yprog = cpl_table_get_data_float(progtab,"Y_coordinate");
153  xtemp = cpl_table_get_data_float(template,"X_coordinate");
154  ytemp = cpl_table_get_data_float(template,"Y_coordinate");
155  if (xprog == NULL || yprog == NULL || xtemp == NULL || ytemp == NULL)
156  FATAL_ERROR
157 
158  /* Calculate the error limit and the number of grid points */
159 
160  aveden = (float)ntemp/(float)(NX*NY);
161  errlim = 1.0/sqrt(4.0*CPL_MATH_PI*aveden);
162  errlim = min(errlim,15.0);
163  ngrid = (int)(srad/errlim);
164  ngrid = (ngrid/2)*2 + 1;
165  ngrid = max(5,min(NGRIDMAX_XY,ngrid));
166  ngrid2 = ngrid/2 + 1;
167 
168  /* Now search for the best solution */
169 
170  ibest = 0;
171  xoffbest = 0.0;
172  yoffbest = 0.0;
173  for (ig = -ngrid2; ig <= ngrid2; ig++) {
174  xoff = (float)ig*errlim*CPL_MATH_SQRT2;
175  for (jg = -ngrid2; jg <= ngrid2; jg++) {
176  yoff = (float)jg*errlim*CPL_MATH_SQRT2;
177  nmatch = 0;
178  for (k = 0; k < nprog; k++) {
179  x = xprog[k] + xoff;
180  y = yprog[k] + yoff;
181  if (casu_fndmatch(x,y,xtemp,ytemp,ntemp,errlim) > -1)
182  nmatch++;
183  }
184  if (nmatch > ibest) {
185  ibest = nmatch;
186  xoffbest = xoff;
187  yoffbest = yoff;
188  }
189  }
190  }
191 
192  /* Now allocate some workspace so that you can calculate a good median
193  coordinate difference */
194 
195  xoffs = cpl_malloc(nprog*sizeof(*xoffs));
196  yoffs = cpl_malloc(nprog*sizeof(*yoffs));
197 
198  /* Now get the coordinate differences and find the medians */
199 
200  nmatch = 0;
201  for (k = 0; k < nprog; k++) {
202  x = xprog[k] + xoffbest;
203  y = yprog[k] + yoffbest;
204  jm = casu_fndmatch(x,y,xtemp,ytemp,ntemp,errlim);
205  if (jm > -1) {
206  xoffs[nmatch] = xtemp[jm] - xprog[k];
207  yoffs[nmatch] = ytemp[jm] - yprog[k];
208  nmatch++;
209  }
210  }
211  if (nmatch > 0) {
212  *xoffset = casu_med(xoffs,NULL,nmatch);
213  *yoffset = casu_med(yoffs,NULL,nmatch);
214  } else {
215  *xoffset = 0.0;
216  *yoffset = 0.0;
217  }
218  *nm = nmatch;
219 
220  /* Create the output table */
221 
222  *outtab = cpl_table_new((cpl_size)nprog);
223  cpl_table_new_column(*outtab,"X_coordinate_1",CPL_TYPE_FLOAT);
224  cpl_table_new_column(*outtab,"Y_coordinate_1",CPL_TYPE_FLOAT);
225  cpl_table_new_column(*outtab,"X_coordinate_2",CPL_TYPE_FLOAT);
226  cpl_table_new_column(*outtab,"Y_coordinate_2",CPL_TYPE_FLOAT);
227  nmatch = 0;
228  for (k = 0; k < nprog; k++) {
229  x = xprog[k] + *xoffset;
230  y = yprog[k] + *yoffset;
231  jm = casu_fndmatch(x,y,xtemp,ytemp,ntemp,1.0);
232  if (jm > -1) {
233  cpl_table_set_float(*outtab,"X_coordinate_1",(cpl_size)nmatch,
234  xtemp[jm]);
235  cpl_table_set_float(*outtab,"Y_coordinate_1",(cpl_size)nmatch,
236  ytemp[jm]);
237  cpl_table_set_float(*outtab,"X_coordinate_2",(cpl_size)nmatch,
238  xprog[k]);
239  cpl_table_set_float(*outtab,"Y_coordinate_2",(cpl_size)nmatch,
240  yprog[k]);
241  nmatch++;
242  }
243  }
244  cpl_table_set_size(*outtab,(cpl_size)nmatch);
245 
246  /* Tidy and exit */
247 
248  freespace(xoffs);
249  freespace(yoffs);
250  GOOD_STATUS
251 }
252 
253 /*---------------------------------------------------------------------------*/
298 /*---------------------------------------------------------------------------*/
299 
300 extern int casu_matchstds(cpl_table *objtab, cpl_table *stdstab, float srad,
301  cpl_table **outtab, int *status) {
302  const char *fctid = "casu_matchstds";
303  char *colname;
304  int nobj,nstd,ngrid,ngrid2,ibest,ig,jg,nmatch,k,*matches,jm,l,dont,null,k2;
305  float *xstd,*ystd,*xobj,*yobj,aveden,errlim,xoffbest,yoffbest,*xoffs;
306  float *yoffs,x,y,xx2,yy2,r2,xx1,yy1,r1,xoffmed,sigx,yoffmed,sigy,xoff,yoff;
307  cpl_propertylist *p;
308  cpl_table *mstds;
309  cpl_array *colnames;
310 
311  /* Inherited status */
312 
313  *outtab = NULL;
314  if (*status != CASU_OK)
315  return(*status);
316 
317  /* Check the size of each of the tables */
318 
319  nobj = (int)cpl_table_get_nrow(objtab);
320  nstd = (int)cpl_table_get_nrow(stdstab);
321  if (nobj == 0) {
322  cpl_msg_warning(fctid,"Object table has no rows");
323  mstds = casu_mkmstd_table(objtab,stdstab);
324  *outtab = cpl_table_extract_selected(mstds);
325  cpl_table_delete(mstds);
326  WARN_RETURN
327  } else if (nstd == 0) {
328  cpl_msg_warning(fctid,"Standards RA/DEC table has no rows");
329  mstds = casu_mkmstd_table(objtab,stdstab);
330  *outtab = cpl_table_extract_selected(mstds);
331  cpl_table_delete(mstds);
332  WARN_RETURN
333  }
334 
335  /* First, sort the two tables by the Y coordinate */
336 
337  p = cpl_propertylist_new();
338  cpl_propertylist_append_bool(p,"Y_coordinate",0);
339  if (cpl_table_sort(objtab,p) != CPL_ERROR_NONE) {
340  cpl_propertylist_delete(p);
341  FATAL_ERROR
342  }
343  cpl_propertylist_erase(p,"Y_coordinate");
344  cpl_propertylist_append_bool(p,"ypredict",0);
345  if (cpl_table_sort(stdstab,p) != CPL_ERROR_NONE) {
346  cpl_propertylist_delete(p);
347  FATAL_ERROR
348  }
349  cpl_propertylist_delete(p);
350 
351  /* Get the x,y coordinates for each table */
352 
353  xobj = cpl_table_get_data_float(objtab,"X_coordinate");
354  yobj = cpl_table_get_data_float(objtab,"Y_coordinate");
355  xstd = cpl_table_get_data_float(stdstab,"xpredict");
356  ystd = cpl_table_get_data_float(stdstab,"ypredict");
357  if (xstd == NULL || ystd == NULL || xobj == NULL || yobj == NULL)
358  FATAL_ERROR
359 
360  /* Calculate the error limit and the number of grid points */
361 
362  // This is the average density of stars in the image
363  // FIXME it is incorrect to assume NX and NY are fixed
364  aveden = (float)max(nstd,nobj)/(float)(NX*NY);
365  // An initial estimate of a sensible error limit
366  errlim = 1.0/sqrt(4.0*CPL_MATH_PI*aveden);
367  errlim = min(errlim,15.0);
368  // The grid size to achieve this
369  ngrid = (int)(srad/errlim);
370  ngrid = (ngrid/2)*2 + 1;
371  // Constrain the number of grid points
372  ngrid = max(5,min(NGRIDMAX_STD,ngrid));
373  ngrid2 = ngrid/2 + 1;
374 
375  cpl_msg_info(fctid, "Search radius is %f pixels. Using a %d x %d grid, with error limit of %f", srad, ngrid, ngrid, errlim);
376 
377  /* Now search for the best solution */
378 
379  ibest = 0;
380  xoffbest = 0.0;
381  yoffbest = 0.0;
382  for (ig = -ngrid2; ig <= ngrid2; ig++) {
383  xoff = (float)ig*errlim*CPL_MATH_SQRT2;
384  for (jg = -ngrid2; jg <= ngrid2; jg++) {
385  yoff = (float)jg*errlim*CPL_MATH_SQRT2;
386  nmatch = 0;
387  for (k = 0; k < nobj; k++) {
388  x = xobj[k] + xoff;
389  y = yobj[k] + yoff;
390  if (casu_fndmatch(x,y,xstd,ystd,nstd,errlim) > -1)
391  nmatch++;
392  }
393  if (nmatch > ibest) {
394  ibest = nmatch;
395  xoffbest = xoff;
396  yoffbest = yoff;
397  }
398  }
399  }
400 
401  /* Now allocate some workspace so that you can calculate a good median
402  coordinate difference */
403 
404  xoffs = cpl_malloc(nstd*sizeof(*xoffs));
405  yoffs = cpl_malloc(nstd*sizeof(*yoffs));
406  matches = cpl_malloc(nstd*sizeof(*matches));
407  for (k = 0; k < nstd; k++)
408  matches[k] = -1;
409 
410  /* Now get the best matches */
411 
412  nmatch = 0;
413  for (k = 0; k < nstd; k++) {
414  x = xstd[k] - xoffbest;
415  y = ystd[k] - yoffbest;
416  jm = casu_fndmatch(x,y,xobj,yobj,nobj,errlim);
417  if (jm > -1) {
418  dont = 0;
419  xx2 = xobj[jm] - x;
420  yy2 = yobj[jm] - y;
421  r2 = sqrt(xx2*xx2 + yy2*yy2);
422  for (l = 0; l < nstd; l++) {
423  if (matches[l] == jm) {
424  xx1 = xobj[jm] - (xstd[l] - xoffbest);
425  yy1 = yobj[jm] - (ystd[l] - yoffbest);
426  r1 = sqrt(xx1*xx1 + yy1*yy1);
427  if (r2 < r1)
428  matches[l] = -1;
429  else
430  dont = 1;
431  break;
432  }
433  }
434  if (dont == 0)
435  matches[k] = jm;
436  }
437  }
438 
439  /* Now get the coordinate difference for the best matches */
440 
441  for (k = 0; k < nstd; k++) {
442  jm = matches[k];
443  if (jm != -1) {
444  xoffs[nmatch] = xobj[jm] - xstd[k];
445  yoffs[nmatch] = yobj[jm] - ystd[k];
446  nmatch++;
447  }
448  }
449  if (nmatch == 0) {
450  xoffmed = 0.0;
451  sigx = 1.0;
452  yoffmed = 0.0;
453  sigy = 1.0;
454  } else {
455  casu_medmad(xoffs,NULL,nmatch,&xoffmed,&sigx);
456  sigx *= 1.48;
457  casu_medmad(yoffs,NULL,nmatch,&yoffmed,&sigy);
458  sigy *= 1.48;
459  }
460 
461  cpl_msg_info(fctid, "Median offset x=%f y=%f, sigx=%f sigy=%f", xoffmed, yoffmed, sigx, sigy);
462 
463  /* Now go through one final time with a reduced error box and get
464  the final matches */
465 
466  errlim = 3.0*max(sigx,sigy);
467  for (k = 0; k < nstd; k++)
468  matches[k] = -1;
469  for (k = 0; k < nstd; k++) {
470  x = xstd[k] + xoffmed;
471  y = ystd[k] + yoffmed;
472  jm = casu_fndmatch(x,y,xobj,yobj,nobj,errlim);
473  if (jm > -1) {
474  dont = 0;
475  xx2 = xobj[jm] - x;
476  yy2 = yobj[jm] - y;
477  r2 = sqrt(xx2*xx2 + yy2*yy2);
478  for (l = 0; l < nstd; l++) {
479  if (matches[l] == jm) {
480  xx1 = xobj[jm] - (xstd[l] + xoffmed);
481  yy1 = yobj[jm] - (ystd[l] + yoffmed);
482  r1 = sqrt(xx1*xx1 + yy1*yy1);
483  if (r2 < r1)
484  matches[l] = -1;
485  else
486  dont = 1;
487 /* break; */
488  }
489  }
490  if (dont == 0)
491  matches[k] = jm;
492  }
493  }
494  jm = matches[1];
495 
496  /* Make a copy of the standards table and add all the columns from the
497  object catalogue to it. Ignore the RA and DEC columns in the catalogue
498  as the standards table will already have these.*/
499 
500  mstds = cpl_table_duplicate(stdstab);
501  colnames = cpl_table_get_column_names(objtab);
502  ig = (int)cpl_array_get_size(colnames);
503  for (k = 0; k < ig; k++) {
504  colname = (char *)cpl_array_get_string(colnames,(cpl_size)k);
505  if (strcasecmp(colname,"RA") && strcasecmp(colname,"DEC"))
506  cpl_table_new_column(mstds,colname,
507  cpl_table_get_column_type(objtab,colname));
508  }
509  cpl_table_unselect_all(mstds);
510  cpl_array_delete(colnames);
511 
512  /* Now go through and find the matches. NB: the columns in objtab
513  are created by casu_imcore which only produces integer, float and
514  double columns, so we don't need to worry about any other type
515  when copying the columns to the matched standards catalogue. */
516 
517  colnames = cpl_table_get_column_names(objtab);
518  ig = (int)cpl_array_get_size(colnames);
519  for (k = 0; k < nstd; k++) {
520  jm = matches[k];
521  if (jm != -1) {
522  for (k2 = 0; k2 < ig; k2++) {
523  colname = (char *)cpl_array_get_string(colnames,
524  (cpl_size)k2);
525  if (!strcasecmp(colname,"RA") || !strcasecmp(colname,"DEC"))
526  continue;
527  null = 0;
528  switch (cpl_table_get_column_type(objtab,colname)) {
529  case CPL_TYPE_INT:
530  cpl_table_set_int(mstds,colname,(cpl_size)k,
531  cpl_table_get_int(objtab,colname,
532  (cpl_size)jm,&null));
533  break;
534  case CPL_TYPE_FLOAT:
535  cpl_table_set_float(mstds,colname,(cpl_size)k,
536  cpl_table_get_float(objtab,colname,
537  (cpl_size)jm,&null));
538  break;
539  case CPL_TYPE_DOUBLE:
540  cpl_table_set_double(mstds,colname,(cpl_size)k,
541  cpl_table_get_double(objtab,colname,
542  (cpl_size)jm,&null));
543  break;
544  case CPL_TYPE_STRING:
545  cpl_table_set_string(mstds,colname,(cpl_size)k,
546  cpl_table_get_string(objtab,colname,
547  (cpl_size)jm));
548  break;
549  default:
550  cpl_msg_info(fctid, "Ignoring column %s of type %d", colname, cpl_table_get_column_type(objtab,colname));
551  break;
552  }
553  }
554  cpl_table_select_row(mstds,(cpl_size)k);
555  }
556  }
557  cpl_array_delete(colnames);
558 
559  /* Now extract the selected rows into the output table */
560 
561  *outtab = cpl_table_extract_selected(mstds);
562  cpl_table_delete(mstds);
563 
564  /* Tidy up */
565 
566  freespace(matches);
567  freespace(xoffs);
568  freespace(yoffs);
569  GOOD_STATUS
570 }
571 
572 /*---------------------------------------------------------------------------*/
592 /*---------------------------------------------------------------------------*/
593 
594 static cpl_table *casu_mkmstd_table(cpl_table *objtab, cpl_table *stdstab) {
595  cpl_table *mstds;
596  char *colname;
597  cpl_array *colnames;
598  cpl_size ncol,i;
599 
600  /* Copy the input standards table */
601 
602  mstds = cpl_table_duplicate(stdstab);
603 
604  /* Loop throught the object table columns and copy all of them over,
605  except for the RA and DEC tables */
606 
607  colnames = cpl_table_get_column_names((const cpl_table *)objtab);
608  ncol = cpl_array_get_size(colnames);
609  for (i = 0; i < ncol; i++) {
610  colname = (char *)cpl_array_get_string(colnames,i);
611  if (strcmp(colname,"RA") && strcmp(colname,"DEC"))
612  cpl_table_new_column(mstds,colname,
613  cpl_table_get_column_type(objtab,colname));
614  }
615  cpl_array_delete(colnames);
616  cpl_table_unselect_all(mstds);
617 
618  /* Get out of here */
619 
620  return(mstds);
621 }
622 
626 /*
627 
628 $Log: casu_match.c,v $
629 Revision 1.2 2015/08/07 13:06:54 jim
630 Fixed copyright to ESO
631 
632 Revision 1.1.1.1 2015/06/12 10:44:32 jim
633 Initial import
634 
635 Revision 1.5 2015/05/20 11:59:48 jim
636 Modified to remove calls to deprecated cpl_table_get_column_name
637 
638 Revision 1.4 2015/02/14 12:32:06 jim
639 fixed typo
640 
641 Revision 1.3 2015/01/29 11:51:56 jim
642 modified comments
643 
644 Revision 1.2 2013/11/21 09:38:14 jim
645 detabbed
646 
647 Revision 1.1.1.1 2013-08-27 12:07:48 jim
648 Imported
649 
650 
651 */
int casu_matchstds(cpl_table *objtab, cpl_table *stdstab, float srad, cpl_table **outtab, int *status)
Match object and standard star tables by their xy coordinates.
Definition: casu_match.c:300
int casu_matchxy(cpl_table *progtab, cpl_table *template, float srad, float *xoffset, float *yoffset, int *nm, cpl_table **outtab, int *status)
Match two lists of x,y coordinates from two tables to find the cartesian offset between them.
Definition: casu_match.c:105
void casu_medmad(float *data, unsigned char *bpm, long np, float *med, float *mad)
Definition: casu_stats.c:347
float casu_med(float *data, unsigned char *bpm, long npts)
Definition: casu_stats.c:89
int casu_fndmatch(float x, float y, float *xlist, float *ylist, int nlist, float err)
Match the x,y coordinates of an object to one from a list.
Definition: casu_utils.c:982