/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package nav.position;
import exception.InvalidPositionException;
import java.text.DecimalFormat;
import nav.util.string.StringUtil;
import nav.util.math.NumUtil;
/**
* Coordinate class. Used to store a coordinate in [DD]D MM.M format.
*
* @author Benjamin Jakobus
* @version 1.0
* @since 1.0
*/
public class Coordinate {
/* The degree part of the position (+ N/E, -W/S). */
private int deg;
/* The decimals of a minute. */
private double minsDecMins;
/* The coordinate as a decimal. */
private double decCoordinate;
/* Whether this coordinate is a latitude or a longitude: : LAT==0, LONG==1. */
private int coordinate;
/* The minutes trailing decimal precision to use for positions. */
public static final int MINPRECISION = 4;
/* The degrees trailing decimal precision to use for positions. */
public static final int DEGPRECISION = 7;
/* Type definitions for coordinates. */
public static final int LAT = 0;
public static final int LNG = 1;
/* Type definitions for the quadrant. */
public static final int E = 0;
public static final int S = 1;
public static final int W = 2;
public static final int N = 3;
/**
* Constructor.
*
* @param deg Degrees.
* @param minsDecMins Minutes.
* @param coordinate Coordinate type (latitude or longitude).
* @param quad Quadrant.
*
* @throws InvalidPositionException
* @since 1.0
*/
public Coordinate(int deg, float minsDecMins, int coOrdinate,
int quad) throws InvalidPositionException {
buildCoOrdinate(deg, minsDecMins, coOrdinate, quad);
if (verify()) {
} else {
throw new InvalidPositionException();
}
}
/**
* Constructor.
*
* @param decCoordinate Coordinate in decimal format.
* @param coordinate Coordinate type (latitude or longitude).
* @throws InvalidPositionException
* @since 1.0
*/
public Coordinate(double decCoordinate, int coOrdinate) throws InvalidPositionException {
DecimalFormat form = new DecimalFormat("#.#######");
this.decCoordinate = decCoordinate;
this.coordinate = coOrdinate;
if (verify()) {
deg = new Float(decCoordinate).intValue();
if (deg < 0) {
minsDecMins = Double.parseDouble(form.format((Math.abs(decCoordinate) - Math.abs(deg)) * 60));
} else {
minsDecMins = Double.parseDouble(form.format((decCoordinate - deg) * 60));
}
} else {
throw new InvalidPositionException();
}
}
/**
* This constructor takes a coordinate in the ALRS formats i.e
* 38∞31.64'N for lat, and 28∞19.12'W for long
* Note: ALRS positions are occasionally written with the decimal minutes
* apostrophe in the 'wrong' place and with an non CP1252 compliant decimal character.
* This issue has to be corrected in the source database.
*
* @param coordinate Coordinate as a <code>String</code>.
* @throws InvalidPositionException
* @since 1.0
*/
public Coordinate(String coOrdinate) throws InvalidPositionException {
//firstly split it into its component parts and dispose of the unneeded characters
String[] items = coOrdinate.split("°");
int deg = Integer.valueOf(items[0]);
items = items[1].split("'");
float minsDecMins = Float.valueOf(items[0]);
char quad = items[1].charAt(0);
switch (quad) {
case 'N':
buildCoOrdinate(deg, minsDecMins, Coordinate.LAT, Coordinate.N);
break;
case 'S':
buildCoOrdinate(deg, minsDecMins, Coordinate.LAT, Coordinate.S);
break;
case 'E':
buildCoOrdinate(deg, minsDecMins, Coordinate.LNG, Coordinate.E);
break;
case 'W':
buildCoOrdinate(deg, minsDecMins, Coordinate.LNG, Coordinate.W);
}
if (verify()) {
} else {
throw new InvalidPositionException();
}
}
/**
* Returns the coordinate as a <code>String</code> in degrees and minutes.
*
* @return The coordinate in decimal format.
* @since 1.0
*/
public String toStringDegMin() {
String str = "";
String quad = "";
StringUtil su = new StringUtil();
switch (coordinate) {
case LAT:
if (decCoordinate >= 0) {
quad = "N";
} else {
quad = "S";
}
str = su.padNumZero(Math.abs(deg), 2);
str += "\u00b0" + su.padNumZero(Math.abs(minsDecMins), 2, MINPRECISION) + "'" + quad;
break;
case LNG:
if (decCoordinate >= 0) {
quad = "E";
} else {
quad = "W";
}
str = su.padNumZero(Math.abs(deg), 3);
str += "\u00b0" + su.padNumZero(Math.abs(minsDecMins), 2, MINPRECISION) + "'" + quad;
}
return str;
}
/**
* Returns the coordinate as a <code>String</code> in decimal format.
*
* @return The coordinate in decimal format.
* @since 1.0
*/
public String toStringDec() {
StringUtil u = new StringUtil();
switch (coordinate) {
case LAT:
return u.padNumZero(decCoordinate, 2, DEGPRECISION);
case LNG:
return u.padNumZero(decCoordinate, 3, DEGPRECISION);
}
return "error";
}
/**
* Returns the coordinate's decimal value
* @return float The decimal value of the coordinate.
* @since 1.0
*/
public double decVal() {
return decCoordinate;
}
/**
* Determines whether a decimal position is valid.
*
* @return Result of validity test; <code>true</code>
* if valid; <code>false</code> if not.
* @since 1.0
*/
private boolean verify() {
switch (coordinate) {
case LAT:
if (Math.abs(decCoordinate) > 90.0) {
return false;
}
break;
case LNG:
if (Math.abs(decCoordinate) > 180) {
return false;
}
}
return true;
}
/**
* Populate this object by parsing the arguments to the function.
* Placed here to allow multiple constructors to use it.
*
* @since 1.0
*/
private void buildCoOrdinate(int deg, float minsDecMins, int coOrdinate,
int quad) {
NumUtil nu = new NumUtil();
switch (coOrdinate) {
case LAT:
switch (quad) {
case N:
this.deg = deg;
this.minsDecMins = minsDecMins;
this.coordinate = coOrdinate;
decCoordinate = nu.Round(this.deg + (float) this.minsDecMins / 60, Coordinate.MINPRECISION);
break;
case S:
this.deg = -deg;
this.minsDecMins = minsDecMins;
this.coordinate = coOrdinate;
decCoordinate = nu.Round(this.deg - ((float) this.minsDecMins / 60), Coordinate.MINPRECISION);
}
case LNG:
switch (quad) {
case E:
this.deg = deg;
this.minsDecMins = minsDecMins;
this.coordinate = coOrdinate;
decCoordinate = nu.Round(this.deg + ((float) this.minsDecMins / 60), Coordinate.MINPRECISION);
break;
case W:
this.deg = -deg;
this.minsDecMins = minsDecMins;
this.coordinate = coOrdinate;
decCoordinate = nu.Round(this.deg - ((float) this.minsDecMins / 60), Coordinate.MINPRECISION);
}
}
}
}