Last modified: 15 Feb 2022

URL: https://cxc.cfa.harvard.edu/ciao/threads/ttt_fov/

Determine if location is inside Field of View

CIAO 4.15 Science Threads


Overview

Synopsis:

It may be necessary to determine if a celestial location (RA, Dec), is located within a Chandra observation. There are several ways to get an approximate answer by doing a radius search around the observation's nominal pointing RA_PNT,DEC_PNT. However, especially for ACIS observations, this can lead to many incorrect results since the number and configuration of CCDs varies between observations. There are also things such as subarrays and windows to consider which can further limit the field of view.

One of the standard Chandra data products is a Field of View (FOV) file -- a FITS region file that provides a polygon around each CCD.

[NOTE]
Note

Remember that due to dither, the square CCDs are "blurred" on the sky so the observed field of view must be described by a polygon.

This thread will show how to use the FOV files to determine if a celestial location, rather than a physical location, is located in a particular Chandra observation.

Purpose:

To determine if a position is within the field of view of a Chandra observation.

Related Links:

Last Update: 15 Feb 2022 - Review for CIAO 4.14. Updated for Python3, new pyregion module, and Repro-5+CALDB-4.9.6.


Contents


Getting Started

Download the sample data: 5794 (SDSS J1004+4112)

unix% download_chandra_obsid 5794 fov
FOVprimary/

acis_fov.fits

Check if celestial location is inside FOV

As with all Chandra FITS region files, the coordinates in the FOV file are stored in sky pixels, on the same tangent plane used by the event file for the observation. However, the POS (position) column also has a World Coordinate System (WCS) to convert the X,Y values to RA,Dec. This can be seen when the file is dmlist'd with the cols options:

unix% dmlist acis_fov.fits cols
 
--------------------------------------------------------------------------------
Columns for Table Block FOV
--------------------------------------------------------------------------------
 
ColNo  Name                 Unit        Type             Range
   1   POS(X,Y)[13]         pixel        Real8(13)      -Inf:+Inf            Position
   2   SHAPE                             String[16]                          Region shape type
   3   R[2]                 pixel        Real8(2)       -Inf:+Inf            Radius
   4   ROTANG[2]            pixel        Real8(2)       -Inf:+Inf            Angle
   5   COMPONENT                         Int2           -                    Component number
   6   CCD_ID                            Int2           -                    CCD ID
 
--------------------------------------------------------------------------------
World Coord Transforms for Columns in Table Block FOV
--------------------------------------------------------------------------------
 
ColNo    Name
1:    EQPOS(RA ) = (+151.1396)[degree] +TAN[(-0.000136667)* (POS(X)-(+4096.50))]
           (DEC)   (+41.2327 )              (+0.000136667)  (   (Y) (+4096.50)) 

By default when CIAO tools and applications try to use the FOV files they will use the POS position values, which are simply sky X and Y values. For example using the CIAO Python region module

unix% python
>>>  from region import *
>>>  myreg = CXCRegion("acis_fov.fits")
>>>  print(myreg)
 
polygon(3467.6263224,3369.28090836,3467.6565089,3369.17640877,3467.71661574,
3369.00061749,3467.83371847,3368.86039342,3467.8694516,3368.83401957,
3467.91668997,3368.81529982,3468.48358382,3368.69476487,3468.70314573,
3368.65086168,3493.34049452,3364.67234601,4473.49875929,3207.66038239,
4498.14437164,3203.73342053,4501.24422987,3203.25600105,4504.06889614,
3202.85557739,4504.23454267,3202.84091307,4504.32267389,3202.83736965,
4504.42159188,3202.83358699,4504.86841334,3202.83134235,4505.00642326,
3202.83089484,4505.0625378,3202.84329322,4505.32268684,3203.2149399,
4506.65550781,3210.61828139,4510.97796182,3235.10243102,4671.97193121,
4241.3238962,4672.00675252,4241.91272155,4671.92356958,4242.71362919,
4671.90199236,4242.80272213,4671.76416795,4243.26341158,4671.74936787,
4243.29409085,4671.59095838,4243.44934783,4670.88311161,4243.97157507,
4670.80892245,4244.01169209,4670.04187616,4244.33543306,4669.45877326,
4244.484041,4668.25805682,4244.76331822,4667.93228389,4244.82547885,
3662.77667455,4405.60051292,3646.01017685,4408.17768906,3635.19102762,
4409.56424316,3634.88682614,4409.58532979,3634.60679034,4409.59359905,
3634.51043422,4409.56149899,3634.48214285,4409.54244325,3634.35678717,
4409.45324929,3633.86037361,4407.37390775,3633.07781324,4403.63323961,
3633.05827282,4403.51162292,3471.95423823,3396.71335899,3467.6263224,
3369.28090836)

