// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/proj/coords/DMSLatLonPoint.java,v $
// $RCSfile: DMSLatLonPoint.java,v $
// $Revision: 1.7 $
// $Date: 2009/02/25 22:34:04 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.proj.coords;
import com.bbn.openmap.util.Assert;
/**
* Encapsulates a latitude and longitude coordinate in degrees, minutes and
* seconds as well as the sign.
* <p>
*
* Original code contributed by Colin Mummery (colin_mummery@yahoo.com)
*/
public class DMSLatLonPoint implements Cloneable {
private final static double MINUTE = 1 / 60.0;
private final static double SECOND = 1 / 3600.0;
/**
* Indicates if the latitude is negative, the actual int values are always
* positive.
*/
public boolean lat_isnegative;
/**
* The number of degrees in the latitude.
*/
public int lat_degrees;
/**
* The number of minutes in the latitude.
*/
public int lat_minutes;
/**
* The number of seconds in the latitude.
*/
public double lat_seconds;
/**
* Indicates if the longitude is negative, the actual int values are always
* positive.
*/
public boolean lon_isnegative;
/**
* The number of degrees in the longitude.
*/
public int lon_degrees;
/**
* The number of minutes in the longitude.
*/
public int lon_minutes;
/**
* The number of seconds in the longitude.
*/
public double lon_seconds;
/**
* Construct a default LatLonPoint with zero values.
*/
public DMSLatLonPoint() {
}
/**
* Create DMSLatLonPoint where lat_degrees and lon_degrees are signed,
* negative for South and West.
*
* @param lat_degrees
* @param lat_minutes
* @param lat_seconds
* @param lon_degrees
* @param lon_minutes
* @param lon_seconds
*/
public DMSLatLonPoint(int lat_degrees, int lat_minutes, double lat_seconds, int lon_degrees,
int lon_minutes, double lon_seconds) {
this(lat_degrees < 0, Math.abs(lat_degrees), lat_minutes, lat_seconds, lon_degrees < 0, Math.abs(lon_degrees), lon_minutes, lon_seconds);
}
/**
* Construct a DMSLatLonPoint from raw int lat/lon. All parameters are
* checked for their validity.
*
* @param lat_isnegative boolean value indicating the sign of the latitude
* @param lat_degrees integer number of degrees in latitude
* @param lat_minutes integer number of minutes in latitude
* @param lat_seconds float number of seconds in latitude
* @param lon_isnegative boolean value indicating the sign of the longitude
* @param lon_degrees integer number of degrees in longitude
* @param lon_minutes integer number of minutes in longitude
* @param lon_seconds float number of seconds in longitude
*/
public DMSLatLonPoint(boolean lat_isnegative, int lat_degrees, int lat_minutes,
double lat_seconds, boolean lon_isnegative, int lon_degrees, int lon_minutes,
double lon_seconds) {
this.lat_isnegative = lat_isnegative;
this.lat_degrees = (int) LatLonPoint.normalizeLatitude(lat_degrees);
if (this.lat_degrees < 0) {
// can't have a negative value
this.lat_degrees = -this.lat_degrees;
}
this.lat_minutes = normalize_value(lat_minutes);
this.lat_seconds = normalize_value(lat_seconds);
this.lon_isnegative = lon_isnegative;
this.lon_degrees = (int) LatLonPoint.wrapLongitude(lon_degrees);
if (this.lon_degrees < 0) {
// can't have a negative value
this.lon_degrees = -this.lon_degrees;
}
this.lon_minutes = normalize_value(lon_minutes);
this.lon_seconds = normalize_value(lon_seconds);
}
/**
* Create DMSLatLonPoint from different notation
*
* @param lat_degrees integer degrees for lat, signed negative for South
* @param lat_minutesDotSeconds positive decimal minutes.seconds fraction
* @param lon_degrees integer degrees for lon, signed negative for West
* @param lon_minutesDotSeconds positive decimal minutes.seconds fraction
*/
public DMSLatLonPoint(int lat_degrees, double lat_minutesDotSeconds, int lon_degrees,
double lon_minutesDotSeconds) {
this.lat_isnegative = lat_degrees < 0;
this.lat_degrees = (int) LatLonPoint.normalizeLatitude(Math.abs(lat_degrees));
double lat_minDSec = Math.abs(lat_minutesDotSeconds);
this.lat_minutes = normalize_value((int) lat_minDSec);
this.lat_seconds = normalize_value((lat_minDSec - this.lat_minutes) * 60.0);
this.lon_isnegative = lon_degrees < 0;
this.lon_degrees = (int) LatLonPoint.wrapLongitude(Math.abs(lon_degrees));
double lon_minDSec = Math.abs(lon_minutesDotSeconds);
this.lon_minutes = normalize_value((int) lon_minDSec);
this.lon_seconds = normalize_value((lon_minDSec - this.lon_minutes) * 60.0);
}
/**
* Constructs a new DMSLatLonPoint given a LatLonPoint instance
*
* @param llp A LatLonPoint instance
*/
public DMSLatLonPoint(LatLonPoint llp) {
getDMSLatLonPoint(llp, this);
}
/**
* A static method which takes an instance of a LatLongPoint and sets the
* correct values into an instance of DMSLatLonPoint.
*
* @param llp A LatLonPoint instance.
* @param dllp A DMSLatLonPoint instance.
*/
static void getDMSLatLonPoint(LatLonPoint llp, DMSLatLonPoint dllp) {
// set everything to zero
dllp.lat_degrees = 0;
dllp.lat_minutes = 0;
dllp.lat_seconds = 0f;
dllp.lat_isnegative = false;
dllp.lon_degrees = 0;
dllp.lon_minutes = 0;
dllp.lon_seconds = 0f;
dllp.lon_isnegative = false;
// First do the latitude
double val = llp.getY();
if (val < 0) {
dllp.lat_isnegative = true;
val = -val;
} // remove the sign but remember it
dllp.lat_degrees = (int) Math.floor((double) val);
if (val >= SECOND) {
// If it's less then a second then we assume zero...I
// guess
// we could round up
int deg = (int) val;
// take out the whole degrees
double rem = val - deg;
// Do we have anything left to convert to a minute
if (rem >= MINUTE) { // get the minutes
int min = (int) (rem * 60);
dllp.lat_minutes = min;
rem -= (min * MINUTE);
}
// Any seconds left?
if (rem >= SECOND) { // get the seconds
double sec = (rem * 3600.0);
dllp.lat_seconds = sec;
rem -= (sec * SECOND);
}
} else {
dllp.lat_isnegative = false; // we don't want a negative
// zero
}
// Next repeat the code for longitude, easiest just to repeat
// it
val = llp.getX();
if (val < 0) {
dllp.lon_isnegative = true;
val = -val;
}
dllp.lon_degrees = (int) Math.floor((double) val);
if (val >= SECOND) {
int deg = (int) val;
double rem = val - deg;
if (rem >= MINUTE) {
int min = (int) (rem * 60.0);
dllp.lon_minutes = min;
rem -= (min * MINUTE);
}
if (rem >= SECOND) {
double sec = rem * 3600.0;
dllp.lon_seconds = sec;
rem -= (sec * SECOND);
}
} else {
dllp.lon_isnegative = false;
}
}
/**
* Return a LatLonPoint from this DMSLatLonPoint. The LatLonPoint is
* allocated here.
*
* @return LatLonPoint, full of decimal degrees.
*/
public LatLonPoint getLatLonPoint() {
return getLatLonPoint(null);
}
/**
* Return a LatLonPoint from this DMSLatLonPoint. The LatLonPoint is
* allocated here if the llp is null. If it's not null, then the llp is
* loaded with the proper values.
*
* @param llp the LatLonPoint to load up.
* @return LatLonPoint, full of decimal degrees.
*/
public LatLonPoint getLatLonPoint(LatLonPoint llp) {
double lat = getDecimalLatitude();
double lon = getDecimalLongitude();
if (llp == null) {
return new LatLonPoint.Double(lat, lon);
} else {
llp.setLatLon(lat, lon);
return llp;
}
}
/**
* Returns the latitude as decimal degrees.
*
* @return A float value for the latitude
*/
public double getDecimalLatitude() {
double val = (lat_degrees + (lat_minutes * MINUTE) + (lat_seconds * SECOND));
return (lat_isnegative) ? -val : val;
}
/**
* Returns the longitude as decimal degrees.
*
* @return A float value for the longitude
*/
public double getDecimalLongitude() {
double val = (lon_degrees + (lon_minutes * MINUTE) + (lon_seconds * SECOND));
return (lon_isnegative) ? -val : val;
}
/**
* Returns a string representation of the object.
*
* @return String representation
*/
public String toString() {
return "DMSLatLonPoint[lat_isnegative = " + lat_isnegative + ", lat_degrees = "
+ lat_degrees + ", lat_minutes = " + lat_minutes + ", lat_seconds = " + lat_seconds
+ ", lon_isnegative = " + lon_isnegative + ", lon_degrees = " + lon_degrees
+ ", lon_minutes = " + lon_minutes + ", lon_seconds = " + lon_seconds + "]";
}
/**
* Set DMSLatLonPoint. Sets the current instance values to be the same as
* another DMSLatLonPoint instance.
*
* @param llpt DMSLatLonPoint
*/
public void setDMSLatLon(DMSLatLonPoint llpt) {
lat_isnegative = llpt.lat_isnegative;
lat_degrees = llpt.lat_degrees;
lat_minutes = llpt.lat_minutes;
lat_seconds = llpt.lat_seconds;
lon_isnegative = llpt.lon_isnegative;
lon_degrees = llpt.lon_degrees;
lon_minutes = llpt.lon_minutes;
lon_seconds = llpt.lon_seconds;
}
/**
* Clone the DMSLatLonPoint.
*
* @return clone
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
Assert.assertExp(false, "DMSLatLonPoint: internal error!");
return null;// statement not reached
}
}
/**
* Determines whether two DMSLatLonPoints are exactly equal.
*
* @param obj Object
* @return boolean
*/
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DMSLatLonPoint pt = (DMSLatLonPoint) obj;
return (pt.lat_isnegative == lat_isnegative && pt.lat_degrees == lat_degrees
&& pt.lat_minutes == lat_degrees && pt.lat_seconds == lat_seconds
&& pt.lon_isnegative == lon_isnegative && pt.lon_degrees == lon_degrees
&& pt.lon_minutes == lon_minutes && pt.lon_seconds == lon_seconds);
}
/**
* Sets the minutes and seconds to something sane.
*
* @param val an int value for the minutes or seconds
* @return int value normalized
*/
final public static int normalize_value(int val) {
val %= 60;
if (val < 0) {
val += 60;
}
return val;
}
/**
* Sets the minutes and seconds to something sane.
*
* @param val an double value for the minutes or seconds
* @return float value normalized
*/
final public static double normalize_value(double val) {
val %= 60f;
if (val < 0f) {
val += 60f;
}
return val;
}
/**
* Generate a hash value for the point. Hash by spreading the values across
* a 32 bit int, ignoring the sign allow 8 bits (max 255) for degrees, 7
* bits (max 127) for (minutes + seconds) so the total is 30 bits.
*
* @return An int hash value representing the point.
*/
public int hashCode() {
return (lat_degrees | lon_degrees << 8 | (lat_minutes + (int) lat_seconds) << 16
| (lon_minutes + (int) lon_seconds) << 23);
}
}