I want to be able to add an exact distance to some GPS coordinates.
I have a longitude and a latitude and I want to add a distance, let's say 30 meters.
I found below formula but when I test it, it does not seem to be that accurate because the resulting long and lat are 37m away from the beginning coords.
public static Coordinates addDistanceToCoordinates(String lat, String lon, double distance) {
Double latitude = Double.valueOf(lat);
Double longitude = Double.valueOf(lon);
double lat0 = cos(Math.PI / 180.0 * latitude);
double x = longitude + (180/Math.PI)*(distance/6378137)/cos(lat0);
double y = latitude + (180/Math.PI)*(distance/6378137);
return Coordinates.builder().lon(x).lat(y).build();
}
If you have a center (x,y) and you move on the x axis by 30 meters and on the y axis by another 30 meters your distance from the center won't be 30.
It will be Math.sqrt( Math.pow(x, 2) + Math.pow(y, 2) );.
Specifically, there are an infinite number of points that are 30 meters distant from the center (or your initial coordinates).
If you want to move in only one direction, then you can simply add/subtract 30 meters in either of your axis.
As you already did:
double x = longitude + (180/Math.PI)*(distance/6378137)/cos(lat0);
or
double y = latitude + (180/Math.PI)*(distance/6378137);
but not both...
You are still better off by using angles in your calculations, which will turn handy when you move on both axis.
By knowing which direction you are headed to, for example 50° from the x axis,
double a = Math.toRadians(50); // degrees
double x = longitude + (180/Math.PI) * (30 / 6378137)/cos(lat0) * Math.cos(a);
double y = latitude + (180/Math.PI) * (30 / 6378137) * Math.sin(a);
Coming back to your question, if you want to move on the x axis and the y axis by the same distance and end up exactly 30 meters away from the center, then your angle will be double a = Math.toRadians(45); (if you head North-East) *
In fact you will obtain for both (30 / 6378137) * Math.cos(a) and (30 / 6378137) * Math.sin(a) a result of x1 = y1 = 3.325924707417923e-06.
If you then apply Pythagoras
double finalDistance = Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * 6378137;
you will find finalDistance to be 30 meters from your initial coordinates.
*
The correct calculation would be Math.toRadians(45 * (2 * n - 1)); | where n = [1, 2, 3, 4]
Your code adds 30 meters north and 30 meters east, resulting in 42.4 meters northeast.
The calculation is assuming earth as a sphere instead of an ellipsoid, but that's mostly OK, can make a difference of max. 0.2 percent. It uses the biggest earth diameter (equator - equator) instead of some mean value, which will result in points too close to the starting point most of the time, but agian, taht can give an error of maybe 0.2 percent.
The calculation assumes the lat/lon grid to be rectangular, which is OK as long as the distances are short and you stay away from north or south pole.
So, all of this doesn't explain the 20 percent error you are reporting. The problem must be outside of the code you showed us.
The most suspicious remaining aspect is the string conversion. You'll need at least 5 decimal places for lat / lon degree values to get a 1 meter resolution.
Or maybe the tool that told you about the 37 meters isn't correct or somehow incompatible with the data...
Related
This is the expression given as solution in the book I referred to but it seems to be beyond my understanding. Please anyone help to understand how are we exactly writing this angle? Why subtract pi/2? I really have no idea
xs=(int)(Math.cos(s*3.14f/30-3.14f/2)*45+xcentre);
ys=(int)(Math.sin(s*3.14f/30-3.14f/2)*45+ycentre);
xm=(int)(Math.cos(m*3.14f/30-3.14f/2)*40+xcentre);
ym=(int)(Math.sin(m*3.14f/30-3.14f/2)*40+ycentre);
xh=(int)(Math.cos((h*30+m/2)*3.14f/180-3.14f/2)*30+xcentre);
yh=(int)(Math.sin((h*30+m/2)*3.14f/180-3.14f/2)*30+ycentre);
Seems like it's giving the cartesian coordinates of the ends of the three hands of a clock centered at the origin, with up being negative y, and right being positive x. I can say this just by trying some values.
I wrapped your basic code like this:
static class ClockCoords {
int xs, ys;
int xm, ym;
int xh, yh;
public String toString() {
return String.format("h: %3d,%3d m: %3d,%3d s: %3d,%3d", xh, yh, xm, ym, xs, ys);
}
}
public static ClockCoords coords(int h, int m, int s) {
ClockCoords r = new ClockCoords();
int xcentre = 0;
int ycentre = 0;
r.xs = (int) (Math.cos(s * 3.14f / 30 - 3.14f / 2) * 45 + xcentre);
r.ys = (int) (Math.sin(s * 3.14f / 30 - 3.14f / 2) * 45 + ycentre);
r.xm = (int) (Math.cos(m * 3.14f / 30 - 3.14f / 2) * 40 + xcentre);
r.ym = (int) (Math.sin(m * 3.14f / 30 - 3.14f / 2) * 40 + ycentre);
r.xh = (int) (Math.cos((h * 30 + m / 2) * 3.14f / 180 - 3.14f / 2) * 30 + xcentre);
r.yh = (int) (Math.sin((h * 30 + m / 2) * 3.14f / 180 - 3.14f / 2) * 30 + ycentre);
return r;
}
public static void main(String[] args) throws IOException {
System.out.println(coords(0, 0, 0));
System.out.println(coords(0, 0, 45));
System.out.println(coords(6, 0, 0));
System.out.println(coords(6, 30, 0));
}
which gives me this:
h: 0,-29 m: 0,-39 s: 0,-44
h: 0,-29 m: 0,-39 s: -44, 0
h: 0, 29 m: 0,-39 s: 0,-44
h: -7, 28 m: 0, 39 s: 0,-44
So, if I haven't messed this up, the hour hand is 29 units long, the minute hand is 39 units long, and the second hand is 44 units long. At the start of the day, the first entry, they're all pointing straight up. 45 seconds into the day, the second entry, sees the second hand pointing off to the left, but the other two hands are still point straight up given integer granularity and hands that aren't too long. The third entry, 6:00, just flips the hour hand to point down. The fourth entry is interesting, 6:30, in that it points the minute hand down, but also moves the hour hand a little to the left and up...our first hand in a position not at a 90 degree angle to center. Interesting stuff. As to the WHY of all of this...I assume it's all basic trig and cartesian math.
Wait a sec...there one thing strange I don't get...I see in the formulas where each hand is given a different length. Why are the hand lengths I see off by 1 from what's in the formulas? My guess is that it has to do with the very rough approximation of PI that's used. I think those hands aren't pointing quite straight up. That's all I can think of. Maybe tomorrow I'll try plugging in a better value for PI.
Let me provide you some background information as well.
Trigonometry
is all about a circle, having a radius of 1 unit, centered in the origo, that is, point (0, 0). The coordinates of a point on the circle can be calculated as x = cos(alpha), y = sin(alpha), where alpha is the angle. Since all the points of the circle are of equal distance from the center, you only need to know the angle to compute the coordinates, which are (cos(alpha), sin(alpha)).
Reason: Look at Pythagoras's theorem, which is, by the way, a consequence of the law of cosines, where the largest angle of the triangle is of 90 degree.
Pythagoras's theorem states that
where a, b and c are lengths of sides of the triangle, the triangle being perpendicular and a < c > b is true. So, the cos and sin is due to trigonometrical coordinates.
Radians, PI/2
Units of angle can be measured in degrees (360 degrees being the full circle) or radians (2 PI being the full circle). Let's talk in radians. In math, 0 radians is the angle of the upmost point of the circle. pi/2 is the left-most. pi is the downmost. 3pi/2 is the rightmost. Notice, that as the angle increases your point moves counter-clockwise.
Since this is periodic, it is true that
2 * k * pi + alpha = alpha
On screens, y points downwards, by contrast to the standard in math, where y vertex points upwards. So, if you start from 0 radians, that will be the downmost point when shown on the screen and due to the invert y, as you increase the angle, you move to the clockwise direction. Removing pi / 2 from 0 points you to the rightmost point.
Variables
(xh, yh) are the hour coordinates
(xm, ym) are the minute coordinates
(xs, ys) are the second coordinates
They are the hands of a traditional clock.
h is the hour angle
m is the minute angle
s is the second angle
(xcentre, ycentre) are the coordinates of the center. They might be different from (0, 0), so coordinate moving, the so-called translation is supported.
Step sizes
There are 60 seconds in a minute and there are 60 minutes in an hour, so the angle step needs to be the same for them, even though, the frequency of steps is different, while there are 24 hours in a day, so the angle step of hour needs to be both less frequent and larger (12 positions on a traditional clock).
Length of hands
Hour, minute and second hands (no pun intended) differ in their length, this is why the unit of the trigonometric circle is multiplied by 45, 40 and 30, respectively.
As time passes, presumably the angles are changed and subsequently their coordinates change.
A less-exhaustive explanation, looking only at seconds:
xs = (int)(Math.cos(s*3.14f/30-3.14f/2)*45+xcentre);
ys = (int)(Math.sin(s*3.14f/30-3.14f/2)*45+ycentre);
As Steve's answer notes, drawing a line from (xcentre, ycentre) to (xs, ys) would display a seconds-hand, with "up" being "y = 0" -- so that axis is reversed compare to traditional plots. This actually works in our favor: normally, as angles grow, they are displayed as going anti-clockwise. Since the y axis is reversed in screen coordinates, this is fine for us: our hands will advance clockwise, as expected.
Let us look at the maths, using xs:
xs = Math.cos(s*3.14f/30-3.14f/2)*45; // removed rounding & using xcentre=0
xs = Math.cos((s/30)*PI - PI/2) * sHandLength; // use PI=3.14..., sHandLength=45
Now, at time 0, we want the seconds-hand to look straight up. But normally cos(0) = 1. If we substract 90º (PI/2), it will be looking straight up:
xs = Math.cos((s/30)*PI - zeroIsUp) * sHandLength;
We can undo this to keep on reasoning about the remaining expression:
xs = Math.cos((s/30)*PI); // sHandlength of 1, time 0 is left
All that is missing is that mysterious s/30*PI. Let's make a small table:
seconds(s) radians(argument to Math.cos())
0 0
15 PI/2 -- a quarter-turn of the hand
30 PI -- a half-turn
45 3*PI/2 -- a three-quarters turn
60 2*PI, or 0 -- a full turn
So it seems that it is simply that (s/30) * PI is short for (s/60) * (2*PI).
For a book on Java, I would have expected a much clearer way of writing this. My personal preference would have been:
xs = (int)(Math.cos(base60toRadians(s) - zeroIsUp) * secondHandLength + xCentre);
ys = (int)(Math.sin(base60toRadians(s) - zeroIsUp) * secondHandLength + yCentre);
// ... somewhere else
private float base60toRadians(int b60) { return (b60 / 60) * (2 * Math.PI); }
The coordinates of the clock's centre are (xcentre, ycentre).
With a geometric affine translation to the centre you can calculate like from (0, 0).
The hands are rotating clock wise, in the negative direction.
Now the hands of a clock start at the top, and (0, 1), which has an angle π/2 (90°). (1, 0) being where the angle starts, (-1, 0) halfways having angle π (180°).
The full 360° angle being 2π, 1/60th thereof becomes:
Seconds: 60 seconds = 2π; angle 2π / 60 = π / 30
double startAngle = Math.PI / 2; // Value 0 has angle π/2
double rs = 45;
xs = (int)(Math.cos(-s * Math.PI / 30 - startAngle) * rs + xcentre);
ys = ... sin ...
Hours: 24 hours = 2π; angle 2π / 24 = π / 12
I need to calculate speed after each 10 seconds or less (currently i am using fused location api to get the location after each 10 seconds). The problem is that the equipment is too slow and sometimes it gives the distance covers equal to zero.
I have tried using Location.distanceBetween() but it also produces zeros even when the equipment is moving. I have tried to calculate distance by a formula but sometimes distance is too small that it gives zero.
Now i want to calculate average speed. I want to save the points obtained in 1 minute (6 lat long values). And then after each 10 seconds, i want to calculate average speed between them. Thus after each 10 seconds I will add one points at the end and remove one point from the start. That will remove the possibility of zero.
Now is there any formula that can calculate speed or distance from set of lat long values or any better approach will be highly appreciated.
You can calculate distance between two point, that are close enough, using simple geometry
deltaLngMeters = R * cos(latitude) * deltaLongitudeRadians;
deltaLatMeters = R * deltaLatitudeRadians;
whereas deltas are in radians, deltaLatitudeRadians = deltaLatitudeDegrees * pi / 180
Hence distance = sqrt(deltaLngMeters ^2 + deltaLatMeters ^ 2).
To sum up
function distance(point1, point2) {
var degToRad = Math.PI / 180;
return R * degToRad * Math.sqrt(Math.pow(Math.cos(point1.lat * degToRad ) * (point1.lng - point2.lng) , 2) + Math.pow(point1.lat - point2.lat, 2));
}
If you have array of six points, you can calculate average speed.
points = [{lat: .., lng: ..}, ... ]; // 6 points
distancesSum = 0;
for(i = 0; i < distances.length - 1; i++) {
distancesSum += distance(points[i], points[i + 1]);
}
return (distancesSum / (points.length - 1));
Yes, R is for the Earth radius, R = 6371000;// meters
You can use multi threading(Thread.sleep()) to calculate a formula repeatedly for every 10 seconds. You can verify it here https://beginnersbook.com/2013/03/multithreading-in-java/.
For small distances(hope the device won't move at speeds above 1 km/s), earth's surface can be treated as a plane. Then the latitude and longitude will be the coordinates of the device on the Cartesian plane attached to earth. Hence you can calculate the distance by this formula:
√(delta(longitude)^2 + delta(latitude)^2)
delta: difference
I am trying to find closest cluster to the given cluster 1 (see example below). To visualize clusters, I used QGIS (X axis is Longitude and Y axis is Latitude).
First of all I calculated centroids for each cluster. Thus I got the following results:
Cluster 1:
Lat : -83.5
Lon: -159.3
Cluster 2:
Lat: -80.5
Lon: -123.9
Cluster 3:
Lat: -83.4
Lon: 159.4
As a distance metric I use Haversine formula:
/**
* Calculates Haversine distance between two points
* #param lat1
* #param lon1
* #param lat2
* #param lon2
* #return
*/
private static double haversine(double lat1, double lon1, double lat2, double lon2) {
double R = 6372.8;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
double a = Math.pow(Math.sin(dLat / 2),2) + Math.pow(Math.sin(dLon / 2),2) * Math.cos(lat1) * Math.cos(lat2);
double c = 2 * Math.asin(Math.sqrt(a));
return R * c;
}
I got the following results (one can get similar results by running the haversine function):
Distance between Cluster 1 and 2: 628.37
Distance between Cluster 1 and 3: 513.73
So, though it's clear from the picture that Cluster 2 is closer to Cluster 1 than Cluster 3, the formula says that Cluster 3 is closer.
Which formula should I better use for this case?
As #Azat says, your picture is wrong. For two reasons, actually.
Firstly, you've forgotten something very important - the "edges" of the map at +/- 180 degrees actually meet on the physical globe. That is, you need to (at minimum) turn your picture into a cylinder, like some 2d video games do.
Secondly, you need to remember what makes calculating distances on a sphere via lat/long so tricky: as you near the north/south poles, identical longitude values will get closer together, while latitude values will stay the same distance apart.
If you plot these on a sphere properly, it should really look like this:
(Courtesy of google earth)
The red line is about where the 180 degree latitude mark is (clusters 1 and 3 are almost reflections over that line). NOW it's clear that Cluster 3 is closer to Cluster 1 than Cluster 2 is.
All your calculation are totally exact.
You're really close to the south pole that why you make huge errors drawing points like if earth was flat.
Here is a pic from google earth where earth is round, we can see that 1 is closer to 3 than to 2 :
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 have data with latitude and longitude stored in my SQLite database, and I want to get the nearest locations to the parameters I put in (ex. My current location - lat/lng, etc.).
I know that this is possible in MySQL, and I've done quite some research that SQLite needs a custom external function for the Haversine formula (calculating distance on a sphere), but I haven't found anything that is written in Java and works.
Also, if I want to add custom functions, I need the org.sqlite .jar (for org.sqlite.Function), and that adds unnecessary size to the app.
The other side of this is, I need the Order by function from SQL, because displaying the distance alone isn't that much of a problem - I already did it in my custom SimpleCursorAdapter, but I can't sort the data, because I don't have the distance column in my database. That would mean updating the database every time the location changes and that's a waste of battery and performance. So if someone has any idea on sorting the cursor with a column that's not in the database, I'd be grateful too!
I know there are tons of Android apps out there that use this function, but can someone please explain the magic.
By the way, I found this alternative: Query to get records based on Radius in SQLite?
It's suggesting to make 4 new columns for cos and sin values of lat and lng, but is there any other, not so redundant way?
1) At first filter your SQLite data with a good approximation and decrease amount of data that you need to evaluate in your java code. Use the following procedure for this purpose:
To have a deterministic threshold and more accurate filter on data, It is better to calculate 4 locations that are in radius meter of the north, west, east and south of your central point in your java code and then check easily by less than and more than SQL operators (>, <) to determine if your points in database are in that rectangle or not.
The method calculateDerivedPosition(...) calculates those points for you (p1, p2, p3, p4 in picture).
/**
* Calculates the end-point from a given source at a given range (meters)
* and bearing (degrees). This methods uses simple geometry equations to
* calculate the end-point.
*
* #param point
* Point of origin
* #param range
* Range in meters
* #param bearing
* Bearing in degrees
* #return End-point from the source given the desired range and bearing.
*/
public static PointF calculateDerivedPosition(PointF point,
double range, double bearing)
{
double EarthRadius = 6371000; // m
double latA = Math.toRadians(point.x);
double lonA = Math.toRadians(point.y);
double angularDistance = range / EarthRadius;
double trueCourse = Math.toRadians(bearing);
double lat = Math.asin(
Math.sin(latA) * Math.cos(angularDistance) +
Math.cos(latA) * Math.sin(angularDistance)
* Math.cos(trueCourse));
double dlon = Math.atan2(
Math.sin(trueCourse) * Math.sin(angularDistance)
* Math.cos(latA),
Math.cos(angularDistance) - Math.sin(latA) * Math.sin(lat));
double lon = ((lonA + dlon + Math.PI) % (Math.PI * 2)) - Math.PI;
lat = Math.toDegrees(lat);
lon = Math.toDegrees(lon);
PointF newPoint = new PointF((float) lat, (float) lon);
return newPoint;
}
And now create your query:
PointF center = new PointF(x, y);
final double mult = 1; // mult = 1.1; is more reliable
PointF p1 = calculateDerivedPosition(center, mult * radius, 0);
PointF p2 = calculateDerivedPosition(center, mult * radius, 90);
PointF p3 = calculateDerivedPosition(center, mult * radius, 180);
PointF p4 = calculateDerivedPosition(center, mult * radius, 270);
strWhere = " WHERE "
+ COL_X + " > " + String.valueOf(p3.x) + " AND "
+ COL_X + " < " + String.valueOf(p1.x) + " AND "
+ COL_Y + " < " + String.valueOf(p2.y) + " AND "
+ COL_Y + " > " + String.valueOf(p4.y);
COL_X is the name of the column in the database that stores latitude values and COL_Y is for longitude.
So you have some data that are near your central point with a good approximation.
2) Now you can loop on these filtered data and determine if they are really near your point (in the circle) or not using the following methods:
public static boolean pointIsInCircle(PointF pointForCheck, PointF center,
double radius) {
if (getDistanceBetweenTwoPoints(pointForCheck, center) <= radius)
return true;
else
return false;
}
public static double getDistanceBetweenTwoPoints(PointF p1, PointF p2) {
double R = 6371000; // m
double dLat = Math.toRadians(p2.x - p1.x);
double dLon = Math.toRadians(p2.y - p1.y);
double lat1 = Math.toRadians(p1.x);
double lat2 = Math.toRadians(p2.x);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2)
* Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = R * c;
return d;
}
Enjoy!
I used and customized this reference and completed it.
Chris's answer is really useful (thanks!), but will only work if you are using rectilinear coordinates (eg UTM or OS grid references). If using degrees for lat/lng (eg WGS84) then the above only works at the equator. At other latitudes, you need to decrease the impact of longitude on the sort order. (Imagine you're close to the north pole... a degree of latitude is still the same as it is anywhere, but a degree of longitude may only be a few feet. This will mean that the sort order is incorrect).
If you are not at the equator, pre-calculate the fudge-factor, based on your current latitude:
<fudge> = Math.pow(Math.cos(Math.toRadians(<lat>)),2);
Then order by:
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) +
(<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN) * <fudge>)
It's still only an approximation, but much better than the first one, so sort order inaccuracies will be much rarer.
I know this has been answered and accepted but thought I'd add my experiences and solution.
Whilst I was happy to do a haversine function on the device to calculate the accurate distance between the user's current position and any particular target location there was a need to sort and limit the query results in order of distance.
The less than satisfactory solution is to return the lot and sort and filter after the fact but this would result in a second cursor and many unnecessary results being returned and discarded.
My preferred solution was to pass in a sort order of the squared delta values of the long and lats:
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) +
(<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN))
There's no need to do the full haversine just for a sort order and there's no need to square root the results therefore SQLite can handle the calculation.
EDIT:
This answer is still receiving love. It works fine in most cases but if you need a little more accuracy, please check out the answer by #Teasel below which adds a "fudge" factor that fixes inaccuracies that increase as the latitude approaches 90.
In order to increase performance as much as possible I suggest improve #Chris Simpson's idea with the following ORDER BY clause:
ORDER BY (<L> - <A> * LAT_COL - <B> * LON_COL + LAT_LON_SQ_SUM)
In this case you should pass the following values from code:
<L> = center_lat^2 + center_lon^2
<A> = 2 * center_lat
<B> = 2 * center_lon
And you should also store LAT_LON_SQ_SUM = LAT_COL^2 + LON_COL^2 as additional column in database. Populate it inserting your entities into database. This slightly improves performance while extracting large amount of data.
Try something like this:
//locations to calculate difference with
Location me = new Location("");
Location dest = new Location("");
//set lat and long of comparison obj
me.setLatitude(_mLat);
me.setLongitude(_mLong);
//init to circumference of the Earth
float smallest = 40008000.0f; //m
//var to hold id of db element we want
Integer id = 0;
//step through results
while(_myCursor.moveToNext()){
//set lat and long of destination obj
dest.setLatitude(_myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LATITUDE)));
dest.setLongitude(_myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LONGITUDE)));
//grab distance between me and the destination
float dist = me.distanceTo(dest);
//if this is the smallest dist so far
if(dist < smallest){
//store it
smallest = dist;
//grab it's id
id = _myCursor.getInt(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_ID));
}
}
After this, id contains the item you want from the database so you can fetch it:
//now we have traversed all the data, fetch the id of the closest event to us
_myCursor = _myDBHelper.fetchID(id);
_myCursor.moveToFirst();
//get lat and long of nearest location to user, used to push out to map view
_mLatNearest = _myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LATITUDE));
_mLongNearest = _myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LONGITUDE));
Hope that helps!