#!/usr/bin/env python3 """ Generate roman_footprint_rgps_w51.html using real WFI SCA polygons computed via pysiaf from roman_gps.sim.ecsv. Layers: • Deep+Spec_W51 — 8 unique WFI_CEN pointings (dedicated deep W51 field) • Disk7 (all) — 632 unique WFI_CEN pointings covering l=29–50.5° """ import sys, json, math import numpy as np from astropy.table import Table from astropy.coordinates import SkyCoord import astropy.units as u import pysiaf from pysiaf.utils.rotations import attitude_matrix # ── paths ──────────────────────────────────────────────────────────────────── ECSV = ( "/Users/adam/repos/roman_notebooks/notebooks/" "footprint_visualization/aux_data/roman_gps.sim.ecsv" ) OUT_HTML = "/Users/adam/Downloads/roman_footprint_rgps_w51.html" # ── pysiaf setup ───────────────────────────────────────────────────────────── print("Loading Roman SIAF …") rsiaf = pysiaf.Siaf("Roman") wfi_cen = rsiaf['WFI_CEN'] V2Ref = wfi_cen.V2Ref # +1546.38" (WFI focal-plane offset from V3 boresight) V3Ref = wfi_cen.V3Ref # -892.79" sensors = [rsiaf[f"WFI{j:02d}_FULL"] for j in range(1, 19)] def sca_polygons(ra_cen, dec_cen, pa): """Return list of 18 SCA polygons, each [[ra,dec], …] × 4 corners.""" att = attitude_matrix(V2Ref, V3Ref, ra_cen, dec_cen, pa) polys = [] for sensor in sensors: sensor.set_attitude_matrix(att) c = sensor.corners("sky") # [ra_4pts, dec_4pts] polys.append([[round(float(c[0][i]), 5), round(float(c[1][i]), 5)] for i in range(4)]) return polys # ── read ECSV ──────────────────────────────────────────────────────────────── print("Reading roman_gps.sim.ecsv …") t = Table.read(ECSV) def unique_pointings(target_name): """Return sorted list of unique (ra, dec, pa) tuples for a TARGET_NAME.""" sub = t[t["TARGET_NAME"] == target_name] pts = {} for r in sub: key = (round(float(r["RA"]), 6), round(float(r["DEC"]), 6), round(float(r["PA"]), 3)) pts[key] = True return sorted(pts.keys()) # ── Deep+Spec_W51 ───────────────────────────────────────────────────────────── print("Computing polygons for Deep+Spec_W51 …") w51_pts = unique_pointings("Deep+Spec_W51") print(f" {len(w51_pts)} unique pointings → {len(w51_pts)*18} polygons") w51_data = [] for ra, dec, pa in w51_pts: coord = SkyCoord(ra=ra * u.deg, dec=dec * u.deg, frame="icrs") gal = coord.galactic w51_data.append({ "ra": ra, "dec": dec, "pa": pa, "l": round(float(gal.l.deg), 4), "b": round(float(gal.b.deg), 4), "polys": sca_polygons(ra, dec, pa), }) print(f" Done.") # ── Disk7 (all) ─────────────────────────────────────────────────────────────── print("Computing polygons for Disk7 (all 632 pointings) …") d7_pts = unique_pointings("Disk7") print(f" {len(d7_pts)} unique pointings → {len(d7_pts)*18} polygons") print(" (this may take ~30 s) …") d7_data = [] for i, (ra, dec, pa) in enumerate(d7_pts): if (i + 1) % 100 == 0: print(f" {i+1}/{len(d7_pts)} …") coord = SkyCoord(ra=ra * u.deg, dec=dec * u.deg, frame="icrs") gal = coord.galactic d7_data.append({ "ra": ra, "dec": dec, "pa": pa, "l": round(float(gal.l.deg), 4), "b": round(float(gal.b.deg), 4), "polys": sca_polygons(ra, dec, pa), }) print(f" Done.") # ── serialize to JSON ───────────────────────────────────────────────────────── print("Serialising …") w51_json = json.dumps(w51_data, separators=(",", ":")) d7_json = json.dumps(d7_data, separators=(",", ":")) print(f" Deep+Spec_W51 JSON: {len(w51_json)//1024} KB") print(f" Disk7 JSON: {len(d7_json)//1024} KB") # ── HTML ────────────────────────────────────────────────────────────────────── HTML = f""" Roman RGPS — W51 Footprint (pysiaf)
⏳ Building WFI overlay polygons…

Roman RGPS — W51 WFI Footprint

Disk7 (l=29–50.5°, PA=0°)
Deep+Spec W51 (l=49.4°)
18 SCAs/pointing · PA=0° (nominal)
Source: roman_gps.sim.ecsv (APT output)
Geometry: pysiaf Roman SIAF
WFI01_FULL … WFI18_FULL apertures
Roman RGPS · pysiaf WFI geometry · {len(d7_data)} Disk7 + {len(w51_data)} Deep W51 pointings · 18 SCAs each
""" with open(OUT_HTML, "w") as f: f.write(HTML) size_kb = len(HTML) // 1024 print(f"\nWrote {OUT_HTML}") print(f"File size: {size_kb} KB ({len(HTML):,} bytes)")