public double degBetween(OrderedTriple t) { return Rotater.rad2Deg(radBetween(t)); }
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 }