// // MSATnav.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 Meteosat (MSAT) type nav. This code was modified * from the original FORTRAN code (nvxmsat.dlm) on the McIDAS system. It * only supports latitude/longitude to line/element transformations (LL) * and vice/versa. Transform to 'XYZ' not implemented. * @see <A HREF="http://www.ssec.wisc.edu/mcidas/doc/prog_man.html"> * McIDAS Programmer's Manual</A> * * @author Don Murray */ public final class MSATnav extends AREAnav { private boolean isEastPositive = true; final double NOMORB=42164.; // nominal radial distance of satellite (km) final double EARTH_RADIUS=6378.155; // earth equatorial radius (km) int itype; double h; double a; double rp; double lpsi2; double deltax; double deltay; double rflon; // reference longitude; double sublon; int[] ioff = new int[3]; /** * Set up for the real math work. Must pass in the int array * of the MSAT nav 'codicil'. * * @param iparms the nav block from the image file * @throws IllegalArgumentException * if the nav block is not a MSAT type. */ public MSATnav (int[] iparms) throws IllegalArgumentException { /* No longer needed. Kept for consistency with nvxmsat.dlm if (ifunc != 1) { if (iparms[0] == XY ) itype = 1; if (iparms[0] == LL ) itype = 2; return; } */ if (iparms[0] != MSAT ) throw new IllegalArgumentException("Invalid navigation type" + iparms[0]); itype = 2; System.arraycopy(iparms, 3, ioff, 0, 3); h = (double) NOMORB - EARTH_RADIUS; a = 1./297.; rp = EARTH_RADIUS / (1. + a); lpsi2=1; deltax=18./2500.; deltay=18./2500.; rflon=0.0; sublon = McIDASUtil.integerLatLonToDouble(iparms[6]); } /** 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 xele, xlin; double xele2, xlin2; double xfi, xla; double x, y; double xr, yr; double tanx, tany; double val1, val2; double yk; double vmu; double cosrf, sinrf; double teta; double xt, yt, zt; double rs; int number = linele[0].length; double[][] latlon = new double[2][number]; // Convert array to Image coordinates for computations double[][] imglinele = areaCoordToImageCoord(linele); for (int point=0; point < number; point++) { xlin = imglinele[indexLine][point]; xele = imglinele[indexEle][point]; xele2 = xele/2.; xlin2 = xlin/2.; x = 1250.5 - xele2; y = ioff[2] - (xlin2 + ioff[1] - ioff[0]); xr = x; yr = y; x = xr*lpsi2*deltax*DEGREES_TO_RADIANS; y = yr*lpsi2*deltay*DEGREES_TO_RADIANS; rs = EARTH_RADIUS + h; tanx = Math.tan(x); tany = Math.tan(y); val1=1.+tanx*tanx; val2=1.+(tany*tany)*((1.+a)*(1.+a)); yk=rs/EARTH_RADIUS; if ((val1*val2) > ((yk*yk)/(yk*yk-1))) { latlon[indexLat][point] = Double.NaN; latlon[indexLon][point] = Double.NaN; } else { vmu = (rs-(EARTH_RADIUS*(Math.sqrt((yk*yk)- (yk*yk-1)*val1*val2))))/(val1*val2); cosrf = Math.cos(rflon*DEGREES_TO_RADIANS); sinrf = Math.sin(rflon*DEGREES_TO_RADIANS); xt = (rs*cosrf) + (vmu*(tanx*sinrf - cosrf)); yt = (rs*sinrf) - (vmu*(tanx*cosrf + sinrf)); zt = vmu*tany/Math.cos(x); teta = Math.asin(zt/rp); xfi = (Math.atan(((Math.tan(teta))*EARTH_RADIUS)/rp))* RADIANS_TO_DEGREES; xla=-Math.atan(yt/xt)*RADIANS_TO_DEGREES; // change longitude for correct subpoint xla = xla + sublon; // put longitude into East Positive (form) if (isEastPositive) xla = -xla; latlon[indexLat][point] = xfi; latlon[indexLon][point] = xla; } // end lat/lon point calculation } // 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 y; double x1, y1; double xfi, xla; double rom; double r1, r2; double coslo, sinlo; double teta; double xt, yt, zt; double px, py; double rs; double reph, rpph; double xr, yr; int number = latlon[0].length; double[][] linele = new double[2][number]; for (int point=0; point < number; point++) { x1 = latlon[indexLat][point]; // expects positive East Longitude. y1 = isEastPositive ? latlon[indexLon][point] : -latlon[indexLon][point]; // if in cartesian coords, transform to lat/lon if (itype == 1) { y = latlon[indexLon][point]; // NXYZLL(x,y,z,zlat,zlon); y1 = -y1; } // correct for sublon y1 = y1 + sublon; xfi = x1*DEGREES_TO_RADIANS; xla = y1*DEGREES_TO_RADIANS; rom = (EARTH_RADIUS*rp)/ Math.sqrt( rp*rp*Math.cos(xfi)*Math.cos(xfi)+ EARTH_RADIUS*EARTH_RADIUS*Math.sin(xfi)*Math.sin(xfi)); y = Math.sqrt(h*h+rom*rom-2*h*rom*Math.cos(xfi)*Math.cos(xla)); r1 = y*y + rom*rom; r2 = h*h; if (r1 > r2) // invalid point { linele[indexLine][point] = Double.NaN; linele[indexEle][point] = Double.NaN; } else // calculate line an element { rs = EARTH_RADIUS + h; reph = EARTH_RADIUS; rpph = rp; coslo = Math.cos(rflon*DEGREES_TO_RADIANS); sinlo = Math.sin(rflon*DEGREES_TO_RADIANS); teta = Math.atan((rpph/reph)*Math.tan(xfi)); xt = reph*Math.cos(teta)*Math.cos(xla); yt = reph*Math.cos(teta)*Math.sin(xla); zt = rpph*Math.sin(teta); px = Math.atan((coslo*(yt-rs*sinlo)-(xt-rs*coslo)*sinlo)/ (sinlo*(yt-rs*sinlo)+(xt-rs*coslo)*coslo)); py = Math.atan(zt*((Math.tan(px)*sinlo- coslo)/(xt-rs*coslo))*Math.cos(px)); px = px*RADIANS_TO_DEGREES; py = py*RADIANS_TO_DEGREES; xr = px/(deltax*lpsi2); yr = py/(deltay*lpsi2); xr = 1250.5-xr; yr = yr + ioff[2] + ioff[1] - ioff[0]; xr = xr*2; yr = 5000-yr*2; linele[indexLine][point] = yr; linele[indexEle][point] = xr; } // end calculations } // 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 xele, xlin; double xele2, xlin2; double xfi, xla; double x, y; double xr, yr; double tanx, tany; double val1, val2; double yk; double vmu; double cosrf, sinrf; double teta; double xt, yt, zt; double rs; int number = linele[0].length; float[][] latlon = new float[2][number]; // Convert array to Image coordinates for computations float[][] imglinele = areaCoordToImageCoord(linele); for (int point=0; point < number; point++) { xlin = imglinele[indexLine][point]; xele = imglinele[indexEle][point]; xele2 = xele/2.; xlin2 = xlin/2.; x = 1250.5 - xele2; y = ioff[2] - (xlin2 + ioff[1] - ioff[0]); xr = x; yr = y; x = xr*lpsi2*deltax*DEGREES_TO_RADIANS; y = yr*lpsi2*deltay*DEGREES_TO_RADIANS; rs = EARTH_RADIUS + h; tanx = Math.tan(x); tany = Math.tan(y); val1=1.+tanx*tanx; val2=1.+(tany*tany)*((1.+a)*(1.+a)); yk=rs/EARTH_RADIUS; if ((val1*val2) > ((yk*yk)/(yk*yk-1))) { latlon[indexLat][point] = Float.NaN; latlon[indexLon][point] = Float.NaN; } else { vmu = (rs-(EARTH_RADIUS*(Math.sqrt((yk*yk)- (yk*yk-1)*val1*val2))))/(val1*val2); cosrf = Math.cos(rflon*DEGREES_TO_RADIANS); sinrf = Math.sin(rflon*DEGREES_TO_RADIANS); xt = (rs*cosrf) + (vmu*(tanx*sinrf - cosrf)); yt = (rs*sinrf) - (vmu*(tanx*cosrf + sinrf)); zt = vmu*tany/Math.cos(x); teta = Math.asin(zt/rp); xfi = (Math.atan(((Math.tan(teta))*EARTH_RADIUS)/rp))* RADIANS_TO_DEGREES; xla=-Math.atan(yt/xt)*RADIANS_TO_DEGREES; // change longitude for correct subpoint xla = xla + sublon; // put longitude into East Positive (form) if (isEastPositive) xla = -xla; latlon[indexLat][point] = (float) xfi; latlon[indexLon][point] = (float) xla; } // end lat/lon point calculation } // 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 y; double x1, y1; double xfi, xla; double rom; double r1, r2; double coslo, sinlo; double teta; double xt, yt, zt; double px, py; double rs; double reph, rpph; double xr, yr; int number = latlon[0].length; float[][] linele = new float[2][number]; for (int point=0; point < number; point++) { x1 = latlon[indexLat][point]; // expects positive East Longitude. y1 = isEastPositive ? latlon[indexLon][point] : -latlon[indexLon][point]; // if in cartesian coords, transform to lat/lon if (itype == 1) { y = latlon[indexLon][point]; // NXYZLL(x,y,z,zlat,zlon); y1 = -y1; } // correct for sublon y1 = y1 + sublon; xfi = x1*DEGREES_TO_RADIANS; xla = y1*DEGREES_TO_RADIANS; rom = (EARTH_RADIUS*rp)/ Math.sqrt( rp*rp*Math.cos(xfi)*Math.cos(xfi)+ EARTH_RADIUS*EARTH_RADIUS*Math.sin(xfi)*Math.sin(xfi)); y = Math.sqrt(h*h+rom*rom-2*h*rom*Math.cos(xfi)*Math.cos(xla)); r1 = y*y + rom*rom; r2 = h*h; if (r1 > r2) // invalid point { linele[indexLine][point] = Float.NaN; linele[indexEle][point] = Float.NaN; } else // calculate line an element { rs = EARTH_RADIUS + h; reph = EARTH_RADIUS; rpph = rp; coslo = Math.cos(rflon*DEGREES_TO_RADIANS); sinlo = Math.sin(rflon*DEGREES_TO_RADIANS); teta = Math.atan((rpph/reph)*Math.tan(xfi)); xt = reph*Math.cos(teta)*Math.cos(xla); yt = reph*Math.cos(teta)*Math.sin(xla); zt = rpph*Math.sin(teta); px = Math.atan((coslo*(yt-rs*sinlo)-(xt-rs*coslo)*sinlo)/ (sinlo*(yt-rs*sinlo)+(xt-rs*coslo)*coslo)); py = Math.atan(zt*((Math.tan(px)*sinlo- coslo)/(xt-rs*coslo))*Math.cos(px)); px = px*RADIANS_TO_DEGREES; py = py*RADIANS_TO_DEGREES; xr = px/(deltax*lpsi2); yr = py/(deltay*lpsi2); xr = 1250.5-xr; yr = yr + ioff[2] + ioff[1] - ioff[0]; xr = xr*2; yr = 5000-yr*2; linele[indexLine][point] = (float) yr; linele[indexEle][point] = (float) xr; } // end calculations } // end point loop // Return in 'File' coordinates return imageCoordToAreaCoord(linele, linele); } }