int *smf_jsatiles_data( Grp *igrp, size_t size, int *ntile, int *status ){ /* Local Variables */ AstFrame *frm = NULL; AstFrameSet *fs; JCMTState *state; const char *trsys; const char *trsys_last; dim_t *hits = NULL; dim_t *ph; dim_t iframe; double *gx = NULL; double *gy = NULL; double *p1; double *p2; double *px; double *py; double *trac1 = NULL; double *trac2 = NULL; double fov; double point1[ 2 ]; double point2[ 2 ]; double search; int *tiles = NULL; int dim[ 2 ]; int i; int ix; int iy; int lbnd[ 2 ]; int ubnd[ 2 ]; size_t ifile; smfData *data = NULL; smfHead *hdr = NULL; smfJSATiling skytiling; smf_inst_t inst = SMF__INST_NONE; smf_inst_t instrument; smf_subinst_t subinst; /* Initialise */ *ntile = 0; /* Check inherited status */ if( *status != SAI__OK ) return tiles; /* Start an AST context so that all AST objects created in this function are annulled automatically. */ astBegin; /* Loop round all the input NDFs. */ trsys_last = ""; for( ifile = 1; ifile <= size && *status == SAI__OK; ifile++ ) { /* Obtain information about the current input NDF. */ smf_open_file( igrp, ifile, "READ", SMF__NOCREATE_DATA, &data, status ); /* Get a pointer to the header. */ hdr = data->hdr; /* Get the instrument. */ if( hdr->instrument == INST__SCUBA2 ) { subinst = smf_calc_subinst( hdr, status ); if( subinst == SMF__SUBINST_850 ) { inst = SMF__INST_SCUBA_2_850; } else { inst = SMF__INST_SCUBA_2_450; } } else if( hdr->instrument == INST__ACSIS ) { inst = SMF__INST_HARP; } else if( *status == SAI__OK ) { *status = SAI__ERROR; smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); errRep( "", "No tiles are yet defined for the instrument that " "created ^FILE.", status ); } /* If this is the first file, it defines the instrument in use. */ if( ifile == 1 ) { instrument = inst; /* Get the parameters that define the layout of sky tiles for the instrument. */ smf_jsatiling( instrument, &skytiling, status ); /* Create a FrameSet describing the whole sky in which each pixel corresponds to a single tile. The current Frame is ICRS (RA,Dec) and the base Frame is grid coords in which each grid pixel corresponds to a single tile. */ smf_jsatile( 0, &skytiling, 0, NULL, &fs, NULL, lbnd, ubnd, status ); /* Allocate an image with one pixel for each tile, and fill it with zeros. */ dim[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1; dim[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1; hits = astCalloc( dim[0]*dim[1], sizeof( *hits ) ); /* Get the radius of the field of view in radians. */ fov = 0.5*(skytiling.fov*AST__DD2R)/3600.0; /* If this is not the first file, report an error if the instrument has changed... */ } else if( instrument != inst && *status == SAI__OK ) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); errRep( "", "The file ^FILE was created by a different instrument " "to the previous files.", status ); } /* Re-map the current Frame of the hits map WCS FrameSet to be the tracking system used by the current file (if it has changed). */ trsys = sc2ast_convert_system( (hdr->allState)[0].tcs_tr_sys, status ); if( *status == SAI__OK && strcmp( trsys, trsys_last ) ) { astSetC( fs, "System", trsys ); trsys_last = trsys; frm = astGetFrame( fs, AST__CURRENT ); } /* Get the radius of the search circle. */ search = fov + sqrt( hdr->instap[ 0 ]*hdr->instap[ 0 ] + hdr->instap[ 1 ]*hdr->instap[ 1 ] ); /* Ensure our work arrays are big enough to hold the current file. */ trac1 = astGrow( trac1, 4*hdr->nframes, sizeof( *trac1 ) ); trac2 = astGrow( trac2, 4*hdr->nframes, sizeof( *trac2 ) ); gx = astGrow( gx, 4*hdr->nframes, sizeof( *gx ) ); gy = astGrow( gy, 4*hdr->nframes, sizeof( *gy ) ); /* Check pointers can be used safely. */ if( *status == SAI__OK ) { /* Loop round all time slices, getting the tracking coords at the corners of a box that enclose the field of view. */ p1 = trac1; p2 = trac2; state = hdr->allState; for( iframe = 0; iframe < hdr->nframes; iframe++,state++ ) { point1[ 0 ] = state->tcs_tr_ac1; point1[ 1 ] = state->tcs_tr_ac2; for( i = 0; i < 4; i++ ) { astOffset2( frm, point1, i*AST__DPIBY2, search, point2 ); *(p1++) = point2[ 0 ]; *(p2++) = point2[ 1 ]; } } /* Convert them to grid coords in the hits map. */ astTran2( fs, 4*hdr->nframes, trac1, trac2, 0, gx, gy ); /* Loop round them all again. Update the hits map to indicate how many points fall in each tile. */ px = gx; py = gy; for( iframe = 0; iframe < 4*hdr->nframes; iframe++,px++,py++ ) { ix = (int)( *px + 0.5 ) - 1; iy = (int)( *py + 0.5 ) - 1; hits[ ix + iy*dim[ 0 ] ]++; } } /* Close the current input data file. */ smf_close_file( &data, status); data = NULL; } /* Form a list of all the tiles that receive any data. */ ph = hits; for( iy = 0; iy < dim[ 1 ]; iy++ ) { for( ix = 0; ix < dim[ 0 ]; ix++,ph++ ) { if( *ph > 0 ) { tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) ); if( *status == SAI__OK ) { tiles[ *ntile - 1 ] = smf_jsatilexy2i( ix, iy, &skytiling, status ); } } } } /* Free resources. */ hits = astFree( hits ); trac1 = astFree( trac1 ); trac2 = astFree( trac2 ); gx = astFree( gx ); gy = astFree( gy ); if( *status != SAI__OK ) { tiles = astFree( tiles ); *ntile = 0; } astEnd; return tiles; }
/* Main entry */ void smf_jsadicer( int indf, const char *base, int trim, smf_inst_t instrument, smf_jsaproj_t proj, size_t *ntile, Grp *grp, int *status ){ /* Local Variables: */ AstBox *box; AstFitsChan *fc; AstFrame *specfrm = NULL; AstFrame *tile_frm = NULL; AstFrameSet *iwcs; AstFrameSet *tfs = NULL; AstFrameSet *tile_wcs; AstMapping *ndf_map = NULL; AstMapping *p2pmap = NULL; AstMapping *specmap = NULL; AstMapping *tile_map = NULL; AstRegion *region; Grp *grpt = NULL; char *path; char dtype[ NDF__SZFTP + 1 ]; char jsatile_comment[45]; char type[ NDF__SZTYP + 1 ]; const char *dom = NULL; const char *keyword; const char *latsys = NULL; const char *lonsys = NULL; double *pd; double dlbnd[3]; double dubnd[3]; double gcen[3]; double lbnd_in[3]; double lbnd_out[3]; double ubnd_in[3]; double ubnd_out[3]; float *pf; int *created_tiles = NULL; int *tiles; int axlat; int axlon; int axspec; int bbox[ 6 ]; int i; int ifrm; int igrid; int indfo; int indfs; int indfx; int inperm[3]; int ipixel; int ishpx; int isxph; int itile; int ix; int iy; int iz; int junk; int latax = -1; int lbnd[3]; int lbnd_tile[ 3 ]; int lbndx[ NDF__MXDIM ]; int lonax = -1; int nbase; int ndim; int ndimx; int nfrm; int nsig; int ntiles; int olbnd[ 3 ]; int oubnd[ 3 ]; int outperm[ 3 ]; int place; int qual; int tile_index; int tile_lbnd[2]; int tile_ubnd[2]; int ubnd[3]; int ubnd_tile[ 3 ]; int ubndx[ NDF__MXDIM ]; int var; size_t iext; size_t size; smfJSATiling tiling; unsigned char *ipq = NULL; void *ipd = NULL; void *ipv = NULL; /* Initialise */ *ntile = 0; /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* Begin an NDF context. */ ndfBegin(); /* Note the used length of the supplied base string. If it ends with ".sdf", reduce it by 4. */ nbase = astChrLen( base ); if( !strcmp( base + nbase - 4, ".sdf" ) ) nbase -= 4; /* Allocate a buffer large enough to hold the full path for an output NDF. */ path = astMalloc( nbase + 25 ); /* Get the WCS from the NDF. */ kpg1Gtwcs( indf, &iwcs, status ); /* Note if the NDF projection is HPX or XPH. */ ishpx = astChrMatch( astGetC( iwcs, "Projection" ), "HEALPix" ); isxph = astChrMatch( astGetC( iwcs, "Projection" ), "polar HEALPix" ); /* Report an error if the NDFs projection is neither of these. */ if( !ishpx && !isxph && *status == SAI__OK ) { ndfMsg( "N", indf ); *status = SAI__ERROR; errRep( "", "The input NDF (^N) does not appear to be gridded " "on the JSA all-sky pixel grid.", status ); } /* Get the bounds of the NDF in pixel indices and the the corresponding double precision GRID bounds (reduce the size of the grid by a small amount to avoid problems with tiles that are on the edge of the valid sky regions - astMapRegion can report an error for such tiles). Also store the GRID coords of the centre. Also count the number of significant pixel axes. */ ndfBound( indf, 3, lbnd, ubnd, &ndim, status ); nsig = 0; for( i = 0; i < ndim; i++ ) { dlbnd[ i ] = 0.5 + 0.1; dubnd[ i ] = ubnd[ i ] - lbnd[ i ] + 1.5 - 0.1; gcen[ i ] = 0.5*( dlbnd[ i ] + dubnd[ i ] ); if( ubnd[ i ] > lbnd[ i ] ) nsig++; } /* Find the one-based indices of the RA, Dec and spectral axes in the current Frame of the NDF. */ axlon = 0; if( astGetI( iwcs, "IsLonAxis(1)" ) ) { axlon = 1; lonsys = astGetC( iwcs, "System(1)" ); } else if( astGetI( iwcs, "IsLonAxis(2)" ) ) { axlon = 2; lonsys = astGetC( iwcs, "System(2)" ); } else if( ndim == 3 && astGetI( iwcs, "IsLonAxis(3)" ) ) { axlon = 3; lonsys = astGetC( iwcs, "System(3)" ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: Cannot find the longitude axis in the " "input NDF.", status ); } axlat = 0; if( astGetI( iwcs, "IsLatAxis(1)" ) ) { axlat = 1; latsys = astGetC( iwcs, "System(1)" ); } else if( astGetI( iwcs, "IsLatAxis(2)" ) ) { axlat = 2; latsys = astGetC( iwcs, "System(2)" ); } else if( ndim == 3 && astGetI( iwcs, "IsLatAxis(3)" ) ) { axlat = 3; latsys = astGetC( iwcs, "System(3)" ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: Cannot find the latitude axis in the " "input NDF.", status ); } axspec = 6 - axlon - axlat; /* Report an error if the spatial axes are not ICRS RA and Dec. */ if( ( lonsys && strcmp( lonsys, "ICRS" ) ) || ( latsys && strcmp( latsys, "ICRS" ) ) ) { if( *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf ); errRep( "", "smf_jsadicer: The spatial axes in '^N' are not " "ICRS RA and Dec.", status ); } } /* Create a Box describing the region covered by the NDF pixel grid in GRID coords. */ box = astBox( astGetFrame( iwcs, AST__BASE ), 1, dlbnd, dubnd, AST__NULL, " " ); /* Map this Box into the current WCS Frame of the NDF. */ region = astMapRegion( box, iwcs, iwcs ); /* If no instrument was specified, we will determine the instrument from the contexts of the FITS extension. Copy the NDF FITS extension to a FitsChan. Annul the error if the NDF no FITS extension. */ if( instrument == SMF__INST_NONE && *status == SAI__OK ) { kpgGtfts( indf, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = NULL; } } else { fc = NULL; } /* Get the parameters of the required tiling scheme. */ smf_jsainstrument( NULL, fc, instrument, &tiling, status ); /* Get a list of the JSA tiles touched by the supplied NDF. */ tiles = smf_jsatiles_region( region, &tiling, &ntiles, status ); if( ntiles == 0 && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_jsadicer: No JSA tiles found touching supplied NDF " "(programming error).", status ); } /* Does the input NDF have a Variance component? */ ndfState( indf, "Variance", &var, status ); /* Does the input NDF have a Quality component? */ ndfState( indf, "Quality", &qual, status ); /* Decide on the data type to use: _REAL or _DOUBLE. */ ndfMtype( "_REAL,_DOUBLE", indf, indf, "Data", type, sizeof(type), dtype, sizeof(dtype), status ); /* Tell the user what is happening. */ msgBlank( status ); msgOutf( "", "Dicing %s into JSA tiles:", status, ( nsig == 2 ) ? "map" : "cube" ); /* Loop round all tiles that overlap the supplied NDF. */ for( itile = 0; itile < ntiles && *status == SAI__OK; itile++ ) { tile_index = tiles[ itile ]; /* Get the spatial pixel bounds of the current tile within the requested JSA all-sky projection. Also get the (2D) WCS FrameSet for the tile. */ smf_jsatile( tile_index, &tiling, 0, proj, NULL, &tile_wcs, NULL, tile_lbnd, tile_ubnd, status ); /* Extract the tile pixel->WCS mapping and WCS Frame. We know the indices of the required Frames because they are hard-wired in smf_jsatile. */ tile_map = astGetMapping( tile_wcs, 3, 2 ); tile_frm = astGetFrame( tile_wcs, 2 ); /* Find the indices of the grid and pixel frames in the input NDF. */ ipixel = -1; igrid = astGetI( iwcs, "Base" ); nfrm = astGetI( iwcs, "NFrame" ); for( ifrm = 0; ifrm < nfrm; ifrm++ ) { dom = astGetC( astGetFrame( iwcs, ifrm + 1 ), "Domain" ); if( astChrMatch( dom, "PIXEL" ) ) ipixel = ifrm + 1; } /* If required, extract the pixel->spectral mapping and spectral frame in the input NDF, and add it in parallel with the above tile mapping. */ if( ndim == 3 ) { astSetI( iwcs, "Base", ipixel ); tfs = atlFrameSetSplit( iwcs, "DSBSPECTRUM SPECTRUM", NULL, NULL, status ); astSetI( iwcs, "Base", igrid ); if( tfs ) { specmap = astGetMapping( tfs, AST__BASE, AST__CURRENT ); specfrm = astGetFrame( tfs, AST__CURRENT ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; ndfMsg( "N", indf ); errRep( "", "smf_jsadicer: Cannot find the spectral axis " "in '^N'.", status ); } tile_map = (AstMapping *) astCmpMap( tile_map, specmap, 0, " " ); tile_frm = (AstFrame *) astCmpFrame( tile_frm, specfrm, " " ); } /* Ensure the Epoch is inherited form the input NDF. */ astSetD( tile_frm, "Epoch", astGetD( iwcs, "Epoch" ) ); /* Currently tile axis 1 is RA, axis 2 is Dec and axis 3 (if present) is spectral. Append a PermMap that re-orders these tile WCS axes to match those of the NDF. */ outperm[ axlon - 1 ] = 1; outperm[ axlat - 1 ] = 2; outperm[ axspec - 1 ] = 3; inperm[ 0 ] = axlon; inperm[ 1 ] = axlat; inperm[ 2 ] = axspec; tile_map = (AstMapping *) astCmpMap( tile_map, astPermMap( ndim, inperm, ndim, outperm, NULL, " " ), 1, " " ); tile_map = astSimplify( tile_map ); /* Also re-order the WCS axes in the tile frame. */ astPermAxes( tile_frm, outperm ); /* We want the zero-based indicies of the input pixel axes corresponding to ra, dec and spectral. So find the indicies of the pixel axes in the supplied NDF that are most closely aligned with each WCS axis. */ atlPairAxes( iwcs, NULL, gcen, NULL, inperm, status ); if( inperm[ 0 ] == axlon ) { lonax = 0; } else if( inperm[ 1 ] == axlon ) { lonax = 1; } else { lonax = 2; } if( inperm[ 0 ] == axlat ) { latax = 0; } else if( inperm[ 1 ] == axlat ) { latax = 1; } else { latax = 2; } /* To get the mapping from pixel coords in the input NDF to pixel coords in the output NDF, we invert the above mapping so that it goes from WCS to pixel, and append it to the end of the NDF pixel->WCS mapping. */ ndf_map = astGetMapping( iwcs, ipixel, AST__CURRENT ); astInvert( tile_map ); p2pmap = (AstMapping *) astCmpMap( ndf_map, tile_map, 1, " " ); p2pmap = astSimplify( p2pmap ); astInvert( tile_map ); /* Show the bounds of the tile within the input NDF. */ msgOutiff( MSG__DEBUG, "", " tile %d has bounds (%d:%d,%d:%d) " "within the output NDF.", status, tile_index, tile_lbnd[ 0 ], tile_ubnd[ 0 ], tile_lbnd[ 1 ], tile_ubnd[ 1 ] ); /* Next job is to find the pixel bounds of the output NDF to create which will hold data for the current tile. First map the pixel bounds of the whole tile from output to input. */ lbnd_in[ 0 ] = tile_lbnd[ 0 ] - 0.5; lbnd_in[ 1 ] = tile_lbnd[ 1 ] - 0.5; lbnd_in[ 2 ] = lbnd[ 2 ] - 0.5; ubnd_in[ 0 ] = tile_ubnd[ 0 ] - 0.5; ubnd_in[ 1 ] = tile_ubnd[ 1 ] - 0.5; ubnd_in[ 2 ] = ubnd[ 2 ] - 0.5; astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 1, lbnd_out + 0, ubnd_out + 0, NULL, NULL ); astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 2, lbnd_out + 1, ubnd_out + 1, NULL, NULL ); if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 0, 3, lbnd_out + 2, ubnd_out + 2, NULL, NULL ); lbnd_tile[ 0 ] = floor( lbnd_out[ 0 ] ) + 1; lbnd_tile[ 1 ] = floor( lbnd_out[ 1 ] ) + 1; lbnd_tile[ 2 ] = floor( lbnd_out[ 2 ] ) + 1; ubnd_tile[ 0 ] = floor( ubnd_out[ 0 ] ) + 1; ubnd_tile[ 1 ] = floor( ubnd_out[ 1 ] ) + 1; ubnd_tile[ 2 ] = floor( ubnd_out[ 2 ] ) + 1; /* Show the bounds of the tile within the input NDF. */ msgOutiff( MSG__DEBUG, "", " tile %d has bounds (%d:%d,%d:%d) " "within the input NDF.", status, tile_index, lbnd_tile[ 0 ], ubnd_tile[ 0 ], lbnd_tile[ 1 ], ubnd_tile[ 1 ] ); /* If required, trim the bounds to the extent of the input NDF. */ if( trim ) { if( lbnd_tile[ 0 ] < lbnd[ 0 ] ) lbnd_tile[ 0 ] = lbnd[ 0 ]; if( lbnd_tile[ 1 ] < lbnd[ 1 ] ) lbnd_tile[ 1 ] = lbnd[ 1 ]; if( lbnd_tile[ 2 ] < lbnd[ 2 ] ) lbnd_tile[ 2 ] = lbnd[ 2 ]; if( ubnd_tile[ 0 ] > ubnd[ 0 ] ) ubnd_tile[ 0 ] = ubnd[ 0 ]; if( ubnd_tile[ 1 ] > ubnd[ 1 ] ) ubnd_tile[ 1 ] = ubnd[ 1 ]; if( ubnd_tile[ 2 ] > ubnd[ 2 ] ) ubnd_tile[ 2 ] = ubnd[ 2 ]; } /* Check there is some overlap. */ if( lbnd_tile[ 0 ] <= ubnd_tile[ 0 ] && lbnd_tile[ 1 ] <= ubnd_tile[ 1 ] && lbnd_tile[ 2 ] <= ubnd_tile[ 2 ] ){ /* Now need to check if this section of the input NDF contains any good values. We also find the bounding box of the good values (within the input pixel coordinate system). So first obtain and map the required section of the input NDF. */ ndfSect( indf, ndim, lbnd_tile, ubnd_tile, &indfs, status ); ndfMap( indfs, "Data", type, "Read", &ipd, &junk, status ); if( var ) ndfMap( indfs, "Variance", type, "Read", &ipv, &junk, status ); if( qual ) ndfMap( indfs, "Quality", "_UBYTE", "Read", (void **) &ipq, &junk, status ); /* Initialise the pixel bounds (within the input NDF) of the box holding good data values for the current tile. */ bbox[ 0 ] = INT_MAX; bbox[ 1 ] = INT_MAX; bbox[ 2 ] = INT_MAX; bbox[ 3 ] = -INT_MAX; bbox[ 4 ] = -INT_MAX; bbox[ 5 ] = -INT_MAX; /* Loop round all pixels in the section. */ if( *status == SAI__OK ) { if( !strcmp( type, "_REAL" ) ) { pf = (float *) ipd; for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) { for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) { for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) { if( *(pf++) != VAL__BADR ) { if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix; if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy; if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz; if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix; if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy; if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz; } } } } } else { pd = (double *) ipd; for( iz = lbnd_tile[ 2 ]; iz <= ubnd_tile[ 2 ]; iz++ ) { for( iy = lbnd_tile[ 1 ]; iy <= ubnd_tile[ 1 ]; iy++ ) { for( ix = lbnd_tile[ 0 ]; ix <= ubnd_tile[ 0 ]; ix++ ) { if( *(pd++) != VAL__BADD ) { if( ix < bbox[ 0 ] ) bbox[ 0 ] = ix; if( iy < bbox[ 1 ] ) bbox[ 1 ] = iy; if( iz < bbox[ 2 ] ) bbox[ 2 ] = iz; if( ix > bbox[ 3 ] ) bbox[ 3 ] = ix; if( iy > bbox[ 4 ] ) bbox[ 4 ] = iy; if( iz > bbox[ 5 ] ) bbox[ 5 ] = iz; } } } } } /* Skip empty tiles. */ if( bbox[ 0 ] != INT_MAX ) { msgOutf( "", " tile %d", status, tile_index ); /* If required, trim the bounds to the edges of the bounding box. */ if( trim >= 2 ) { olbnd[ 0 ] = bbox[ 0 ]; olbnd[ 1 ] = bbox[ 1 ]; olbnd[ 2 ] = bbox[ 2 ]; oubnd[ 0 ] = bbox[ 3 ]; oubnd[ 1 ] = bbox[ 4 ]; oubnd[ 2 ] = bbox[ 5 ]; } else { olbnd[ 0 ] = lbnd_tile[ 0 ]; olbnd[ 1 ] = lbnd_tile[ 1 ]; olbnd[ 2 ] = lbnd_tile[ 2 ]; oubnd[ 0 ] = ubnd_tile[ 0 ]; oubnd[ 1 ] = ubnd_tile[ 1 ]; oubnd[ 2 ] = ubnd_tile[ 2 ]; } /* Modify these pixel bounds so that they refer to the output NDF. */ lbnd_in[ 0 ] = olbnd[ 0 ] - 0.5; lbnd_in[ 1 ] = olbnd[ 1 ] - 0.5; lbnd_in[ 2 ] = olbnd[ 2 ] - 0.5; ubnd_in[ 0 ] = oubnd[ 0 ] - 0.5; ubnd_in[ 1 ] = oubnd[ 1 ] - 0.5; ubnd_in[ 2 ] = oubnd[ 2 ] - 0.5; astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 1, lbnd_out + 0, ubnd_out + 0, NULL, NULL ); astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 2, lbnd_out + 1, ubnd_out + 1, NULL, NULL ); if( ndim == 3 ) astMapBox( p2pmap, lbnd_in, ubnd_in, 1, 3, lbnd_out + 2, ubnd_out + 2, NULL, NULL ); olbnd[ 0 ] = floor( lbnd_out[ 0 ] ) + 1; olbnd[ 1 ] = floor( lbnd_out[ 1 ] ) + 1; olbnd[ 2 ] = floor( lbnd_out[ 2 ] ) + 1; oubnd[ 0 ] = floor( ubnd_out[ 0 ] ) + 1; oubnd[ 1 ] = floor( ubnd_out[ 1 ] ) + 1; oubnd[ 2 ] = floor( ubnd_out[ 2 ] ) + 1; /* Get the full path to the output NDF for the current tile, and create an NDF placeholder for it. */ sprintf( path, "%.*s_%d", nbase, base, tile_index ); ndfPlace( NULL, path, &place, status ); /* Create a new output NDF by copying the meta-data from the input NDF section. */ ndfScopy( indfs, "Units", &place, &indfo, status ); /* Set the pixel bounds of the output NDF to the values found above and copy the input data for the current tile into it. */ smf1_jsadicer( indfo, olbnd, oubnd, tile_map, tile_frm, p2pmap, ipd, ipv, ipq, status ); /* Add the name of this output NDF to the group holding the names of the output NDFs that have actually been created. */ if( grp ) grpPut1( grp, path, 0, status ); /* Add a TILENUM header to the output FITS extension. */ kpgGtfts( indfo, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = astFitsChan( NULL, NULL, " " ); /* If the last card is "END", remove it. */ } else { astSetI( fc, "Card", astGetI( fc, "NCARD" ) ); keyword = astGetC( fc, "CardName" ); if( keyword && !strcmp( keyword, "END" ) ) astDelFits( fc ); } one_snprintf(jsatile_comment, 45, "JSA all-sky tile index (Nside=%i)", status, tiling.ntpf); atlPtfti( fc, "TILENUM", tile_index, jsatile_comment, status ); kpgPtfts( indfo, fc, status ); fc = astAnnul( fc ); /* Now store an STC-S polygon that describes the shortest boundary enclosing the good data in the output NDF, and store it as an NDF extension. */ kpgPutOutline( indfo, 0.5, 1, status ); /* We now reshape any extension NDFs contained within the output NDF to have the same spatial bounds as the main NDF (but only for extension NDFs that originally have the same spatial bounds as the supplied NDF). Get a group containing paths to all extension NDFs in the output NDF. */ ndgMoreg( indfo, &grpt, &size, status ); /* Loop round each output extension NDF. */ for( iext = 1; iext <= size && *status == SAI__OK; iext++ ) { ndgNdfas( grpt, iext, "Update", &indfx, status ); /* Get its bounds. */ ndfBound( indfx, NDF__MXDIM, lbndx, ubndx, &ndimx, status ); /* See if this extension NDF has the same bounds on the spatial axes as the supplied NDF. */ if( ndimx > 1 && lbndx[ lonax ] == lbnd[ lonax ] && lbndx[ latax ] == lbnd[ latax ] && ubndx[ lonax ] == ubnd[ lonax ] && ubndx[ latax ] == ubnd[ latax ] ) { /* If so, change the bounds of the output extension NDF so that they are the same as the main NDF on the spatial axes, and map the original contents of the NDF onto the new pixel grid. */ smf1_jsadicer( indfx, olbnd, oubnd, tile_map, tile_frm, p2pmap, NULL, NULL, NULL, status ); } /* Annul the extension NDF identifier. */ ndfAnnul( &indfx, status ); } /* Free resources associated with the current tile. */ grpDelet( &grpt, status ); ndfAnnul( &indfo, status ); /* Issue warnings about empty tiles. */ } else { msgOutiff( MSG__VERB, "", " tile %d is empty and so will not be " "created", status, tile_index ); } } /* Free the section of the input NDF. */ ndfAnnul( &indfs, status ); /* Append the index of this tile in the list of tiles to be created. */ created_tiles = astGrow( created_tiles, ++(*ntile), sizeof( *created_tiles ) ); if( *status == SAI__OK ) created_tiles[ *ntile - 1 ] = tile_index; } else { msgOutiff( MSG__DEBUG, "", " Tile %d does not overlap the input " "NDF after trimming.", status, tile_index ); } } msgBlank( status ); /* Write the indicies of the created tiles out to a parameter. */ if( *ntile ) parPut1i( "JSATILELIST", *ntile, created_tiles, status ); /* Free resources. */ created_tiles = astFree( created_tiles ); tiles = astFree( tiles ); path = astFree( path ); /* End the NDF context. */ ndfEnd( status ); /* End the AST context. */ astEnd; }
int *smf_jsatiles_region( AstRegion *region, smfJSATiling *skytiling, int *ntile, int *status ){ /* Local Variables */ AstFrameSet *fs; AstKeyMap *km; AstRegion *region2; AstRegion *space_region; AstRegion *tregion; AstSkyFrame *skyframe; char text[ 200 ]; const char *key; double *mesh = NULL; double *xmesh; double *ymesh; int *tiles = NULL; int axes[ 2 ]; int i; int ineb; int itile2; int itile; int ix; int iy; int key_index; int lbnd[ 2 ]; int mapsize; int npoint; int old_sv; int overlap; int ubnd[ 2 ]; int value; int xoff[ 4 ] = { -1, 0, 1, 0 }; int xt; int yoff[ 4 ] = { 0, 1, 0, -1 }; int yt; /* Initialise */ *ntile = 0; /* Check inherited status */ if( *status != SAI__OK ) return tiles; /* Start an AST context so that all AST objects created in this function are annulled automatically. */ astBegin; /* Identify the celestial axes in the Region. */ atlFindSky( (AstFrame *) region, &skyframe, axes + 1, axes, status ); /* Report an error if no celestial axes were found. */ if( !skyframe && *status == SAI__OK ) { space_region = NULL; *status = SAI__ERROR; errRep( "", "The current WCS Frame in the supplied Region or " "NDF does not include celestial longitude and latitude axes.", status ); /* Otherwise, if the Region itself is 2-dimensional, it does not contain any other axes, so just use it as is. */ } else if( astGetI( region, "Naxes" ) == 2 ) { space_region = astClone( region ); /* Otherwise, create a new Region by picking the celestial axes from the supplied Region. Report an error if a Region cannot be created in this way. */ } else { space_region = astPickAxes( region, 2, axes, NULL ); if( !astIsARegion( space_region ) && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "The celestial longitude and latitude axes in the " "supplied Region or NDF are not independent of the other " "axes.", status ); } } /* Create a FrameSet describing the whole sky in which each pixel corresponds to a single tile in SMF__JSA_HPX projection. The current Frame is ICRS (RA,Dec) and the base Frame is grid coords in which each grid pixel corresponds to a single tile. */ smf_jsatile( -1, skytiling, 0, SMF__JSA_HPX, NULL, &fs, NULL, lbnd, ubnd, status ); /* Map the Region using the FrameSet obtained above so that the new Region describes offsets in tiles from the lower left tile. If "space_region" is a Polygon, ensure that the SimpVertices attribute is set so that the simplify method will take non-linearities into account (such as the region being split by the RA=12h meridian). */ astInvert( fs ); fs = astConvert( space_region, fs, "SKY" ); if( !fs && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "Cannot convert the supplied Region to ICRS.", status ); goto L999; } old_sv = -999; if( astIsAPolygon( space_region ) ){ if( astTest( space_region, "SimpVertices" ) ) { old_sv = astGetI( space_region, "SimpVertices" ); } astSetI( space_region, "SimpVertices", 0 ); } region2 = astMapRegion( space_region, fs, fs ); if( astIsAPolygon( space_region ) ){ if( old_sv == -999 ) { astClear( space_region, "SimpVertices" ); } else { astSetI( space_region, "SimpVertices", old_sv ); } } /* Get a mesh of all-sky "grid" positions (actually tile X and Y indices) covering the region. Since the mesh positions are limited in number and placed arbitrarily within the Region, the mesh will identify some, but potentially not all, of the tiles that overlap the Region. */ astGetRegionMesh( region2, 0, 0, 2, &npoint, NULL ); mesh = astMalloc( 2*npoint*sizeof( *mesh ) ); astGetRegionMesh( region2, 0, npoint, 2, &npoint, mesh ); /* Find the index of the tile containing each mesh position, and store them in a KeyMap using the tile index as the key and "1" (indicating the tile overlaps the region) as the value. The KeyMap is sorted by age of entry. Neighbouring tiles will be added to this KeyMap later. If an entry has a value of zero, it means the tile does not overlap the supplied Region. If the value is positive, it means the tile does overlap the supplied Region. If the value is negative, it means the tile has not yet been tested to see if it overlaps the supplied Region. */ km = astKeyMap( "SortBy=KeyAgeDown" ); xmesh = mesh; ymesh = mesh + npoint; for( i = 0; i < npoint && *status == SAI__OK; i++ ) { ix = (int)( *(xmesh++) + 0.5 ) - 1; iy = (int)( *(ymesh++) + 0.5 ) - 1; itile = smf_jsatilexy2i( ix, iy, skytiling, status ); if (itile != VAL__BADI) { sprintf( text, "%d", itile ); astMapPut0I( km, text, 1, NULL ); } } /* Starting with the oldest entry in the KeyMap, loop round checking all entries, in the order they were added, until all have been checked. Checking an entry may cause further entries to be added to the end of the KeyMap. */ key_index = 0; mapsize = astMapSize( km ); while( key_index < mapsize && *status == SAI__OK ) { key = astMapKey( km, key_index++ ); /* Convert the key string to an integer tile index. */ itile = atoi( key ); /* Get the integer value associated with the tile. */ astMapGet0I( km, key, &value ); /* If the tile associated with the current KeyMap entry has not yet been tested for overlap with the requested Region (as shown by the entry value being -1), test it now. */ if( value == -1 ) { /* Get a Region covering the tile. */ smf_jsatile( itile, skytiling, 0, SMF__JSA_HPX, NULL, NULL, &tregion, lbnd, ubnd, status ); /* See if this Region overlaps the user supplied region. Set the value of the KeyMap entry to +1 or 0 accordingly. */ overlap = astOverlap( tregion, space_region ); if( overlap == 0 ) { if( *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "Cannot align supplied Region with the sky " "tile coordinate system (programming error).", status ); } } else if( overlap == 1 || overlap == 6 ) { value = 0; } else { value = 1; } astMapPut0I( km, key, value, NULL ); } /* Skip the current KeyMap entry if the corresponding tile does not overlap the requested Region (as shown by the entry value being zero). */ if( value == 1 ) { /* The current tile overlaps the supplied Region, so add the tile index to the returned list of tile indices. */ tiles = astGrow( tiles, ++(*ntile), sizeof( *tiles ) ); if( *status == SAI__OK ) { tiles[ *ntile - 1 ] = itile; /* Add the adjoining tiles to the end of the KeyMap so that they will be tested in their turn, giving them a value of -1 to indicate that they have not yet been tested to see if they overlap the supplied Region. Ignore adjoining tiles that are already in the keyMap. */ smf_jsatilei2xy( itile, skytiling, &xt, &yt, NULL, status ); for( ineb = 0; ineb < 4; ineb++ ) { itile2 = smf_jsatilexy2i( xt + xoff[ ineb ], yt + yoff[ ineb ], skytiling, status ); if( itile2 != VAL__BADI ) { sprintf( text, "%d", itile2 ); if( !astMapHasKey( km, text ) ) { astMapPut0I( km, text, -1, NULL ); mapsize++; } } } } } } /* Arrive here if an error occurs. */ L999:; /* Free resources. */ mesh = astFree( mesh ); if( *status != SAI__OK ) { tiles = astFree( tiles ); *ntile = 0; } astEnd; return tiles; }
void smurf_jsatileinfo( int *status ) { /* Local Variables */ AstCmpRegion *overlap; AstFitsChan *fc; AstFrameSet *fs; AstObject *obj; AstRegion *region; AstRegion *target; HDSLoc *cloc = NULL; HDSLoc *xloc = NULL; char *jcmt_tiles; char *tilendf = NULL; char text[ 200 ]; double dec[ 8 ]; double dist; double dlbnd[ 2 ]; double dubnd[ 2 ]; double gx[ 8 ]; double gy[ 8 ]; double maxdist; double norm_radec[2]; double point1[ 2 ]; double point2[ 2 ]; double ra[ 8 ]; double ra0; double dec0; int *ipntr; int axes[ 2 ]; int create; int dirlen; int el; int exists; int flag; int i; int indf1; int indf2; int indf3; int itile; int iv; int jtile; int lbnd[ 2 ]; int local_origin; int nc; int place; int tlbnd[ 2 ]; int tubnd[ 2 ]; int ubnd[ 2 ]; smf_jsaproj_t proj; int xt; int yt; smfJSATiling skytiling; void *pntr; /* Check inherited status */ if( *status != SAI__OK ) return; /* Start a new AST context. */ astBegin; /* Get the instrument to use abnd get the parameters describing the layout of its JSA tiles. */ smf_jsainstrument( "INSTRUMENT", NULL, SMF__INST_NONE, &skytiling, status ); /* Return the maximum tile index. */ parPut0i( "MAXTILE", skytiling.ntiles - 1, status ); /* Abort if an error has occurred. */ if( *status != SAI__OK ) goto L999; /* Decide what sort of projection to use. */ parChoic( "PROJ", "HPX", "HPX,HPX12,XPHN,XPHS", 1, text, sizeof(text), status ); proj = smf_jsaproj_fromstr( text, 1, status ); /* If required, create an all-sky NDF in which each pixel covers the area of a single tile, and holds the integer tile index. The NDF has an initial size of 1x1 pixels, but is expanded later to the required size. */ lbnd[ 0 ] = ubnd[ 0 ] = lbnd[ 1 ] = ubnd[ 1 ] = 1; ndfCreat( "ALLSKY", "_INTEGER", 2, lbnd, ubnd, &indf3, status ); /* If a null (!) value was supplied for parameter ALLSKY, annull the error and pass on. */ if( *status == PAR__NULL ) { errAnnul( status ); /* Otherwise, create a FrameSet describing the whole sky in which each pixel corresponds to a single tile. */ } else { smf_jsatile( -1, &skytiling, 0, proj, NULL, &fs, NULL, lbnd, ubnd, status ); /* Change the bounds of the output NDF. */ ndfSbnd( 2, lbnd, ubnd, indf3, status ); /* Store the FrameSet in the NDF. */ ndfPtwcs( fs, indf3, status ); /* Map the data array. */ ndfMap( indf3, "Data", "_INTEGER", "WRITE/BAD", (void **) &ipntr, &el, status ); /* Create all-sky map using XPH projection. */ if( *status == SAI__OK ) { /* Loop round every tile index. */ for( jtile = 0; jtile < skytiling.ntiles; jtile++ ) { /* Get the zero-based (x,y) indices of the tile within an HPX projection. This flips the bottom left half-facet up to the top right. */ smf_jsatilei2xy( jtile, &skytiling, &xt, &yt, NULL, status ); /* Convert these HPX indices to the corresponding indices within the required projection. Note, the lower left facet is split by the above call to smf_jsatilei2xy tile (i.e. (xt,yt) indices are *not* in the "raw" mode). For instance, (0,0) is not a valid tile. */ smf_jsatilexyconv( &skytiling, proj, xt, yt, 0, &xt, &yt, status ); /* Get the vector index of the corresponding element of the all-sky NDF. */ if( proj == SMF__JSA_HPX || proj == SMF__JSA_HPX12 ) { iv = xt + 5*skytiling.ntpf*yt; } else { iv = xt + 4*skytiling.ntpf*yt; } /* Report an error if this element has already been assigned a tile index. Otherwise, store the tile index. */ if( ipntr[ iv ] == VAL__BADI ) { ipntr[ iv ] = jtile; } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "%s projection assigns multiple tiles to " "pixel (%d,%d).", status, text, xt, yt ); break; } } } /* Store NDF title. */ sprintf( text, "JSA tile indices for %s data", skytiling.name ); ndfCput( text, indf3, "TITLE", status ); /* Store the instrument as a component in the SMURF extension. */ ndfXnew( indf3, "SMURF", "INSTRUMENT", 0, 0, &xloc, status ); ndfXpt0c( skytiling.name, indf3, "SMURF", "INSTRUMENT", status ); datAnnul( &xloc, status ); /* Close the NDF. */ ndfAnnul( &indf3, status ); } /* Abort if an error has occurred. */ if( *status != SAI__OK ) goto L999; /* Get the zero-based index of the required tile. If a null value is supplied, annull the error and skip to the end. */ parGdr0i( "ITILE", 0, 0, skytiling.ntiles - 1, 0, &itile, status ); if( *status == PAR__NULL ) { errAnnul( status ); goto L999; } /* See if the pixel origin is to be at the centre of the tile. */ parGet0l( "LOCAL", &local_origin, status ); /* Display the tile number. */ msgBlank( status ); msgSeti( "ITILE", itile ); msgSeti( "MAXTILE", skytiling.ntiles - 1); msgOut( " ", " Tile ^ITILE (from 0 to ^MAXTILE):", status ); /* Get the FITS header, FrameSet and Region defining the tile, and the tile bounds in pixel indices. */ smf_jsatile( itile, &skytiling, local_origin, proj, &fc, &fs, ®ion, lbnd, ubnd, status ); /* Write the FITS headers out to a file, annulling the error if the header is not required. */ if( *status == SAI__OK ) { atlDumpFits( "HEADER", fc, status ); if( *status == PAR__NULL ) errAnnul( status ); } /* If required, write the Region out to a text file. */ if( *status == SAI__OK ) { atlCreat( "REGION", (AstObject *) region, status ); if( *status == PAR__NULL ) errAnnul( status ); } /* Store the lower and upper pixel bounds of the tile. */ parPut1i( "LBND", 2, lbnd, status ); parPut1i( "UBND", 2, ubnd, status ); /* Display pixel bounds on the screen. */ msgSeti( "XL", lbnd[ 0 ] ); msgSeti( "XU", ubnd[ 0 ] ); msgSeti( "YL", lbnd[ 1 ] ); msgSeti( "YU", ubnd[ 1 ] ); msgOut( " ", " Pixel bounds: (^XL:^XU,^YL:^YU)", status ); /* Get the RA,Dec at the tile centre. */ if( astTest( fs, "SkyRef" ) ) { ra0 = astGetD( fs, "SkyRef(1)" ); dec0 = astGetD( fs, "SkyRef(2)" ); /* Format the central RA and Dec. and display. Call astNorm on the coordinates provided that the frame set has the correct number of axes (which it should as it comes from smf_jsatile). */ norm_radec[0] = ra0; norm_radec[1] = dec0; if (astGetI(fs, "Naxes") == 2) astNorm(fs, norm_radec); msgSetc( "RACEN", astFormat( fs, 1, norm_radec[ 0 ] )); msgSetc( "DECCEN", astFormat( fs, 2, norm_radec[ 1 ] )); msgOut( " ", " Centre (ICRS): RA=^RACEN DEC=^DECCEN", status ); /* Transform a collection of points on the edge of the region (corners and side mid-points) from GRID coords to RA,Dec. */ point1[ 0 ] = 0.5; point1[ 1 ] = 0.5; point2[ 0 ] = ubnd[ 0 ] - lbnd[ 0 ] + 1; point2[ 1 ] = ubnd[ 1 ] - lbnd[ 1 ] + 1; gx[ 0 ] = point1[ 0 ]; /* Bottom left */ gy[ 0 ] = point1[ 1 ]; gx[ 1 ] = point1[ 0 ]; /* Centre left */ gy[ 1 ] = gy[ 0 ]; gx[ 2 ] = point1[ 0 ]; /* Top left */ gy[ 2 ] = point2[ 1 ]; gx[ 3 ] = gx[ 0 ]; /* Top centre */ gy[ 3 ] = point2[ 1 ]; gx[ 4 ] = point2[ 0 ]; /* Top right */ gy[ 4 ] = point2[ 1 ]; gx[ 5 ] = point2[ 0 ]; /* Centre right */ gy[ 5 ] = gy[ 0 ]; gx[ 6 ] = point2[ 0 ]; /* Bottom right */ gy[ 6 ] = point1[ 1 ]; gx[ 7 ] = gx[ 0 ]; /* Bottom centre */ gy[ 7 ] = point1[ 1 ]; astTran2( fs, 8, gx, gy, 1, ra, dec ); /* Find the arc-distance from the centre to the furthest point from the centre. */ point1[ 0 ] = ra0; point1[ 1 ] = dec0; maxdist = -1.0; for( i = 1; i < 8; i++ ) { if( ra[ i ] != AST__BAD && dec[ i ] != AST__BAD ) { point2[ 0 ] = ra[ i ]; point2[ 1 ] = dec[ i ]; dist = astDistance( fs, point1, point2 ); if( dist > maxdist ) maxdist = dist; } } /* Convert from radius to diameter. */ maxdist *= 2.0; /* Format this size as a dec value (i.e. arc-distance) and display it. */ if( maxdist > 0.0 ) { msgSetc( "SIZE", astFormat( fs, 2, maxdist ) ); msgOut( " ", " Size: ^SIZE", status ); } else { maxdist = AST__BAD; msgOut( " ", " Size: <unknown>", status ); } /* If a discontinuity passes through the tile, the centre and size may be unknown. */ } else { ra0 = AST__BAD; dec0 = AST__BAD; maxdist = AST__BAD; msgOut( " ", " Centre (ICRS): RA=<unknown> DEC=<unknown>", status ); msgOut( " ", " Size: <unknown>", status ); } /* Write the tile centre ra and dec in radians to the output parameters. */ parPut0d( "RACEN", norm_radec[ 0 ], status ); parPut0d( "DECCEN", norm_radec[ 1 ], status ); /* Write the size to the output parameter as radians. */ parPut0d( "SIZE", maxdist, status ); /* Get the translation of the environment variable JSA_TILE_DIR. */ jcmt_tiles = getenv( "JSA_TILE_DIR" ); /* Initialise the path to the tile's NDF to hold the root directory. Use the current working directory if JSA_TILE_DIR is undefined. */ if( jcmt_tiles ) { nc = 0; tilendf = astAppendString( tilendf, &nc, jcmt_tiles ); } else { nc = 512; jcmt_tiles = astMalloc( nc ); while( !getcwd( jcmt_tiles, nc ) ) { nc *= 2; jcmt_tiles = astRealloc( jcmt_tiles, nc ); } nc = 0; tilendf = astAppendString( tilendf, &nc, jcmt_tiles ); jcmt_tiles = astFree( jcmt_tiles ); } /* Complete the path to the tile's NDF. */ tilendf = astAppendString( tilendf, &nc, "/" ); tilendf = astAppendString( tilendf, &nc, skytiling.subdir ); dirlen = nc; sprintf( text, "/tile_%d.sdf", itile ); tilendf = astAppendString( tilendf, &nc, text ); /* Write it to the output parameter. */ parPut0c( "TILENDF", tilendf, status ); /* See if the NDF exists, and store the flag in the output parameter. */ exists = access( tilendf, F_OK ) ? 0 : 1; parPut0l( "EXISTS", exists, status ); /* If the NDF does not exist, create it if required. */ parGet0l( "CREATE", &create, status ); if( !exists && create && *status == SAI__OK ) { /* Write the NDF info to the screen. */ msgSetc( "NDF", tilendf ); msgOutif( MSG__NORM, " ", " NDF: ^NDF (created)", status ); /* Temporarily terminate the NDF path at the end of the subdirectory. */ tilendf[ dirlen ] = 0; /* Create the required directory (does nothing if the directory already exists). It is given read/write/search permissions for owner and group, and read/search permissions for others. */ (void) mkdir( tilendf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ); /* Replace the character temporarily overwritten above. */ tilendf[ dirlen ] = '/'; /* Now create the tile's NDF. */ ndfPlace( NULL, tilendf, &place, status ); ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf1, status ); /* Fill its data array with zeros. */ ndfMap( indf1, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el, status ); /* Store the WCS FrameSet. */ ndfPtwcs( fs, indf1, status ); /* If the instrument jsatiles.have variance, fill the variance array with zeros. */ if( skytiling.var ) { ndfMap( indf1, "Variance", skytiling.type, "WRITE/ZERO", &pntr, &el, status ); } /* Create a SMURF extension. */ ndfXnew( indf1, SMURF__EXTNAME, SMURF__EXTTYPE, 0, NULL, &xloc, status ); /* Store the tile number and instrument name in the extension. */ datNew0I( xloc, "TILE", status ); datFind( xloc, "TILE", &cloc, status ); datPut0I( cloc, itile, status ); datAnnul( &cloc, status ); datNew0C( xloc, "INSTRUMENT", strlen( skytiling.name ), status ); datFind( xloc, "INSTRUMENT", &cloc, status ); datPut0C( cloc, skytiling.name, status ); datAnnul( &cloc, status ); /* Create a weights NDF within the SMURF extension, and fill its data array with zeros. */ ndfPlace( xloc, "WEIGHTS", &place, status ); ndfNew( skytiling.type, 2, lbnd, ubnd, &place, &indf2, status ); ndfMap( indf2, "Data", skytiling.type, "WRITE/ZERO", &pntr, &el, status ); ndfPtwcs( fs, indf2, status ); ndfAnnul( &indf2, status ); /* Annul the extension locator and the main NDF identifier. */ datAnnul( &xloc, status ); ndfAnnul( &indf1, status ); /* Write the NDF info to the screen. */ } else { msgSetc( "NDF", tilendf ); msgSetc( "E", exists ? "exists" : "does not exist" ); msgOut( " ", " NDF: ^NDF (^E)", status ); } /* Initialise TBND and TLBND to indicate no overlap. */ tlbnd[ 0 ] = 1; tlbnd[ 1 ] = 1; tubnd[ 0 ] = 0; tubnd[ 1 ] = 0; /* Attempt to to get an AST Region (assumed to be in some 2D sky coordinate system) using parameter "TARGET". */ if( *status == SAI__OK ) { kpg1Gtobj( "TARGET", "Region", (void (*)( void )) F77_EXTERNAL_NAME(ast_isaregion), &obj, status ); /* Annul the error if none was obtained. */ if( *status == PAR__NULL ) { errAnnul( status ); /* Otherwise, use the supplied object. */ } else { target = (AstRegion *) obj; /* If the target Region is 3-dimensional, remove the third axis, which is assumed to be a spectral axis. */ if( astGetI( target, "Naxes" ) == 3 ) { axes[ 0 ] = 1; axes[ 1 ] = 2; target = astPickAxes( target, 2, axes, NULL ); } /* See if there is any overlap between the target and the tile. */ overlap = NULL; flag = astOverlap( region, target ); if( flag == 0 ) { msgOut( "", " Cannot convert between the coordinate system of the " "supplied target and the tile.", status ); } else if( flag == 1 || flag == 6 ) { msgOut( "", " There is no overlap between the target and the tile.", status ); } else if( flag == 2 ) { msgOut( "", " The tile is contained within the target.", status ); tlbnd[ 0 ] = lbnd[ 0 ]; tlbnd[ 1 ] = lbnd[ 1 ]; tubnd[ 0 ] = ubnd[ 0 ]; tubnd[ 1 ] = ubnd[ 1 ]; } else if( flag == 3 ) { overlap = astCmpRegion( region, target, AST__AND, " " ); } else if( flag == 4 ) { overlap = astCmpRegion( region, target, AST__AND, " " ); } else if( flag == 5 ) { msgOut( "", " The target and tile are identical.", status ); tlbnd[ 0 ] = lbnd[ 0 ]; tlbnd[ 1 ] = lbnd[ 1 ]; tubnd[ 0 ] = ubnd[ 0 ]; tubnd[ 1 ] = ubnd[ 1 ]; } else if( *status == SAI__OK ) { *status = SAI__OK; errRepf( "", "Unexpected value %d returned by astOverlap " "(programming error).", status, flag ); } /* If a region containing the intersection of the tile and target was created above, map it into the grid coordinate system of the tile. */ if( overlap ) { overlap = astMapRegion( overlap, astGetMapping( fs, AST__CURRENT, AST__BASE ), astGetFrame( fs, AST__BASE ) ); /* Get its GRID bounds. */ astGetRegionBounds( overlap, dlbnd, dubnd ); /* Convert to integer. */ tlbnd[ 0 ] = ceil( dlbnd[ 0 ] - 0.5 ); tlbnd[ 1 ] = ceil( dlbnd[ 1 ] - 0.5 ); tubnd[ 0 ] = ceil( dubnd[ 0 ] - 0.5 ); tubnd[ 1 ] = ceil( dubnd[ 1 ] - 0.5 ); /* Convert to PIXEL indices within the tile. */ tlbnd[ 0 ] += lbnd[ 0 ] - 1; tlbnd[ 1 ] += lbnd[ 1 ] - 1; tubnd[ 0 ] += lbnd[ 0 ] - 1; tubnd[ 1 ] += lbnd[ 1 ] - 1; msgOutf( "", " The target overlaps section (%d:%d,%d:%d).", status, tlbnd[ 0 ], tubnd[ 0 ], tlbnd[ 1 ], tubnd[ 1 ] ); } } } /* Store the pixel index bounds of the tiles overlap with the target. */ parPut1i( "TLBND", 2, tlbnd, status ); parPut1i( "TUBND", 2, tubnd, status ); /* Arrive here if an error occurs. */ L999:; /* Free resources. */ tilendf = astFree( tilendf ); /* End the AST context. */ astEnd; /* Issue a status indication.*/ msgBlank( status ); if( *status == SAI__OK ) { msgOutif( MSG__VERB, "", "JSATILEINFO succeeded.", status); } else { msgOutif( MSG__VERB, "", "JSATILEINFO failed.", status); } }
void smf_getrefwcs( const char *param, Grp *igrp, AstFrameSet **specwcs, AstFrameSet **spacewcs, int *isjsa, int *status ){ /* Local Variables */ AstFrame *frm = NULL; AstFrameSet *refwcs = NULL; /* The WCS FrameSet from the reference NDF */ AstRegion *circle; char text[ 255 ]; /* Parameter value */ int *tiles; int i; int jsatiles; int lbnd[2]; /* Lower pixel index bounds of mid tile */ int ntile; int perm[ 2 ]; int refndf; /* NDF identifier for the refence NDF */ int ubnd[2]; /* Upper pixel index bounds of mid tile */ size_t code; smfData *data = NULL; /* Structure describing 1st input file */ smfJSATiling skytiling; smf_inst_t inst = SMF__INST_NONE; smf_jsaproj_t proj; /* Specific JSA projection to use */ smf_subinst_t subinst; /* Initialise the returned values. */ *specwcs = NULL; *spacewcs = NULL; *isjsa = 0; /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* If the JSAILES parameter is TRUE, then we use the JSA all-sky pixel grid regardless of the setting of REF. */ parGet0l( "JSATILES", &jsatiles, status ); if( jsatiles ) { strcpy( text, "JSA" ); *isjsa = 1; /* Otherwise, first get the parameter value as a string. Use subpar to avoid problem caused by interpretion of the text within the parameter system. */ } else { subParFindpar( param, &code, status ); subParGetname( code, text, sizeof(text), status ); } /* If no value was supplied, annul the error and do nothing more. */ if( *status == PAR__NULL ) { errAnnul( status ); /* If it is "JSA", or one of the JSA projection codes, we return WCS that describes one of the the JSA all-sky pixel grids. */ } else if( *status == SAI__OK ) { proj = smf_jsaproj_fromstr( text, 0, status ); if( astChrMatch( text, "JSA" ) || proj != SMF__JSA_NULL ) { *isjsa = 1; /* Report an error if the instrument cannot be determined. */ if( !igrp ) { *status = SAI__ERROR; errRep( "", "smf_getrefwcs: Cannot use the JSA all-sky pixel " "grid since no input group has been supplied (possibly " "programming error).", status ); } else { /* Open the first input file. */ smf_open_file( NULL, igrp, 1, "READ", SMF__NOCREATE_DATA, &data, status ); if( *status == SAI__OK ) { /* Get the instrument. */ if( data->hdr->instrument == INST__SCUBA2 ) { subinst = smf_calc_subinst( data->hdr, status ); if( subinst == SMF__SUBINST_850 ) { inst = SMF__INST_SCUBA_2_850; } else { inst = SMF__INST_SCUBA_2_450; } } else if( data->hdr->instrument == INST__ACSIS ) { inst = SMF__INST_ACSIS; } else if( *status == SAI__OK ) { *status = SAI__ERROR; if( data->file ) { smf_smfFile_msg( data->file, "FILE", 1, "one or more of " "the input data files" ); } else { msgSetc( "FILE", "one or more of the input data files" ); } errRep( "", "No tiles are yet defined for the instrument that " "created ^FILE.", status ); } /* Get the parameters that define the layout of sky tiles for the instrument. */ smf_jsatiling( inst, &skytiling, status ); /* For "JSA" - choose the best projection. */ if( astChrMatch( text, "JSA" ) ) { /* Use the FITS headers in the first raw data file to create an AST Circle describing the approximate area of the observation within the tracking system. */ circle = smf_mapregion_approx( igrp, status ); /* Convert the circle to ICRS (as used by the JSA all-sky grid). */ astSetC( circle, "System", "ICRS" ); /* Get a list of the tiles that touch this circle. */ tiles = smf_jsatiles_region( circle, &skytiling, &ntile, status ); /* Choose the best projection (i.e. the projection that puts the circle furthest away from any singularities). */ proj = smf_jsaproj( ntile, tiles, &skytiling, status); /* Free resources. */ tiles = astFree( tiles ); circle = astAnnul( circle ); /* If a good projection was specified, use it. Otherwise report an error. */ } else if( proj == SMF__JSA_NULL && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "Bad value '%s' supplied for parameter %s.", status, text, param ); } /* Report the projection type. */ msgOutf( " ", "The %s will be created on the JSA %s " "pixel grid.", status, (data->hdr->instrument==INST__ACSIS)?"cube":"map", smf_jsaproj_tostr( proj ) ); /* All tiles within the same JSA projection use the same WCS, so we get the WCS FrameSet for an arbitrary central tile, and use it for the full map. The exception is that tiles within the HPX facet that is split between bottom-left and top-right, use a different WCS (they have different reference points). But our choice of projection should mean that the map never falls in that facet. The base Frame will be GRID coords within the tile, and the current Frame will be ICRS (RA,Dec). */ smf_jsatile( ((skytiling.ntpf * skytiling.ntpf - 1) * 2) / 3, &skytiling, 0, proj, NULL, spacewcs, NULL, lbnd, ubnd, status ); /* Change the base Frame to be PIXEL. */ for( i = 1; i <= astGetI( *spacewcs, "NFrame" ); i++ ) { frm = astGetFrame( *spacewcs, i ); if( astChrMatch( astGetC( frm, "Domain" ), "PIXEL" ) ) { astSetI( *spacewcs, "Base", i ); } frm = astAnnul( frm ); } } /* Close the current input data file. */ smf_close_file( NULL, &data, status); } /* Otherwise get the parameter value as an NDF. */ } else { ndfAssoc( param, "READ", &refndf, status ); /* Get the WCS FrameSet from the reference NDF. */ ndfGtwcs( refndf, &refwcs, status ); /* Attempt to extract a new FrameSet from this WCS FrameSet, in which the current Frame is a SkyFrame, and the base Frame is a 2D PIXEL Frame. Since the NDF library sets the GRID Frame to be the Base Frame, we need to make the PIXEL Frame the base Frame first. The NDF library ensures that the pixel Frame is Frame 2. */ astSetI( refwcs, "Base", 2 ); *spacewcs = atlFrameSetSplit( refwcs, "SKY", NULL, NULL, status ); if( !(*spacewcs) ) { if( *status == SAI__OK ) { ndfMsg( "N", refndf ); *status = SAI__ERROR; errRep( "", "The supplied reference NDF (^N) either has no " "celestial WCS axes, or the celestial axes cannot " "be separated from the non-celestial axes.", status ); } /* The rest of makemap assumes that the sky frame axes are in the default order (lon,lat). If this is not the case, permute them. */ } else if( astGetI( *spacewcs, "IsLatAxis(1)" ) ) { perm[ 0 ] = 2; perm[ 1 ] = 1; astPermAxes( *spacewcs, perm ); } /* Now look for the spectral WCS (described by a DSBSpecFrame). */ smf_getspectralwcs( refwcs, 1, specwcs, status ); /* We no longer need the NDF so annul it. */ ndfAnnul( &refndf, status ); } } /* If no error has occurred, export any returned FrameSet pointers from the current AST context so that it will not be annulled when the AST context is ended. Otherwise, ensure a null pointer is returned. */ if( *status == SAI__OK ) { if( *spacewcs ) astExport( *spacewcs ); if( *specwcs ) astExport( *specwcs ); } else { if( *spacewcs ) *spacewcs = astAnnul( *spacewcs ); if( *specwcs ) *specwcs = astAnnul( *specwcs ); } /* End the AST context. This will annul all AST objects created within the context (except for those that have been exported from the context). */ astEnd; }
void smurf_jsatilelist( int *status ) { /* Local Variables */ AstFitsChan *fc = NULL; AstFrameSet *fs = NULL; AstObject *obj; AstRegion *region; Grp *igrp = NULL; Grp *sgrp = NULL; double vertex_data[ 2*MAXVERT ]; int *tiles = NULL; int i; int indf; int lbnd[2]; int ntile; int nvert_dec; int nvert_ra; int ubnd[2]; size_t size; size_t ssize; smfJSATiling tiling; /* Check inherited status */ if( *status != SAI__OK ) return; /* Start a new AST context. */ astBegin; /* Attempt to to get an AST Region. */ kpg1Gtobj( "IN", "Region", (void (*)( void )) F77_EXTERNAL_NAME(ast_isaregion), &obj, status ); region = (AstRegion *) obj; /* If successful, attempt to access the IN parameter as an NDF. If this works, we may be able to determine the instrument by looking at its FITS extension. */ if( *status == SAI__OK && region ) { ndfExist( "IN", "Read", &indf, status ); /* If we got an NDF, get a FitsChan holding the contents of its FITS extension. Annul the error if the NDF has no FITS extension. */ if( indf != NDF__NOID ) { kpgGtfts( indf, &fc, status ); if( *status == KPG__NOFTS ) { errAnnul( status ); fc = NULL; } ndfAnnul( &indf, status ); } /* Select a JSA instrument and get the parameters defining the layout of tiles for the selected instrument. */ smf_jsainstrument( "INSTRUMENT", fc, SMF__INST_NONE, &tiling, status ); /* Get the list of identifiers for tiles that overlap the region. */ tiles = smf_jsatiles_region( region, &tiling, &ntile, status ); /* If a null value was supplied for IN, attempt to get the positions of vertices on the sky to define the region. */ } else if( *status == PAR__NULL ) { errAnnul( status ); parGet1d( "VERTEX_RA", MAXVERT, vertex_data, &nvert_ra, status ); parGet1d( "VERTEX_DEC", MAXVERT, vertex_data + MAXVERT, &nvert_dec, status ); if( nvert_ra != nvert_dec && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", "Differing numbers of RA (%d) and Dec (%d) vertex values " "supplied.", status, nvert_ra, nvert_dec ); } /* Convert from degrees to radians. */ for( i = 0; i < nvert_ra; i++ ) { vertex_data[ i ] *= AST__DD2R; vertex_data[ MAXVERT + i ] *= AST__DD2R; } /* Select a JSA instrument and get the parameters defining the layout of tiles for the selected instrument. */ smf_jsainstrument( "INSTRUMENT", NULL, SMF__INST_NONE, &tiling, status ); /* Create a frame in which to define the region - we arbitrarily use tile 1. */ smf_jsatile( 1, &tiling, 0, NULL, &fs, NULL, lbnd, ubnd, status ); /* Create the region. */ region = (AstRegion *) astPolygon( fs, nvert_ra, MAXVERT, vertex_data, NULL, " " ); /* If the region is unbounded, it is probably because the vertices were given in the wrong order. Invert the Polyfon to correct this. */ if( !astGetI( region, "bounded" ) ) astNegate( region ); /* Get the list of identifiers for tiles that overlap the region. */ tiles = smf_jsatiles_region( region, &tiling, &ntile, status ); } /* If the IN parameter could not be accessed as a Region, annull any error and get a group of input data files. */ if( !region || *status == SAI__ERROR ) { if( *status != SAI__OK ) errAnnul( status ); kpg1Rgndf( "IN", 0, 1, "", &igrp, &size, status ); /* Get a group containing just the files holding science data. */ smf_find_science( NULL, igrp, &sgrp, 0, NULL, NULL, 1, 1, SMF__NULL, NULL, NULL, NULL, NULL, status ); /* Check we have at least once science file. */ ssize = grpGrpsz( sgrp, status ); if( ssize == 0 ) { msgOutif( MSG__NORM, " ", "None of the supplied input frames were SCIENCE.", status ); /* Get the list of identifiers for tiles that receive any data. */ } else { tiles = smf_jsatiles_data( sgrp, ssize, &tiling, &ntile, status ); } /* Delete the groups. */ if( igrp ) grpDelet( &igrp, status); if( sgrp ) grpDelet( &sgrp, status); } /* Sort the list of overlapping tiles into ascending order. */ if( *status == SAI__OK ) { qsort( tiles, ntile, sizeof( *tiles ), jsatilelist_icomp ); /* Display the list of overlapping tiles. */ msgBlank( status ); msgOutf( "", " %s tiles touched by supplied data:", status, tiling.name ); msgBlank( status ); for( i = 0; i < ntile; i++ ) { msgSeti( "I", tiles[ i ] ); msgOut( "", " ^I", status ); } msgBlank( status ); /* Write out the list of overlapping tiles to the output parameter. */ parPut1i( "TILES", ntile, tiles, status ); } /* Free resources. */ tiles = astFree( tiles ); /* End the AST context. */ astEnd; /* Issue a status indication.*/ msgBlank( status ); if( *status == SAI__OK ) { msgOutif( MSG__VERB, "", "JSATILELIST succeeded.", status); } else { msgOutif( MSG__VERB, "", "JSATILELIST failed.", status); } }