I am creating an app that has a features that needs to find supermarkets that are in a 5km range from the user.
I thought that using longitude and latitude would be a good idea (I should store those two values as atributes on a supermarket table and I'd be ready to go).
Once I created my table and inserted it's values, I went to look for a solution on how could I get those supermarkets from my database.
What I had in mind would be a select with a not so complex WHERE clause that would solve all my problems. Instead, I found a bunch of different ways of doing it:
1) Create an square around the user location, find the supermarkets that are inside of it and then find the ones that are actually in that range
2) Create extra coluns on your table
3) Use the cosine law
4) Find out how to convert latitude/longitude to kilometers and use Pythagorean Theorem (for ranges that are not so wide)
The three problems are that any solution is made on the database (with a where clause), I have no idea which one has more performance or is better in any aspect and it's not like I'm getting how to use those exemples. So... What should I use remembering that I have and SQLite database and android?
Thanks :)
ADDING MORE INFORMATION:
The first problem was to know which one I should use.
The problem I had with this one was the this:
My user location is:
longitude = -22.82753105;
latitude = -47.03398022;
My where statement
WHERE latitude < -47.078946300295925 AND
latitude > -46.989014139704054 AND
longitude > -22.76155626735494 AND
longitude < -22.89350583264506
I have a market on my database which location is the same the user and I do get a list with this very element once I select the ones that are in the square, but when I iterate on my list, it removes my element from the list bacause the if return FALSE.
if (getDistanceBetweenTwoPoints(mercado, userLocation) <= RANGE)
On the getDistanceBetweenTwoPoints, if I debug it, I get:
dLat = -0.4224822382331485
dLon = 0.4224822382331485
lat1 = -0.39841557692373836
lat2 = -0.8208978151568869
a = 0.07157979929009457
c = 0.5416864422451098
d = 3451084.323543594
And since the if compares "d" with range, it's like this:
if(3451084.323543594 <= 5000) { //my range is 5km
// keep element
else
// remove it from list
Does anyone can tell me what is the problem??
Related
I have a SQLite database which stores latitude and longitude. I have to find if the users current location is between the Latitude and Longitude(Which is stored in Database) or not.
I been looking for a while to find a solution for this problem but couldn't get it. Most of them are related with the distance calculation.
How to find the current location(obtained in the app) is between the Latitude and Longitude(Which is stored in SQLite Database)?
I am not sure if you exactly want but what i think
what you have : two geographical point (two pair of attitude and longitude )
what you want : calculate the adjoining regions covered between the two geo points , and check if the user is within that are
for that what you can do is :
1 : suppose the two points are diameteric terminals of a circle and search if the user is within this circle , like this Image
this approach might make you search a little irrelavant region
2 : suppose the two point are two ends of a straight line , consider a distance , say 50 km , on both sides , and search the 100 km wide strip from point A to point B
like this Image
Assume you have two locations a and b and your current location c.
C
/ \
A---B
You can use the dotproduct to calculate if c is between a and b:
If the dotproduct of two vectors is >0, the degrees between these vectors is <90°.
Know you need two calculations like this:
if(Vector(AB) * Vector(AC) > 0 && Vector(BC) * Vector(BA) > 0)
// your location is between
You can also look here and here for more details.
I have database with about 12 000 entries. Each entry has given latitude, longitude and empty distance. What I need to do is to find 25 nearest entries from current GPS position. My ORM is greenDao.
Have 2 problems:
I don't know distance between me and entries yet and I can't load all entries to RAM because when I do, heap goes up to 70MB and app crashes at OutOfMemoryException (so I need to use lazy loading).
I tried this approach:
Get iterator for given table
load entry, calculate its distance from my current position, save entry to ArrayList buffer (I flush buffer every 1000 entries back into db (it is just updateInTx(...)) and then clean it)
repeat point 2 until iterator.hasNext();
query from entries with limit(25).orderAsc()
result
This works, but from point 1-3 it is very very slow (takes about 25 sec on Nexus 7) . Rest takes about 1.5 sec.
I have to do this every time when user launches app or requests data refresh.
Any ideas how to solve it better way?
Thanks
EDIT:
This is function for calculating distance, so its hard to make this in SQL :(
double getDistance(GPSCoords myPos, Place place) {
double dlong = (place.getLongitude() - myPos.getLongitude()) * d2r;
double dlat = (place.getLatitude() - myPos.getLatitude()) * d2r;
double a = Math.pow(Math.sin(dlat / 2.0), 2) + Math.cos(myPos.getLatitude() * d2r)
* Math.cos(place.getLatitude() * d2r) * Math.pow(Math.sin(dlong / 2.0), 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = 6367 * c;
return d;
}
You should be able to let SQL do the work inside the database:
select ((x - ?)*(x - ?) + (y - ?)*(y - ?)) as distsq from entries
order by dist limit 20
Unfortunately sqlite doesn't provide exponentiation, so the duplicated terms are needed.
If this is still not fast enough, another approach would be to make bounding box queries centered on your location, adjusting the size of the bounding box by binary search until you have 30 or a few more entries. Indexes on each of the x and y dimension will speed these along.
Edit Since the OP says earth curvature is important, a bounding box technique is probably the best approach we can get with unextended sqlite. Here is a proposed algorithm:
Let P be the current position
Let Slat = lat0 be the bounding box latitude half-size initialized with a "best guess"
Let Slon = lon0 be the bounding box longitude half-size initialized with a "best guess"
// NB the best guesses should cover an approximately square area on the ground
loop
Let W = P.lon - Slon, E = P.lon + Slon, N = P.lat + Slat, S = P.lat - Slat
C = select count(*) from entries
where W <= lon and lon <= E and S <= lat and lat <= N
if C indicates the result is too big (e.g. for memory or read time),
Slat = 0.5 * Slat
Slon = 0.5 * Slon
else
Let R be the result of the same query for * instead of count(*)
Let D be the geometric distance from P to the nearest point on bounding box
Compute r.dist for all r in R (in memory)
Sort R by dist (in memory)
Throw away the tail elements of R where r.dist > D
// Can't use these because points outside bounding box might be closer!
If at least 20 remaining R elements,
return top 20
else
Slat = 2 * Slat
Slon = 2 * Slon
end if
end if
end loop
Note you need indices for lat and lon. I don't know how good the SQLite query optimizer is in this case. A good optimizer will pick either the lat or lon index based on statistics accumulated from past queries, use this to quickly find all points in the bounding box range for that dimension, then do a scan of this result to get the final. If the optimizer is not that clever, you want to index only the dimension likely to produce the smallest initial result: in the average case this is the one with greatest geometric extent (distance covered).
The r* tree index will make the bounding box queries much faster, but at least through Jelly Bean, you'd have to provide your own SQLite instance with this extension included. Perhaps later Android versions included it? I don't know.
Additionally, if you get to the point of including a custom SQLite with the app, it would be pretty easy to add the distance (with curvature) function as an extension.
I don't see why exactly you feel you need to lazy load your entries. The 70MB heap number sounds pretty suspicious with only 12k entries. Are you grabbing the entire row just to calculate distance? Try just grabbing the columns you need:
Latitude
Longitude
Primary Key
Assuming each is 8 bytes apiece, that's 24 * 12000 bytes, or roughly 280 kilobytes. Give it some overhead room for just being Java, but you're still looking at something very manageable.
Then you can do the calculations in code, and just have it spit out the primary key for each of the closest points. A second query can grab just those 25(the entire row this time), and you're done!
There are a lot of examples of distance calculation using different flavors of SQL out there. Loading every row from your DB and calculating how far it is, then sorting and taking the closest is going to be slow just from the back and forth to the database. Doing the calculation in SQL and only retrieving the ones you need is going to be much more performant.
You can try moving the distance calculation to the sql db.
you can also put some smarter code, that will run distance calculation until he finds 25 places that their distance from current location is less than x(you choose). or even less than 25 items (maybe you just need 7 to fill the screen) and than continue the calculation in background when user is already in the app.
It'll be a much better user experience.
I have a local database with over 2000 locations that I am trying to search through based on the users location. I only want to display a few markers in the Map Fragment nearby the user, but have so far been unable to find a way to do it.
Currently the database is in the form of a csv file, so I can change it to a different type easily. But I was waiting to do so until I could find a utility that can search the coordinates based on a radius around the user. Anybody have any suggestions?
You can define a LatLngBounds object for some box around the user, and then use bounds.contains(LatLng) to determine which of your locations are in that radius. I've used this method for collections of about the same size as yours and it was fast enough for our purposes. You will be searching a rectangle, not a radius, but since the map is a rectangle, maybe that makes more sense anyway?
This is a tricky topic and solution is not straight forward. I am briefly listing down the steps I have used and this works for well over 100k assets in my case.
Assuming you already have logic to get your current location and lets call if as myLocation.
Add your location data in sqlite file and treat location coordinates as nothing more than float.
Use offset as 0.001 which is roughly around 100 m and use the following in the query.
(myTable.lat > (myLocation.lat - offset) AND myTable.lat < (myLocation.lat + offset)) AND
(myTable.lon > (myLocation.lon - offset) AND myTable.lon < (myLocation.lon + offset))
This should give you the list of assets within a small radius
The trick here is to treat location as float and not really location.
For Offset: http://en.wikipedia.org/wiki/Decimal_degrees
I have a database full of map Locations (Latitude, Longitude), is there a way I could do a SQL query to return me all points around a certain location?
Right now I basically grab everything in the database and loop through each one and check the distance using
Location.distanceBetween(startPoint.latitude, startPoint.longitude,endPoint.latitude,endPoint.longitude, results);
and keeping items that are within the set distance but there could be a lot of points to loop through.
So is there anyway to do this in an SQL statement?
You can use a WHERE clause expression to filter on angular distance r from an origin (x0, y0). Since SQLite doesn't have a square root function, you'll have to use the squared distance:
SELECT ... FROM ...
WHERE (Latitude-x0)*(Latitude-x0) + (Longitude-y0)*(Longitude-y0) < r*r;
The only place this won't work well is near the poles or the prime meridian. It's also a planar approximation to the sphere, so it will only work for values of r that are quite small. Finally, it scales latitude and longitude equally, so the selected region looks more and more elliptical the farther away the origin is from the equator.
You will have to convert linear distance (e.g., "within 30 meters") to latitude/longitude differences. This is a rather complex subject because the Earth is not a perfect sphere. However, for rough calculations you can use the approximation that 1 nautical mile = 1852 meters = 1 arc minute of longitude at the equator. Since lines of longitude get closer together as the latitude moves away from the equator, you will need to use some trig to figure out what value of r to use at a given latitude. For more info on this problem see this thread or this one, or search the web for "convert meters to latitude longitude".
I have a database full of map Locations (Latitude, Longitude), is there a way I could do a SQL query to return me all points around a certain location?
You can easily check a square region.
Using very simplified latitude / longitude coordinates say you're at [25.86, 57.03] and you wanted everything in the "neighborhood" (+/- .05) you can use a query like this:
SELECT * FROM Coords WHERE (latitude BETWEEN 25.81 AND 25.91) AND (longitude BETWEEN 56.98 AND 57.08);
You can also use the SQLite R*Tree extension.
An R-Tree is a special index that is designed for doing range queries. R-Trees are most commonly used in geospatial systems where each entry is a rectangle with minimum and maximum X and Y coordinates.
The source code to the SQLite R*Tree module is included as part of the SQLite3 amalgamation but is disabled by default. To enable the R*Tree module, simply compile with the SQLITE_ENABLE_RTREE C-preprocessor macro defined.
I have a SQLite database in Android with a table that contains longitude, latitude and other columns.. now I want to get all records that have the distance smaller or equal to a given distance.
For example, I know my current coordinates(lat,long) and I want to get from my table all records that are at the maximum distance of 10km from me.
I found some links on stack but nothing too solid. Is there someone that knows an optimized solution for this problem?
I have thought that I could get all records that have latitude smaller than my lat + distance and greater than my lat - distance and longitude smaller than my long + distance and greater than long - distance. After this query I should check for some unwanted records since the query is not returning only the wanted records..
Is this a good idea?
You probably want to do this in two parts,
1) Run a query where you find all records that are within a certain value + or - of the current lat/lng of your location, the where clause might look like:
where (#latitude > (lat - .001) and #latitude > (lat - .001)) and
(#longitude> (lng- .001) and #longitude> (longitude- .001))
2) with the rough results from above, use the great circle/haversine method to determine what the actual distance between each location is (great circle/haversinse is already part of the android maps api).
Just for the record, it can be useful.
I don't know what you are trying to do, or if you have a backend or a server. But, if you DO have a server which stores these locations, you can (and should) use MongoDB to store it. It has a great support for geospatial information.
Here is an example of how you do what you want using MongoDB: http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-Querying