bl* henry_draper_get(hd_catalog_t* hdcat, double racenter, double deccenter, double r_arcsec) { double r2; double xyz[3]; kdtree_qres_t* q; bl* res; int i; hd_entry_t hd; radecdeg2xyzarr(racenter, deccenter, xyz); r2 = arcsec2distsq(r_arcsec); q = kdtree_rangesearch(hdcat->kd, xyz, r2); if (!q) { return NULL; } res = bl_new(256, sizeof(hd_entry_t)); for (i=0; i<q->nres; i++) { double* pt = q->results.d + i*3; xyzarr2radecdeg(pt, &(hd.ra), &(hd.dec)); hd.hd = q->inds[i] + 1; bl_append(res, &hd); } kdtree_free_query(q); return res; }
int startree_get_radec(startree_t* s, int starid, double* ra, double* dec) { double xyz[3]; int rtn; rtn = startree_get(s, starid, xyz); if (rtn) return rtn; xyzarr2radecdeg(xyz, ra, dec); return rtn; }
static int fit_tan_wcs_solve(const double* starxyz, const double* fieldxy, const double* weights, int N, const double* crpix, const tan_t* tanin, tan_t* tanout, double* p_scale) { int i, j, k; double field_cm[2] = {0, 0}; double cov[4] = {0, 0, 0, 0}; double R[4] = {0, 0, 0, 0}; double scale; // projected star coordinates double* p; // relative field coordinates double* f; double pcm[2] = {0, 0}; double w = 0; double totalw; gsl_matrix* A; gsl_matrix* U; gsl_matrix* V; gsl_vector* S; gsl_vector* work; gsl_matrix_view vcov; gsl_matrix_view vR; double crxyz[3]; double star_cm[3] = {0, 0, 0}; assert(((tanin != NULL) && (crpix != NULL)) || ((tanin == NULL) && (crpix == NULL))); if (tanin) { // default vals... memcpy(tanout, tanin, sizeof(tan_t)); } else { memset(tanout, 0, sizeof(tan_t)); } // -allocate and fill "p" and "f" arrays. ("projected" and "field") p = malloc(N * 2 * sizeof(double)); f = malloc(N * 2 * sizeof(double)); // -get field center-of-mass totalw = 0.0; for (i=0; i<N; i++) { w = (weights ? weights[i] : 1.0); field_cm[0] += w * fieldxy[i*2 + 0]; field_cm[1] += w * fieldxy[i*2 + 1]; totalw += w; } field_cm[0] /= totalw; field_cm[1] /= totalw; // Subtract it out. for (i=0; i<N; i++) { f[2*i+0] = fieldxy[2*i+0] - field_cm[0]; f[2*i+1] = fieldxy[2*i+1] - field_cm[1]; } if (tanin) { // Use original WCS to set the center of projection to the new crpix. tan_pixelxy2xyzarr(tanin, crpix[0], crpix[1], crxyz); for (i=0; i<N; i++) { Unused anbool ok; // -project the stars around crval ok = star_coords(starxyz + i*3, crxyz, TRUE, p + 2*i, p + 2*i + 1); assert(ok); } } else { // -get the star center-of-mass (this will become the tangent point CRVAL) for (i=0; i<N; i++) { w = (weights ? weights[i] : 1.0); star_cm[0] += w * starxyz[i*3 + 0]; star_cm[1] += w * starxyz[i*3 + 1]; star_cm[2] += w * starxyz[i*3 + 2]; } normalize_3(star_cm); // -project the stars around their center of mass for (i=0; i<N; i++) { Unused anbool ok; ok = star_coords(starxyz + i*3, star_cm, TRUE, p + 2*i, p + 2*i + 1); assert(ok); } } // -compute the center of mass of the projected stars and subtract it out. for (i=0; i<N; i++) { w = (weights ? weights[i] : 1.0); pcm[0] += w * p[2*i + 0]; pcm[1] += w * p[2*i + 1]; } pcm[0] /= totalw; pcm[1] /= totalw; for (i=0; i<N; i++) { p[2*i + 0] -= pcm[0]; p[2*i + 1] -= pcm[1]; } // -compute the covariance between field positions and projected // positions of the corresponding stars. for (i=0; i<N; i++) for (j=0; j<2; j++) for (k=0; k<2; k++) cov[j*2 + k] += p[i*2 + k] * f[i*2 + j]; for (i=0; i<4; i++) assert(isfinite(cov[i])); // -run SVD V = gsl_matrix_alloc(2, 2); S = gsl_vector_alloc(2); work = gsl_vector_alloc(2); vcov = gsl_matrix_view_array(cov, 2, 2); vR = gsl_matrix_view_array(R, 2, 2); A = &(vcov.matrix); // The Jacobi version doesn't always compute an orthonormal U if S has zeros. //gsl_linalg_SV_decomp_jacobi(A, V, S); gsl_linalg_SV_decomp(A, V, S, work); // the U result is written to A. U = A; gsl_vector_free(S); gsl_vector_free(work); // R = V U' gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, V, U, 0.0, &(vR.matrix)); gsl_matrix_free(V); for (i=0; i<4; i++) assert(isfinite(R[i])); // -compute scale: make the variances equal. { double pvar, fvar; pvar = fvar = 0.0; for (i=0; i<N; i++) { w = (weights ? weights[i] : 1.0); for (j=0; j<2; j++) { pvar += w * square(p[i*2 + j]); fvar += w * square(f[i*2 + j]); } } scale = sqrt(pvar / fvar); } // -compute WCS parameters. scale = rad2deg(scale); tanout->cd[0][0] = R[0] * scale; // CD1_1 tanout->cd[0][1] = R[1] * scale; // CD1_2 tanout->cd[1][0] = R[2] * scale; // CD2_1 tanout->cd[1][1] = R[3] * scale; // CD2_2 assert(isfinite(tanout->cd[0][0])); assert(isfinite(tanout->cd[0][1])); assert(isfinite(tanout->cd[1][0])); assert(isfinite(tanout->cd[1][1])); if (tanin) { // CRPIX is fixed. tanout->crpix[0] = crpix[0]; tanout->crpix[1] = crpix[1]; // Set CRVAL temporarily... tan_pixelxy2radec(tanin, crpix[0], crpix[1], tanout->crval+0, tanout->crval+1); // Shift CRVAL so that the center of the quad is in the right place. { double ix,iy; double dx,dy; double dxyz[3]; tan_pixelxy2iwc(tanout, field_cm[0], field_cm[1], &ix, &iy); dx = rad2deg(pcm[0]) - ix; dy = rad2deg(pcm[1]) - iy; tan_iwc2xyzarr(tanout, dx, dy, dxyz); xyzarr2radecdeg(dxyz, tanout->crval + 0, tanout->crval + 1); } } else { tanout->crpix[0] = field_cm[0]; tanout->crpix[1] = field_cm[1]; xyzarr2radecdegarr(star_cm, tanout->crval); // FIXME -- we ignore pcm. It should get added back in (after // multiplication by CD in the appropriate units) to either crval or // crpix. It's a very small correction probably of the same size // as the other approximations we're making. } if (p_scale) *p_scale = scale; free(p); free(f); return 0; }
int main(int argc, char** args) { int c; char* wcsfn = NULL; char* outfn = NULL; char* infn = NULL; sip_t sip; double scale = 1.0; anbool pngformat = TRUE; char* hdpath = NULL; anbool HD = FALSE; cairos_t thecairos; cairos_t* cairos = &thecairos; cairo_surface_t* target = NULL; cairo_t* cairot = NULL; cairo_surface_t* surfbg = NULL; cairo_t* cairobg = NULL; cairo_surface_t* surfshapes = NULL; cairo_t* cairoshapes = NULL; cairo_surface_t* surfshapesmask = NULL; cairo_t* cairoshapesmask = NULL; cairo_surface_t* surffg = NULL; cairo_t* cairo = NULL; double lw = 2.0; // circle linewidth. double cw = 2.0; double ngc_fraction = 0.02; // NGC linewidth double nw = 2.0; // leave a gap short of connecting the points. double endgap = 5.0; // circle radius. double crad = endgap; double fontsize = 14.0; double label_offset = 15.0; int W = 0, H = 0; unsigned char* img = NULL; anbool NGC = FALSE, constell = FALSE; anbool bright = FALSE; anbool common_only = FALSE; anbool print_common_only = FALSE; int Nbright = 0; double ra, dec, px, py; int i, N; anbool justlist = FALSE; anbool only_messier = FALSE; anbool grid = FALSE; double gridspacing = 0.0; double gridcolor[3] = { 0.2, 0.2, 0.2 }; int loglvl = LOG_MSG; char halign = 'L'; char valign = 'C'; sl* json = NULL; anbool whitetext = FALSE; while ((c = getopt(argc, args, OPTIONS)) != -1) { switch (c) { case 'V': valign = optarg[0]; break; case 'O': halign = optarg[0]; break; case 'F': ngc_fraction = atof(optarg); break; case 'h': print_help(args[0]); exit(0); case 'J': json = sl_new(4); break; case 'G': gridspacing = atof(optarg); break; case 'g': { char *tail = NULL; gridcolor[0] = strtod(optarg,&tail); if (*tail) { tail++; gridcolor[1] = strtod(tail,&tail); } if (*tail) { tail++; gridcolor[2] = strtod(tail,&tail); } } break; case 'D': HD = TRUE; break; case 'd': hdpath = optarg; break; case 'M': only_messier = TRUE; break; case 'n': nw = atof(optarg); break; case 'f': fontsize = atof(optarg); break; case 'L': justlist = TRUE; outfn = NULL; break; case 'x': whitetext = TRUE; break; case 'v': loglvl++; break; break; case 'j': print_common_only = TRUE; break; case 'c': common_only = TRUE; break; case 'b': Nbright = atoi(optarg); break; case 'B': bright = TRUE; break; case 'N': NGC = TRUE; break; case 'C': constell = TRUE; break; case 'p': pngformat = FALSE; break; case 's': scale = atof(optarg); break; case 'o': outfn = optarg; break; case 'i': infn = optarg; break; case 'w': wcsfn = optarg; break; case 'W': W = atoi(optarg); break; case 'H': H = atoi(optarg); break; } } log_init(loglvl); log_to(stderr); fits_use_error_system(); if (optind != argc) { print_help(args[0]); exit(-1); } if (!(outfn || justlist) || !wcsfn) { logerr("Need (-o or -L) and -w args.\n"); print_help(args[0]); exit(-1); } // read WCS. logverb("Trying to parse SIP/TAN header from %s...\n", wcsfn); if (!file_exists(wcsfn)) { ERROR("No such file: \"%s\"", wcsfn); exit(-1); } if (sip_read_header_file(wcsfn, &sip)) { logverb("Got SIP header.\n"); } else { ERROR("Failed to parse SIP/TAN header from %s", wcsfn); exit(-1); } if (!(NGC || constell || bright || HD || grid)) { logerr("Neither constellations, bright stars, HD nor NGC/IC overlays selected!\n"); print_help(args[0]); exit(-1); } if (gridspacing > 0.0) grid = TRUE; // adjust for scaling... lw /= scale; cw /= scale; nw /= scale; crad /= scale; endgap /= scale; fontsize /= scale; label_offset /= scale; if (!W || !H) { W = sip.wcstan.imagew; H = sip.wcstan.imageh; } if (!(infn || (W && H))) { logerr("Image width/height unspecified, and no input image given.\n"); exit(-1); } if (infn) { cairoutils_fake_ppm_init(); img = cairoutils_read_ppm(infn, &W, &H); if (!img) { ERROR("Failed to read input image %s", infn); exit(-1); } cairoutils_rgba_to_argb32(img, W, H); } else if (!justlist) { // Allocate a black image. img = calloc(4 * W * H, 1); if (!img) { SYSERROR("Failed to allocate a blank image on which to plot!"); exit(-1); } } if (HD && !hdpath) { logerr("If you specify -D (plot Henry Draper objs), you also have to give -d (path to Henry Draper catalog)\n"); exit(-1); } if (!justlist) { /* Cairo layers: -background: surfbg / cairobg --> gets drawn first, in black, masked by surfshapesmask -shapes: surfshapes / cairoshapes --> gets drawn second, masked by surfshapesmask -foreground/text: surffg / cairo --> gets drawn last. */ surffg = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, W, H); cairo = cairo_create(surffg); cairo_set_line_join(cairo, CAIRO_LINE_JOIN_BEVEL); cairo_set_antialias(cairo, CAIRO_ANTIALIAS_GRAY); cairo_set_source_rgba(cairo, 1.0, 1.0, 1.0, 1.0); cairo_scale(cairo, scale, scale); //cairo_select_font_face(cairo, "helvetica", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_select_font_face(cairo, "DejaVu Sans Mono Book", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cairo, fontsize); surfshapes = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, W, H); cairoshapes = cairo_create(surfshapes); cairo_set_line_join(cairoshapes, CAIRO_LINE_JOIN_BEVEL); cairo_set_antialias(cairoshapes, CAIRO_ANTIALIAS_GRAY); cairo_set_source_rgba(cairoshapes, 1.0, 1.0, 1.0, 1.0); cairo_scale(cairoshapes, scale, scale); cairo_select_font_face(cairoshapes, "DejaVu Sans Mono Book", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cairoshapes, fontsize); surfshapesmask = cairo_image_surface_create(CAIRO_FORMAT_A8, W, H); cairoshapesmask = cairo_create(surfshapesmask); cairo_set_line_join(cairoshapesmask, CAIRO_LINE_JOIN_BEVEL); cairo_set_antialias(cairoshapesmask, CAIRO_ANTIALIAS_GRAY); cairo_set_source_rgba(cairoshapesmask, 1.0, 1.0, 1.0, 1.0); cairo_scale(cairoshapesmask, scale, scale); cairo_select_font_face(cairoshapesmask, "DejaVu Sans Mono Book", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cairoshapesmask, fontsize); cairo_paint(cairoshapesmask); cairo_stroke(cairoshapesmask); surfbg = cairo_image_surface_create(CAIRO_FORMAT_A8, W, H); cairobg = cairo_create(surfbg); cairo_set_line_join(cairobg, CAIRO_LINE_JOIN_BEVEL); cairo_set_antialias(cairobg, CAIRO_ANTIALIAS_GRAY); cairo_set_source_rgba(cairobg, 0, 0, 0, 1); cairo_scale(cairobg, scale, scale); cairo_select_font_face(cairobg, "DejaVu Sans Mono Book", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cairobg, fontsize); cairos->bg = cairobg; cairos->fg = cairo; cairos->shapes = cairoshapes; cairos->shapesmask = cairoshapesmask; cairos->imgW = (float)W/scale; cairos->imgH = (float)H/scale; // } if (grid) { double ramin, ramax, decmin, decmax; double ra, dec; double rastep = gridspacing / 60.0; double decstep = gridspacing / 60.0; // how many line segments int N = 10; double px, py; int i; cairo_set_source_rgba(cairo, gridcolor[0], gridcolor[1], gridcolor[2], 1.0); sip_get_radec_bounds(&sip, 100, &ramin, &ramax, &decmin, &decmax); logverb("Plotting grid lines from RA=%g to %g in steps of %g; Dec=%g to %g in steps of %g\n", ramin, ramax, rastep, decmin, decmax, decstep); for (dec = decstep * floor(decmin / decstep); dec<=decmax; dec+=decstep) { logverb(" dec=%g\n", dec); for (i=0; i<=N; i++) { ra = ramin + ((double)i / (double)N) * (ramax - ramin); if (!sip_radec2pixelxy(&sip, ra, dec, &px, &py)) continue; // first time, move_to; else line_to ((ra == ramin) ? cairo_move_to : cairo_line_to)(cairo, px, py); } cairo_stroke(cairo); } for (ra = rastep * floor(ramin / rastep); ra <= ramax; ra += rastep) { //for (dec=decmin; dec<=decmax; dec += (decmax - decmin)/(double)N) { logverb(" ra=%g\n", ra); for (i=0; i<=N; i++) { dec = decmin + ((double)i / (double)N) * (decmax - decmin); if (!sip_radec2pixelxy(&sip, ra, dec, &px, &py)) continue; // first time, move_to; else line_to ((dec == decmin) ? cairo_move_to : cairo_line_to)(cairo, px, py); } cairo_stroke(cairo); } cairo_set_source_rgba(cairo, 1.0, 1.0, 1.0, 1.0); } } if (constell) { N = constellations_n(); logverb("Checking %i constellations.\n", N); for (c=0; c<N; c++) { const char* shortname = NULL; const char* longname; il* lines; il* uniqstars; il* inboundstars; float r,g,b; int Ninbounds; int Nunique; cairo_text_extents_t textents; double cmass[3]; uniqstars = constellations_get_unique_stars(c); inboundstars = il_new(16); Nunique = il_size(uniqstars); debug("%s: %zu unique stars.\n", shortname, il_size(uniqstars)); // Count the number of unique stars belonging to this contellation // that are within the image bounds Ninbounds = 0; for (i=0; i<il_size(uniqstars); i++) { int star; star = il_get(uniqstars, i); constellations_get_star_radec(star, &ra, &dec); debug("star %i: ra,dec (%g,%g)\n", il_get(uniqstars, i), ra, dec); if (!sip_radec2pixelxy(&sip, ra, dec, &px, &py)) continue; if (px < 0 || py < 0 || px*scale > W || py*scale > H) continue; Ninbounds++; il_append(inboundstars, star); } il_free(uniqstars); debug("%i are in-bounds.\n", Ninbounds); // Only draw this constellation if at least 2 of its stars // are within the image bounds. if (Ninbounds < 2) { il_free(inboundstars); continue; } // Set the color based on the location of the first in-bounds star. // This is a hack -- we have two different constellation // definitions with different numbering schemes! if (!justlist && (il_size(inboundstars) > 0)) { // This is helpful for videos: ensuring that the same // color is chosen for a constellation in each frame. int star = il_get(inboundstars, 0); constellations_get_star_radec(star, &ra, &dec); if (whitetext) { r = g = b = 1; } else { color_for_radec(ra, dec, &r, &g, &b); } cairo_set_source_rgba(cairoshapes, r,g,b,0.8); cairo_set_line_width(cairoshapes, cw); cairo_set_source_rgba(cairo, r,g,b,0.8); cairo_set_line_width(cairo, cw); } // Draw circles around each star. // Find center of mass (of the in-bounds stars) cmass[0] = cmass[1] = cmass[2] = 0.0; for (i=0; i<il_size(inboundstars); i++) { double xyz[3]; int star = il_get(inboundstars, i); constellations_get_star_radec(star, &ra, &dec); if (!sip_radec2pixelxy(&sip, ra, dec, &px, &py)) continue; if (px < 0 || py < 0 || px*scale > W || py*scale > H) continue; if (!justlist) { cairo_arc(cairobg, px, py, crad+1.0, 0.0, 2.0*M_PI); cairo_stroke(cairobg); cairo_arc(cairoshapes, px, py, crad, 0.0, 2.0*M_PI); cairo_stroke(cairoshapes); } radecdeg2xyzarr(ra, dec, xyz); cmass[0] += xyz[0]; cmass[1] += xyz[1]; cmass[2] += xyz[2]; } cmass[0] /= il_size(inboundstars); cmass[1] /= il_size(inboundstars); cmass[2] /= il_size(inboundstars); xyzarr2radecdeg(cmass, &ra, &dec); il_free(inboundstars); if (!sip_radec2pixelxy(&sip, ra, dec, &px, &py)) continue; shortname = constellations_get_shortname(c); longname = constellations_get_longname(c); assert(shortname && longname); logverb("%s at (%g, %g)\n", longname, px, py); if (Ninbounds == Nunique) { printf("The constellation %s (%s)\n", longname, shortname); } else { printf("Part of the constellation %s (%s)\n", longname, shortname); } if (justlist) continue; // If the label will be off-screen, move it back on. cairo_text_extents(cairo, shortname, &textents); if (px < 0) px = 0; if (py < textents.height) py = textents.height; if ((px + textents.width)*scale > W) px = W/scale - textents.width; if ((py+textents.height)*scale > H) py = H/scale - textents.height; logverb("%s at (%g, %g)\n", shortname, px, py); add_text(cairos, longname, px, py, halign, valign); // Draw the lines. cairo_set_line_width(cairo, lw); lines = constellations_get_lines(c); for (i=0; i<il_size(lines)/2; i++) { int star1, star2; double ra1, dec1, ra2, dec2; double px1, px2, py1, py2; double dx, dy; double dist; double gapfrac; star1 = il_get(lines, i*2+0); star2 = il_get(lines, i*2+1); constellations_get_star_radec(star1, &ra1, &dec1); constellations_get_star_radec(star2, &ra2, &dec2); if (!sip_radec2pixelxy(&sip, ra1, dec1, &px1, &py1) || !sip_radec2pixelxy(&sip, ra2, dec2, &px2, &py2)) continue; dx = px2 - px1; dy = py2 - py1; dist = hypot(dx, dy); gapfrac = endgap / dist; cairo_move_to(cairoshapes, px1 + dx*gapfrac, py1 + dy*gapfrac); cairo_line_to(cairoshapes, px1 + dx*(1.0-gapfrac), py1 + dy*(1.0-gapfrac)); cairo_stroke(cairoshapes); } il_free(lines); } logverb("done constellations.\n"); } if (bright) { double dy = 0; cairo_font_extents_t extents; pl* brightstars = pl_new(16); if (!justlist) { cairo_set_source_rgba(cairoshapes, 0.75, 0.75, 0.75, 0.8); cairo_font_extents(cairo, &extents); dy = extents.ascent * 0.5; cairo_set_line_width(cairoshapes, cw); } N = bright_stars_n(); logverb("Checking %i bright stars.\n", N); for (i=0; i<N; i++) { const brightstar_t* bs = bright_stars_get(i); if (!sip_radec2pixelxy(&sip, bs->ra, bs->dec, &px, &py)) continue; if (px < 0 || py < 0 || px*scale > W || py*scale > H) continue; if (!(bs->name && strlen(bs->name))) continue; if (common_only && !(bs->common_name && strlen(bs->common_name))) continue; if (strcmp(bs->common_name, "Maia") == 0) continue; pl_append(brightstars, bs); } // keep only the Nbright brightest? if (Nbright && (pl_size(brightstars) > Nbright)) { pl_sort(brightstars, sort_by_mag); pl_remove_index_range(brightstars, Nbright, pl_size(brightstars)-Nbright); } for (i=0; i<pl_size(brightstars); i++) { char* text; const brightstar_t* bs = pl_get(brightstars, i); if (!sip_radec2pixelxy(&sip, bs->ra, bs->dec, &px, &py)) continue; if (bs->common_name && strlen(bs->common_name)) if (print_common_only || common_only) text = strdup(bs->common_name); else asprintf_safe(&text, "%s (%s)", bs->common_name, bs->name); else text = strdup(bs->name); logverb("%s at (%g, %g)\n", text, px, py); if (json) { sl* names = sl_new(4); char* namearr; if (bs->common_name && strlen(bs->common_name)) sl_append(names, bs->common_name); if (bs->name) sl_append(names, bs->name); namearr = sl_join(names, "\", \""); sl_appendf(json, "{ \"type\" : \"star\", " " \"pixelx\": %g, " " \"pixely\": %g, " " \"name\" : \"%s\", " " \"names\" : [ \"%s\" ] } " , px, py, (bs->common_name && strlen(bs->common_name)) ? bs->common_name : bs->name, namearr); free(namearr); sl_free2(names); } if (bs->common_name && strlen(bs->common_name)) printf("The star %s (%s)\n", bs->common_name, bs->name); else printf("The star %s\n", bs->name); if (!justlist) { float r,g,b; // set color based on RA,Dec to match constellations above. if (whitetext) { r = g = b = 1; } else { color_for_radec(bs->ra, bs->dec, &r, &g, &b); } cairo_set_source_rgba(cairoshapes, r,g,b,0.8); cairo_set_source_rgba(cairo, r,g,b, 0.8); } if (!justlist) add_text(cairos, text, px + label_offset, py + dy, halign, valign); free(text); if (!justlist) { // plot a black circle behind the light circle... cairo_arc(cairobg, px, py, crad+1.0, 0.0, 2.0*M_PI); cairo_stroke(cairobg); cairo_arc(cairoshapes, px, py, crad, 0.0, 2.0*M_PI); cairo_stroke(cairoshapes); } } pl_free(brightstars); } if (NGC) { double imscale; double imsize; double dy = 0; cairo_font_extents_t extents; if (!justlist) { cairo_set_source_rgb(cairoshapes, 1.0, 1.0, 1.0); cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0); cairo_set_line_width(cairo, nw); cairo_font_extents(cairo, &extents); dy = extents.ascent * 0.5; } // arcsec/pixel imscale = sip_pixel_scale(&sip); // arcmin imsize = imscale * (imin(W, H) / scale) / 60.0; N = ngc_num_entries(); logverb("Checking %i NGC/IC objects.\n", N); for (i=0; i<N; i++) { ngc_entry* ngc = ngc_get_entry(i); sl* str; sl* names; double pixsize; float ara, adec; char* text; if (!ngc) break; if (ngc->size < imsize * ngc_fraction) continue; if (ngcic_accurate_get_radec(ngc->is_ngc, ngc->id, &ara, &adec) == 0) { ngc->ra = ara; ngc->dec = adec; } if (!sip_radec2pixelxy(&sip, ngc->ra, ngc->dec, &px, &py)) continue; if (px < 0 || py < 0 || px*scale > W || py*scale > H) continue; str = sl_new(4); //sl_appendf(str, "%s %i", (ngc->is_ngc ? "NGC" : "IC"), ngc->id); names = ngc_get_names(ngc, NULL); if (names) { int n; for (n=0; n<sl_size(names); n++) { if (only_messier && strncmp(sl_get(names, n), "M ", 2)) continue; sl_append(str, sl_get(names, n)); } } sl_free2(names); text = sl_implode(str, " / "); printf("%s\n", text); pixsize = ngc->size * 60.0 / imscale; if (!justlist) { // black circle behind the white one... cairo_arc(cairobg, px, py, pixsize/2.0+1.0, 0.0, 2.0*M_PI); cairo_stroke(cairobg); cairo_move_to(cairoshapes, px + pixsize/2.0, py); cairo_arc(cairoshapes, px, py, pixsize/2.0, 0.0, 2.0*M_PI); debug("size: %f arcsec, pixsize: %f pixels\n", ngc->size, pixsize); cairo_stroke(cairoshapes); add_text(cairos, text, px + label_offset, py + dy, halign, valign); } if (json) { char* namelist = sl_implode(str, "\", \""); sl_appendf(json, "{ \"type\" : \"ngc\", " " \"names\" : [ \"%s\" ], " " \"pixelx\" : %g, " " \"pixely\" : %g, " " \"radius\" : %g }" , namelist, px, py, pixsize/2.0); free(namelist); } free(text); sl_free2(str); } } if (HD) { double rac, decc, ra2, dec2; double arcsec; hd_catalog_t* hdcat; bl* hdlist; int i; if (!justlist) cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0); logverb("Reading HD catalog: %s\n", hdpath); hdcat = henry_draper_open(hdpath); if (!hdcat) { ERROR("Failed to open HD catalog"); exit(-1); } logverb("Got %i HD stars\n", henry_draper_n(hdcat)); sip_pixelxy2radec(&sip, W/(2.0*scale), H/(2.0*scale), &rac, &decc); sip_pixelxy2radec(&sip, 0.0, 0.0, &ra2, &dec2); arcsec = arcsec_between_radecdeg(rac, decc, ra2, dec2); // Fudge arcsec *= 1.1; hdlist = henry_draper_get(hdcat, rac, decc, arcsec); logverb("Found %zu HD stars within range (%g arcsec of RA,Dec %g,%g)\n", bl_size(hdlist), arcsec, rac, decc); for (i=0; i<bl_size(hdlist); i++) { double px, py; char* txt; hd_entry_t* hd = bl_access(hdlist, i); if (!sip_radec2pixelxy(&sip, hd->ra, hd->dec, &px, &py)) { continue; } if (px < 0 || py < 0 || px*scale > W || py*scale > H) { logverb(" HD %i at RA,Dec (%g, %g) -> pixel (%.1f, %.1f) is out of bounds\n", hd->hd, hd->ra, hd->dec, px, py); continue; } asprintf_safe(&txt, "HD %i", hd->hd); if (!justlist) { cairo_text_extents_t textents; cairo_text_extents(cairo, txt, &textents); cairo_arc(cairobg, px, py, crad+1.0, 0.0, 2.0*M_PI); cairo_stroke(cairobg); cairo_arc(cairoshapes, px, py, crad, 0.0, 2.0*M_PI); cairo_stroke(cairoshapes); px -= (textents.width * 0.5); py -= (crad + 4.0); add_text(cairos, txt, px, py, halign, valign); } if (json) sl_appendf(json, "{ \"type\" : \"hd\"," " \"pixelx\": %g, " " \"pixely\": %g, " " \"name\" : \"HD %i\" }" , px, py, hd->hd); printf("%s\n", txt); free(txt); } bl_free(hdlist); henry_draper_close(hdcat); } if (json) { FILE* fout = stderr; char* annstr = sl_implode(json, ",\n"); fprintf(fout, "{ \n"); fprintf(fout, " \"status\": \"solved\",\n"); fprintf(fout, " \"git-revision\": %s,\n", AN_GIT_REVISION); fprintf(fout, " \"git-date\": \"%s\",\n", AN_GIT_DATE); fprintf(fout, " \"annotations\": [\n%s\n]\n", annstr); fprintf(fout, "}\n"); free(annstr); } sl_free2(json); json = NULL; if (justlist) return 0; target = cairo_image_surface_create_for_data(img, CAIRO_FORMAT_ARGB32, W, H, W*4); cairot = cairo_create(target); cairo_set_source_rgba(cairot, 0, 0, 0, 1); // Here's where you set the background surface's properties... cairo_set_source_surface(cairot, surfbg, 0, 0); cairo_mask_surface(cairot, surfshapesmask, 0, 0); cairo_stroke(cairot); // Add on the shapes. cairo_set_source_surface(cairot, surfshapes, 0, 0); //cairo_mask_surface(cairot, surfshapes, 0, 0); cairo_mask_surface(cairot, surfshapesmask, 0, 0); cairo_stroke(cairot); // Add on the foreground. cairo_set_source_surface(cairot, surffg, 0, 0); cairo_mask_surface(cairot, surffg, 0, 0); cairo_stroke(cairot); // Convert image for output... cairoutils_argb32_to_rgba(img, W, H); if (pngformat) { if (cairoutils_write_png(outfn, img, W, H)) { ERROR("Failed to write PNG"); exit(-1); } } else { if (cairoutils_write_ppm(outfn, img, W, H)) { ERROR("Failed to write PPM"); exit(-1); } } cairo_surface_destroy(target); cairo_surface_destroy(surfshapesmask); cairo_surface_destroy(surffg); cairo_surface_destroy(surfbg); cairo_surface_destroy(surfshapes); cairo_destroy(cairo); cairo_destroy(cairot); cairo_destroy(cairobg); cairo_destroy(cairoshapes); cairo_destroy(cairoshapesmask); free(img); return 0; }
anbool tpv_xyzarr2pixelxy(const tpv_t* tpv, const double* xyz, double *px, double *py) { double ra, dec; xyzarr2radecdeg(xyz, &ra, &dec); return tpv_radec2pixelxy(tpv, ra, dec, px, py); }
int wcs_pv2sip(const char* wcsinfn, int ext, const char* wcsoutfn, anbool scamp_head_file, double* xy, int Nxy, int imageW, int imageH, anbool forcetan) { qfits_header* hdr = NULL; double* radec = NULL; int rtn = -1; tan_t tanwcs; double x,y, px,py; double xyz[3]; double* xorig = NULL; double* yorig = NULL; double* rddist = NULL; int i, j; // 1 x y r x2 xy y2 x3 x2y xy2 y3 r3 x4 x3y x2y2 xy3 y4 // x5 x4y x3y2 x2y3 xy4 y5 r5 x6 x5y x4y2, x3y3 x2y4 xy5 y6 // x7 x6y x5y2 x4y3 x3y4 x2y5 xy6 y7 r7 int xp[] = { 0, 1, 0, 0, 2, 1, 0, 3, 2, 1, 0, 0, 4, 3, 2, 1, 0, 5, 4, 3, 2, 1, 5, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 0}; int yp[] = { 0, 0, 1, 0, 0, 1, 2, 0, 1, 2, 3, 0, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 0}; int rp[] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7}; double xpows[8]; double ypows[8]; double rpows[8]; double pv1[40]; double pv2[40]; double r; if (scamp_head_file) { size_t sz = 0; char* txt; char* prefix; int np; int nt; unsigned char* txthdr; sl* lines; int i; txt = file_get_contents(wcsinfn, &sz, TRUE); if (!txt) { ERROR("Failed to read file %s", wcsinfn); goto bailout; } lines = sl_split(NULL, txt, "\n"); prefix = "SIMPLE = T / Standard FITS file " "BITPIX = 8 / ASCII or bytes array " "NAXIS = 0 / Minimal header " "EXTEND = T / There may be FITS ext " "WCSAXES = 2 / "; np = strlen(prefix); nt = np + FITS_LINESZ * sl_size(lines); txthdr = malloc(nt); memset(txthdr, ' ', np + FITS_LINESZ * sl_size(lines)); memcpy(txthdr, prefix, np); for (i=0; i<sl_size(lines); i++) memcpy(txthdr + np + i*FITS_LINESZ, sl_get(lines, i), strlen(sl_get(lines, i))); sl_free2(lines); hdr = qfits_header_read_hdr_string(txthdr, nt); free(txthdr); free(txt); } else { char* ct; hdr = anqfits_get_header2(wcsinfn, ext); ct = fits_get_dupstring(hdr, "CTYPE1"); if ((ct && streq(ct, "RA---TPV")) || forcetan) { // http://iraf.noao.edu/projects/ccdmosaic/tpv.html logmsg("Replacing CTYPE1 = %s header with RA---TAN\n", ct); fits_update_value(hdr, "CTYPE1", "RA---TAN"); } ct = fits_get_dupstring(hdr, "CTYPE2"); if ((ct && streq(ct, "DEC--TPV")) || forcetan) { logmsg("Replacing CTYPE2 = %s header with DEC--TAN\n", ct); fits_update_value(hdr, "CTYPE2", "DEC--TAN"); } } if (!hdr) { ERROR("Failed to read header: file %s, ext %i\n", wcsinfn, ext); goto bailout; } tan_read_header(hdr, &tanwcs); for (i=0; i<sizeof(pv1)/sizeof(double); i++) { char key[10]; sprintf(key, "PV1_%i", i); pv1[i] = qfits_header_getdouble(hdr, key, 0.0); sprintf(key, "PV2_%i", i); pv2[i] = qfits_header_getdouble(hdr, key, 0.0); } xorig = malloc(Nxy * sizeof(double)); yorig = malloc(Nxy * sizeof(double)); rddist = malloc(2 * Nxy * sizeof(double)); for (j=0; j<Nxy; j++) { xorig[j] = xy[2*j+0]; yorig[j] = xy[2*j+1]; tan_pixelxy2iwc(&tanwcs, xorig[j], yorig[j], &x, &y); r = sqrt(x*x + y*y); xpows[0] = ypows[0] = rpows[0] = 1.0; for (i=1; i<sizeof(xpows)/sizeof(double); i++) { xpows[i] = xpows[i-1]*x; ypows[i] = ypows[i-1]*y; rpows[i] = rpows[i-1]*r; } px = py = 0; for (i=0; i<sizeof(xp)/sizeof(int); i++) { px += pv1[i] * xpows[xp[i]] * ypows[yp[i]] * rpows[rp[i]]; py += pv2[i] * ypows[xp[i]] * xpows[yp[i]] * rpows[rp[i]]; } tan_iwc2xyzarr(&tanwcs, px, py, xyz); xyzarr2radecdeg(xyz, rddist+2*j, rddist+2*j+1); } // { starxy_t sxy; tweak_t* t; il* imgi; il* refi; int sip_order = 5; int sip_inv_order = 5; sxy.N = Nxy; sxy.x = xorig; sxy.y = yorig; imgi = il_new(256); refi = il_new(256); for (i=0; i<Nxy; i++) { il_append(imgi, i); il_append(refi, i); } t = tweak_new(); t->sip->a_order = t->sip->b_order = sip_order; t->sip->ap_order = t->sip->bp_order = sip_inv_order; tweak_push_wcs_tan(t, &tanwcs); tweak_push_ref_ad_array(t, rddist, Nxy); tweak_push_image_xy(t, &sxy); tweak_push_correspondence_indices(t, imgi, refi, NULL, NULL); tweak_go_to(t, TWEAK_HAS_LINEAR_CD); if (imageW) t->sip->wcstan.imagew = imageW; if (imageH) t->sip->wcstan.imageh = imageH; sip_write_to_file(t->sip, wcsoutfn); tweak_free(t); } rtn = 0; bailout: free(xorig); free(yorig); free(rddist); qfits_header_destroy(hdr); free(radec); return rtn; }
anbool sip_xyzarr2pixelxy(const sip_t* sip, const double* xyz, double *px, double *py) { double ra, dec; xyzarr2radecdeg(xyz, &ra, &dec); return sip_radec2pixelxy(sip, ra, dec, px, py); }
void tan_iwc2radec(const tan_t* tan, double x, double y, double *p_ra, double *p_dec) { double xyz[3]; tan_iwc2xyzarr(tan, x, y, xyz); xyzarr2radecdeg(xyz, p_ra, p_dec); }
// Pixels to RA,Dec in degrees. void tan_pixelxy2radec(const tan_t* tan, double px, double py, double *ra, double *dec) { double xyz[3]; tan_pixelxy2xyzarr(tan, px, py, xyz); xyzarr2radecdeg(xyz, ra,dec); }
static void plot_targets(cairo_t* cairo, plot_args_t* pargs, plotann_t* ann) { int i; double cra, cdec; plotstuff_get_radec_center_and_radius(pargs, &cra, &cdec, NULL); for (i=0; i<bl_size(ann->targets); i++) { target_t* tar = bl_access(ann->targets, i); double px,py; double cx,cy; double dx,dy, r; double ex,ey; double ly, ry, tx, bx; double distdeg; anbool okquadrant; char* txt; logverb("Target: \"%s\" at (%g,%g)\n", tar->name, tar->ra, tar->dec); okquadrant = plotstuff_radec2xy(pargs, tar->ra, tar->dec, &px, &py); px -= 1; py -= 1; if (okquadrant && px >= 0 && px < pargs->W && py >= 0 && py < pargs->H) { // inside the image! logverb("Target \"%s\" is inside the image, at pixel (%g,%g)\n", tar->name, px, py); plotstuff_stack_marker(pargs, px, py); plotstuff_stack_text(pargs, cairo, tar->name, px, py); continue; } // outside the image: find intersection point. cx = pargs->W / 2.0; cy = pargs->H / 2.0; if (okquadrant) { logverb("Target \"%s\" is outside the image, at pixel (%g,%g)\n", tar->name, px, py); dx = px - cx; dy = py - cy; } else { double cxyz[3]; double txyz[3]; double vec[3]; int j; double ra,dec; logverb("Target \"%s\" is way outside the image.\n", tar->name); // fallback. radecdeg2xyzarr(cra, cdec, cxyz); radecdeg2xyzarr(tar->ra, tar->dec, txyz); for (j=0; j<3; j++) vec[j] = cxyz[j] + 0.1 * txyz[j]; normalize_3(vec); xyzarr2radecdeg(vec, &ra, &dec); okquadrant = plotstuff_radec2xy(pargs, ra, dec, &px, &py); assert(okquadrant); dx = px - cx; dy = py - cy; if ((dx*dx + dy*dy) < (cx*cx + cy*cy)) { double scale = 3.0 * sqrt(cx*cx + cy*cy) / sqrt(dx*dx + dy*dy); dx *= scale; dy *= scale; } } ly = (-(pargs->W/2.0) / dx) * dy + cy; ry = ( (pargs->W/2.0) / dx) * dy + cy; bx = (-(pargs->H/2.0) / dy) * dx + cx; tx = ( (pargs->H/2.0) / dy) * dx + cx; logverb("ly %g, ry %g, bx %g, tx %g\n", ly, ry, bx, tx); if (px < cx && ly >= 0 && ly < pargs->H) { ex = 0.0; ey = ly; } else if (px >= cx && ry >= 0 && ry < pargs->H) { ex = pargs->W - 1; ey = ry; } else if (py < cy && bx >= 0 && bx < pargs->W) { ex = bx; ey = 0; } else if (py >= cy && tx >= 0 && tx < pargs->W) { ex = tx; ey = pargs->H - 1; } else { logverb("None of the edges are in bounds: px,py=(%g,%g); ly=%g, ry=%g, bx=%g, tx=%g\n", px,py,ly,ry,bx,tx); continue; } dx = ex - cx; dy = ey - cy; r = sqrt(dx*dx + dy*dy); px = (r-100.0) / r * dx + cx; py = (r-100.0) / r * dy + cy; plotstuff_stack_arrow(pargs, px, py, ex, ey); logverb("Arrow from (%g,%g) to (%g,%g)\n", px, py, ex, ey); distdeg = deg_between_radecdeg(cra, cdec, tar->ra, tar->dec); asprintf_safe(&txt, "%s: %.1f deg", tar->name, distdeg); plotstuff_stack_text(pargs, cairo, txt, px, py); } }
static void plot_constellations(cairo_t* cairo, plot_args_t* pargs, plotann_t* ann) { int i, N; double ra,dec,radius; double xyzf[3]; // Find the field center and radius anwcs_get_radec_center_and_radius(pargs->wcs, &ra, &dec, &radius); logverb("Plotting constellations: field center %g,%g, radius %g\n", ra, dec, radius); radecdeg2xyzarr(ra, dec, xyzf); radius = deg2dist(radius); N = constellations_n(); for (i=0; i<N; i++) { int j, k; // Find the approximate center and radius of this constellation // and see if it overlaps with the field. il* stars = constellations_get_unique_stars(i); double xyzj[3]; double xyzc[3]; double maxr2 = 0; dl* rds; xyzc[0] = xyzc[1] = xyzc[2] = 0.0; xyzj[0] = xyzj[1] = xyzj[2] = 0.0; for (j=0; j<il_size(stars); j++) { constellations_get_star_radec(il_get(stars, j), &ra, &dec); radecdeg2xyzarr(ra, dec, xyzj); for (k=0; k<3; k++) xyzc[k] += xyzj[k]; } normalize_3(xyzc); for (j=0; j<il_size(stars); j++) { constellations_get_star_radec(il_get(stars, j), &ra, &dec); maxr2 = MAX(maxr2, distsq(xyzc, xyzj, 3)); } il_free(stars); maxr2 = square(sqrt(maxr2) + radius); if (distsq(xyzf, xyzc, 3) > maxr2) { xyzarr2radecdeg(xyzc, &ra, &dec); logverb("Constellation %s (center %g,%g, radius %g) out of bounds\n", constellations_get_shortname(i), ra, dec, dist2deg(sqrt(maxr2) - radius)); logverb(" dist from field center to constellation center is %g deg\n", distsq2deg(distsq(xyzf, xyzc, 3))); logverb(" max radius: %g\n", distsq2deg(maxr2)); continue; } if (ann->constellation_pastel) { float r,g,b; xyzarr2radecdeg(xyzc, &ra, &dec); color_for_radec(ra, dec, &r,&g,&b); plotstuff_set_rgba2(pargs, r,g,b, 0.8); plotstuff_builtin_apply(cairo, pargs); } // Phew, plot it. if (ann->constellation_lines) { rds = constellations_get_lines_radec(i); logverb("Constellation %s: plotting %zu lines\n", constellations_get_shortname(i), dl_size(rds)/4); for (j=0; j<dl_size(rds)/4; j++) { double r1,d1,r2,d2; double r3,d3,r4,d4; double off = ann->constellation_lines_offset; r1 = dl_get(rds, j*4+0); d1 = dl_get(rds, j*4+1); r2 = dl_get(rds, j*4+2); d2 = dl_get(rds, j*4+3); if (anwcs_find_discontinuity(pargs->wcs, r1, d1, r2, d2, &r3, &d3, &r4, &d4)) { logverb("Discontinuous: %g,%g -- %g,%g\n", r1, d1, r2, d2); logverb(" %g,%g == %g,%g\n", r3,d3, r4,d4); plot_offset_line_rd(NULL, pargs, r1,d1,r3,d3, off, 0.); plot_offset_line_rd(NULL, pargs, r4,d4,r2,d2, 0., off); } else { plot_offset_line_rd(NULL, pargs, r1,d1,r2,d2, off, off); } plotstuff_stroke(pargs); } dl_free(rds); } if (ann->constellation_labels || ann->constellation_markers) { // Put the label at the center of mass of the stars that // are in-bounds int Nin = 0; stars = constellations_get_unique_stars(i); xyzc[0] = xyzc[1] = xyzc[2] = 0.0; logverb("Labeling %s: %zu stars\n", constellations_get_shortname(i), il_size(stars)); for (j=0; j<il_size(stars); j++) { constellations_get_star_radec(il_get(stars, j), &ra, &dec); if (!anwcs_radec_is_inside_image(pargs->wcs, ra, dec)) continue; if (ann->constellation_markers) plotstuff_marker_radec(pargs, ra, dec); radecdeg2xyzarr(ra, dec, xyzj); for (k=0; k<3; k++) xyzc[k] += xyzj[k]; Nin++; } logverb(" %i stars in-bounds\n", Nin); if (ann->constellation_labels && Nin) { const char* label; normalize_3(xyzc); xyzarr2radecdeg(xyzc, &ra, &dec); if (ann->constellation_labels_long) label = constellations_get_longname(i); else label = constellations_get_shortname(i); plotstuff_text_radec(pargs, ra, dec, label); } il_free(stars); } } }
int main(int argc, char *argv[]) { int argchar; char* progname = argv[0]; index_t* index; fitstable_t* table; char* indexfn = NULL; char* outfn = NULL; int i, D, N; while ((argchar = getopt (argc, argv, OPTIONS)) != -1) { switch (argchar) { case 'i': indexfn = optarg; break; case 'o': outfn = optarg; break; default: case 'h': printHelp(progname); exit(0); } } if (!(indexfn && outfn)) { printHelp(progname); exit(-1); } index = index_load(indexfn, 0, NULL); if (!index) { ERROR("Failed to open index"); exit(-1); } table = fitstable_open_for_writing(outfn); if (!table) { ERROR("Failed to open output file for writing"); exit(-1); } D = index_get_quad_dim(index); fitstable_add_write_column_array(table, fitscolumn_i32_type(), D, "quads", ""); if (fitstable_write_primary_header(table) || fitstable_write_header(table)) { ERROR("Failed to write headers"); exit(-1); } N = index_nquads(index); for (i=0; i<N; i++) { unsigned int quad[D]; quadfile_get_stars(index->quads, i, quad); if (fitstable_write_row(table, quad)) { ERROR("Failed to write quad %i", i); exit(-1); } } if (fitstable_fix_header(table)) { ERROR("Failed to fix quad header"); exit(-1); } fitstable_next_extension(table); fitstable_clear_table(table); // write star RA,Dec s. fitstable_add_write_column(table, fitscolumn_double_type(), "RA", "deg"); fitstable_add_write_column(table, fitscolumn_double_type(), "Dec", "deg"); if (fitstable_write_header(table)) { ERROR("Failed to write star header"); exit(-1); } N = index_nstars(index); for (i=0; i<N; i++) { double xyz[3]; double ra, dec; startree_get(index->starkd, i, xyz); xyzarr2radecdeg(xyz, &ra, &dec); if (fitstable_write_row(table, &ra, &dec)) { ERROR("Failed to write star %i", i); exit(-1); } } if (fitstable_fix_header(table) || fitstable_close(table)) { ERROR("Failed to fix star header and close"); exit(-1); } index_close(index); return 0; }
void test_hd_1(CuTest* tc) { hd_catalog_t* hdcat; int* invperm; int* present; int ind, i, N; double xyz[3]; double ra, dec; int strangehds[] = { 40142, 40441, 40672, 40746, 40763, 40764, 104176, 104193, 163635, 224698, 224699, 129371 }; if (!file_readable("hd.fits")) { printf("File \"hd.fits\" does not exist; test skipped.\n"); return; } hdcat = henry_draper_open("hd.fits"); CuAssertPtrNotNull(tc, hdcat); N = hdcat->kd->ndata; invperm = calloc(N, sizeof(int)); CuAssertPtrNotNull(tc, invperm); CuAssertIntEquals(tc, 0, kdtree_check(hdcat->kd)); kdtree_inverse_permutation(hdcat->kd, invperm); present = calloc(N, sizeof(int)); for (i=0; i<N; i++) { CuAssert(tc, "invperm in range", invperm[i] < N); present[invperm[i]]++; } for (i=0; i<N; i++) { CuAssertIntEquals(tc, 1, present[i]); } free(present); // Where is "HD n" ? for (i=0; i<10; i++) { bl* res; int j; ind = invperm[i]; kdtree_copy_data_double(hdcat->kd, ind, 1, xyz); xyzarr2radecdeg(xyz, &ra, &dec); printf("HD %i: RA,Dec %g, %g\n", i+1, ra, dec); res = henry_draper_get(hdcat, ra, dec, 10.0); CuAssertPtrNotNull(tc, res); for (j=0; j<bl_size(res); j++) { hd_entry_t* hd = bl_access(res, j); printf("res %i: HD %i, RA, Dec %g, %g\n", j, hd->hd, hd->ra, hd->dec); } bl_free(res); } for (i=0; i<sizeof(strangehds)/sizeof(int); i++) { ind = invperm[strangehds[i]-1]; kdtree_copy_data_double(hdcat->kd, ind, 1, xyz); xyzarr2radecdeg(xyz, &ra, &dec); printf("HD %i: RA,Dec %g, %g\n", strangehds[i], ra, dec); } free(invperm); henry_draper_close(hdcat); }