/** Get the scaling when it is described as a matrix */ private boolean extractScaler2(double crpix1, double crpix2) throws TransformationException { // Look for the CD matrix... // double m11 = h.getDoubleValue("CD" + lonAxis + "_" + lonAxis, NaN); double m12 = h.getDoubleValue("CD" + lonAxis + "_" + latAxis, NaN); double m21 = h.getDoubleValue("CD" + latAxis + "_" + lonAxis, NaN); double m22 = h.getDoubleValue("CD" + latAxis + "_" + latAxis, NaN); boolean matrix = !isNaN(m11 + m12 + m21 + m22); if (!matrix) { return false; } wcsKeys.put("CD1_1", m11); wcsKeys.put("CD1_2", m12); wcsKeys.put("CD2_1", m21); wcsKeys.put("CD2_2", m22); m11 = toRadians(m11); m12 = toRadians(m12); m21 = toRadians(m21); m22 = toRadians(m22); // we have // t = a11 (x-x0) + a12 (y-y0); u = a21(x-x0) + a22(y-y0) // t = a11x + a12y - a11 x0 - a12 y0; // Scaler s = new Scaler(-m11 * crpix1 - m12 * crpix2, -m21 * crpix1 - m22 * crpix2, m11, m12, m21, m22); s = s.inverse(); // Are longitude and latitude in unusual order? if (lonAxis > latAxis) { s.interchangeAxes(); } this.scale = s; add(s); setWCSScale(s); return true; }
/** * Write FITS WCS keywords given key values. Only relatively simple WCSs are handled here. We * assume we are dealing with axes 1 and 2. * * @param h The header to be updated. * @param s A Scaler giving the transformation between standard projection coordinates and * pixel/device coordinates. * @param projString A three character string giving the projection used. Supported projections * are: "Tan", "Sin", "Ait", "Car", "Zea". * @param coordString A string giving the coordinate system used. The first character gives the * general frame. For most frames the remainder of the string gives the equinox of the * coordinate system. E.g., J2000, B1950, Galactic, "E2000", "H2020.10375". */ public void updateHeader( Header h, Scaler s, double[] crval, String projString, String coordString) throws Exception { if (proj.isFixedProjection()) { h.addValue("CRVAL1", toDegrees(proj.getReferencePoint()[0]), "Fixed reference center"); h.addValue("CRVAL2", toDegrees(proj.getReferencePoint()[1]), "Fixed reference center"); } else { h.addValue("CRVAL1", crval[0], "Reference longitude"); h.addValue("CRVAL2", crval[1], "Reference latitude"); } coordString = coordString.toUpperCase(); String[] prefixes = new String[2]; char c = coordString.charAt(0); if (c == 'J' || c == 'I') { h.addValue("RADESYS", "FK5", "Coordinate system"); prefixes[0] = "RA--"; prefixes[1] = "DEC-"; } else if (c == 'B') { h.addValue("RADESYS", "FK4", "Coordinate system"); prefixes[0] = "RA--"; prefixes[1] = "DEC-"; } else { prefixes[0] = c + "LON"; prefixes[1] = c + "LAT"; } if (c != 'G' && c != 'I') { try { double equinox = Double.parseDouble(coordString.substring(1)); h.addValue("EQUINOX", equinox, "Epoch of the equinox"); } catch (Exception e) { // Couldn't parse out the equinox } } if (c == 'I') { h.addValue("EQUINOX", 2000, "ICRS coordinates"); } String upProj = projString.toUpperCase(); h.addValue("CTYPE1", prefixes[0] + "-" + upProj, "Coordinates -- projection"); h.addValue("CTYPE2", prefixes[1] + "-" + upProj, "Coordinates -- projection"); // Note that the scaler transforms from the standard projection // coordinates to the pixel coordinates. // P = P0 + M X where X is the standard coordinates and P is the // pixel coordinates. So the reference pixels are just the constants // in the scaler. // Remember that FITS pixels are offset by 0.5 from 0 offset pixels. h.addValue("CRPIX1", s.x0 + 0.5, "X reference pixel"); h.addValue("CRPIX2", s.y0 + 0.5, "Y reference pixel"); // Remember that the FITS values are of the form // X = M(P-P0) // so we'll need to invert the scaler. // // Do we need a matrix? if (abs(s.a01) < 1.e-14 && abs(s.a10) < 1.e-14) { // No cross terms, so we'll just use CDELTs h.addValue("CDELT1", toDegrees(1 / s.a00), "X scale"); h.addValue("CDELT2", toDegrees(1 / s.a11), "Y scale"); } else { // We have cross terms. It's simplest // just to use the CD matrix and not worry about // normalization. First invert the matrix to get // the transformation in the direction that FITS uses. Scaler rev = s.inverse(); h.addValue("CD1_1", toDegrees(rev.a00), "Matrix element"); h.addValue("CD1_2", toDegrees(rev.a01), "Matrix element"); h.addValue("CD2_1", toDegrees(rev.a10), "Matrix element"); h.addValue("CD2_2", toDegrees(rev.a11), "Matrix element"); } }
/** Handle the NEAT special projection */ private void doNeatWCS() throws TransformationException { // The NEAT transformation from the standard spherical system // includes: // Transformation to J2000 spherical coordinates (a null operation) // A tangent plane projection to the standard plane // A scaler transformation to corrected pixel coordinates. // A distorter to distorted pixel coordinates // A scaler transformation of distorted coordinates to actual pixels CoordinateSystem csys = CoordinateSystem.factory("J2000"); this.csys = csys; // The RA0/DEC0 pair are the actual center of the projection. double cv1 = toRadians(h.getDoubleValue("RA0")); double cv2 = toRadians(h.getDoubleValue("DEC0")); wcsKeys.put("CRVAL1", toDegrees(cv1)); wcsKeys.put("CRVAL2", toDegrees(cv2)); Projection proj = new Projection("Tan", new double[] {cv1, cv2}); this.proj = proj; double cd1 = toRadians(h.getDoubleValue("CDELT1")); double cd2 = toRadians(h.getDoubleValue("CDELT2")); wcsKeys.put("CDELT1", toDegrees(cd1)); wcsKeys.put("CDELT2", toDegrees(cd2)); wcsScale = abs(cd1); double cp1 = h.getDoubleValue("CRPIX1"); double cp2 = h.getDoubleValue("CRPIX2"); wcsKeys.put("CPRIX1", cp1); wcsKeys.put("CRPIX2", cp2); wcsKeys.put("CTYPE1", "RA---XTN"); wcsKeys.put("CTYPE2", "DEC--XTN"); Scaler s1 = new Scaler(0., 0., -1 / cd1, 0, 0, -1 / cd2); // Note that the the A0,A1,A2, B0,B1,B2 rotation // is relative to the original pixel values, so // we need to put this in the secondary scaler. // double x0 = h.getDoubleValue("X0"); double y0 = h.getDoubleValue("Y0"); Distorter dis = new skyview.geometry.distorter.Neat( h.getDoubleValue("RADIAL"), h.getDoubleValue("XRADIAL"), h.getDoubleValue("YRADIAL")); double a0 = h.getDoubleValue("A0"); double a1 = h.getDoubleValue("A1"); double a2 = h.getDoubleValue("A2"); double b0 = h.getDoubleValue("B0"); double b1 = h.getDoubleValue("B1"); double b2 = h.getDoubleValue("B2"); // The reference pixel is to be computed in the distorted frame. double[] cpix = new double[] {cp1, cp2}; double[] cout = new double[2]; dis.transform(cpix, cout); Scaler s2 = new Scaler( cout[0] - a0 - a1 * x0 - a2 * y0, cout[1] - b0 - b2 * x0 - b1 * y0, -(1 + a1), -a2, -b2, -(1 + b1)); wcsKeys.put("A0", a0); wcsKeys.put("A1", a1); wcsKeys.put("A2", a2); wcsKeys.put("B0", b0); wcsKeys.put("B1", b1); wcsKeys.put("B2", b2); this.distort = dis; add(csys.getSphereDistorter()); add(csys.getRotater()); add(proj.getRotater()); add(proj.getProjecter()); this.scale = s1; // Note that s1 is defined from the projection plane to the pixels coordinates, // so we don't need to invert it. // // But the second scaler, s2, used in the NEAT correction // is defined in the direction from pixels to sphere, so we // need to take its inverse. this.scale = this.scale.add(s2.inverse()); add(this.scale); add(dis); }
/** Handle a DSS projection */ private void doDSSWCS() throws TransformationException { double plateRA = h.getDoubleValue("PLTRAH") + h.getDoubleValue("PLTRAM") / 60 + h.getDoubleValue("PLTRAS") / 3600; plateRA = toRadians(15 * plateRA); wcsKeys.put("PLTRAH", h.getDoubleValue("PLTRAH")); wcsKeys.put("PLTRAM", h.getDoubleValue("PLTRAM")); wcsKeys.put("PLTRAS", h.getDoubleValue("PLTRAS")); double plateDec = h.getDoubleValue("PLTDECD") + h.getDoubleValue("PLTDECM") / 60 + h.getDoubleValue("PLTDECS") / 3600; plateDec = toRadians(plateDec); if (h.getStringValue("PLTDECSN").substring(0, 1).equals("-")) { plateDec = -plateDec; } wcsKeys.put("PLTDECD", h.getDoubleValue("PLTDECD")); wcsKeys.put("PLTDECM", h.getDoubleValue("PLTDECM")); wcsKeys.put("PLTDECS", h.getDoubleValue("PLTDECS")); wcsKeys.put("PLTDECSN", h.getStringValue("PLTDECSN")); double plateScale = h.getDoubleValue("PLTSCALE"); double xPixelSize = h.getDoubleValue("XPIXELSZ"); double yPixelSize = h.getDoubleValue("YPIXELSZ"); wcsKeys.put("PLTSCALE", plateScale); wcsKeys.put("XPIXELSZ", xPixelSize); wcsKeys.put("YPIXELSZ", yPixelSize); double[] xCoeff = new double[20]; double[] yCoeff = new double[20]; for (int i = 1; i <= 20; i += 1) { xCoeff[i - 1] = h.getDoubleValue("AMDX" + i); yCoeff[i - 1] = h.getDoubleValue("AMDY" + i); wcsKeys.put("AMDX" + i, xCoeff[i - 1]); wcsKeys.put("AMDY" + i, yCoeff[i - 1]); } double[] ppo = new double[6]; for (int i = 1; i <= 6; i += 1) { ppo[i - 1] = h.getDoubleValue("PPO" + i); wcsKeys.put("PPO" + i, ppo[i - 1]); } double plateCenterX = ppo[2]; double plateCenterY = ppo[5]; double cdelt1 = -plateScale / 1000 * xPixelSize / 3600; double cdelt2 = plateScale / 1000 * yPixelSize / 3600; wcsScale = abs(cdelt1); // This gives cdelts in degrees per pixel. // CNPIX pixels use a have the first pixel going from 1 - 2 so they are // off by 0.5 from FITS (which in turn is offset by 0.5 from the internal // scaling, but we handle that elsewhere). double crpix1 = plateCenterX / xPixelSize - h.getDoubleValue("CNPIX1", 0) - 0.5; double crpix2 = plateCenterY / yPixelSize - h.getDoubleValue("CNPIX2", 0) - 0.5; wcsKeys.put("CNPIX1", h.getDoubleValue("CNPIX1", 0)); wcsKeys.put("CNPIX2", h.getDoubleValue("CNPIX2", 0)); Projection proj = new Projection("Tan", new double[] {plateRA, plateDec}); this.proj = proj; CoordinateSystem coords = CoordinateSystem.factory("J2000"); this.csys = coords; cdelt1 = toRadians(cdelt1); cdelt2 = toRadians(cdelt2); Scaler s = new Scaler(-cdelt1 * crpix1, -cdelt2 * crpix2, cdelt1, 0, 0, cdelt2); // Got the transformers ready. Add them in properly. add(coords.getSphereDistorter()); add(coords.getRotater()); add(proj.getRotater()); add(proj.getProjecter()); this.distort = new skyview.geometry.distorter.DSS( plateRA, plateDec, xPixelSize, yPixelSize, plateScale, ppo, xCoeff, yCoeff); add(this.distort); this.scale = s.inverse(); add(this.scale); }
/** Get the scaling when CDELT is specified */ private boolean extractScaler1(double crpix1, double crpix2) throws TransformationException { boolean matrix = false; double cdelt1 = h.getDoubleValue("CDELT" + lonAxis, NaN); double cdelt2 = h.getDoubleValue("CDELT" + latAxis, NaN); wcsKeys.put("CDELT1", cdelt1); wcsKeys.put("CDELT2", cdelt2); if (isNaN(cdelt1) || isNaN(cdelt2)) { return false; } // We use 1 indexing to match better with the FITS files. double m11, m12, m21, m22; // We've got minimal information... We might have more. double crota = h.getDoubleValue("CROTA" + latAxis, NaN); if (!isNaN(crota) && crota != 0) { wcsKeys.put("CROTA2", crota); crota = toRadians(crota); m11 = cos(crota); m12 = sin(crota); m21 = -sin(crota); m22 = cos(crota); matrix = true; } else { m11 = h.getDoubleValue("PC" + lonAxis + "_" + lonAxis, NaN); m12 = h.getDoubleValue("PC" + lonAxis + "_" + latAxis, NaN); m21 = h.getDoubleValue("PC" + latAxis + "_" + lonAxis, NaN); m22 = h.getDoubleValue("PC" + latAxis + "_" + latAxis, NaN); matrix = !isNaN(m11 + m12 + m21 + m22); if (matrix) { wcsKeys.put("PC1_1", m11); wcsKeys.put("PC1_2", m12); wcsKeys.put("PC2_1", m21); wcsKeys.put("PC2_2", m22); } } // Note that Scaler is defined with parameters t = x0 + a00 x + a01 y; u = y0 + a10 x + a11 y // which is different from what we have here... // t = scalex (x-x0), u = scaley (y-y0) // t = scalex x - scalex x0; u = scaley y - scaley y0 // or // t = scalex [a11 (x-x0) + a12 (y-y0)], u = scaley [a21 (x-x0) + a22 (y-y0)] -> // t = scalex a11 x - scalex a11 x0 + scalex a12 y + scalex a12 y0 -> // t = - scalex (a11 x0 + a12 y0) + scalex a11 x + scalex a12 y (and similarly for u) Scaler s; cdelt1 = toRadians(cdelt1); cdelt2 = toRadians(cdelt2); if (!matrix) { s = new Scaler(-cdelt1 * crpix1, -cdelt2 * crpix2, cdelt1, 0, 0, cdelt2); } else { s = new Scaler( -cdelt1 * (m11 * crpix1 + m12 * crpix2), -cdelt2 * (m21 * crpix1 + m22 * crpix2), cdelt1 * m11, cdelt1 * m12, cdelt2 * m21, cdelt2 * m22); } // Note that this scaler transforms from pixel coordinates to standard projection // plane coordinates. We want the inverse transformation as the scaler. s = s.inverse(); // Are lon and lat in unusual order? if (lonAxis > latAxis) { s.interchangeAxes(); } this.scale = s; add(s); setWCSScale(s); return true; }