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.
Related
I am in the process of taking over a project and noticed that, when the vincenty formulae is used,
it has been written in a unusual way. This is how it's written:
String direct(double distance, double initialBearing, double positionlat, double positionlong) {
// if (this.height != 0) throw new RangeError('point must be on the surface of
// the ellipsoid');
double φ1 = this.toRad(positionlat)/* .toRadians() */, λ1 = this.toRad(positionlong)/* .toRadians() */;
double α1 = this.toRad(initialBearing);
double s = distance;
// allow alternative ellipsoid to be specified
// double ellipsoid = /*this.datum ? this.datum.ellipsoid :*/
// LatLonEllipsoidal.ellipsoids.WGS84;
// const {a, b, f} = ellipsoid;
double a = 6378137;
double b = 6356752.314245;
double f = 1 / 298.257223563;
// double a = ellipsoid;
// double b = ellipsoid;
// double f = ellipsoid;
double sinα1 = Math.sin(α1);
double cosα1 = Math.cos(α1);
double tanU1 = (1 - f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
double σ1 = Math.atan2(tanU1, cosα1); // σ1 = angular distance on the sphere from the equator to P1
double sinα = cosU1 * sinα1; // α = azimuth of the geodesic at the equator
double cosSqα = 1 - sinα * sinα;
double uSq = cosSqα * (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 σ = s / (b * A);
Double sinσ = null, cosσ = null, Δσ = null; // σ = angular distance P� P₂ on the sphere
Double cos2σₘ = null; // σₘ = angular distance on the sphere from the equator to the midpoint of the
// line
Double σʹ = null, iterations = 0d;
do {
cos2σₘ = Math.cos(2 * σ1 + σ);
sinσ = Math.sin(σ);
cosσ = Math.cos(σ);
Δσ = B * sinσ * (cos2σₘ + B / 4 * (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)
- B / 6 * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
σʹ = σ;
σ = s / (b * A) + Δσ;
} while (Math.abs(σ - σʹ) > 1e-12 && ++iterations < 100);
if (iterations >= 100) {
//throw new Exception("Vincenty formula failed to converge"); // not possible?
System.err.println("Warning: Vincenty formula failed to converge!");
}
double x = sinU1 * sinσ - cosU1 * cosσ * cosα1;
double φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - f) * Math.sqrt(sinα * sinα + x * x));
double λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1);
double C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα));
double L = λ - (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
double λ2 = λ1 + L;
double α2 = Math.atan2(sinα, -x);
// const destinationPoint = new
// LatLonEllipsoidal_Vincenty(this.toDeg(φ2)/*.toDegrees()*/,
// this.toDeg(λ2)/*.toDegrees()*/, 0, undefined);
return this.toDeg(φ2) + ";" + this.toDeg(λ2);/*
* { lat: this.toDeg(φ2), lng: this.toDeg(λ2)
*/
/*
* destinationPoint.g point: destinationPoint, finalBearing:
* Dms.wrap360(this.toDeg(α2)/*.toDegrees()
*//*
* ), iterations: iterations,
*/
// };
}
Now the IDE (eclipse) is responding, that it can't handle variable names such as e.g. φ1. Is there a elegant solution to fixing this or do I have to re-write it?
Most probably, the source code was written in the UTF-8 encoding, and your Eclipse is configured to interpret the sources as ANSI, ISO-8859-1, cp1252 or similar.
Then, what you see as φ is in fact the two-byte UTF-8 representation of the greek character phi (φ), interpreted according to ANSI.
Configure Eclipse to expect the UTF-8 encoding (Preferences / General / Workspace / Text file encoding), then it should be able to compile.
This example shows why it is a bad idea even today to use characters outside ASCII in source code.
Is there any known java library which allows me to convert WGS 84 cords to OSGB36 or if there is a good formula I can use? I am currently using this one, but it's not very accurate so I'm wondering if there's a better one I can use.
private static double[] Wgs84ToBNG(double inLat, double inLon) {
double lat = inLat * Math.PI / 180.0;
double lon = inLon * Math.PI / 180.0;
double a = 6377563.396; // Airy 1830 major & minor semi-axes
double b = 6356256.910;
double F0 = 0.9996012717; // NatGrid scale factor on central meridian
double lat0 = 49 * Math.PI / 180.0; // NatGrid true origin
double lon0 = -2 * Math.PI / 180.0;
double N0 = -100000; // northing & easting of true origin, metres
double E0 = 400000;
double e2 = 1 - (b * b) / (a * a); // eccentricity squared
double n = (a - b) / (a + b), n2 = n * n, n3 = n * n * n;
double cosLat = Math.cos(lat), sinLat = Math.sin(lat);
double nu = a * F0 / Math.sqrt(1 - e2 * sinLat * sinLat); // transverse
// radius of
// curvature
double rho = a * F0 * (1 - e2)
/ Math.pow(1 - e2 * sinLat * sinLat, 1.5); // meridional radius
// of curvature
double eta2 = nu / rho - 1;
double Ma = (1 + n + (5 / 4) * n2 + (5 / 4) * n3) * (lat - lat0);
double Mb = (3 * n + 3 * n * n + (21 / 8) * n3) * Math.sin(lat - lat0)
* Math.cos(lat + lat0);
double Mc = ((15 / 8) * n2 + (15 / 8) * n3)
* Math.sin(2 * (lat - lat0)) * Math.cos(2 * (lat + lat0));
double Md = (35 / 24) * n3 * Math.sin(3 * (lat - lat0))
* Math.cos(3 * (lat + lat0));
double M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
double cos3lat = cosLat * cosLat * cosLat;
double cos5lat = cos3lat * cosLat * cosLat;
double tan2lat = Math.tan(lat) * Math.tan(lat);
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 = lon - lon0;
double dLon2 = dLon * dLon;
double dLon3 = dLon2 * dLon;
double dLon4 = dLon3 * dLon;
double dLon5 = dLon4 * dLon;
double dLon6 = dLon5 * dLon;
double N = I + II * dLon2 + III * dLon4 + IIIA * dLon6;
double E = E0 + IV * dLon + V * dLon3 + VI * dLon5;
double[] returnValue = { E, N };
return returnValue;
}
Have a look at this page
In the section "Programs/Source Code" you will find a subsection entitled "WGS84 Lat/Long <=> OSGB36 Grid References". In this subsection there are several links to utilities providing this functionality.
In this other post you can find the source code for such a task with explanations.
If you are unsure about the correctness of other libraries I would compare results with PROJ.4. If you don't want to use the native lib there is also a pure Java port for PROJ.4 out there.
Using PROJ.4 for WGS84 Lat/Long <=> OSGB36 has been discussed here:
PROJ.4 library and OSGB36
Using cs2cs:
http://trac.osgeo.org/proj/wiki/man_cs2cs
OSGB36 string:
http://spatialreference.org/ref/epsg/27700/proj4/
WGS84 string:
http://spatialreference.org/ref/epsg/4326/proj4/
Java Port:
http://sourceforge.net/projects/jmapprojlib/
I've done a bit of work on this and have used the document A guide to coordinate systems in Great Britain It tells you all you need to know if you read it carefully.
From the look of the variable names used in your formula you may well have studied the same document. I think the most important paragraph in the whole document relating to converting between coordinate systems is this (from the bottom of page 30)
To summarise: For a simple datum change of latitude and longitude coordinates from datum A to
datum B, first convert to Cartesian coordinates (formulae in annexe B), taking all ellipsoid heights as
zero and using the ellipsoid parameters of datum A; then apply a Helmert transformation from datum A
to datum B using equation (3); finally convert back to latitude and longitude using the ellipsoid
parameters of datum B (formulae in annexe C), discarding the datum B ellipsoid height.
It describes the 3 steps you have to take. The formula you have quoted will convert a latitude/longitude to an easting/northing in the same datum. It's not a transformation
From the naming of your method you are passing WGS84 lat/lon in, so you should:
1) Put all thoughts of a grid reference system (and associated true origins etc) out of your mind until you have converted the lat/lon from one datum to the other
2) Convert that WGS84 lat/lon into the 3D Cartesians (that's the x, y and z) for the WGS84 datum, using the formulae B1 to B5 in the OS Guide. Make sure you use the parameters (major/minor axes) for the WGS84 datum
3) Using the Helmert transformation referred to, convert the Cartesians you've just calculated into Cartesians relative to the Airy 1830 ellipsoid. You'll find the 7 parameters you need to get the new Cartesians in section 6.6
The new coordinates xGB, yGB, zGB are:
double xGB = tx + (x * (1 + s)) + (-rz * y) + (ry * z);
double yGB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);
double zGB = tz + (-ry * x) + (rx * y) + (z * (1 + s));
They don't actually spell that out, they just assume you remember your matrix maths
4) Now convert those new Cartesians (which are relative to the OSGB36 datum) into a lat/lon relative to that OSGB36 datum using formulae B6 to B8
From here you can go on to work out grid eastings and northings using that formula you have quoted
I have used jcoord. Very useful and simple to implement.
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.
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[])