This shows the POLYGON shape and the typical ACIS sky position values (in the 3000-5000 range). However, this is not directly useful when a celestial location, RA,Dec in decimal degrees, needs to be checked.

Using the CXC Datamodel virtual file syntax, it is possible to change the above example to use the WCS values in the virtual EQPOS column. By using a [cols ] filter, the virtual EQPOS column will be used instead of the sky positions as shown below

>>>  mycelreg = CXCRegion("acis_fov.fits[cols eqpos,shape,component]")
>>>  print(mycelreg)
polygon(151.258014222,41.1313973997,151.25800872,41.1313831235,151.257997772,
41.1313591096,151.257976491,41.1313399667,151.257970001,41.1313363687,
151.257961425,41.1313338188,151.257858536,41.1313174472,151.257818686,
41.1313114864,151.253347423,41.1307720755,151.075524743,41.1093454231,
151.071054978,41.1088060117,151.070492786,41.1087404092,151.069980498,
41.1086853585,151.069950455,41.1086833352,151.06993447,41.1086828407,
151.069916528,41.1086823123,151.069835482,41.1086819536,151.06981045,
41.1086818765,151.06980027,41.1086835644,151.069753025,41.1087343256,
151.069510124,41.1097459549,151.068722246,41.1130915976,151.039298954,
41.250585464,151.039292495,41.2506659309,151.039307441,41.2507754018,
151.039311344,41.2507875813,151.039336296,41.2508505648,151.03933898,
41.25085476,151.039367742,41.2508760045,151.039496299,41.2509474918,
151.039509776,41.2509529866,151.039649138,41.250997357,151.039755101,
41.2510177622,151.039973305,41.2510561265,151.04003251,41.251064675,
151.222775437,41.2730570221,151.225824701,41.2734071147,151.227792311,
41.2735951989,151.227847631,41.2735980405,151.227898555,41.2735991337,
151.22791607,41.2735947339,151.227921212,41.2735921259,151.227943991,
41.2735799195,151.228033894,41.2732956776,151.228175536,41.2727843501,
151.228179067,41.2727677266,151.257235426,41.1351472552,151.258014222,
41.1313973997)

The additional columns, shape and component are needed to complete the FITS region definition. The polygon points are now the sky X,Y values converted to RA,Dec.

The regInsideRegion routine can then be used to check whether a celestial location is within the field-of-view. Below the nominal pointing is checked along with a location several degrees away.

>>>  !dmlist acis_fov.fits header,clean | grep PNT   
RA_PNT                     151.1439070093 [deg]     Pointing RA
DEC_PNT                     41.2308402045 [deg]     Pointing Dec
ROLL_PNT                    99.0663114612 [deg]     Pointing Roll

>>>  pointing=mycelreg.is_inside(151.1395864022, 41.2326566497 )
>>>  random=mycelreg.is_inside(145.2, 45.0 )

>>>  print(f"Is _PNT inside FOV? {pointing}")
Is _PNT inside FOV? True

>>>  print(f"Is random location inside FOV? {random}")
Is random location inside FOV? False

Checking several FOV files

