// Created by plusminus on 21:28:12 - 25.09.2008
package org.mozilla.osmdroid.util;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import org.mozilla.osmdroid.api.IGeoPoint;
import org.mozilla.osmdroid.util.constants.GeoConstants;
import org.mozilla.osmdroid.views.util.constants.MathConstants;
import java.io.Serializable;
/**
* @author Nicolas Gramlich
* @author Theodore Hong
*/
public class GeoPoint implements IGeoPoint, MathConstants, GeoConstants, Parcelable, Serializable, Cloneable {
// ===========================================================
// Constants
// ===========================================================
public static final Parcelable.Creator<GeoPoint> CREATOR = new Parcelable.Creator<GeoPoint>() {
@Override
public GeoPoint createFromParcel(final Parcel in) {
return new GeoPoint(in);
}
@Override
public GeoPoint[] newArray(final int size) {
return new GeoPoint[size];
}
};
// ===========================================================
// Fields
// ===========================================================
static final long serialVersionUID = 1L;
private int mLongitudeE6;
private int mLatitudeE6;
// ===========================================================
// Constructors
// ===========================================================
private int mAltitude;
public GeoPoint(final int aLatitudeE6, final int aLongitudeE6) {
this.mLatitudeE6 = aLatitudeE6;
this.mLongitudeE6 = aLongitudeE6;
}
public GeoPoint(final int aLatitudeE6, final int aLongitudeE6, final int aAltitude) {
this.mLatitudeE6 = aLatitudeE6;
this.mLongitudeE6 = aLongitudeE6;
this.mAltitude = aAltitude;
}
public GeoPoint(final double aLatitude, final double aLongitude) {
this.mLatitudeE6 = (int) (aLatitude * 1E6);
this.mLongitudeE6 = (int) (aLongitude * 1E6);
}
public GeoPoint(final double aLatitude, final double aLongitude, final double aAltitude) {
this.mLatitudeE6 = (int) (aLatitude * 1E6);
this.mLongitudeE6 = (int) (aLongitude * 1E6);
this.mAltitude = (int) aAltitude;
}
public GeoPoint(final Location aLocation) {
this(aLocation.getLatitude(), aLocation.getLongitude(), aLocation.getAltitude());
}
public GeoPoint(final GeoPoint aGeopoint) {
this.mLatitudeE6 = aGeopoint.mLatitudeE6;
this.mLongitudeE6 = aGeopoint.mLongitudeE6;
this.mAltitude = aGeopoint.mAltitude;
}
// ===========================================================
// Parcelable
// ===========================================================
private GeoPoint(final Parcel in) {
this.mLatitudeE6 = in.readInt();
this.mLongitudeE6 = in.readInt();
this.mAltitude = in.readInt();
}
public static GeoPoint fromDoubleString(final String s, final char spacer) {
final int spacerPos1 = s.indexOf(spacer);
final int spacerPos2 = s.indexOf(spacer, spacerPos1 + 1);
if (spacerPos2 == -1) {
return new GeoPoint(
(int) (Double.parseDouble(s.substring(0, spacerPos1)) * 1E6),
(int) (Double.parseDouble(s.substring(spacerPos1 + 1, s.length())) * 1E6));
} else {
return new GeoPoint(
(int) (Double.parseDouble(s.substring(0, spacerPos1)) * 1E6),
(int) (Double.parseDouble(s.substring(spacerPos1 + 1, spacerPos2)) * 1E6),
(int) Double.parseDouble(s.substring(spacerPos2 + 1, s.length())));
}
}
// ===========================================================
// Getter & Setter
// ===========================================================
public static GeoPoint fromInvertedDoubleString(final String s, final char spacer) {
final int spacerPos1 = s.indexOf(spacer);
final int spacerPos2 = s.indexOf(spacer, spacerPos1 + 1);
if (spacerPos2 == -1) {
return new GeoPoint(
(int) (Double.parseDouble(s.substring(spacerPos1 + 1, s.length())) * 1E6),
(int) (Double.parseDouble(s.substring(0, spacerPos1)) * 1E6));
} else {
return new GeoPoint(
(int) (Double.parseDouble(s.substring(spacerPos1 + 1, spacerPos2)) * 1E6),
(int) (Double.parseDouble(s.substring(0, spacerPos1)) * 1E6),
(int) Double.parseDouble(s.substring(spacerPos2 + 1, s.length())));
}
}
public static GeoPoint fromIntString(final String s) {
final int commaPos1 = s.indexOf(',');
final int commaPos2 = s.indexOf(',', commaPos1 + 1);
if (commaPos2 == -1) {
return new GeoPoint(
Integer.parseInt(s.substring(0, commaPos1)),
Integer.parseInt(s.substring(commaPos1 + 1, s.length())));
} else {
return new GeoPoint(
Integer.parseInt(s.substring(0, commaPos1)),
Integer.parseInt(s.substring(commaPos1 + 1, commaPos2)),
Integer.parseInt(s.substring(commaPos2 + 1, s.length()))
);
}
}
public static GeoPoint fromCenterBetween(final GeoPoint geoPointA, final GeoPoint geoPointB) {
return new GeoPoint((geoPointA.getLatitudeE6() + geoPointB.getLatitudeE6()) / 2,
(geoPointA.getLongitudeE6() + geoPointB.getLongitudeE6()) / 2);
}
@Override
public int getLongitudeE6() {
return this.mLongitudeE6;
}
public void setLongitudeE6(final int aLongitudeE6) {
this.mLongitudeE6 = aLongitudeE6;
}
@Override
public int getLatitudeE6() {
return this.mLatitudeE6;
}
public void setLatitudeE6(final int aLatitudeE6) {
this.mLatitudeE6 = aLatitudeE6;
}
@Override
public double getLongitude() {
return this.mLongitudeE6 * 1E-6;
}
@Override
public double getLatitude() {
return this.mLatitudeE6 * 1E-6;
}
// ===========================================================
// Methods from SuperClass/Interfaces
// ===========================================================
public int getAltitude() {
return this.mAltitude;
}
public void setAltitude(final int aAltitude) {
this.mAltitude = aAltitude;
}
public void setCoordsE6(final int aLatitudeE6, final int aLongitudeE6) {
this.mLatitudeE6 = aLatitudeE6;
this.mLongitudeE6 = aLongitudeE6;
}
@Override
public GeoPoint clone() {
return new GeoPoint(this.mLatitudeE6, this.mLongitudeE6, this.mAltitude);
}
@Override
public String toString() {
return new StringBuilder().append(this.mLatitudeE6).append(",").append(this.mLongitudeE6).append(",").append(this.mAltitude)
.toString();
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
final GeoPoint rhs = (GeoPoint) obj;
return rhs.mLatitudeE6 == this.mLatitudeE6 && rhs.mLongitudeE6 == this.mLongitudeE6 && rhs.mAltitude == this.mAltitude;
}
@Override
public int hashCode() {
return 37 * (17 * mLatitudeE6 + mLongitudeE6) + mAltitude;
}
@Override
public int describeContents() {
return 0;
}
// ===========================================================
// Methods
// ===========================================================
@Override
public void writeToParcel(final Parcel out, final int flags) {
out.writeInt(mLatitudeE6);
out.writeInt(mLongitudeE6);
out.writeInt(mAltitude);
}
/**
* @return distance in meters
* @see <a href="http://www.geocities.com/DrChengalva/GPSDistance.html">GPSDistance.html</a>
*/
public int distanceTo(final IGeoPoint other) {
final double a1 = DEG2RAD * this.mLatitudeE6 / 1E6;
final double a2 = DEG2RAD * this.mLongitudeE6 / 1E6;
final double b1 = DEG2RAD * other.getLatitudeE6() / 1E6;
final double b2 = DEG2RAD * other.getLongitudeE6() / 1E6;
final double cosa1 = Math.cos(a1);
final double cosb1 = Math.cos(b1);
final double t1 = cosa1 * Math.cos(a2) * cosb1 * Math.cos(b2);
final double t2 = cosa1 * Math.sin(a2) * cosb1 * Math.sin(b2);
final double t3 = Math.sin(a1) * Math.sin(b1);
final double tt = Math.acos(t1 + t2 + t3);
return (int) (RADIUS_EARTH_METERS * tt);
}
/**
* @return bearing in degrees
* @see <a href="http://groups.google.com/group/osmdroid/browse_thread/thread/d22c4efeb9188fe9/bc7f9b3111158dd">discussion</a>
*/
public double bearingTo(final IGeoPoint other) {
final double lat1 = Math.toRadians(this.mLatitudeE6 / 1E6);
final double long1 = Math.toRadians(this.mLongitudeE6 / 1E6);
final double lat2 = Math.toRadians(other.getLatitudeE6() / 1E6);
final double long2 = Math.toRadians(other.getLongitudeE6() / 1E6);
final double delta_long = long2 - long1;
final double a = Math.sin(delta_long) * Math.cos(lat2);
final double b = Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(delta_long);
final double bearing = Math.toDegrees(Math.atan2(a, b));
final double bearing_normalized = (bearing + 360) % 360;
return bearing_normalized;
}
/**
* Calculate a point that is the specified distance and bearing away from this point.
*
* @see <a href="http://www.movable-type.co.uk/scripts/latlong.html">latlong.html</a>
* @see <a href="http://www.movable-type.co.uk/scripts/latlon.js">latlon.js</a>
*/
public GeoPoint destinationPoint(final double aDistanceInMeters, final float aBearingInDegrees) {
// convert distance to angular distance
final double dist = aDistanceInMeters / RADIUS_EARTH_METERS;
// convert bearing to radians
final float brng = DEG2RAD * aBearingInDegrees;
// get current location in radians
final double lat1 = DEG2RAD * getLatitudeE6() / 1E6;
final double lon1 = DEG2RAD * getLongitudeE6() / 1E6;
final double lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) + Math.cos(lat1)
* Math.sin(dist) * Math.cos(brng));
final double lon2 = lon1
+ Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(lat1), Math.cos(dist)
- Math.sin(lat1) * Math.sin(lat2));
final double lat2deg = lat2 / DEG2RAD;
final double lon2deg = lon2 / DEG2RAD;
return new GeoPoint(lat2deg, lon2deg);
}
public String toDoubleString() {
return new StringBuilder().append(this.mLatitudeE6 / 1E6).append(",")
.append(this.mLongitudeE6 / 1E6).append(",").append(this.mAltitude).toString();
}
public String toInvertedDoubleString() {
return new StringBuilder().append(this.mLongitudeE6 / 1E6).append(",")
.append(this.mLatitudeE6 / 1E6).append(",").append(this.mAltitude).toString();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}