// // ABINnav.java // /* This source file is part of the edu.wisc.ssec.mcidas package and is Copyright (C) 1998 - 2017 by Tom Whittaker, Tommy Jasmin, Tom Rink, Don Murray, James Kelly, Bill Hibbard, Dave Glowacki, Curtis Rueden and others. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package edu.wisc.ssec.mcidas; import static java.lang.Math.PI; import static java.lang.Math.asin; import static java.lang.Math.atan; import static java.lang.Math.cos; import static java.lang.Math.sin; import static java.lang.Math.sqrt; import static java.lang.Math.tan; /** * ABINnav is used to provide {@literal "navigation"} for ABIN image data. * * This code is essentially a direct port of {@code nvxabin.dlm} from McIDAS-X. * Note: the variable naming convention has been retained from the original * McIDAS-X source code. */ public class ABINnav extends AREAnav { /** Radius of satellite orbit (kilometers). */ private static final double dh = 42164.16000000000349245965480804443359375; /** Convert degrees to radians. */ private static final double deg_to_rad = PI / 180.0; /** Radius of the Earth at the equator (kilometers). */ private static final double r_eq = 6378.136999999999716237653046846389770508; /** Radius of the Earth at the equator squared (kilometers). */ // note: dreq2 and r_eq2 have been kept around to conform to McIDAS-X code private static final double dreq2 = r_eq * r_eq; /** Radius of the Earth at the poles (kilometers). */ private static final double drpo = 6356.752300000000104773789644241333007812; /** Radius of the Earth at the poles squared (kilometers). */ private static final double drpo2 = drpo * drpo; /** Radius of the Earth at the equator squared (kilometers). */ // note: dreq2 and r_eq2 have been kept around to conform to McIDAS-X code private static final double r_eq2 = dreq2; /** * Pre-calculated constant (value is dh^2 - r_eq^2). * * @see #dh * @see #r_eq */ private static final double d = dh * dh - r_eq2; private static final double FP = 1.006802999999999892466462370066437870264; private boolean isEastPositive = true; private int itype = 1; /** Line offset. */ private double loff; /** Column offset. */ private double coff; /** Line factor. */ private double lfac; /** Column factor. */ private double cfac; /** Subpoint. */ private double plon; /** Base resolution. */ private double bres; /** * Initializes the ABIN navigation code with the given set of navigation * parameters. * * @param navblock Navigation parameters from image file. * * @throws IllegalArgumentException if {@code navblock} is not ABIN. */ public ABINnav(int[] navblock) { if (navblock[0] != ABIN) { throw new IllegalArgumentException("Invalid navigation type: " + navblock[0]); } loff = navblock[1] / 100000000.0; coff = navblock[2] / 100000000.0; lfac = navblock[3] / 100000000.0; cfac = navblock[4] / 100000000.0; plon = navblock[5] / 10.0; bres = navblock[6]; } /** * Convert satellite lines/elements to latitude/longitude coordinates. * * @param linele Array of line/element pairs. * Where {@code linele[indexLine]} are {@literal "lines"} * and {@code linele[indexEle]} are {@literal "elements"}. * These coordinates must be {@literal "file"} rather than * {@literal "image"} coordinates. * * @return Array of latitude/longitude pairs. {@code latlon[indexLat]} are * latitudes and {@code latlon[indexLon]} are longitudes. */ public double[][] toLatLon(double[][] linele) { final double sub_lon_radians = plon * (PI / 180.0); double xlin; double xele; double lamda_goes; double theta_goes; double lamda_geos; double theta_geos; double cosx; double cosy; double sinx; double siny; double c1; double c2; double sd; double sdd; double sn; double s1; double s2; double s3; double sxy; int length = linele[indexLine].length; double[][] latLons = new double[2][length]; double[][] imageLineElems = areaCoordToImageCoord(linele); for (int point = 0; point < length; point++) { double rlin = imageLineElems[indexLine][point]; double rele = imageLineElems[indexEle][point]; // start img_to_ll xlin = 0.0; xele = 0.0; lamda_goes = 0.0; theta_goes = 0.0; lamda_geos = 0.0; theta_geos = 0.0; cosx = 0.0; cosy = 0.0; sinx = 0.0; siny = 0.0; c1 = 0.0; c2 = 0.0; sd = 0.0; sdd = 0.0; sn = 0.0; s1 = 0.0; s2 = 0.0; s3 = 0.0; sxy = 0.0; double xlat; double xlon; // adjust using Base RESolution xlin = (rlin + bres - 1) / bres; xele = (rele + bres - 1) / bres; // Intermediate coordinates (coordinates will be radians) theta_goes = xlin * lfac + loff; lamda_goes = xele * cfac + coff; // convert GOES to GEOS theta_geos = asin(sin(theta_goes) * cos(lamda_goes)); lamda_geos = atan(tan(lamda_goes) / cos(theta_goes)); // SIN and COS for computations below cosx = cos(lamda_geos); cosy = cos(theta_geos); sinx = sin(lamda_geos); siny = sin(theta_geos); c1 = dh * cosx * cosy * dh * cosx * cosy; c2 = (cosy * cosy + FP * siny * siny) * d; sdd = c1 - c2; if ((sdd < 0.0)) { xlat = Double.NaN; xlon = Double.NaN; } else { sd = sqrt(sdd); sn = (dh * cosx * cosy - sd) / (cosy * cosy + FP * siny * siny); s1 = dh - sn * cosx * cosy; s2 = sn * sinx * cosy; s3 = -(sn * siny); sxy = sqrt(s1 * s1 + (s2 * s2)); xlon = atan(s2 / s1) + sub_lon_radians; xlat = atan(-(FP * s3 / sxy)); // convert radians to degrees xlon = xlon * (180.0 / PI); xlat = xlat * (180.0 / PI); // Longitudes in [-180,180] if ((xlon > 180)) { xlon = xlon - 360.0; } if ((xlon < -180)) { xlon = xlon + 360.0; } } // end img_to_ll latLons[indexLat][point] = xlat; latLons[indexLon][point] = xlon; } return latLons; } /** * Convert latitudes/longitudes to satellite lines/elements. * * @param latlon Array of latitude/longitude pairs. * Where {@code latlon[indexLat]} are latitudes and * {@code latlon[indexLon]} are longitudes. * * @return Array of line/element pairs. {@code linele[indexLine]} are lines * and {@code linele[indexEle]} are elements. These coordinates are * {@literal "file"} rather than {@literal "image"} coordinates. */ public double[][] toLinEle(double[][] latlon) { final double d_geographic_ssl = plon * deg_to_rad; double rlin; double rele; double d_geographic_lat; double d_geocentric_lat; double d_geographic_lon; double r_earth; double r_1; double r_2; double r_3; double lamda; double theta; int length = latlon[indexLat].length; double[][] lineEles = new double[2][length]; for (int point = 0; point < length; point++) { double rlat = latlon[indexLat][point]; double rlon = latlon[indexLon][point]; if (!isEastPositive) { rlon = -rlon; } // start ll_to_img rlin = 0.0; rele = 0.0; d_geographic_lat = 0.0; d_geocentric_lat = 0.0; d_geographic_lon = 0.0; r_earth = 0.0; r_1 = 0.0; r_2 = 0.0; r_3 = 0.0; lamda = 0.0; theta = 0.0; double xlin; double xele; // Earth (Geographic) Coordinates are converted to Radians d_geographic_lat = rlat * deg_to_rad; d_geographic_lon = rlon * deg_to_rad; d_geocentric_lat = atan(drpo2 / dreq2 * tan(d_geographic_lat)); r_earth = drpo / sqrt(1.0 - (dreq2 - drpo2) / dreq2 * cos(d_geocentric_lat) * cos(d_geocentric_lat)); r_1 = dh - r_earth * cos(d_geocentric_lat) * cos(d_geographic_lon - d_geographic_ssl); r_2 = -(r_earth * cos(d_geocentric_lat) * sin(d_geographic_lon - d_geographic_ssl)); r_3 = r_earth * sin(d_geocentric_lat); if ((r_1 > dh)) { xlin = Double.NaN; xele = Double.NaN; } else { lamda = asin(-(r_2 / sqrt(r_1 * r_1 + r_2 * r_2 + r_3 * r_3))); theta = atan(r_3 / r_1); // image line and element rlin = (theta - loff) / lfac; rele = (lamda - coff) / cfac; // Adjust using Base RESolution xlin = rlin * bres - (bres - 1); xele = rele * bres - (bres - 1); } // end of ll_to_img lineEles[indexLine][point] = xlin; lineEles[indexEle][point] = xele; } return imageCoordToAreaCoord(lineEles, lineEles); } }