I created a database in SQL locally on my phone for an app and then information is uploaded to a database on a server in the same format. In the tables I have GPS coordinates structured as a string with a tab between latitude and longitude. Looking back it makes more sense to have the database structured so that latitude and longitude are in two separate columns as type doubles (seems like it would perform better for searching and sorting).
Would it be much faster for sorting if I turned the string into two Doubles latitude and longitude columns? Is it even possible to search GPS coordinates in this format: "x.xxxx y.yyyy"? Is there a better way to sort/store GPS coordinates in SQL tables for searching?
Looking back, I wish I had planned this out more carefully. I know planning saves you lots of time down the road. Well, I suppose that applies here.
Yes, you should use separate columns for lat/lon.
Floating point representation may or may not be a good idea; when searching for exact coordinates this would not work too well. When looking for a range of values float will be just fine.
Depending on the size of your table and query patterns you may consider creating indexes for performance.
You can use microdegrees to store the values efficiently as integers.
int lat = (int)(location.getLatitude() * 1000000);
int lon = (int)(location.getLongitude() * 1000000);
There is a bonus if you are going to add a MapView later on, because Android Maps API is using the same number format in GeoPoint
Yes if you need to use the two numbers separately in a calculation they should be kept separately.
If you don't care about the accuracy of your numbers then use type Double but be warned numbers are stored as an approximation and not as they appear.
If you require exact output as input then try using Decimal or Numeric as types.
More information here: MYSQL precision math
Related
I have table "vehicle_location" and "coordinates" column in table datatype is geomatry
and in my controller i am getting lat and long and radius in request so i want to find vehicle location data using radius
Have a look at the formula explained on Wikipedia: https://en.wikipedia.org/wiki/Great-circle_distance
You'll find someone asking about the same question here: Measuring the distance between two coordinates in PHP
Ideally, it would be good to be able to reduce the calculation of distances to only cars that are not to far from your location. So typically, I would start by an SQL query that only returns the vehicules which have latitude and longitude values in the nearby, according to the given radius.
Then, in a second step, you can calculate all the distances between these cars and your position, with the algorithm (which takes some calculation time) and sort them after.
The ideal thing to do is to try and do the calculation directly in SQL if possible, so that you can sort them and filter them with the radius. If it gets to complicated then do the calculation and sorting in PHP.
I'm building a php web app with Laravel 5.5 and I need to display a list of places (eg. stores) sorted by their distance from a user-specified location.
The places will be stored in a MySQL database and should be retrieved as Eloquent ORM model instances.
Doing some research I found many posts and questions on this topic (presenting different solutions), but, having very little experience with databases and geolocation/geospatial analysis, they mostly confused me, and I'd like to know what approach to follow and what are the best practices in this case.
Most answers I read suggest using the haversine formula or the spherical law of cosines in the SQL query, which would look something like (example taken from this answer):
$sf = 3.14159 / 180; // scaling factor
$sql = "SELECT * FROM table
WHERE lon BETWEEN '$minLon' AND '$maxLon'
AND lat BETWEEN '$minLat' AND '$maxLat'
ORDER BY ACOS(SIN(lat*$sf)*SIN($lat*$sf) + COS(lat*$sf)*COS($lat*$sf)*COS((lon-$lon)*$sf))";
This post points out the fact that, over short distances, assuming the Earth flat and computing a simple euclidean distance is a good approximation and is faster than using the haversine formula.
Since I only need to sort places within a single city at a time, this seems to be a good solution.
However, most of these posts and SO answers are a few years old and I was wondering if there is now (MySQL 5.7) a better solution.
For example, none of those post use any of MySQL “Spatial Analysis Functions”, like ST_Distance_Sphere and ST_Distance which seem to be exactly for that purpose.
Is there any reason (eg. performance, precision) not to use these functions instead of writing the formula in the query? (I don't know which algorithm is internally used for these functions)
I also don't know how I should store the coordinates of each place.
Most of the examples I've seen assume the coordinates to be stored in separate lat, lon columns as doubles or as FLOAT(10,6) (as in this example by google), but also MySQL POINT data type seems appropriate for storing geographic coordinates.
What are the pros and cons of these two approaches?
How can indexes be used to speed up these kind of queries? For example I've read about “spatial indexes”, but I think they can only be used for limiting the results with something like MBRContains(), not to actually order the results by distance.
So, how should I store the coordinates of places and how should I query them to be ordered by distance?
Other than the ST_Distance_Sphere, 5.7 does not bring anything extra to the table. (SPATIAL was already implemented.)
For 'thousands' of points, the code you have is probably the best. Include
INDEX(lat, lng),
INDEX(lng, lat)
And I would not worry about the curvature of the earth unless you are stretching thousands of miles (kms). Even then the code and that function should be good enough.
Do not use FLOAT(m,n), use only FLOAT. The link below gives the precision available to FLOAT and other representations.
If you have so many points that you can't cache the table and its indexes entirely (many millions of points), you could use this , which uses a couple of tricks to avoid lengthy scans like the above solution. Because of PARTITION limitations, lat/lng are represented as scaled integers. (But that is easy enough to convert in the input/output.) The earth's curvature, poles, and dateline are all handled.
I use a table that has lat & long associate with zip codes that I found. I use the haversine formula to find all zipcodes within a certain range. I then use that list of zip codes that are returned from that query and find all business with those zip codes. Maybe that solution will work for you. It was pretty easy to implement. This also eliminates you having to know the lat and long for the each business as long as you know the zip code.
Use ST_DISTANCE_SPHERE or MBRContains to get distance between points or points within a bound - much faster than doing Haversine formula which can't use indices and is not built for querying distances and because MySql is slow with range queries. Refer mysql documentation.
Haversine formula is probably good for small applications and most of the older answer refer to that solution because older versions of MySql innodb did not have spatial indexes.
The broad method of doing it is as follows - the below is from my working code in Java - hope you can tailor it for PHP as per your needs
First save the incoming data as a Point in database (Do note that the coordinate formula uses longitude, latitude convention)
GeometryFactory factory = new GeometryFactory();
Point point = factory.createPoint(new Coordinate(officeDto.getLongitude(), officeDto.getLatitude()));//IMP:Longitude,Latitude
officeDb.setLocation(point);
Create Spatial Indexes using the following in mysql
CREATE SPATIAL INDEX location ON office (location);
You might get the error "All parts of a SPATIAL index must be NOT NULL". That is because spatial indexes can only be created if the field is NOT NULL - in such a case convert the field to non-null
Finally, call the custom function ST_DISTANCE_SPHERE from your code as follows.
SELECT st_distance_sphere( office.getLocation , project.getLocation)
as distance FROM ....
Note: office.getLocation and project.getLocation both return POINT types. Native SQL method is as below from documentation
ST_Distance_Sphere(g1, g2 [, radius])
which returns the mimimum spherical distance between two points and/or multipoints on a sphere, in meters, or NULL if any geometry argument is NULL or empty.
Small problem about Google Maps and storing/retrieving coordinates. I run a DB on MySql where I store longitude/latitudes of points in 2 separate float fields. I would like to perform queries to find the points fitting the bounds of a given google map object.
I get the min/max lng/lat from the current google map and perform a query through PHP. Everything works fine, except when I have negative values for the longitude. Which doesn't return any result whilst values fitting the min/max lng exist... My query looks like this:
SELECT * FROM points WHERE ((lng BETWEEN :lng_min AND :lng_max) AND (lat BETWEEN :lat_min AND :lat_max))
I checked each single variable sent to the query / stored in the database and each one is a valid float. This doesn't seem to make any sense. Am I missing something?
An issue here is where in your query a negative value causes problems. For instance, if lng_min < 0 and lng_max >0, then you should be retrieving data as usually. However, if lng_max<0 and lng_min>0 (it can happen, as the earth is round and transitioning from -180 degrees to +180 degrees longitude is only a few footsteps away), then it makes sense for the database not to retrieve any values at all. If the latter is the case, then you should previously check if lng_max <0 and lng_min>0 and make an origin shift (e.g. (a+180) % 360) for all longitudes in the query. In any case, a few examples of lng_min/lng_max pairs where the query fails would be helpful.
In an app I'm building, One of the features i'd like users to be able to discover people around them easily. I'm using the GPS to get the latitude and longitude, then storing that information in the mysql db along with the other user information under a column for latitude and another with the longitude. What's would the best practice be to do a query that looks at the person whos making the query's information...and then have it find the closest possible relatable lat and longitude then start there and limit it to 24 results? And furthermore, how would I remember where it stopped so I could allow another query to start where it left off and return more getting further and further away
Basically, to generalize how can I do a mysql query that starts as close as it can to 2 latitude and longitude points that I supply and then have it grab the next 24 sorted by closest to furthest?
I feel like its going to be hard to do because its being based on 2 columns. Is there a way I should/could be combining the GPS values into 1 column so it will be easy to find relative distance?
Maybe I could somehow get the zip code (but then that might cause non US problems). I'm not sure. I'm stuck.
Just search for "Haversine Formula" here on Stackoverflow and you will find several related questions.
As #cdonner mentioned, there are a number of resources for the Haversine formula which you use to transform lat and long into distance. You would pass in a distance variable based on how your formula is set up, usually based on miles and run your query starting at the closest radius. Using a php loop, you can simply increase the distance and re-run the query until you get the desired number of results. And do check out that google link re #Maleck13 as well, very helpful.
This question already has answers here:
MySQL Great Circle Distance (Haversine formula)
(9 answers)
Closed 2 years ago.
Each user in my db is associated to a city (with it's longitude and latitude)
How would I go about finding out which cities are close to one another?
i.e. in England, Cambridge is fairly close to London.
So If I have a user who lives in Cambridge. Users close to them would be users living in close surrounding cities, such as London, Hertford etc.
Any ideas how I could go about this? And also, how would I define what is close? i.e. in the UK close would be much closer than if it were in the US as the US is far more spread out.
Ideas and suggestions. Also, do you know any services that provide this sort of functionality?
Thanks
If you can call an external web service, you can use the GeoNames API for locating nearby cities within some radius that you define:
http://www.geonames.org/export/web-services.html
Getting coordinates from City names is called reverse geo coding. Google maps has a nice Api fot that.
There is also the Geonames project where you get huge databases of cities, zip codes etc and their cooridnates
However if you already have the coordinates, its a simple calculation to get the distance.
The tricky thing is to get a nice performant version of it. You probably have it stored in a mysql database, so you need to do it there and fast.
It is absolutely possible. I once did a project including that code, I will fetch it and post it here.
However to speed things up I would recommend first doing a rectangular selection around the center coordinates. This is very, very fast using bee tree indexes or even better stuff like multidimensional range search. Then inside that you can then calculate the exact distances on a limited set of data.
Outside that recangular selection the directions are so vast that it does not need to be displayed or calculated so accurately. Or just display the country, continent or something like that.
I am still at the office but when i get home i can fetch the codes for you. Int he meantime it would be good if you could inform me how you store your data.
Edit: in the mean time here you have a function which looks right to me (i did it without a function in one query...)
CREATE FUNCTION `get_distance_between_geo_locations`(`lat1` FLOAT, `long1` FLOAT, `lat2` FLOAT, `long2` FLOAT)
RETURNS FLOAT
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE distance FLOAT DEFAULT -1;
DECLARE earthRadius FLOAT DEFAULT 6371.009;
-- 3958.761 --miles
-- 6371.009 --km
DECLARE axis FLOAT;
IF ((lat1 IS NOT NULL) AND (long1 IS NOT NULL) AND (lat2 IS NOT NULL) AND (long2 IS NOT NULL)) THEN -- bit of protection against bad data
SET axis = (SIN(RADIANS(lat2-lat1)/2) * SIN(RADIANS(lat2-lat1)/2) + COS(RADIANS(lat1)) * COS(RADIANS(lat2)) * SIN(RADIANS(long2-long1)/2) * SIN(RADIANS(long2-long1)/2));
SET distance = earthRadius * (2 * ATAN2(SQRT(axis), SQRT(1-axis)));
END IF;
RETURN distance;
END;
i quoted this from here: http://sebastian-bauer.ws/en/2010/12/12/geo-koordinaten-mysql-funktion-zur-berechnung-des-abstands.html
and here is another link: http://www.andrewseward.co.uk/2010/04/sql-function-to-calculate-distance.html
The simplest way to do this would be to calculate a bounding box from the latitude and longitude of the city and a distance (by converting the distance to degrees of longitude).
Once you have that box (min latitude, max latitude, min longitude, max longitude), query for other cities whose latitude and longitude are inside the bounding box. This will get you an approximate list, and should be quite fast as it will be able to use any indexes you might have on the latitude and longitude columns.
From there you can narrow the list down if desired using a real "distance between points on a sphere" function.
You need a spatial index or GIS functionality. What database are you using? MySQL and PostgreSQL both have GIS support which would allow you to find the N nearest cities using an SQL query.
Another option you might want to consider would be to put all of the cities into a spatial search tree like a kd-tree. Kd-trees efficiently support nearest-neighbor searches, as well as fast searches for all points in a given bounding box. You could then find nearby cities by searching for a few of the city's nearest neighbors, then using the distance to those neighbors to get an estimate size for a bounding box to search in.