package ecologylab.bigsemantics.metadata.builtins;
import ecologylab.bigsemantics.metadata.Metadata;
import ecologylab.sensor.location.EarthData;
import ecologylab.serialization.annotations.simpl_inherit;
import ecologylab.serialization.annotations.simpl_scalar;
/**
* An object for representing a set of 3d coordinates on the earth's surface: latitude, longitude,
* and altitude.
*
* @author Zachary O. Toups (zach@ecologylab.net)
*/
@simpl_inherit
public class GeoLocation extends Metadata implements EarthData
{
/** The latitude, expressed in degrees in double-precision degrees. */
@simpl_scalar
double latitude;
/** The longitude, expressed in degrees in double-precision degrees. */
@simpl_scalar
double longitude;
/** The altitude, expressed in meters. */
@simpl_scalar
double altitude;
/**
*
*/
public GeoLocation()
{
}
public GeoLocation(double lat, double lon, double alt)
{
this.latitude = lat;
this.longitude = lon;
this.altitude = alt;
}
public double getLatitude()
{
return latitude;
}
public void setLatitude(double lat)
{
this.latitude = lat;
kMLCommaDelimited = null;
}
public double getLongitude()
{
return longitude;
}
public void setLongitude(double lon)
{
this.longitude = lon;
kMLCommaDelimited = null;
}
public double getAltitude()
{
return altitude;
}
public void setAltitude(double alt)
{
this.altitude = alt;
kMLCommaDelimited = null;
}
/**
* A String reprsenting the current data for KML; cached for re-use and computed only when needed.
*/
String kMLCommaDelimited = null;
/**
* Get the set of coordinates, serialized for use in KML / Google Earth.
*
* @return
*/
public String getKMLCommaDelimitedString()
{
if (kMLCommaDelimited == null)
{
kMLCommaDelimited = this.longitude + "," + this.latitude + "," + this.altitude;
}
return kMLCommaDelimited;
}
/**
* @param that
* @return positive if this is farther north than that, negative if that is more north; 0 if they
* lie on exactly the same parallel.
*/
public double compareNS(GeoLocation that)
{
return this.getLatitude() - that.getLatitude();
}
/**
* @param that
* @return compares two GPSDatum's based on the acute angle between their longitudes. Returns 1 if
* this is farther east than that, -1 if this is farther west, 0 if the two points lie on
* the same arc, 180/-180 if they are opposite.
*/
public double compareEW(GeoLocation that)
{
double diff = getLongitude() - that.getLongitude();
if (diff > 180)
{
return diff - 360;
}
else if (diff < -180)
{
return diff + 360;
}
else
{
return diff;
}
}
/**
* Uses the haversine formula to compute the great-circle direct distance from this to the other
* point. Does not take into account altitude.
*
* Result is given in meters.
*
* Formula used from http://www.movable-type.co.uk/scripts/latlong.html.
*
* @param other
* @return great-circle distance between this and other, in meters.
*/
public double distanceTo(GeoLocation other)
{
return this.distanceTo(other.getLatitude(), other.getLongitude());
}
/**
* Uses the haversine formula to compute the great-circle direct distance from this to the other
* point. Does not take into account altitude.
*
* Result is given in meters.
*
* Formula used from http://www.movable-type.co.uk/scripts/latlong.html.
*
* @param otherLat
* @param otherLon
* @return great-circle distance between this and other, in meters.
*/
public double distanceTo(double otherLat, double otherLon)
{
double deltaLat = Math.toRadians(otherLat - this.getLatitude());
double deltaLon = Math.toRadians(otherLon - this.getLongitude());
double a = (Math.sin(deltaLat / 2.0) * Math.sin(deltaLat / 2.0))
+ (Math.cos(Math.toRadians(this.getLatitude())) * Math.cos(Math.toRadians(otherLat))
* Math.sin(deltaLon / 2.0) * Math.sin(deltaLon / 2.0));
double c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
return c * RADIUS_EARTH_METERS;
}
}