I'm working on a project that will track a vehicle as it drives around. The location of the vehicle is taken from the Geoloqi API and showed on a map that automatically is updated. The coordinates is passed to an AHAH function in Drupal that will load some more info about position via some other API:s. One of them is Geonames.org.
I do not want to load data from remote servers too often as this will increase load and use our credits too fast. However. I do want it to be as real time as possible as this is essential to the project. So I thought of doing it like this:
Geonames gives a string of GPS locations for the current street so that it can be drawn on a map. The "line" is given to the next intersection. We would eat our tickets too fast if we asked for current street name every time the location changed but how about at every intersection?
Would it be possible to, in PHP, check if a given gps coordinate is at the line between two other coordinates? It would also be good if this line could be extended a few meters in all directions in order to clear any offset.
Thank you so much for all suggestions.
Your original question comes down to a simple math problem: find an equation of a line given two points that it passes. This part is simple: say, you have two points with (lat1, lon1) and (lat2, lon2) coordinates, then the equation of the line will be
(lon2 - lon1) * X + (lat1 - lat2) * Y + (lat1 * lon2 + lat2 * lon1) = 0
Now, when you get any other point (lat, lon), just put those coordinates as (X, Y) respectively into the equation. If you get 0, then the point is on the line. Then check that your lat is between lat1 and lat2 and your lon is between lon1 and lon2. If yes, then the new point is on the line between the two existing points.
The more complicated part comes from two things:
GPS error. To account for that, you can do some approximation, for example, instead of comparing to 0, give it some leeway, e.g. compare from -err to +err where err is some value that you'd define.
The fact that most streets are not straight. The picture below shows the problem. If you look at the line between the two ends of the top street, the top of the vertical street will be on that line - but that's not what you want. .
Maybe a better approach would be to use a distance change. Do not re-query position if the GPS-reported distance changed by less than some predefined value.
Related
I have a group of users. The user count could be 50 or could be 2000. Each should have a long/lat that I have retrieved from Google Geo api.
I need to query them all, and group them by proximity and a certain count. Say the count is 12 and I have 120 users in the group. I want to group people by how close they are (long/lat) to other people. So that I wind up with 10 groups of people who are close in proximity.
I currently have the google geo coding api setup and would prefer to use that.
TIA.
-- Update
I have been googling about this for awhile and it appears that I am looking for a spatial query that returns groups by proximity.
Keep in mind that this problem grows exponentially with every user you add, as the amount of distance calculations is linked to the square of the number of users (it's actually N*(N-1) distances... so a 2000 user base would mean almost 4 million distance calculations on every pass. Just keep that in mind when sizing the resources you need
Are you looking to group them based on straight-line (actually great circle) distance or based on walking/driving distance?
If the former, the great circle distance can be approximated with simple math if you're able to tolerate a small margin of error and wish to assume the earth is a sphere. From GCMAP.com:
Earth's hypothetical shape is called the geoid and is approximated by
an ellipsoid or an oblate sphereoid. A simpler model is to use a
sphere, which is pretty close and makes the math MUCH easier. Assuming
a sphere of radius 6371.2 km, convert longitude and latitude to
radians (multiply by pi/180) and then use the following formula:
theta = lon2 - lon1
dist = acos(sin(lat1) × sin(lat2) + cos(lat1) × cos(lat2) × cos(theta))
if (dist < 0) dist = dist + pi
dist = dist × 6371.2
The resulting distance is in kilometers.
Now, if you need precise calculations and are willing to spend the CPU cycles needed for much complex math, you can use Vincenty's Formulae, which uses the WGS-84 reference ellipsoid model of the earth which is used for navigation, mapping and whatnot. More info HERE
As to the algorithm itself, you need to build a to-from matrix with the result of each calculation. Each row and column would represent each node. Two simplifications you may consider:
Distance does not depend on direction of travel, so $dist[n][m] == $dist[m][n] (no need to calculate the whole matrix, just half of it)
Distance from a node to itself is always 0, so no need to calculate it, but since you're intending to group by proximity, to avoid a user being grouped with itself, you may want to always force $dist[m][m] to an arbitrarily defined and abnormally large constant ($dist[m][m] = 22000 (miles) for instance. Will work as long as all your users are on the planet)
After making all the calculations, use an array sorting method to find the X closest nodes to each node and there you have it
(you may or may not want to prevent a user being grouped on more than one group, but that's just business logic)
Actual code would be a little too much to provide at this time without seeing some of your progress first, but this is basically what you need to do algoritmically.
... it appears that I am looking for a spatial query that returns groups by proximity. ...
You could use hdbscan. Your groups are actually clusters in hdbscan wording. You would need to work with min_cluster_size and min_samples to get your groups right.
https://hdbscan.readthedocs.io/en/latest/parameter_selection.html
https://hdbscan.readthedocs.io/en/latest/
It appears that hdbscan runs under Python.
Here are two links on how to call Python from PHP:
Calling Python in PHP,
Running a Python script from PHP
Here is some more information on which clustering algorithm to choose:
http://nbviewer.jupyter.org/github/scikit-learn-contrib/hdbscan/blob/master/notebooks/Comparing%20Clustering%20Algorithms.ipynb
http://scikit-learn.org/stable/modules/clustering.html#clustering
Use GeoHash algorithm[1]. There is a PHP implementation[2]. You may pre-calculate geohashes with different precision, store them in SQL database alongside lat-lon values and query using native GROUP BY.
https://en.wikipedia.org/wiki/Geohash
https://github.com/lvht/geohash
I have in my DB addresses with lat and lng coordinates.
I would like to find all the addresses that are distant less than, for example, 10km from a given point.
For the geolocation of the addresses, I use openstreetmap nominatim.
Is there any PHP library to do that ? Or a simple calculation ?
Thanks all !
EDIT : I can't use a method to calculate the distance between two points as I don't know the first point (that will be the adresse of the person searching for other adresses). If I use this method, this means that I must take EACH point in the database (which can be a lot !) and compare it to the address given. This would be too long.
I would like a method to know the limit coordinates of the circle : for example, if I know that the limit of the circle are : lat(12, 20) and lng(-3, 7), I can search in my db all the coordinate where 12 < lat < 20 and -3 < lng <7. (These coordinates are examples, I don't even know if they are possible :))
I have a table that stores longitude / latitude / radius ( miles ) per row. What I am trying to figure out is how to select ONLY the rows that my current point is within.
This is the opposite of querying locations within a given radius of my current location, instead, I actually want to query locations that my current point is within.
Any help here would be greatly appreciated.
This is easily a math problem. The distance between 2 points (x1, y1) and (x2, y2) can be calculated as follows:
Simple Pythagoras. Query your database, point one is the point you want to find, point two is the one you get from the database, if the result of the above statement is smaller then the radius, it's in your range. (assuming all units match. You'll probably want to convert the lat/lon difference to km/whatever unit your radius is)
Note that if you want to have an accurate radius calculation for good ol' earth latitudes/longitudes, you'd have to use more complicated functions than the simple pythagorean distance mentioned in the accepted answer; look for "great circle distance", as e.g. posted in another thread already; however, for your purposes, the pythagorean distance should be close enough I guess; it delivers a reasonably good approximation, except if your latitude/longitude positions are somewhere near the poles.
And, just for completeness, your problem is actually not the opposite to the question "What locations are in a certain radius around my location?" - it's the exact same, only from the other direction - but since distance calculations are symmetric, it actually doesn't matter which direction you check!
How could I retrieve a list of cities which are enroute(waypoints) between 2 gps coordinates?
I have a table of all cities, lat-lon.
So if I have a starting location (lat-lon) and ending location (lat-lon)...
It must be very easy to determine the path of cities (from table) to pass by(waypoints) to get from start(lat-lon) to en (lat-lon)?
I have looked different algorithms and bearing. Still not clear for me.
If you're using the between point A and B method, then you'd just query the cities with Latitude and Longitude between the first and the second, respectively.
If you want to get the cities that are within X miles of a straight line from A to B, then you'd calculate the starting point and slope, and then query cities which are within X miles of the line that creates
If you're not using a simple point A to point B method which ignores roads, then you'll need some kind of data on the actual roads between A and B for us to give you an answer. This can be done using a Node system in your db, and it can also be done by using various geolocation APIs that are out there.
the solution to this can be found by standard discrete routing algorithms
those algorithms need a set of nodes (start, destination, your cities) and edges between those nodes (representing the possible roads or more generally the distances between the locations.)
nodes and edges form a graph ... start point and destination are known ... now you can use algorithms like A* or djikstra to solve a route along this graph
a typical problem for this approach could be that you don't have definitions for the edges (the usable direct paths between locations). you could create such a "road network" in various ways, for example:
initialize "Network_ID" with 0
take your starting location, and find the closest other location. measure the distance and multiply it by a factor. now connect each location to the original location which has a distance less than this value and is not connected to the current location yet. add all locations that were connected by this step to a list. mark the current location with the current "Network_ID" repeat this step for the next location on that list. if your list runs out of locations, increment "Network_ID" and choose a random location that has not yet been processed and repeat the step
after all locations have been processed you have one or more road networks (if more than one, they are not connected yet. add a suitable connection edge between them, or restart the process with a greater factor)
you have to make sure, that either start and destination have the same network_ID or that both networks have been connected
Hmm... I have used BETWEEN min AND max for something like this, but not quite the same.
try maybe:
SELECT * from `cities` WHERE `lat` BETWEEN 'minlat' AND 'maxlat' AND `lon` BETWEEN 'minlon' and 'maxlon';
something like that may work
look at mysql comparisons here:
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html
I know this is a late answer, but if you are still working on this problem you should read this:-
http://dev.mysql.com/doc/refman/5.6/en/spatial-extensions.html
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.