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.
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'm making a class and I'm trying to create a method that takes time as a parameter and does this formula to find the distance of something:
d(t) = vt cosθ
where d(t) is distance, v is velocity, t is time, and θ is the angle in degrees.
This is how I wrote it out in Java and then I tested it but didn't get the right result.
If I calculate distance with these values: velocity = 21 time = 43 angle = 62 I get a result of 423.93, but if I put it in my test class and use the method (and put in the same values) I get 487.89.
What did I do wrong? Why am I getting this value?
Method:
public double getDistance(double time) {
double vt = velocity * time;
angle = (int) Math.toRadians(angle);
double cosineCalc = Math.cos(angle);
double distance = vt * cosineCalc;
return distance;
Tester class implementation:
Projectile testOne = new Projectile(21, 62);
double actual = testOne.getDistance(43);
The issue is that (int) Math.toRadians(angle) loses precision and ends up rounding it to an integer. To make it work, make sure that angle is kept as a double and remove the (int) cast.
angle = (int) Math.toRadians(angle);
remove the (int) casting and it should work.
I'm implementing the calculations of the book Practical Astronomy with your Calculator or Spreadsheet. Until now my calculations yield exactly the same result as the example calculations in the book.
However, arriving at §39 "Calculating corrections for parallax" I encounter a difference that I fail to understand.
The task at hand is described as follows:
As an example, let us calculate the apparent right ascension and declination of the Moon on 26
February 1979 at 16h 45m UT when observed from a location 60 metres above sea-level on longitude 100° W and latitude 50° N. The geocentric coordinates were α = 22h 35m 19s and δ = −7° 41′ 13′′, and the Moon’s equatorial horizontal parallax was 1° 01′ 09′′.
The book describes the sequence of calculation as follows:
Yet my outcome of step 7 is −31.993415, but book says −31,994415. If I do the mathematics of step 7 with the values of the book on a calculator the result is −31.993415 too, so my outcome seems to be right and the book's wrong....
I could live with that but there is a difference in step 10 too. My result is -8,570634, the books result is -8.538165, a rather large difference. I've read over step 10 time and time again to see if there is an error in my code, but I don't see it.
As until now my calculations and the books calculations are exactly the same I'm stuck. Am I doing something wrong (preferred), or did the book make an error (let's hope there aren't any more...)
My Java code for this function is as follows:
static EquatorialCoordinate parallax(EquatorialCoordinate body, ObserverLocation observer, ZonedDateTime zdt, double P) {
double Hd = 15d * raha(body.α, zdt, observer.λ);
step("α", body.α);
step("δ", body.δ);
step("φ", observer.φ);
step("λ", observer.λ);
step("h", observer.h);
step("H", Hd);
double H = toRadians(Hd);
Parallax ρ = parallax(observer.φ, observer.h);
step("P", P);
P = toRadians(P);
double δ = toRadians(body.δ);
double r = 1d / sin(P);
step("r", r);
double ρsinφ = ρ.sin;
double ρcosφ = ρ.cos;
step("ρcosφ'",ρcosφ);
step("ρsinφ'",ρsinφ);
double Δ = atan((ρcosφ * sin(H)) / ((r * cos(δ)) - (ρcosφ * cos(H))));
step("Δ", toDegrees(Δ));
H += Δ;
step("H'", toDegrees(H));
Δ = toDegrees(Δ);
double α$ = body.α - (Δ / 15d);
step("α'", α$);
double divident = (r * sin(δ)) - ρsinφ;
double divisor = ( r * cos(δ) * cos(H) ) - ρcosφ;
double δ$ = atan(cos(H) * (divident / divisor));
δ$ = toDegrees(δ$);
step("δ'", δ$);
return new EquatorialCoordinate(α$, δ$);
}
The "step" function does a simple formatted printf. The output of this program is:
α 22.588611
δ -7.686944
φ 50.000000
λ -100.000000
h 60.000000
H -31.642500
P 1.019167
r 56.221228
ρcosφ' 0.644060
ρsinφ' 0.762422
Δ -0.350915
H' -31.993414
α' 22.612005
δ' -8.570634
The resulting δ' is -8° 34' 14.28" instead of -8° 32' 17"
I've replaced my calculated value of H' with the books value to see if the book contains a carried error, but even if I do so the value is wrong.
Thus... my big question is, is my implementation wrong (and where, I can't see it), or were the books calculations wrong.
(Edit:) Class is annotated with strictfp, using java.util.StrictMath.
You write
H += Δ;
Which changes the value of H.
Then you write
double δ$ = atan(cos(H) * (divident / divisor));
Which uses the new version of H when it should use the old value.
Thanks to #svasa, I found that the divisor of step 10 should contain H, not H'.
The correct code is:
static EquatorialCoordinate parallax(EquatorialCoordinate body, ObserverLocation observer, ZonedDateTime zdt, double P) {
double H = toRadians(15d * raha(body.α, zdt, observer.λ));
P = toRadians(P);
Parallax ρ = parallax(observer.φ, observer.h);
double δ = toRadians(body.δ);
double r = 1d / sin(P);
double Δ = atan((ρ.cosφ * sin(H)) / ((r * cos(δ)) - (ρ.cosφ * cos(H))));
double H$ = H + Δ;
double α$ = body.α - (toDegrees(Δ) / 15d);
double δ$ = toDegrees(atan(cos(H$) * ((r * sin(δ) - ρ.sinφ) / (r * cos(δ) * cos(H) - ρ.cosφ))));
return new EquatorialCoordinate(α$, δ$);
}
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 ...)
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);