This question is related to another stackoverflow discussion distance between long&lat points
Here is the code from the top voted answer:
/*
* Calculate distance between two points in latitude and longitude taking
* into account height difference. If you are not interested in height
* difference pass 0.0. Uses Haversine method as its base.
*
* lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters
* el2 End altitude in meters
*/
private double distance(double lat1, double lat2, double lon1, double lon2,
double el1, double el2) {
final int R = 6371; // Radius of the earth
Double latDistance = deg2rad(lat2 - lat1);
Double lonDistance = deg2rad(lon2 - lon1);
Double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
+ Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2))
* Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = R * c * 1000; // convert to meters
double height = el1 - el2;
distance = Math.pow(distance, 2) + Math.pow(height, 2);
return Math.sqrt(distance);
}
private double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
The top voted answer has the following comment:
"Why not Math.toRadians() instead of deg2rad()? It would be really self-containing."
I looked up the Math.toRadians() method in the documentation and noticed this:
"Converts an angle measured in degrees to an approximately equivalent angle measured in radians. The conversion from degrees to radians is generally inexact."
Is the top voted answer's deg2rad method more or less exact than the Math.toRadians() method?
Using the deg2rad method performs two arithmetic operations and one Math.Pi look up, its not clear how Math.toRadians() performs the convention. Assuming that this distance calculation may be performed frequently and quick response to user input is desired, which conversion method would scale more efficiently?
If the answer to question 1 is that the two methods have roughly the same inexactness/accuracy, I think that I would use Math.toRadians. Using Math.ToRadians makes the code more readable, and I assume that it would scale more efficiently as well.
Math.toRadians is implemented like this:
public static double toRadians(double angdeg) {
return angdeg / 180.0 * PI;
}
1) If there is a difference, it's negligible. Math.toRadians does the division first, while that answer does the multiplication first.
2) The only way to find out for sure is to test it, but I would expect that neither is faster since they both do the same thing.
In Java 9, the implementations of toRadians and toDegrees were changed to this:
public static double toRadians(double angdeg) {
return angdeg * DEGREES_TO_RADIANS;
}
public static double toDegrees(double angrad) {
return angrad * RADIANS_TO_DEGREES;
}
where DEGREES_TO_RADIANS and RADIANS_TO_DEGREES are literal constants. According to the following sources, this gives a 3-fold performance increase in a JMH micro-benchmark.
(We can also infer that the JIT compiler is not performing an optimization that is equivalent to the above. I presume that is because such an optimization could alter the computation's results. That would make it incorrect in general. The JIT compiler probably cannot make the judgement which way gives more accurate results, and it certainly cannot judge if accuracy ... or reproducibility ... is the most important criterion.)
The JDK bug database entries that relate to this are:
https://bugs.openjdk.java.net/browse/JDK-8051808
https://bugs.openjdk.java.net/browse/JDK-4477961
In summary, the answer for Java 9 and later is that the standard Math functions are faster than the alternative version. (Whether this was true in Java 8 and earlier is still untested ...)
Related
I have converted the formula into Java provided here. But accuracy is a problem. We are using GPS coordinates.
We are using iPhone provided GPS location that is upto 10 decimal point accuracy.
/*
* Latitude and Longitude are in Degree
* Unit Of Measure : 1 = Feet,2 = Kilometer,3 = Miles
*/
//TODO 3 Change Unit of Measure and DISTANCE_IN_FEET constants to Enum
public static Double calculateDistance(double latitudeA,double longitudeA,double latitudeB,double longitudeB,short unitOfMeasure){
Double distance;
distance = DISTANCE_IN_FEET *
Math.acos(
Math.cos(Math.toRadians(latitudeA)) * Math.cos(Math.toRadians(latitudeB))
*
Math.cos(Math.toRadians(longitudeB) - Math.toRadians(longitudeA))
+
Math.sin(Math.toRadians(latitudeA))
*
Math.sin(Math.toRadians(latitudeB))
);
return distance;
}
FYI : public static final int DISTANCE_IN_FEET = 20924640;
And then I use Math.round(distance); to convert to long.
For actual 25 feet, I am getting 7 feet output.
You need the haversine formula.
Here's my java implementation:
/**
* Calculates the distance in km between two lat/long points
* using the haversine formula
*/
public static double haversine(
double lat1, double lng1, double lat2, double lng2) {
int r = 6371; // average radius of the earth in km
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
* Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = r * c;
return d;
}
According to Wikipedia, 10 feet is close to the limit of what you can expect from normal GPS accuracy. (And that assumes you are getting a good GPS signal.) This post on http://gis.stackexchange.com provides more detailed numbers. It basically says that 3 meter horizontal resolution is the best you are likely to get with an ordinary GPS receiver.
So if you are comparing locations provided by two different (ordinary) receivers with best case ( 3 meter / 10 feet ) resolution on each, you are still not going to be able to tell reliably if one receiver is within 10 feet of the other.
(Note your 10 digits of "precision" may well be illusory. The real issue is how accurate the locations are; i.e. how big the error bars are ... on two different devices.)
So in that case what other alternatives I have?
The linked articles mention WAAS and Carrier Phase GPS (CPGPS). Another option is WPS.
I am little bit lost with double decimal point at the moment.
I have basically two methods, which will set the values for double amount and double receive. Then another integer variable where I would like to set the (receive - amount) * 100.
For example if I have two double values and I want to set their difference to an int value, then would it be possible?
My problem is that if I try to find the difference between two values, then e.g. (10.0- 9.40), then it will be 0.599999999. How can I get 0.60 out of it inside the method and use it? I know how to use NumberFormat or DecimalFormat. Should I use one inside the method to set the number of decimal points?
you can round off the value im using a decimalformat to round off the number. You can pass a double variable inside the method and this will return a number rounded off to 2 decimal points.
double RoundTo2Decimals(double val) {
DecimalFormat df2 = new DecimalFormat("###.##");
return Double.valueOf(df2.format(val));
}
You can use BigDecimal to perform the rounding, or you can use maths like this. It basically multiplies by 100, rounds and divides by 100.
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* #param d value to round
* #return rounded value
*/
public static double round2(double d) {
final double factor = 1e2;
return d > WHOLE_NUMBER / factor || d < -WHOLE_NUMBER / factor ? d :
(long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor;
}
Easiest solution could be below. Modifications and improvements are welcomed.
double x =10.0;
double y =9.40;
int xy =0;
DecimalFormat df = new DecimalFormat("#.##");
xy = (int) (Double.valueOf(df.format(x-y))*100);
System.out.println(xy);
I think I figured it out by using Math.round().
I will just ask whether my solution is a good or a bad idea to use? I am not just so familiar with BigDecimal. Long story short about the code. Example inputs are as: a = 9.40 and b = 10.0
private int difference;
private double amountDue;
private double receive;
public void setAmount(double a) {
amountDue = a;
}
public void receive(double b) {
receive = b;
difference = (int)Math.round(100 * (receive - amount));
I just needed to get int difference as 0.60 * 100 = 60, but as I mentioned before then just calculating the difference caused 0.59999999.
Just an extra question. Is it ok for me to initialize int balance variable inside one method as I have done?
This question already has answers here:
MySQL Great Circle Distance (Haversine formula)
(9 answers)
Closed 7 years ago.
I don't have all that much SQL experience and honestly don't have a clue where to go from here, most people that ask a question like this are using MS Server or something, and can use a bunch of parameters/values that I can't. I'm using MySQL.
Here's the code in Java I use to create a random location within X miles.
public static GeoPosition randomLocation(GeoPosition location, double radius) {
Random random = new Random();
// Convert radius from miles to meters
double meters = radius * 1609.34;
// Convert radius from meters to degrees
double radiusInDegrees = meters / 111300f;
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.cos(t);
double y = w * Math.sin(t);
// Adjust the x-coordinate for the shrinking of the east-west distances
double new_x = x / Math.cos(location.latitude());
double foundLongitude = new_x + location.longitude();
double foundLatitude = y + location.latitude();
return new GeoPosition(foundLongitude, foundLatitude);
}
These GeoPositions (Groups of longitude/latitude) are stored in the database. However now I need to figure out how to get all of the rows in the database within a radius of X miles.
When writing this out in Java, the distanceBetween method looked like this:
public static double distanceBetween(GeoPosition a, GeoPosition b) {
double longDif = a.longitude() - b.longitude();
double distance =
Math.sin(deg2rad(a.latitude()))
*
Math.sin(deg2rad(b.latitude()))
+
Math.cos(deg2rad(a.latitude()))
*
Math.cos(deg2rad(b.latitude()))
*
Math.cos(deg2rad(longDif));
distance = Math.acos(distance);
distance = rad2deg(distance);
distance = distance * 60 * 1.1515; // Convert to meters
distance = distance * 0.8684; // Convert to miles.
return distance;
}
private static double rad2deg(double rad) {
return (rad * 180.0 / Math.PI);
}
private static double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
and I just had to loop through a collection to find all of the positions, where distanceBetween was true. However this seems like more work on the client that needs to be done.
Is there a proper way to handle returning all results within X miles of a latitude/longitude from the database? because returning every result from the server is going to become a bandwidth killer really fast.
Question marked as PHP as well, because that's what I'm going to be using to poll the database.
In your place I simply moved the distance calculation to the mysql side. Although your code is complex, the Haversine formula with it can be calculated, is not. Here you can see also mysql code examples for the calculation, it doesn't even need a stored function.
Unfortunately, this simple formula is already too complex to be indexable from mysql. This solution will solve your bandwidth killing problem, but maybe it will overload your mysql. It will depend on your query rate and the number of your positions.
In case of a mysql overload I suggest to ask this in a new question.
There is a stackexchange site for geographic information systems here.
I'm trying to create a program in Java to calculate the inside angles of any triangle when the user inputs the side lengths. I've seen a few questions similar to this but I can`t get mine to work.
I want this to calculate the angle in degrees but it keeps giving me the wrong answer or not a number (NaN). I've tried putting it all in to one equation in case it was just rounding errors but it just gave the same answer. I've since put it back into this format to make it easier to read.
public class Triangles
{
// variables already declared and user inputs double sideOne, sideTwo, sideThree
threeSq=sideThree*sideThree;
twoSq=sideTwo*sideTwo;
oneSq=sideOne*sideOne;
public static double getAngleOne(double oneSq, double twoSq, double threeSq, double sideOne, double sideTwo, double sideThree)
{
double angOne;
angOne = (oneSq + twoSq - threeSq) / (2 * sideOne * sideTwo);
angOne = Math.toRadians(angOne);
angOne = Math.acos(angOne);
angOne = Math.toDegrees(angOne);
return angOne;
}
public static double getAngleTwo(double oneSq, double twoSq, double threeSq, double sideOne, double sideTwo, double sideThree)
{
double angTwo;
angTwo = (twoSq + threeSq - oneSq) / (2 * sideTwo * sideThree);
angTwo = Math.toRadians(angTwo);
angTwo = Math.acos(angTwo);
angTwo = Math.toDegrees(angTwo);
return angTwo;
}
public static double getAngleThree(double oneSq, double twoSq, double threeSq, double sideOne, double sideTwo, double sideThree)
{
double angThree;
angThree = (oneSq + threeSq - twoSq) / (2 * sideOne * sideThree);
angThree = Math.toRadians(angThree);
angThree = Math.acos(angThree);
angThree = Math.toDegrees(angThree);
return angThree;
}
}
I`m using the cosine law, but it is not giving me the correct answer. For example, when I input the side lengths as 3, 3 and 3 it gives me 71.68993312052173; when I input 5, 6 and 7 (sides 1, 2 and 3 respectively), I get NaN.
edit:
Thanks for the advice, I have changed all the ints to doubles and my math was the problem (forgot brackets around the oneSq + twoSq - threeSq)
I put up the full revised code but it is still giving the wrong answer, for a triangle with all sides the same, it should return 60 for all three but it`s returning 89.49999365358626.
After correcting the computation of the ratios there still remains one thing to do: Lose the lines
angOne = Math.toRadians(angOne);
at this point, angOne does not contain any angle. If the sides obey the triangle inequality, angOne should at that point contain a number between -1 and 1 that does not need converting.
The ratio of the areas for an equilateral triangle is 0.5. The operations convert-to-radians, acos, convert-to-degrees can be combined as
M*acos(x/M) = M*(pi/2-asin(x/M)),
with the multiplier M=180/pi. Since x/M is small, the result is approximately
M*(pi/2-x/M)=90-x,
resulting in a value close to 89.5, as obtained in your last trial.
Of course, the desired result is M*acos(0.5)=M*(pi/3)=60.
Apart from not using double values, your calculations are probably not correct.
According to cosine law
cosγ = (a^2 + b^2 - c^2)/2ab
so change ang = oneSq + threeSq - twoSq / (2 * sideOne * sideThree); to
double ang = (oneSq + twoSq - threeSq)*1.0 / (2 * sideOne * sideTwo);
Is it even that inaccurate? I re-implented the whole thing with Apfloat arbitrary precision and it made no difference which I should have known to start with!!
public static double bearing(LatLng latLng1, LatLng latLng2) {
double deltaLong = toRadians(latLng2.longitude - latLng1.longitude);
double lat1 = toRadians(latLng1.latitude);
double lat2 = toRadians(latLng2.latitude);
double y = sin(deltaLong) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(deltaLong);
double result = toDegrees(atan2(y, x));
return (result + 360.0) % 360.0;
}
#Test
public void testBearing() {
LatLng first = new LatLng(36.0, 174.0);
LatLng second = new LatLng(36.0, 175.0);
assertEquals(270.0, LatLng.bearing(second, first), 0.005);
assertEquals(90.0, LatLng.bearing(first, second), 0.005);
}
The first assertion in the test gives this:
java.lang.AssertionError:
expected:<270.0> but
was:<270.29389750911355>
0.29 seems to quite a long way off? Is this the formula i chose to implement?
If you've done what you seem to have done and done it correctly you have figured out the bearing of A from B along the shortest route from A to B which, on the surface of the spherical (ish) Earth is the arc of the great circle between A and B, NOT the arc of the line of latitude between A and B.
Mathematica's geodetic functions give the bearings, for your test positions, as 89.7061 and 270.294.
So, it looks as if (a) your calculation is correct but (b) your navigational skills need polishing up.
Are you sure this is due to numeric problems? I must admit, that I don't exactly know what you are trying to calculate, but when you dealing with angles on a sphere, small deviations from what you would expect in euclidian geometry.
java.lang.AssertionError: expected:<270.0> but was:<270.29389750911355>
This 0.29 absolute error represents a relative error of 0.1%. How is this "a long way off"?
Floats will give 7 significant digits; doubles are good for 16. Could be the trig functions or the degrees to radians conversion.
Formula looks right, if this source is to be believed.
If I plug your start and final values into that page, the result that they report is 089°42′22″. If I subtract your result from 360 and convert to degrees, minutes, and seconds your result is identical to theirs. Either you're both correct or you're both wrong.