// // MOLLnav.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; /** * Navigation class for Mollweide (MOLL) type nav. This code was modified * from the original FORTRAN code (nvxmoll.dlm) on the McIDAS system. It * only supports latitude/longitude to line/element transformations (LL) * and vice/versa. Transform to 'XYZ' not implemented. * @see edu.wisc.ssec.mcidas.AREAnav * @see <A HREF="http://www.ssec.wisc.edu/mcidas/doc/prog_man.html"> * McIDAS Programmer's Manual</A> * * @author Don Murray */ public final class MOLLnav extends AREAnav { private boolean isEastPositive = true; private double drad; private double decc; private double[] tlat = new double[101]; private double[] t = new double[101]; private double[][] coef = new double[4][101]; private double[] lattbl = new double[91]; private double xrow, xcol, rpix, xqlon; private int itype, iwest, icord; private double asq = 40683833.48; private double bsq = 40410330.18; private double ab = 40546851.22; private double ecc = .081992e0; private double eccsqr = 6.72265e-3; private int kwest = -1; private int kcord = 0; private final int KMPP = 1263358032; private final int PPMK = 1347439947; /** * Set up for the real math work. Must pass in the int array * of the MOLL nav 'codicil'. * * @param iparms the nav block from the image file * @throws IllegalArgumentException * if the nav block is not a MOLL type. */ public MOLLnav (int[] iparms) throws IllegalArgumentException { double res; double x; int i; /* No longer needed. Here for consistency with nvxmoll.dlm if (ifunc != 1) { if (iparms[0] == XY ) itype = 1; if (iparms[0] == LL ) itype = 2; return; } */ if (iparms[0] != MOLL ) throw new IllegalArgumentException("Invalid navigation type" + iparms[0]); itype = 2; xrow = iparms[1]; xcol = iparms[2]; xqlon = iparms[4]; drad = iparms[6]/1000.e0; double r = drad; if (iparms[14] == KMPP || iparms[14] == PPMK) { res = iparms[3]; rpix = (0.7071*r)/res; } else { rpix = iparms[3]; } decc = iparms[7]/1.e6; iwest = iparms[9]; if (iwest >= 0 ) iwest = 1; icord = iparms[8]; // set up coefficients; // CALL LLOPT(DRAD,DECC,IWEST,IPARMS(9)) asq = drad*drad; ecc = decc; eccsqr = ecc*ecc; double dpole = Math.sqrt(asq*(1.e0-eccsqr)); bsq = dpole*dpole; ab = drad*dpole; if(iwest < 0) kwest=1; if(icord < 0) kcord=-1; for (i = 0; i < tlat.length; i++) { x = i/100.; if (x >= 1.) { t[i] = 1.; tlat[i] = 1.57080/DEGREES_TO_RADIANS; } else { t[i] = x; tlat[i] = Math.asin((Math.asin(x) +x*Math.sqrt(1.0-x*x))/1.57080)/DEGREES_TO_RADIANS; } } // create cubic spline table - from asspl2.for int n = 100; int m = n - 1; double w, z, t1, s, u, v, zs, zq, ws, wq, aa, ba, ca, da; for (i = 0; i < n; i++) { if (i != 0) t1 = (t[i+1]-t[i-1])/(tlat[i+1]-tlat[i-1]); else { w = (t[1]+t[2])/2.0; z = (tlat[1]+tlat[2])/2.0; t1 = (w-t[0])/(z-tlat[0]); t1 = 2.0*(t[1]-t[0])/(tlat[1]-tlat[0])-t1; } if (i != m) s = (t[i+2]-t[i])/(tlat[i+2]-tlat[i]); else { w = (t[n-1]+t[n-2])/2.0; z = (tlat[n-1]+tlat[n-2])/2.0; s = (t[n]-w)/(tlat[n]-z); s = 2.0*(t[n]-t[n-1])/(tlat[n]-tlat[n-1])-s; } u=t[i+1]; v=t[i]; w=(tlat[i+1]+tlat[i])/2.0; z=(tlat[i+1]-tlat[i])/2.0; zs=z*z; zq=z*zs; ws=w*w; wq=w*ws; aa=.5*(u+v)-.25*z*(s-t1); ba=.75*(u-v)/z-.25*(s+t1); ca=.25*(s-t1)/z; da=.25*(s+t1)/zs-.25*(u-v)/zq; coef[0][i]=aa-ba*w+ca*ws-da*wq; coef[1][i]=ba-2.0*ca*w+3.0*da*ws; coef[2][i]=ca-3.0*da*w; coef[3][i]=da; } for (int j = 0; j < 4; j++) { coef[j][n] = coef[j][n-1]; } i = 0; int j, k; for (int l = 0; l < 91; l++) { u = l; if (i >= n-1) i=0; if (u < tlat[i] || u > tlat[i+1]) { i = 0; j = n; do { k = (i+j)/2; if (u < tlat[k]) j=k; if (u >= tlat[k]) i=k; } while (j > i+1); } lattbl[l] = coef[0][i]+u*(coef[1][i]+u*(coef[2][i]+u*coef[3][i])); } } /** * Converts from satellite coordinates to latitude/longitude * * @param linele array of line/element pairs. Where * linele[indexLine][] is a 'line' and * linele[indexEle][] is an element. These are in * 'file' coordinates (not "image" coordinates.) * * @return latlon[][] array of lat/long pairs. Output array is * latlon[indexLat][] of latitudes and * latlon[indexLon][] of longitudes. * */ public double[][] toLatLon(double[][] linele) { double xlin, xele; double xldif, xedif; double w; double xlat, xlon; double ylat, ylon; double snlt, cslt, snln, csln, r, tnlt; int number = linele[0].length; double[][] latlon = new double[2][number]; // Convert from file to Image coordinates for calculations double[][] imglinele = areaCoordToImageCoord(linele); for (int point=0; point < number; point++) { xlin = imglinele[indexLine][point]; xele = imglinele[indexEle][point]; xldif = Math.abs(xlin - xrow)/rpix; xedif = (xcol - xele)/rpix; // WLH 8 March 2000 // if (xldif > 1.0) if (xlin != xlin || xele != xele || xldif > 1.0) { xlat = Double.NaN; xlon = Double.NaN; } else { w = Math.sqrt(1.0 - xldif*xldif); if (w == 0.0 || Math.abs(xedif/w) > 2.0) { xlat = Double.NaN; xlon = Double.NaN; } else { xlat = Math.asin((Math.asin(xldif)+ xldif*w)/1.57080)/DEGREES_TO_RADIANS; if (xlin > xrow) xlat = -xlat; // Compute angular displacement from std longitude (XQLON) xlon = -90.*(xedif/w); xlon = xqlon - xlon; // Force angles to (-180 < XLON < 180) if (xlon > 180.) xlon = xlon - 360.; if (xlon < -180.) xlon = xlon + 360.; // Convert to cartesian? coordinates if (itype == 1) { // LLCART(YLAT,YLON,XLAT,XLON,Z) ylat=DEGREES_TO_RADIANS*xlat; if (kcord >= 0) ylat = Math.atan2(bsq*Math.sin(ylat), asq*Math.cos(ylat)); ylon = kwest*DEGREES_TO_RADIANS*xlon; snlt = Math.sin(ylat); cslt = Math.cos(ylat); csln = Math.cos(ylon); snln = Math.sin(ylon); tnlt = Math.pow((snlt/cslt),2.0); r = ab*Math.sqrt((1.0+tnlt)/(bsq+asq*tnlt)); xlat = r*cslt*csln; xlon = r*cslt*snln; } } } // transform from McIDAS (west positive longitude) coordinates if (isEastPositive) xlon = -xlon; latlon[indexLat][point] = xlat; latlon[indexLon][point] = xlon; } // end point for loop return latlon; } /** * toLinEle converts lat/long to satellite line/element * * @param latlon array of lat/long pairs. Where latlon[indexLat][] * are latitudes and latlon[indexLon][] are longitudes. * * @return linele[][] array of line/element pairs. Where * linele[indexLine][] is a line and linele[indexEle][] * is an element. These are in 'file' coordinates * (not "image" coordinates); */ public double[][] toLinEle(double[][] latlon) { double xlin, xele; double xlat, xlon; double flat, t, t2, w, diff_lon, xedif; int isign, ilat; int number = latlon[0].length; double[][] linele = new double[2][number]; for (int point=0; point < number; point++) { xlat = latlon[indexLat][point]; // transform to McIDAS (west longitude positive) coordinates xlon = isEastPositive ? - latlon[indexLon][point] : latlon[indexLon][point]; // WLH 8 March 2000 if (Double.isNaN(xlat) || Double.isNaN(xlon) || (Math.abs(xlat) > 90.) ) { // DRM 10 June 2003 xele = Double.NaN; xlin = Double.NaN; } else { isign = -1; if (xlat < 0.0) isign = 1; ilat = (int) (Math.abs(xlat)); flat = Math.abs(xlat) - ilat; t = lattbl[ilat]; if (ilat != 90) t = t + flat*(lattbl[ilat+1] - lattbl[ilat]); t2 = Math.max(0.0, 1.0-t*t); w = Math.sqrt(t2); //** Here we need to handle the problem of computing //** angular differences across the dateline. diff_lon = xlon - xqlon; if (diff_lon < -180.0) diff_lon = diff_lon + 360.; if (diff_lon > 180.0) diff_lon = diff_lon - 360.; xedif = w * (diff_lon)/90.; if (Math.abs(xedif) > 2.0) { xele = Double.NaN; xlin = Double.NaN; } else { xele = xcol - xedif*rpix; xlin = isign*t*rpix + xrow; } } // end if (xlat == xlat && xlon == xlon) linele[indexLine][point] = xlin; linele[indexEle][point] = xele; } // end point loop // Return in 'File' coordinates return imageCoordToAreaCoord(linele, linele); } /** * Converts from satellite coordinates to latitude/longitude * * @param linele array of line/element pairs. Where * linele[indexLine][] is a 'line' and * linele[indexEle][] is an element. These are in * 'file' coordinates (not "image" coordinates.) * * @return latlon[][] array of lat/long pairs. Output array is * latlon[indexLat][] of latitudes and * latlon[indexLon][] of longitudes. * */ public float[][] toLatLon(float[][] linele) { double xlin, xele; double xldif, xedif; double w; double xlat, xlon; double ylat, ylon; double snlt, cslt, snln, csln, r, tnlt; int number = linele[0].length; float[][] latlon = new float[2][number]; // Convert from file to Image coordinates for calculations float[][] imglinele = areaCoordToImageCoord(linele); for (int point=0; point < number; point++) { xlin = imglinele[indexLine][point]; xele = imglinele[indexEle][point]; xldif = Math.abs(xlin - xrow)/rpix; xedif = (xcol - xele)/rpix; // WLH 8 March 2000 // if (xldif > 1.0) if (xlin != xlin || xele != xele || xldif > 1.0) { xlat = Double.NaN; xlon = Double.NaN; } else { w = Math.sqrt(1.0 - xldif*xldif); if (w == 0.0 || Math.abs(xedif/w) > 2.0) { xlat = Double.NaN; xlon = Double.NaN; } else { xlat = Math.asin((Math.asin(xldif)+ xldif*w)/1.57080)/DEGREES_TO_RADIANS; if (xlin > xrow) xlat = -xlat; // Compute angular displacement from std longitude (XQLON) xlon = -90.*(xedif/w); xlon = xqlon - xlon; // Force angles to (-180 < XLON < 180) if (xlon > 180.) xlon = xlon - 360.; if (xlon < -180.) xlon = xlon + 360.; // Convert to cartesian? coordinates if (itype == 1) { // LLCART(YLAT,YLON,XLAT,XLON,Z) ylat=DEGREES_TO_RADIANS*xlat; if (kcord >= 0) ylat = Math.atan2(bsq*Math.sin(ylat), asq*Math.cos(ylat)); ylon = kwest*DEGREES_TO_RADIANS*xlon; snlt = Math.sin(ylat); cslt = Math.cos(ylat); csln = Math.cos(ylon); snln = Math.sin(ylon); tnlt = Math.pow((snlt/cslt),2.0); r = ab*Math.sqrt((1.0+tnlt)/(bsq+asq*tnlt)); xlat = r*cslt*csln; xlon = r*cslt*snln; } } } // transform from McIDAS (west positive longitude) coordinates if (isEastPositive) xlon = -xlon; latlon[indexLat][point] = (float) xlat; latlon[indexLon][point] = (float) xlon; } // end point for loop return latlon; } /** * toLinEle converts lat/long to satellite line/element * * @param latlon array of lat/long pairs. Where latlon[indexLat][] * are latitudes and latlon[indexLon][] are longitudes. * * @return linele[][] array of line/element pairs. Where * linele[indexLine][] is a line and linele[indexEle][] * is an element. These are in 'file' coordinates * (not "image" coordinates); */ public float[][] toLinEle(float[][] latlon) { double xlin, xele; double xlat, xlon; double flat, t, t2, w, diff_lon, xedif; int isign, ilat; int number = latlon[0].length; float[][] linele = new float[2][number]; for (int point=0; point < number; point++) { xlat = latlon[indexLat][point]; // transform to McIDAS (west longitude positive) coordinates xlon = isEastPositive ? - latlon[indexLon][point] : latlon[indexLon][point]; // WLH 8 March 2000 if (Double.isNaN(xlat) || Double.isNaN(xlon) || (Math.abs(xlat) > 90.) ) { // DRM 10 June 2003 xele = Double.NaN; xlin = Double.NaN; } else { isign = -1; if (xlat < 0.0) isign = 1; ilat = (int) (Math.abs(xlat)); flat = Math.abs(xlat) - ilat; t = lattbl[ilat]; if (ilat != 90) t = t + flat*(lattbl[ilat+1] - lattbl[ilat]); t2 = Math.max(0.0, 1.0-t*t); w = Math.sqrt(t2); //** Here we need to handle the problem of computing //** angular differences across the dateline. diff_lon = xlon - xqlon; if (diff_lon < -180.0) diff_lon = diff_lon + 360.; if (diff_lon > 180.0) diff_lon = diff_lon - 360.; xedif = w * (diff_lon)/90.; if (Math.abs(xedif) > 2.0) { xele = Double.NaN; xlin = Double.NaN; } else { xele = xcol - xedif*rpix; xlin = isign*t*rpix + xrow; } } // end if (xlat == xlat && xlon == xlon) linele[indexLine][point] = (float) xlin; linele[indexEle][point] = (float) xele; } // end point loop // Return in 'File' coordinates return imageCoordToAreaCoord(linele, linele); } }