private void extractCoordinateSystem() throws TransformationException { // To get the coordinate system we look at the CTYPEn, EQUINOX, RADESYSm String lonType = h.getStringValue("CTYPE" + lonAxis).substring(0, 4); String latType = h.getStringValue("CTYPE" + latAxis).substring(0, 4); String coordSym = null; CoordinateSystem coords; if (lonType.equals("RA--") && latType.equals("DEC-")) { coordSym = frame() + equinox(); } else { if (lonType.charAt(0) != latType.charAt(0)) { throw new TransformationException( "Inconsistent axes definitions:" + lonType + "," + latType); } if (lonType.equals("GLON")) { coordSym = "G"; } else if (lonType.equals("ELON")) { coordSym = "E" + equinox(); } else if (lonType.equals("HLON")) { coordSym = "H" + equinox(); } } coords = CoordinateSystem.factory(coordSym); this.csys = coords; add(coords.getSphereDistorter()); add(coords.getRotater()); }
/** Create a simple WCS given a scaler, CoordinateSystem and Projection. */ public WCS(CoordinateSystem csys, Projection proj, Scaler scale) throws TransformationException { this.csys = csys; this.proj = proj; this.scale = scale; add(csys.getSphereDistorter()); add(csys.getRotater()); add(proj.getRotater()); add(proj.getProjecter()); add(proj.getDistorter()); add(scale); setWCSScale(scale); }
/** 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; }
/** 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); }
private void extractProjection() throws TransformationException { Projection proj = null; Scaler ncpScale = null; String lonType = h.getStringValue("CTYPE" + lonAxis).substring(5, 8); String latType = h.getStringValue("CTYPE" + latAxis).substring(5, 8); if (!lonType.equals(latType)) { throw new TransformationException( "Inconsistent projection in FITS header: " + lonType + "," + latType); } if (lonType.equals("AIT")) { proj = new Projection("Ait"); } else if (lonType.equals("CAR")) { proj = new Projection("Car"); // Allow non-central latitudes for the Cartesian projection. try { double lon = h.getDoubleValue("CRVAL" + lonAxis); if (lon != 0) { proj.setReference(toRadians(lon), 0); } } catch (Exception e) { System.err.println("Unable to read reference longitude in Cartesian projection"); } } else if (lonType.equals("CSC")) { proj = new Projection("Csc"); } else if (lonType.equals("SFL") || lonType.equals("GLS")) { proj = new Projection("Sfl"); } else if (lonType.equals("TOA")) { proj = new Projection("Toa"); } else { double crval1 = h.getDoubleValue("CRVAL" + lonAxis, NaN); double crval2 = h.getDoubleValue("CRVAL" + latAxis, NaN); if (isNaN(crval1 + crval2)) { throw new TransformationException("Unable to find reference coordinates in FITS header"); } wcsKeys.put("CRVAL1", crval1); wcsKeys.put("CRVAL2", crval2); if (lonType.equals("TAN") || lonType.equals("SIN") || lonType.equals("ZEA") || lonType.equals("ARC") || lonType.equals("STG")) { String type = lonType.substring(0, 1) + lonType.substring(1, 3).toLowerCase(); proj = new Projection(type, new double[] {toRadians(crval1), toRadians(crval2)}); double lonpole = h.getDoubleValue("LONPOLE", NaN); if (!isNaN(lonpole)) { wcsKeys.put("LONPOLE", lonpole); } // ---- Following is probably erroneous ----- // The WCS standard indicates that the default LONPOLE for // a projection is 180 when the CRVAL latitude is less than // the native latitude of the projection (90 degrees for the projections // handled here) and 0 otherwise. This means that for a projection // around the pole the default lonpole is 0. Some data (the SFD surveys) // seem to require that we do a rotation of 180 degrees to accommodate // this. However we do not implement this unless the LONPOLE is // explicitly given since this seems non-intuitive to me and I suspect // that a user who is not careful enough to specify a LONPOLE in this // situation probably doesn't understand what is going on anyway. // ----- We now assume that our standard processing of // ----- zenithal projections handles lonpole of 180 and that // ----- this is the default for all zenithal images. // ----- Previously we assumed that we were using lonPole=0 at // ----- at the poles, but we weren't.... // if (!isNaN(lonpole)) { double lonDefault = 180; if (lonpole != lonDefault) { Rotater r = proj.getRotater(); Rotater lon = new Rotater("Z", toRadians(lonpole - lonDefault), 0, 0); if (r != null) { proj.setRotater(r.add(lon)); } else { proj.setRotater(lon); } } } } else if (lonType.equals("NCP")) { // Sin projection with projection centered at pole. double[] xproj = new double[] {toRadians(crval1), PI / 2}; if (crval2 < 0) { xproj[1] = -xproj[1]; } double poleOffset = sin(xproj[1] - toRadians(crval2)); // Have we handled South pole here? proj = new Projection("Sin", xproj); // NCP scales the Y-axis to accommodate the distortion of the SIN projection away // from the pole. ncpScale = new Scaler(0, poleOffset, 1, 0, 0, 1); ncpScale = ncpScale.add(new Scaler(0., 0., 1, 0, 0, 1 / sin(toRadians(crval2)))); } else { throw new TransformationException("Unsupported projection type:" + lonType); } } this.proj = proj; if (ncpScale != null) { this.scale = ncpScale; } add(proj.getRotater()); add(proj.getProjecter()); add(ncpScale); // Ignored if null }
/** 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; }