I want to add direction arrows to my application to make it looks like this. Is there any solutions or advices?
Here is my solution:
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.jettaxi.R;
import java.util.ArrayList;
public class ArrowDirectionsOverlay extends Overlay {
private Context context;
private ArrayList<GeoPoint> directions;
private Bitmap arrowBitmap;
private int maximalPositionX;
private int maximalPositionY;
public ArrowDirectionsOverlay(Context context) {
this.context = context;
directions = new ArrayList<GeoPoint>();
arrowBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.direction);
}
public void addDirectionPoint(GeoPoint destination) {
directions.add(destination);
}
public void clear() {
directions.clear();
}
private double getAngle(GeoPoint center, GeoPoint destination) {
double lat1 = center.getLatitudeE6() / 1000000.;
double lon1 = center.getLongitudeE6() / 1000000.;
double lat2 = destination.getLatitudeE6() / 1000000.;
double lon2 = destination.getLongitudeE6() / 1000000.;
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 :
cosU1cosU2 * sinLambda / sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 :
cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared *
(-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared *
(-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) *
cosSqAlpha *
(4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B * sinSigma * // (6)
(cos2SM + (B / 4.0) *
(cosSigma * (-1.0 + 2.0 * cos2SMSq) -
(B / 6.0) * cos2SM *
(-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SMSq)));
lambda = L +
(1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma *
(cos2SM + C * cosSigma *
(-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
float distance = (float) (b * A * (sigma - deltaSigma));
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
return finalBearing;
}
private void drawArrow(Canvas canvas, MapView mapView, GeoPoint destination) {
Point screenPts = mapView.getProjection().toPixels(destination, null);
int deltaX = mapView.getMapCenter().getLatitudeE6() - destination.getLatitudeE6();
int deltaY = mapView.getMapCenter().getLongitudeE6() - destination.getLongitudeE6();
double angle = getAngle(mapView.getMapCenter(), destination);
double tan = Math.tan(Math.toRadians(angle));
Matrix matrix = new Matrix();
matrix.postRotate((float) angle);
Bitmap rotatedBmp = Bitmap.createBitmap(
arrowBitmap,
0, 0,
arrowBitmap.getWidth(),
arrowBitmap.getHeight(),
matrix,
true
);
int currentPositionX = screenPts.x - (rotatedBmp.getWidth() / 2);
int currentPositionY = screenPts.y - (rotatedBmp.getHeight() / 2);
if ((currentPositionX < 0) || (currentPositionY < 0) ||
(currentPositionX > maximalPositionX) || (currentPositionY > maximalPositionY)) {
int arrowPositionX = (int) (mapView.getWidth() / 2 - Math.signum(deltaX) * mapView.getHeight() / 2 * tan);
arrowPositionX = Math.min(Math.max(arrowPositionX, 0), maximalPositionX);
int arrowSpanX = (int) (mapView.getWidth() / 2.);
int arrowPositionY = (int) (mapView.getHeight() / 2 + Math.signum(deltaY) * arrowSpanX / tan);
arrowPositionY = Math.min(Math.max(arrowPositionY, 0), maximalPositionY);
canvas.drawBitmap(
rotatedBmp,
arrowPositionX,
arrowPositionY,
null
);
}
}
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
maximalPositionX = mapView.getWidth() - arrowBitmap.getWidth();
maximalPositionY = mapView.getHeight() - arrowBitmap.getHeight();
for (GeoPoint geoPoint : directions) {
drawArrow(canvas, mapView, geoPoint);
}
}
}
Bitmap arrow points top in my case
My solution would be to do a search for the places of interest (in this case coffee shops) which are within a certain distance from the users current location. This distance could be a 'screen' north, south, east or west of your current location - the screen distance will depend on your zoom level. The shops which are not on the current screen would then be represented by an arrow placed on the screen edge with the direction set between your current location and the shop.
Related
I'm trying to come up with function that could fill in gps coordinates between two points every second. There are few posts about this here, but I couldn't find something complete. The closest answer I found was:
Interpolate between 2 GPS locations based on walking speed
I modified one of the answer using the bearing. However, it still doesn't seem to work. Especially I think the distance calculation is wrong. Could someone look at the code below and change?
Thank you!
import java.util.ArrayList;
public class Test {
double radius = 6371;
public Test() {
Location start = new Location(lat, lon);
Location end = new Location(lat, lon);
double speed = 1.39;
double distance = CalculateDistanceBetweenLocations(start, end);
double duration = distance / speed;
System.out.println(distance + ", " + speed + ", " + duration);
ArrayList<Location> locations = new ArrayList<Location>();
for (double i = 0; i < duration; i += 1.0) {
double bearing = CalculateBearing(start, end);
double distanceInKm = speed / 1000;
Location intermediaryLocation = CalculateDestinationLocation(start, bearing, distanceInKm);
locations.add(intermediaryLocation);
System.out.println(intermediaryLocation.latitude + ", " + intermediaryLocation.longitude);
start = intermediaryLocation;
}
}
double DegToRad(double deg) {
return (deg * Math.PI / 180);
}
double RadToDeg(double rad) {
return (rad * 180 / Math.PI);
}
double CalculateBearing(Location startPoint, Location endPoint) {
double lat1 = DegToRad(startPoint.latitude);
double lat2 = DegToRad(endPoint.latitude);
double deltaLon = DegToRad(endPoint.longitude - startPoint.longitude);
double y = Math.sin(deltaLon) * Math.cos(lat2);
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon);
double bearing = Math.atan2(y, x);
return (RadToDeg(bearing) + 360) % 360;
}
Location CalculateDestinationLocation(Location point, double bearing, double distance) {
distance = distance / radius;
bearing = DegToRad(bearing);
double lat1 = DegToRad(point.latitude);
double lon1 = DegToRad(point.longitude);
double lat2 = Math
.asin(Math.sin(lat1) * Math.cos(distance) + Math.cos(lat1) * Math.sin(distance) * Math.cos(bearing));
double lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(distance) * Math.cos(lat1),
Math.cos(distance) - Math.sin(lat1) * Math.sin(lat2));
lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
return new Location(RadToDeg(lat2), RadToDeg(lon2));
}
double CalculateDistanceBetweenLocations(Location startPoint, Location endPoint) {
double lat1 = DegToRad(startPoint.latitude);
double lon1 = DegToRad(startPoint.longitude);
double lat2 = DegToRad(endPoint.latitude);
double lon2 = DegToRad(endPoint.longitude);
double deltaLat = lat2 - lat1;
double deltaLon = lon2 - lon1;
double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
+ Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - 1));
return (radius * c);
}
public static void main(String[] args) {
new Test();
}
class Location {
public double latitude, longitude;
public Location(double lat, double lon) {
latitude = lat;
longitude = lon;
}
}
}
You have a typing error in line
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - 1));
It should be
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
Your method CalculateDistanceBetweenLocations contains an this line:
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - 1));
which is equivalent to
double c = 2 * Math.atan2(Math.sqrt(a), 0.0);
which means that the result of Math.atan2 is always pi, independent of the value of a as long as a is positive.
Therefore CalculateDistanceBetweenLocations always returns 20015.086796020572 independent of the input coordinates.
I am almost new to Java programming. I want to implement the following code in MATSim (it is a simulation programme for urban planning purposes). This code is about converting WGS84 (longitude and latitude) to OSGB36 (British National Grid).
https://gist.github.com/schoenobates/3497544#file-osgb-java
The MATSim library has an interface (below) to put the conversion codes in it and use it for visual simulation.
I have already tried many times to deal with errors, but it doesn't work me. Would you please give me some ideas about how I can do this.
package org.matsim.core.utils.geometry;
import org.matsim.api.core.v01.Coord;
/**
* A simple interface to convert coordinates from one coordinate system to
* another one.
*
* #author mrieser
*/
public interface CoordinateTransformation {
/**
* Transforms the given coordinate from one coordinate system to the other.
*
* #param coord The coordinate to transform.
* #return The transformed coordinate.
*/
public Coord transform(Coord coord);
}
Thank you, Somayeh
Ok, the error are here:
I did the below:
It is giving me two syntax errors:
double[] eastANDnorth(double lat, double lon) { for curly parenthesis and comma;
and
return new double(OSGB36N, OSGB36E); for double
package org.matsim.core.utils.geometry.transformations;
import org.matsim.api.core.v01.Coord;
import org.matsim.core.utils.geometry.CoordImpl;
import org.matsim.core.utils.geometry.CoordinateTransformation;
public class WGS84toOSGB36 implements CoordinateTransformation {
#Override
public Coord transform(Coord coord) {
// WGS84 ELLIPSOID
double WGS84_A = 6378137;
double WGS84_B = 6356752.314245;
double WGS84_E2 = ((WGS84_A * WGS84_A) - (WGS84_B * WGS84_B) / (WGS84_A * WGS84_A));
// NstGrid scale factor on central meridian
final double F0 = 0.9996012717;
// Airy 1830 major & minor semi-axes - note .909 not .910
final double AIRY_A = 6377563.396;
final double AIRY_B = 6356256.909;
// NatGrid true origin
final double LAT0 = Math.toRadians(49); //Phi0
final double LON0 = Math.toRadians(-2); //Lamda0
// northing & easting of true origin, metres
final double N0 = -100000;
final double E0 = 400000;
// eccentricity squared
final double E2 = ((AIRY_A * AIRY_A) - (AIRY_B * AIRY_B)) / (AIRY_A *AIRY_A);
final double N = (AIRY_A - AIRY_B) / (AIRY_A + AIRY_B);
final double N2 = N * N;
final double N3 = N * N * N;
final double TX = -446.448;
final double TY = 125.157;
final double TZ = -542.060;
final double RX = Math.toRadians(-0.1502 / 3600);
final double RY = Math.toRadians(-0.2470 / 3600);
final double RZ = Math.toRadians(-0.8421 / 3600);
final double S = 20.4894 / 1e6 + 1;
/*----*/double[] eastANDnorth(double lat, double lon) {/* error in this line */
// -- 1: convert polar to cartesian coordinates (using ellipse 1)
// WGS84 ellipsoid
double sinPhi = Math.sin(lat);
double cosPhi = Math.cos(lat);
double sinLambda = Math.sin(lon);
double cosLambda = Math.cos(lon);
double H = 24.7; // for the moment
double nu = WGS84_A / Math.sqrt(1 - WGS84_E2 * sinPhi * sinPhi);
double x1 = (nu + H) * cosPhi * cosLambda;
double y1 = (nu + H) * cosPhi * sinLambda;
double z1 = ((1-WGS84_E2) * nu + H) * sinPhi;
// -- 2: apply helmert transform using appropriate params
double x2 = TX + x1 * S - y1 * RZ + z1 * RY;
double y2 = TY + x1 * RZ + y1 * S - z1 * RX;
double z2 = TZ - x1 * RY + y1 * RX + z1 * S;
// -- 3: convert cartesian to polar coordinates (using ellipse 2)
double precision = 4 / AIRY_A;
double p = Math.sqrt(x2 * x2 + y2 * y2);
double phi = Math.atan2(z2, p * (1 - E2)), phiP = 2 * Math.PI;
while (Math.abs(phi - phiP) > precision) {
nu = AIRY_A / Math.sqrt(1 - E2 * Math.sin(phi) * Math.sin(phi));
phiP = phi;
phi = Math.atan2(z2 + E2 * nu * Math.sin(phi), p);
}
double lambda = Math.atan2(y2, x2);
// -- 4: now we're in OSGB, get the EN coords
double cosLat = Math.cos(phi), sinLat = Math.sin(phi);
nu = AIRY_A * F0 / Math.sqrt(1 - E2 * sinLat * sinLat);
double rho = AIRY_A * F0 * (1 - E2) / Math.pow(1 - E2 * sinLat * sinLat, 1.5);
double eta2 = nu / rho - 1;
double Ma = (1 + N + (5 / 4) * N2 + (5 / 4) * N3) * (phi - LAT0);
double Mb = (3 * N + 3 * N * N + (21 / 8) * N3) * Math.sin(phi - LAT0) * Math.cos(phi + LAT0);
double Mc = ((15 / 8) * N2 + (15 / 8) * N3) * Math.sin(2 * (phi - LAT0)) * Math.cos(2 * (phi + LAT0));
double Md = (35 / 24) * N3 * Math.sin(3 * (phi - LAT0)) * Math.cos(3 * (phi + LAT0));
double M = AIRY_B * F0 * (Ma - Mb + Mc - Md); // meridional arc
double cos3lat = cosLat * cosLat * cosLat;
double cos5lat = cos3lat * cosLat * cosLat;
double tan2lat = Math.tan(phi) * Math.tan(phi);
double tan4lat = tan2lat * tan2lat;
double I = M + N0;
double II = (nu / 2) * sinLat * cosLat;
double III = (nu / 24) * sinLat * cos3lat * (5 - tan2lat + 9 * eta2);
double IIIA = (nu / 720) * sinLat * cos5lat * (61 - 58 * tan2lat + tan4lat);
double IV = nu * cosLat;
double V = (nu / 6) * cos3lat * (nu / rho - tan2lat);
double VI = (nu / 120) * cos5lat * (5 - 18 * tan2lat + tan4lat + 14 * eta2 - 58 * tan2lat * eta2);
double dLon = lambda - LON0;
double dLon2 = dLon * dLon, dLon3 = dLon2 * dLon, dLon4 = dLon3 * dLon, dLon5 = dLon4 * dLon, dLon6 = dLon5 * dLon;
double OSGB36N = I + II * dLon2 + III * dLon4 + IIIA * dLon6;
double OSGB36E = E0 + IV * dLon + V * dLon3 + VI * dLon5;
/*--------*/return new double(OSGB36N, OSGB36E);/* error in this line */
}
return new CoordImpl(OSGB36N, OSGB36E);
}
}
You are trying to create a array with this expression:
new double(OSGB36N, OSGB36E)
However that's invalid java syntax. You should change it to
new double[]{ OSGB36N, OSGB36E }
Also your're trying to declare a method inside another method, i.e. eastANDnorth inside transform, which is not valid in java.
public static ArrayList<IntPoint> getCircleLineIntersectionPoint(IntPoint pointA, IntPoint pointB, IntPoint center, int radius) {
// returns a list of intersection points between a line which passes through given points,
// pointA and pointB, and a circle described by given radius and center coordinate
double disc, A, B, C, slope, c;
double x1, x2, y1, y2;
IntPoint point1, point2;
ArrayList<IntPoint> intersections = new ArrayList<IntPoint>();
try{
slope = Util.calculateSlope(pointA, pointB);
}catch (UndefinedSlopeException e){
C = Math.pow(center.y, 2) + Math.pow(pointB.x, 2) - 2 * pointB.x * center.x + Math.pow(center.x, 2) - Math.pow(radius, 2);
B = -2 * center.y;
A = 1;
disc = Math.pow(B, 2) - 4 * 1 * C;
if (disc < 0){
return intersections;
}
else{
y1 = (-B + Math.sqrt(disc)) / (2 * A);
y2 = (-B - Math.sqrt(disc)) / (2 * A);
x1 = pointB.x;
x2 = pointB.x;
}
point1 = new IntPoint((int)x1, (int)y1);
point2 = new IntPoint((int)x2, (int)y2);
if (Util.euclideanDistance(pointA, point2) > Util.euclideanDistance(pointA, point1)){
intersections.add(point1);
}
else{
intersections.add(point2);
}
return intersections;
}
if (slope == 0){
C = Math.pow(center.x, 2) + Math.pow(center.y, 2) + Math.pow(pointB.y, 2) - 2 * pointB.y * center.y - Math.pow(radius, 2);
B = -2 * center.x;
A = 1;
disc = Math.pow(B, 2) - 4 * 1 * C;
if (disc < 0){
return intersections;
}
else{
x1 = (-B + Math.sqrt(disc)) / (2*A);
x2 = (-B - Math.sqrt(disc)) / (2*A);
y1 = pointB.y;
y2 = pointB.y;
}
}
else{
c = slope * pointA.x + pointA.y;
B = (2 * center.x + 2 * center.y * slope + 2 * c * slope);
A = 1 + Math.pow(slope, 2);
C = (Math.pow(center.x, 2) + Math.pow(c, 2) + 2 * center.y * c + Math.pow(center.y, 2) - Math.pow(radius, 2));
disc = Math.pow(B, 2) - (4 * A * C);
if (disc < 0){
return intersections;
}
else{
x1 = (-B + Math.sqrt(disc)) / (2 * A);
x2 = (-B - Math.sqrt(disc)) / (2 * A);
y1 = slope * x1 - c;
y2 = slope * x2 - c;
}
}
point1 = new IntPoint((int)x1, (int)y1);
point2 = new IntPoint((int)x2, (int)y2);
if (Util.euclideanDistance(pointA, point2) > Util.euclideanDistance(pointA, point1)){
//if (Util.angleBetween(pointA, pointB, point1) < Math.PI/2){
intersections.add(point1);
//}
}
else{
//if (Util.angleBetween(pointA, pointB, point1) < Math.PI/2){
intersections.add(point2);
//}
}
return intersections;
}
I am using the above algorithm to test for intersection between a circle and a line. It works fine sometimes but at other times it fails. The code represents the equation which is derived from solving for x simultaneously from circle and line equations (x-a)^+(y-b)^2=r^2 and y = mx - mx1 + y1. Has anyone got an idea where I am going wrong either in my maths or elsewhere?
Your calculations seem quite long, and I do not see the use of the different cases you test.
Anyway, since I found the problem interesting I attempted to solve it myself and came up with the following. Feel free to replace double radius by int radius and use IntPoints, but be aware that every time you cast, as discussed in the comments, results that are not exact integer intersection points will become wrong.
The background of the calculations performed is this: From point A, a scaled version of vector AB points to a point on the circle. That point has distance radius from center. Hence, |AC + scalingFactor * AB|=r.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CircleLine {
public static List<Point> getCircleLineIntersectionPoint(Point pointA,
Point pointB, Point center, double radius) {
double baX = pointB.x - pointA.x;
double baY = pointB.y - pointA.y;
double caX = center.x - pointA.x;
double caY = center.y - pointA.y;
double a = baX * baX + baY * baY;
double bBy2 = baX * caX + baY * caY;
double c = caX * caX + caY * caY - radius * radius;
double pBy2 = bBy2 / a;
double q = c / a;
double disc = pBy2 * pBy2 - q;
if (disc < 0) {
return Collections.emptyList();
}
// if disc == 0 ... dealt with later
double tmpSqrt = Math.sqrt(disc);
double abScalingFactor1 = -pBy2 + tmpSqrt;
double abScalingFactor2 = -pBy2 - tmpSqrt;
Point p1 = new Point(pointA.x - baX * abScalingFactor1, pointA.y
- baY * abScalingFactor1);
if (disc == 0) { // abScalingFactor1 == abScalingFactor2
return Collections.singletonList(p1);
}
Point p2 = new Point(pointA.x - baX * abScalingFactor2, pointA.y
- baY * abScalingFactor2);
return Arrays.asList(p1, p2);
}
static class Point {
double x, y;
public Point(double x, double y) { this.x = x; this.y = y; }
#Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
public static void main(String[] args) {
System.out.println(getCircleLineIntersectionPoint(new Point(-3, -3),
new Point(-3, 3), new Point(0, 0), 5));
System.out.println(getCircleLineIntersectionPoint(new Point(0, -2),
new Point(1, -2), new Point(1, 1), 5));
System.out.println(getCircleLineIntersectionPoint(new Point(1, -1),
new Point(-1, 0), new Point(-1, 1), 5));
System.out.println(getCircleLineIntersectionPoint(new Point(-3, -3),
new Point(-2, -2), new Point(0, 0), Math.sqrt(2)));
}
Here a solution with import javax.vecmath.Vector2d;
static Vector2d[] circleLineIntersection1(Vector2d a, Vector2d b, Vector2d o, double radius) {
Vector2d p1 = new Vector2d(a);
Vector2d p2 = new Vector2d(b);
p1.sub(o);
p2.sub(o);
Vector2d d = new Vector2d();
d.sub(p2, p1);
double det = p1.x * p2.y - p2.x * p1.y;
double dSq = d.lengthSquared();
double discrimant = radius * radius * dSq - det * det;
if (discrimant < 0) {
return new Vector2d[0];
}
if (discrimant == 0) {
Vector2d[] t = new Vector2d[1];
t[0] = new Vector2d(det * d.y / dSq + o.x, -det * d.x / dSq + o.y);
return t;
}
double discSqrt = Math.sqrt(discrimant);
double sgn = 1;
if (d.y < 0) {
sgn = -1;
}
Vector2d[] t = new Vector2d[2];
t[0] = new Vector2d((det * d.y + sgn * d.x * discSqrt) / dSq + o.x, (-det * d.x + Math.abs(d.y) * discSqrt) / dSq + o.y);
t[1] = new Vector2d((det * d.y - sgn * d.x * discSqrt) / dSq + o.x, (-det * d.x - Math.abs(d.y) * discSqrt) / dSq + o.y);
return t;
}
What library/header/class is equivalent to the Java Math class?
Background & bonus question:
I'm trying to port this function to Objective-C from Java. Should I rewrite it, or can I copy and paste and rewrite only the parts that are syntactically different? (In other words, will the Java behave the same way if it were run as Objective-C or C?)
Here's the monster function in Java. It essentially is the Vincinty Formula:
private double vincentyFormula(GeoLocation location, int formula) {
double a = 6378137;
double b = 6356752.3142;
double f = 1 / 298.257223563; // WGS-84 ellipsiod
double L = Math.toRadians(location.getLongitude() - getLongitude());
double U1 = Math
.atan((1 - f) * Math.tan(Math.toRadians(getLatitude())));
double U2 = Math.atan((1 - f)
* Math.tan(Math.toRadians(location.getLatitude())));
double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
double lambda = L;
double lambdaP = 2 * Math.PI;
double iterLimit = 20;
double sinLambda = 0;
double cosLambda = 0;
double sinSigma = 0;
double cosSigma = 0;
double sigma = 0;
double sinAlpha = 0;
double cosSqAlpha = 0;
double cos2SigmaM = 0;
double C;
while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0) {
sinLambda = Math.sin(lambda);
cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda)
+ (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)
* (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
if (sinSigma == 0)
return 0; // co-incident points
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
if (Double.isNaN(cos2SigmaM))
cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (ß6)
C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambdaP = lambda;
lambda = L
+ (1 - C)
* f
* sinAlpha
* (sigma + C
* sinSigma
* (cos2SigmaM + C * cosSigma
* (-1 + 2 * cos2SigmaM * cos2SigmaM)));
}
if (iterLimit == 0)
return Double.NaN; // formula failed to converge
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384
* (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double deltaSigma = B
* sinSigma
* (cos2SigmaM + B
/ 4
* (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B
/ 6 * cos2SigmaM
* (-3 + 4 * sinSigma * sinSigma)
* (-3 + 4 * cos2SigmaM * cos2SigmaM)));
double distance = b * A * (sigma - deltaSigma);
// initial bearing
double fwdAz = Math.toDegrees(Math.atan2(cosU2 * sinLambda, cosU1
* sinU2 - sinU1 * cosU2 * cosLambda));
// final bearing
double revAz = Math.toDegrees(Math.atan2(cosU1 * sinLambda, -sinU1
* cosU2 + cosU1 * sinU2 * cosLambda));
if (formula == DISTANCE) {
return distance;
} else if (formula == INITIAL_BEARING) {
return fwdAz;
} else if (formula == FINAL_BEARING) {
return revAz;
} else { // should never happpen
return Double.NaN;
}
}
Java's Math class in very much based on the standard C math.h, so you could use that one.
Looks like you're reimplementing existing code. See MKMetersBetweenMapPoints().
I am wanting to find the distance between two different points. This I know can be accomplished with the great circle distance.
http://www.meridianworlddata.com/Distance-calculation.asp
Once done, with a point and distance I would like to find the point that distance north, and that distance east in order to create a box around the point.
Here is a Java implementation of Haversine formula. I use this in a project to calculate distance in miles between lat/longs.
public static double distFrom(double lat1, double lng1, double lat2, double lng2) {
double earthRadius = 3958.75; // miles (or 6371.0 kilometers)
double dLat = Math.toRadians(lat2-lat1);
double dLng = Math.toRadians(lng2-lng1);
double sindLat = Math.sin(dLat / 2);
double sindLng = Math.sin(dLng / 2);
double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2)
* Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2));
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double dist = earthRadius * c;
return dist;
}
Or you could use SimpleLatLng. Apache 2.0 licensed and used in one production system that I know of: mine.
Short story:
I was searching for a simple geo library and couldn't find one to fit my needs. And who wants to write and test and debug these little geo tools over and over again in every application? There's got to be a better way!
So SimpleLatLng was born as a way to store latitude-longitude data, do distance calculations, and create shaped boundaries.
I know I'm two years too late to help the original poster, but my aim is to help the people like me who find this question in a search. I would love to have some people use it and contribute to the testing and vision of this little lightweight utility.
We've had some success using OpenMap to plot a lot of positional data. There's a LatLonPoint class that has some basic functionality, including distance.
For a more accurate distance (0.5mm) you can also use the Vincenty approximation:
/**
* Calculates geodetic distance between two points specified by latitude/longitude using Vincenty inverse formula
* for ellipsoids
*
* #param lat1
* first point latitude in decimal degrees
* #param lon1
* first point longitude in decimal degrees
* #param lat2
* second point latitude in decimal degrees
* #param lon2
* second point longitude in decimal degrees
* #returns distance in meters between points with 5.10<sup>-4</sup> precision
* #see Originally posted here
*/
public static double distVincenty(double lat1, double lon1, double lat2, double lon2) {
double a = 6378137, b = 6356752.314245, f = 1 / 298.257223563; // WGS-84 ellipsoid params
double L = Math.toRadians(lon2 - lon1);
double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat1)));
double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat2)));
double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
double sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM;
double lambda = L, lambdaP, iterLimit = 100;
do {
sinLambda = Math.sin(lambda);
cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda)
+ (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
if (sinSigma == 0)
return 0; // co-incident points
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
if (Double.isNaN(cos2SigmaM))
cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambdaP = lambda;
lambda = L + (1 - C) * f * sinAlpha
* (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
} while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);
if (iterLimit == 0)
return Double.NaN; // formula failed to converge
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double deltaSigma = B
* sinSigma
* (cos2SigmaM + B
/ 4
* (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM
* (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
double dist = b * A * (sigma - deltaSigma);
return dist;
}
This code was freely adapted from http://www.movable-type.co.uk/scripts/latlong-vincenty.html
Corrected Haversine Distance formula....
public static double HaverSineDistance(double lat1, double lng1, double lat2, double lng2)
{
// mHager 08-12-2012
// http://en.wikipedia.org/wiki/Haversine_formula
// Implementation
// convert to radians
lat1 = Math.toRadians(lat1);
lng1 = Math.toRadians(lng1);
lat2 = Math.toRadians(lat2);
lng2 = Math.toRadians(lng2);
double dlon = lng2 - lng1;
double dlat = lat2 - lat1;
double a = Math.pow((Math.sin(dlat/2)),2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon/2),2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return EARTH_RADIUS * c;
}
http://www.movable-type.co.uk/scripts/latlong.html
public static Double distanceBetweenTwoLocationsInKm(Double latitudeOne, Double longitudeOne, Double latitudeTwo, Double longitudeTwo) {
if (latitudeOne == null || latitudeTwo == null || longitudeOne == null || longitudeTwo == null) {
return null;
}
Double earthRadius = 6371.0;
Double diffBetweenLatitudeRadians = Math.toRadians(latitudeTwo - latitudeOne);
Double diffBetweenLongitudeRadians = Math.toRadians(longitudeTwo - longitudeOne);
Double latitudeOneInRadians = Math.toRadians(latitudeOne);
Double latitudeTwoInRadians = Math.toRadians(latitudeTwo);
Double a = Math.sin(diffBetweenLatitudeRadians / 2) * Math.sin(diffBetweenLatitudeRadians / 2) + Math.cos(latitudeOneInRadians) * Math.cos(latitudeTwoInRadians) * Math.sin(diffBetweenLongitudeRadians / 2)
* Math.sin(diffBetweenLongitudeRadians / 2);
Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return (earthRadius * c);
}
You can use the Java Geodesy Library for GPS, it uses the Vincenty's formulae which takes account of the earths surface curvature.
Implementation goes like this:
import org.gavaghan.geodesy.*;
...
GeodeticCalculator geoCalc = new GeodeticCalculator();
Ellipsoid reference = Ellipsoid.WGS84;
GlobalPosition pointA = new GlobalPosition(latitude, longitude, 0.0);
GlobalPosition userPos = new GlobalPosition(userLat, userLon, 0.0);
double distance = geoCalc.calculateGeodeticCurve(reference, userPos, pointA).getEllipsoidalDistance();
The resulting distance is in meters.
This method would help you find the distance between to geographic location in km.
private double getDist(double lat1, double lon1, double lat2, double lon2)
{
int R = 6373; // radius of the earth in kilometres
double lat1rad = Math.toRadians(lat1);
double lat2rad = Math.toRadians(lat2);
double deltaLat = Math.toRadians(lat2-lat1);
double deltaLon = Math.toRadians(lon2-lon1);
double a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
Math.cos(lat1rad) * Math.cos(lat2rad) *
Math.sin(deltaLon/2) * Math.sin(deltaLon/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double d = R * c;
return d;
}
Kotlin version of Haversine formula. Returned result in meters. Tested on https://www.vcalc.com/wiki/vCalc/Haversine+-+Distance
const val EARTH_RADIUS_IN_METERS = 6371007.177356707
fun distance(lat1: Double, lng1: Double, lat2: Double, lng2: Double): Double {
val latDiff = Math.toRadians(abs(lat2 - lat1))
val lngDiff = Math.toRadians(abs(lng2 - lng1))
val a = sin(latDiff / 2) * sin(latDiff / 2) +
cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
sin(lngDiff / 2) * sin(lngDiff / 2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return EARTH_RADIUS_IN_METERS * c
}
I know that there are many answers, but in doing some research on this topic, I found that most answers here use the Haversine formula, but the Vincenty formula is actually more accurate. There was one post that adapted the calculation from a Javascript version, but it's very unwieldy. I found a version that is superior because:
It also has an open license.
It uses OOP principles.
It has greater flexibility to choose the ellipsoid you want to use.
It has more methods to allow for different calculations in the future.
It is well documented.
VincentyDistanceCalculator
I typically use MATLAB with the Mapping Toolbox, and then use the code in my Java using MATLAB Builder JA. It makes my life a lot simpler. Given most schools have it for free student access, you can try it out (or get the trial version to get over your work).
For Android, there is a simple approach.
public static float getDistanceInMeter(LatLng start, LatLng end) {
float[] results = new float[1];
Location.distanceBetween(start.latitude, start.longitude, end.latitude, end.longitude, results);
return results[0];
}
;
https://developer.android.com/reference/android/location/Location#distanceBetween(lat1,lng1,lat2,lng2,output[])