13ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/* 23ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * To change this template, choose Tools | Templates 33ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * and open the template in the editor. 43ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 53ed852eea50f9d4cd633efb8c2b054b8e33c253cristypackage jme3tools.navigation; 63ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 73ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 83ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 93ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/** 103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * A utlity class for performing position calculations 113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * 123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @author Benjamin Jakobus, based on JMarine (by Cormac Gebruers and Benjamin 133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Jakobus) 143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @version 1.0 153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 16de984cdc3631106b1cbbb8d3972b76a0fc27e8e8cristy */ 173ed852eea50f9d4cd633efb8c2b054b8e33c253cristypublic class NavCalculator { 183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy private double distance; 207ce65e7125a4e1df1a274ce373c537a9df9c16cdCristy private double trueCourse; 213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /* The earth's radius in meters */ 233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static final int WGS84_EARTH_RADIUS = 6378137; 243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy private String strCourse; 253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /* The sailing calculation type */ 273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static final int MERCATOR = 0; 283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static final int GC = 1; 293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /* The degree precision to use for courses */ 313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static final int RL_CRS_PRECISION = 1; 323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /* The distance precision to use for distances */ 343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static final int RL_DIST_PRECISION = 1; 353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static final int METERS_PER_MINUTE = 1852; 363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Constructor 393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param P1 403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param P2 413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param calcType 424c08aed51c5899665ade97263692328eea4af106cristy * @since 1.0 434c08aed51c5899665ade97263692328eea4af106cristy */ 444c08aed51c5899665ade97263692328eea4af106cristy public NavCalculator(Position P1, Position P2, int calcType) { 454c08aed51c5899665ade97263692328eea4af106cristy switch (calcType) { 464c08aed51c5899665ade97263692328eea4af106cristy case MERCATOR: 474c08aed51c5899665ade97263692328eea4af106cristy mercatorSailing(P1, P2); 484c08aed51c5899665ade97263692328eea4af106cristy break; 494c08aed51c5899665ade97263692328eea4af106cristy case GC: 504c08aed51c5899665ade97263692328eea4af106cristy greatCircleSailing(P1, P2); 514c08aed51c5899665ade97263692328eea4af106cristy break; 524c08aed51c5899665ade97263692328eea4af106cristy } 534c08aed51c5899665ade97263692328eea4af106cristy } 544c08aed51c5899665ade97263692328eea4af106cristy 554c08aed51c5899665ade97263692328eea4af106cristy /** 564c08aed51c5899665ade97263692328eea4af106cristy * Constructor 574c08aed51c5899665ade97263692328eea4af106cristy * @since 1.0 584c08aed51c5899665ade97263692328eea4af106cristy */ 594c08aed51c5899665ade97263692328eea4af106cristy public NavCalculator() { 604c08aed51c5899665ade97263692328eea4af106cristy } 613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Determines a great circle track between two positions 643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p1 origin position 653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p2 destination position 663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public GCSailing greatCircleSailing(Position p1, Position p2) { 683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return new GCSailing(new int[0], new float[0]); 693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Determines a Rhumb Line course and distance between two points 733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p1 origin position 743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p2 destination position 753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public RLSailing rhumbLineSailing(Position p1, Position p2) { 773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy RLSailing rl = mercatorSailing(p1, p2); 783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return rl; 793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Determines the rhumb line course and distance between two positions 833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p1 origin position 843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p2 destination position 853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public RLSailing mercatorSailing(Position p1, Position p2) { 873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dLat = computeDLat(p1.getLatitude(), p2.getLatitude()); 893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //plane sailing... 903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (dLat == 0) { 913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy RLSailing rl = planeSailing(p1, p2); 923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return rl; 933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dLong = computeDLong(p1.getLongitude(), p2.getLongitude()); 963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dmp = (float) computeDMPClarkeSpheroid(p1.getLatitude(), p2.getLatitude()); 973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy trueCourse = (float) Math.toDegrees(Math.atan(dLong / dmp)); 993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double degCrs = convertCourse((float) trueCourse, p1, p2); 1003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy distance = (float) Math.abs(dLat / Math.cos(Math.toRadians(trueCourse))); 1013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy RLSailing rl = new RLSailing(degCrs, (float) distance); 1033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy trueCourse = rl.getCourse(); 1043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy strCourse = (dLat < 0 ? "S" : "N"); 1053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy strCourse += " " + trueCourse; 1063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy strCourse += " " + (dLong < 0 ? "W" : "E"); 1073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return rl; 1083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 1123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Calculate a plane sailing situation - i.e. where Lats are the same 1133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p1 1143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p2 1153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return 116952a6063b767d87c452b00435088197d38916dd8cristy * @since 1.0 117952a6063b767d87c452b00435088197d38916dd8cristy */ 118952a6063b767d87c452b00435088197d38916dd8cristy public RLSailing planeSailing(Position p1, Position p2) { 119952a6063b767d87c452b00435088197d38916dd8cristy double dLong = computeDLong(p1.getLongitude(), p2.getLongitude()); 120952a6063b767d87c452b00435088197d38916dd8cristy 1213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double sgnDLong = 0 - (dLong / Math.abs(dLong)); 122952a6063b767d87c452b00435088197d38916dd8cristy if (Math.abs(dLong) > 180 * 60) { 1233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy dLong = (360 * 60 - Math.abs(dLong)) * sgnDLong; 1243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 126952a6063b767d87c452b00435088197d38916dd8cristy double redist = 0; 127952a6063b767d87c452b00435088197d38916dd8cristy double recourse = 0; 128952a6063b767d87c452b00435088197d38916dd8cristy if (p1.getLatitude() == 0) { 129952a6063b767d87c452b00435088197d38916dd8cristy redist = Math.abs(dLong); 130952a6063b767d87c452b00435088197d38916dd8cristy } else { 131952a6063b767d87c452b00435088197d38916dd8cristy redist = Math.abs(dLong * (float) Math.cos(p1.getLatitude() * 2 * Math.PI / 360)); 132952a6063b767d87c452b00435088197d38916dd8cristy } 133952a6063b767d87c452b00435088197d38916dd8cristy recourse = (float) Math.asin(0 - sgnDLong); 134952a6063b767d87c452b00435088197d38916dd8cristy recourse = recourse * 360 / 2 / (float) Math.PI; 1353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (recourse < 0) { 1373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy recourse = recourse + 360; 1383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return new RLSailing(recourse, redist); 1403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 1433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Converts a course from cardinal XddY to ddd notation 1443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param tc 1453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p1 position one 1463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param p2 position two 1473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return 1483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 1493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 1503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static double convertCourse(float tc, Position p1, Position p2) { 151952a6063b767d87c452b00435088197d38916dd8cristy 152952a6063b767d87c452b00435088197d38916dd8cristy double dLat = p1.getLatitude() - p2.getLatitude(); 1533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dLong = p1.getLongitude() - p2.getLongitude(); 154952a6063b767d87c452b00435088197d38916dd8cristy //NE 1553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (dLong >= 0 & dLat >= 0) { 1563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(tc); 1573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //SE 1603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (dLong >= 0 & dLat < 0) { 1613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return 180 - Math.abs(tc); 1623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //SW 1653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (dLong < 0 & dLat < 0) { 1663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return 180 + Math.abs(tc); 1673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //NW 170952a6063b767d87c452b00435088197d38916dd8cristy if (dLong < 0 & dLat >= 0) { 171952a6063b767d87c452b00435088197d38916dd8cristy return 360 - Math.abs(tc); 1723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 173bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy return -1; 1743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 1774c08aed51c5899665ade97263692328eea4af106cristy * Getter method for the distance between two points 1783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return distance 1793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 1803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 181c6da28e61bb609d2b2cfdcc7752106c973415edbcristy public double getDistance() { 182c6da28e61bb609d2b2cfdcc7752106c973415edbcristy return distance; 1833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 1853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 1863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Getter method for the true course 1873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return true course 1883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 1893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 1903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public double getTrueCourse() { 1913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return trueCourse; 1923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 1933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 194e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy /** 1953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Getter method for the true course 1963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return true course 1973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 1983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 199e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy public String getStrCourse() { 2009950d57e1124b73f684fb5946e206994cefda628cristy return strCourse; 2013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 2023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 2043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Computes the difference in meridional parts for two latitudes in minutes 2053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * (based on Clark 1880 spheroid) 2063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lat1 207952a6063b767d87c452b00435088197d38916dd8cristy * @param lat2 2083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return difference in minutes 2093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 2103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 2113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static double computeDMPClarkeSpheroid(double lat1, double lat2) { 2123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double absLat1 = Math.abs(lat1); 2133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double absLat2 = Math.abs(lat2); 2143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double m1 = (7915.704468 * (Math.log(Math.tan(Math.toRadians(45 2163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy + (absLat1 / 2)))) / Math.log(10)) 2173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 23.268932 * Math.sin(Math.toRadians(absLat1)) 2183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 0.052500 * Math.pow(Math.sin(Math.toRadians(absLat1)), 3) 2193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 0.000213 * Math.pow(Math.sin(Math.toRadians(absLat1)), 5)); 2203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double m2 = (7915.704468 * (Math.log(Math.tan(Math.toRadians(45 2223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy + (absLat2 / 2)))) / Math.log(10)) 2233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 23.268932 * Math.sin(Math.toRadians(absLat2)) 2243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 0.052500 * Math.pow(Math.sin(Math.toRadians(absLat2)), 3) 2253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 0.000213 * Math.pow(Math.sin(Math.toRadians(absLat2)), 5)); 2263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if ((lat1 <= 0 && lat2 <= 0) || (lat1 > 0 && lat2 > 0)) { 2273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(m1 - m2); 2283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else { 2293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return m1 + m2; 2301fe0b879964fa7797e3d68574d297922a47c4034Cristy } 2313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 2323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 233f50886b34832135cf6e2e1458f2a324bb1704191cristy /** 2343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Computes the difference in meridional parts for a perfect sphere between 2353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * two degrees of latitude 2363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lat1 2373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lat2 2383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return difference in meridional parts between lat1 and lat2 in minutes 2393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 2403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 2413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static float computeDMPWGS84Spheroid(float lat1, float lat2) { 2423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy float absLat1 = Math.abs(lat1); 2433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy float absLat2 = Math.abs(lat2); 2443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy float m1 = (float) (7915.7045 * Math.log10(Math.tan(Math.toRadians(45 + (absLat1 / 2)))) 2463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 23.01358 * Math.sin(absLat1 - 0.05135) * Math.pow(Math.sin(absLat1), 3)); 2473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy float m2 = (float) (7915.7045 * Math.log10(Math.tan(Math.toRadians(45 + (absLat2 / 2)))) 2493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy - 23.01358 * Math.sin(absLat2 - 0.05135) * Math.pow(Math.sin(absLat2), 3)); 2503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lat1 <= 0 & lat2 <= 0 || lat1 > 0 & lat2 > 0) { 2523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(m1 - m2); 2531fe0b879964fa7797e3d68574d297922a47c4034Cristy } else { 2543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return m1 + m2; 2553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 2563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 257b0a657e13c4aefba39c51292005427b47277869dcristy 258b0a657e13c4aefba39c51292005427b47277869dcristy /** 259952a6063b767d87c452b00435088197d38916dd8cristy * Predicts the position of a target for a given time in the future 260952a6063b767d87c452b00435088197d38916dd8cristy * @param time the number of seconds from now for which to predict the future 261952a6063b767d87c452b00435088197d38916dd8cristy * position 262952a6063b767d87c452b00435088197d38916dd8cristy * @param speed the miles per minute that the target is traveling 2633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param currentLat the target's current latitude 2643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param currentLong the target's current longitude 2653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param course the target's current course in degrees 2663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return the predicted future position 2673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 268acabb847a592ca5e430c1c0949d03acfc0b78bb9cristy */ 269acabb847a592ca5e430c1c0949d03acfc0b78bb9cristy public static Position predictPosition(int time, double speed, 270acabb847a592ca5e430c1c0949d03acfc0b78bb9cristy double currentLat, double currentLong, double course) { 271952a6063b767d87c452b00435088197d38916dd8cristy Position futurePosition = null; 272952a6063b767d87c452b00435088197d38916dd8cristy course = Math.toRadians(course); 273952a6063b767d87c452b00435088197d38916dd8cristy double futureLong = currentLong + speed * time * Math.sin(course); 2743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double futureLat = currentLat + speed * time * Math.cos(course); 2753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy try { 276d15e65928aec551b7388c2863de3e3e628e2e0ddcristy futurePosition = new Position(futureLat, futureLong); 2773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } catch (InvalidPositionException ipe) { 2783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy ipe.printStackTrace(); 2793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 280bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy return futurePosition; 2811fe0b879964fa7797e3d68574d297922a47c4034Cristy 2823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 2833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 2843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 2853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Computes the coordinate of position B relative to an offset given 286bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy * a distance and an angle. 2873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * 2883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param offset The offset position. 2893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param bearing The bearing between the offset and the coordinate 2903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * that you want to calculate. 2913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param distance The distance, in meters, between the offset 2923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * and point B. 2931fe0b879964fa7797e3d68574d297922a47c4034Cristy * @return The position of point B that is located from 2943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * given offset at given distance and angle. 2953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 2963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 2973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static Position computePosition(Position initialPos, double heading, 2983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double distance) { 2993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (initialPos == null) { 3003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return null; 3013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double angle; 3033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (heading < 90) { 3043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy angle = heading; 3053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else if (heading > 90 && heading < 180) { 3063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy angle = 180 - heading; 307bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy } else if (heading > 180 && heading < 270) { 308bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy angle = heading - 180; 309acd2ed254c18c254a0ab5aafa06d1645e5d079d8cristy } else { 3103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy angle = 360 - heading; 3113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy Position newPosition = null; 3143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy // Convert meters into nautical miles 3163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy distance = distance * 0.000539956803; 3174c08aed51c5899665ade97263692328eea4af106cristy angle = Math.toRadians(angle); 3183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double initialLat = initialPos.getLatitude(); 3193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double initialLong = initialPos.getLongitude(); 3203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dlat = distance * Math.cos(angle); 3213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy dlat = dlat / 60; 3224c08aed51c5899665ade97263692328eea4af106cristy dlat = Math.abs(dlat); 3233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double newLat = 0; 3243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if ((heading > 270 && heading < 360) || (heading > 0 && heading < 90)) { 3253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLat = initialLat + dlat; 3263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else if (heading < 270 && heading > 90) { 3274c08aed51c5899665ade97263692328eea4af106cristy newLat = initialLat - dlat; 3283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double meanLat = (Math.abs(dlat) / 2.0) + newLat; 3303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dep = (Math.abs(dlat * 60)) * Math.tan(angle); 3313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double dlong = dep * (1.0 / Math.cos(Math.toRadians(meanLat))); 3323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy dlong = dlong / 60; 3334c08aed51c5899665ade97263692328eea4af106cristy dlong = Math.abs(dlong); 3343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double newLong; 3353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (heading > 180 && heading < 360) { 3363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLong = initialLong - dlong; 3373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else { 3383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLong = initialLong + dlong; 3393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (newLong < -180) { 3423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double diff = Math.abs(newLong + 180); 3433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLong = 180 - diff; 3443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (newLong > 180) { 3473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy double diff = Math.abs(newLong + 180); 3483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLong = (180 - diff) * -1; 349bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy } 350bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy 351acd2ed254c18c254a0ab5aafa06d1645e5d079d8cristy if (heading == 0 || heading == 360 || heading == 180) { 3523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLong = initialLong; 3533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLat = initialLat + dlat; 3543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else if (heading == 90 || heading == 270) { 3553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLat = initialLat; 3563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy// newLong = initialLong + dlong; THIS WAS THE ORIGINAL (IT WORKED) 3574c08aed51c5899665ade97263692328eea4af106cristy newLong = initialLong - dlong; 3583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy try { 3603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newPosition = new Position(newLat, 3613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy newLong); 3624c08aed51c5899665ade97263692328eea4af106cristy } catch (InvalidPositionException ipe) { 3633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy ipe.printStackTrace(); 3643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy System.out.println(newLat + "," + newLong); 3653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return newPosition; 3674c08aed51c5899665ade97263692328eea4af106cristy } 3683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 3703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Computes the difference in Longitude between two positions and assigns the 3713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * correct sign -westwards travel, + eastwards travel 3723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lng1 3734c08aed51c5899665ade97263692328eea4af106cristy * @param lng2 3743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return difference in longitude 3753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 3763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 3773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static double computeDLong(double lng1, double lng2) { 3783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 - lng2 == 0) { 3793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return 0; 3803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy // both easterly 3833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 >= 0 & lng2 >= 0) { 3843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return -(lng1 - lng2) * 60; 385cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy } 386c6da28e61bb609d2b2cfdcc7752106c973415edbcristy //both westerly 3873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 < 0 & lng2 < 0) { 3883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return -(lng1 - lng2) * 60; 3893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 3903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 3913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //opposite sides of Date line meridian 3923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 393952a6063b767d87c452b00435088197d38916dd8cristy //sum less than 180 3943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (Math.abs(lng1) + Math.abs(lng2) < 180) { 3953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 < 0 & lng2 > 0) { 3963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return -(Math.abs(lng1) + Math.abs(lng2)) * 60; 3973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else { 3983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(lng1) + Math.abs(lng2) * 60; 3993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else { 4013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //sum greater than 180 4023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 < 0 & lng2 > 0) { 4033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return -(360 - (Math.abs(lng1) + Math.abs(lng2))) * 60; 4043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } else { 4053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return (360 - (Math.abs(lng1) + Math.abs(lng2))) * 60; 4063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 4113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Computes the difference in Longitude between two positions and assigns the 4123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * correct sign -westwards travel, + eastwards travel 4133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lng1 4143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lng2 4153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return difference in longitude 4163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 4173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 418bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy public static double computeLongDiff(double lng1, double lng2) { 4193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 - lng2 == 0) { 4203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return 0; 421bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy } 4223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy // both easterly 4243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 >= 0 & lng2 >= 0) { 4253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(-(lng1 - lng2) * 60); 42606b627a07ff44e1ff93ef1288c9f428066ded10ddirk } 4273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //both westerly 42808e9a113db499034abb5ad8d59b42f8eca3c641cdirk if (lng1 < 0 & lng2 < 0) { 42908e9a113db499034abb5ad8d59b42f8eca3c641cdirk return Math.abs(-(lng1 - lng2) * 60); 4303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng1 == 0) { 4333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(lng2 * 60); 4343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lng2 == 0) { 4373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return Math.abs(lng1 * 60); 4383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return (Math.abs(lng1) + Math.abs(lng2)) * 60; 4413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 4423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy /** 4443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * Compute the difference in latitude between two positions 4453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lat1 4463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @param lat2 4473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @return difference in latitude 4483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy * @since 1.0 4493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy */ 4503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy public static double computeDLat(double lat1, double lat2) { 4513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //same side of equator 4523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy 4533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy //plane sailing 4543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy if (lat1 - lat2 == 0) { 4553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy return 0; 4563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy } 457 458 //both northerly 459 if (lat1 >= 0 & lat2 >= 0) { 460 return -(lat1 - lat2) * 60; 461 } 462 //both southerly 463 if (lat1 < 0 & lat2 < 0) { 464 return -(lat1 - lat2) * 60; 465 } 466 467 //opposite sides of equator 468 if (lat1 >= 0) { 469 //heading south 470 return -(Math.abs(lat1) + Math.abs(lat2)); 471 } else { 472 //heading north 473 return (Math.abs(lat1) + Math.abs(lat2)); 474 } 475 } 476 477 public static class Quadrant { 478 479 private static final Quadrant FIRST = new Quadrant(1, 1); 480 private static final Quadrant SECOND = new Quadrant(-1, 1); 481 private static final Quadrant THIRD = new Quadrant(-1, -1); 482 private static final Quadrant FOURTH = new Quadrant(1, -1); 483 private final int lonMultiplier; 484 private final int latMultiplier; 485 486 public Quadrant(final int xMultiplier, final int yMultiplier) { 487 this.lonMultiplier = xMultiplier; 488 this.latMultiplier = yMultiplier; 489 } 490 491 static Quadrant getQuadrant(double degrees, boolean invert) { 492 if (invert) { 493 if (degrees >= 0 && degrees <= 90) { 494 return FOURTH; 495 } else if (degrees > 90 && degrees <= 180) { 496 return THIRD; 497 } else if (degrees > 180 && degrees <= 270) { 498 return SECOND; 499 } 500 return FIRST; 501 } else { 502 if (degrees >= 0 && degrees <= 90) { 503 return FIRST; 504 } else if (degrees > 90 && degrees <= 180) { 505 return SECOND; 506 } else if (degrees > 180 && degrees <= 270) { 507 return THIRD; 508 } 509 return FOURTH; 510 } 511 } 512 } 513 514 /** 515 * Converts meters to degrees. 516 * 517 * @param meters The meters that you want to convert into degrees. 518 * @return The degree equivalent of the given meters. 519 * @since 1.0 520 */ 521 public static double toDegrees(double meters) { 522 return (meters / METERS_PER_MINUTE) / 60; 523 } 524 525 /** 526 * Computes the bearing between two points. 527 * 528 * @param p1 529 * @param p2 530 * @return 531 * @since 1.0 532 */ 533 public static int computeBearing(Position p1, Position p2) { 534 int bearing; 535 double dLon = computeDLong(p1.getLongitude(), p2.getLongitude()); 536 double y = Math.sin(dLon) * Math.cos(p2.getLatitude()); 537 double x = Math.cos(p1.getLatitude()) * Math.sin(p2.getLatitude()) 538 - Math.sin(p1.getLatitude()) * Math.cos(p2.getLatitude()) * Math.cos(dLon); 539 bearing = (int) Math.toDegrees(Math.atan2(y, x)); 540 return bearing; 541 } 542 543 /** 544 * Computes the angle between two points. 545 * 546 * @param p1 547 * @param p2 548 * @return 549 */ 550 public static int computeAngle(Position p1, Position p2) { 551 // cos (adj / hyp) 552 double adj = Math.abs(p1.getLongitude() - p2.getLongitude()); 553 double opp = Math.abs(p1.getLatitude() - p2.getLatitude()); 554 return (int) Math.toDegrees(Math.atan(opp / adj)); 555 556// int angle = (int)Math.atan2(p2.getLatitude() - p1.getLatitude(), 557// p2.getLongitude() - p1.getLongitude()); 558 //Actually it's ATan2(dy , dx) where dy = y2 - y1 and dx = x2 - x1, or ATan(dy / dx) 559 } 560 561 public static int computeHeading(Position p1, Position p2) { 562 int angle = computeAngle(p1, p2); 563 // NE 564 if (p2.getLongitude() >= p1.getLongitude() && p2.getLatitude() >= p1.getLatitude()) { 565 return angle; 566 } else if (p2.getLongitude() >= p1.getLongitude() && p2.getLatitude() <= p1.getLatitude()) { 567 // SE 568 return 90 + angle; 569 } else if (p2.getLongitude() <= p1.getLongitude() && p2.getLatitude() <= p1.getLatitude()) { 570 // SW 571 return 270 - angle; 572 } else { 573 // NW 574 return 270 + angle; 575 } 576 } 577 578 public static void main(String[] args) { 579 try { 580 int pos = NavCalculator.computeHeading(new Position(0, 0), new Position(10, -10)); 581// System.out.println(pos.getLatitude() + "," + pos.getLongitude()); 582 System.out.println(pos); 583 } catch (Exception e) { 584 } 585 586 587 588 589 590 } 591} 592