This technique has been scripted into the FOVFiles Python class. After users have downloaded a set of FOV files, they can use this routine to determine which observations, from those they have downloaded, contains data for the requested location. This is essentially a local footprint service. An example is shown here:

 
unix% find_chandra_obsid "SDSS J1004+4112"
# obsid  sepn   inst grat   time    obsdate   piname            target
5794      0.2 ACIS-S NONE   80.1 2005-01-01    Inada "SDSS J1004+4112"
11546     0.1 ACIS-S NONE    6.0 2010-03-08 Kochanek     SDSS1004+4112
11547     0.1 ACIS-S NONE    6.0 2010-06-19 Kochanek     SDSS1004+4112
11548     0.1 ACIS-S NONE    6.0 2010-09-23 Kochanek     SDSS1004+4112
11549     0.1 ACIS-S NONE    6.0 2011-01-30 Kochanek     SDSS1004+4112
14495     0.1 ACIS-S NONE   24.7 2013-01-27 Kochanek     SDSS1004+4112
14496     0.1 ACIS-S NONE   24.7 2013-03-01 Kochanek     SDSS1004+4112
14497     0.1 ACIS-S NONE   24.1 2013-10-05 Kochanek     SDSS1004+4112
14498     0.1 ACIS-S NONE   23.8 2013-11-16 Kochanek     SDSS1004+4112
14499     0.1 ACIS-S NONE   23.3 2014-04-29 Kochanek     SDSS1004+4112
14500     0.1 ACIS-S NONE   24.7 2014-06-02 Kochanek     SDSS1004+4112
19632     0.1 ACIS-S NONE   40.1 2017-01-23  Chartas     SDSS1004+4112
19633     0.1 ACIS-S NONE   39.5 2017-04-16  Chartas     SDSS1004+4112
19634     0.1 ACIS-S NONE   35.9 2017-06-27  Chartas     SDSS1004+4112
19635     0.1 ACIS-S NONE   38.4 2017-10-01  Chartas     SDSS1004+4112
19636     0.1 ACIS-S NONE   36.0 2018-01-17  Chartas     SDSS1004+4112
19637     0.1 ACIS-S NONE   39.5 2018-04-19  Chartas     SDSS1004+4112

unix% mkdir SDSS1004+4112
unix% cd SDSS1004+4112
unix% download_chandra_obsid 5794,11546,11547,11548,11549,14495,14496 fov
unix% mv */primary/*fov1.fits.gz .
unix% python

>>> from ciao_contrib.region.check_fov import FOVFiles
>>> regions = FOVFiles("*fov1.fits.gz")
>>> print(regions)
List of FOV files
  acisf11547_000N002_fov1.fits.gz
  acisf05794_000N003_fov1.fits.gz
  acisf11549_000N002_fov1.fits.gz
  acisf14496_000N001_fov1.fits.gz
  acisf11546_000N002_fov1.fits.gz
  acisf11548_000N002_fov1.fits.gz
  acisf14495_000N001_fov1.fits.gz

>>> regions.inside( 151.25426, 41.090654)
['acisf11549_000N002_fov1.fits.gz', 
'acisf11548_000N002_fov1.fits.gz', 
'acisf14495_000N001_fov1.fits.gz']

In this example all the observations that include SDSS J1004+4112 are located using the find_chandra_obsid script and the FOV files are retrieved. Since the FOV files are small (only a few kBytes per file), they can be retrieved and used quickly before deciding which full datasets need to be downloaded. The FOVFiles object is then used to load all the files that were retrieved and to check the coverage of a location. In this example only 3 out of the 8 observations imaged the location.


Summary

This thread has shown how to use DM virtual file syntax to access the World Coordinate System attached to the sky position column in a Field of View file. This allows the user to easily check if arbitrary celestial coordinate are included in an observation.

[NOTE]
Caveat

The Chandra FOV files are not the tightest possible bounding polygon around the data. It may be slightly bigger (never smaller) than the actual footprint of the date due to dither, bad detector pixels, and based on the roll angle of the spacecraft. The gap is no more than half the dither pattern (based on roll).

This technique is used by the CXC Footprint service. The Level 1 FOV files are converted to celestial coordinates and stored in a database which is queried to determine which observations observed the input position.


History

31 Mar 2014 Initial version.
16 Jul 2014 Added section showing how to use new FOVFiles routines to automate for multiple FOV files.
23 Dec 2014 Reviewed for CIAO 4.7; no changes.
15 Feb 2022 Review for CIAO 4.14. Updated for Python3, new pyregion module, and Repro-5+CALDB-4.9.6.