Querying a DB for locations in an area - php

I'm interested in making a site which will store several locations and needs to be able to find them based on a user's query. Similar to how sites like Yelp, Zomato, Zillow, etc. function. My preferred tech stack is PHP/MySQL.
That said, I have a few related questions. Given a database of locations with their associate latitude/longitude coordinates...
How are they able to query their database of locations and return the ones within X distance of a specific city or ZIP code?
Furthermore, how can they query their database for locations that, instead of being within proximity, are within very specific city or state limits? Where do they get their boundary data from and how do you determine if a coordinate falls within that boundary in an efficient manner?
Lastly, I notice that some of the sites have city-specific links on their site. I would have expected city data to be pulled from remote mapping APIs, but they often associate images and other content with those cities. Do these sites store the names of all cities in their own database? Where do they get their list?

This will help with your first point.
I use this query to search properties in my database by distance from a starting point. The starting point is the longitude and latitude, $coordX and $coordY. I get these from doing a geocode lookup on the Google Maps API:
SELECT properties.*,
(
( ACOS(SIN(".$coordX." * PI() / 180) *
SIN(coordX * PI() / 180) + COS(".$coordX." * PI() / 180) *
COS(coordX * PI() / 180) * COS((".$coordY." - coordY) *
PI() / 180)) * 180 / PI()) * 60 * 1.1515
)
AS distance
FROM properties
order by distance
In my table I store the coordinates of the coordinates of individual properties in the coordX and coordY columns.

Here's MySQL function that will take two latitude/longitude pairs, and give you the distance in degrees between the two points.
It uses the Haversine formula to calculate the distance. Since the Earth is not a perfect sphere, there is some error near the
poles and the equator.
To convert to miles, multiply by 3961.
To convert to kilometers, multiply by 6373.
To convert to meters, multiply by 6373000.
To convert to feet, multiply by (3961 * 5280) 20914080.
DELIMITER $$
CREATE FUNCTION haversine(
lat1 FLOAT, lon1 FLOAT,
lat2 FLOAT, lon2 FLOAT
) RETURNS float
NO SQL
DETERMINISTIC
COMMENT 'Returns the distance in degrees on the Earth between two known points of latitude and longitude. To get miles, multiply by 3961, and km by 6373'
BEGIN
RETURN DEGREES(ACOS(
COS(RADIANS(lat1)) *
COS(RADIANS(lat2)) *
COS(RADIANS(lon2) - RADIANS(lon1)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
));
END;
DELIMITER ;
Add columns to your address table for latitude and longitude with a type of FLOAT(10,6).
Write a php script to do the lookup of the lat/long when the record is saved.
Then you can do a select against the table to get a list of addresses with distances. You can even sort
the results by distance, or limit the result to a certain radius from the reference location.
SELECT
`street`,
`city`,
`state`,
`zip`,
(haversine($ref_location_lat,$ref_location_long,`lat`,`long) * 3961) as `distance`
FROM `address_table`
WHERE (haversine($ref_location_lat,$ref_location_long,`lat`,`long) * 3961) < 300 // Example for limiting returned records to a raduis of 300 miles.
ORDER BY haversine($ref_location_lat,$ref_location_long,`lat`,`long) DESC; // Don't need actual distance for sorting, just relative distance.

Related

Calculate distance between two lat lng without any api

I want to calculate distance between two latitude and longitude sets.
My problem
I have 2500+ zip codes in a table and I have 10000+ shops, Every shops and zip codes have their lat, lng.
Every shop has their delivery distance in KMS.
I want to save all zip codes of a shop which falls within their delivery distance in a table named shop_delivery_zips. I can't use maps at the time on placing order, because shop might not want to deliver at every zips which falls in their delivery distance range.
So, first time i want to insert the data in shop_delivery_zips table by checking their delivery distance.
My solution
First, I thought that i will write a code which will go to every zips codes and calculate distance between shop lat, lng and zip lat,lng and will save data in shop_delivery_zips table, but if i do that, then i have to call the google apis for every time and if i do for all our data, then i have to make 2500*10000 google api calls, which google don't allow in one day, so I can't go with those option.
So, i want to know that is there any way by which i calculate the distance between two lat,lng set. May be by the calculation google use to calculate the same. Please suggest me that solution or any other solution if you know.
Please let me know, if anything is not clear.
I know of this solution to calculate as needed (taken from https://github.com/shafiqpab/distance-between-two-addresses-google-maps-api-php/blob/master/index.php):
//Calculate distance from latitude and longitude
$theta = $longitudeFrom - $longitudeTo;
$dist = sin(deg2rad($latitudeFrom)) * sin(deg2rad($latitudeTo)) +
cos(deg2rad($latitudeFrom)) * cos(deg2rad($latitudeTo)) *
cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.8515;
$unit = strtoupper($unit);
// for units in KM
$kilometers = round($miles * 1.609344, PHP_ROUND_HALF_UP).' km';
Is that what you are looking for?
While #tbedner has the correct solution mathematically, you do not want to perform this calculation against every location in the list, as the math is very non-trivial.
First you define a simple bounding box, eg:
lat BETWEEN $min_lat AND $max_lat
AND
lng BETWEEN $min_lng AND $max_lng
Which is very simple and can easily leverage indexes, and then you do the complicated math on the much smaller subset of locations that fall inside the box to find the ones that are within the radius you want.

mysql - select records with near distance from each other

I have a mysql table containing locations, for example:
ID latitude longitude value
1 11.11111 22.22222 1
2 33.33333 44.44444 2
3 11.11112 22.22223 5
I want to select records which are located near to each other (in the above example, rows 1 and 3), so that I can insert them in a new table as one record. By saying near, let's say 100 meters. I would like the new table to be:
ID latitude longitude value
1 11.11111 22.22222 3
2 33.33333 44.44444 2
As you can see, I would like to keep the coordinates of the first record and insert an average of the two records in column 'value'.
The formula I use for distance is the following:
R*SQRT(((lon2*c-lon1*c)*cos(0.5*(lat2*c+lat1*c)))^2 + (lat2*c-lat1*c)^2)
where
[lat1, lon1] the coordinates of the first location,
[lat2, lon2] the coordinates of the second location,
[R] the average radius of Earth in meters,
[c] a number to convert degrees to radians.
The formula works well for short distances.
So, my problem is not the conversion of lat,lon to distances but my SQL. I know how to select records that have a maximum distance of 100 meters from specific lat,lon coordinates but I dont know how to select records with a maximum distance from each other.
One way I did it -and it works- is by looping the records one by one in PHP and for each one, making an SQL query to select records that are near. But this way, I do an SQL query in a loop and as far as I know, this is a bad practice, especially if the records are gonna be thousands.
I hope I was clear. If not, I would be glad to give you additional information.
Thanks for helping.
Here is a SQL to get all places within the range:
SELECT
ID,
latitude,
longitude,
(6371 * acos (cos( radians(origin.latitude)) * cos( radians( destination.latitude ))
* cos( radians(destination.longitude) - radians(origin.longitude)) + sin(radians(origin.latitude))
* sin( radians(destination.latitude)))) AS distance
FROM myTable as destination, myTable as origin
WHERE destination.id = myId
HAVING distance < 100 --some value in kilometers
6371 is a constant for kilometers.
3959 is a constant for miles.
This topic have more answers: MySQL Great Circle Distance (Haversine formula)

Get places near geographical coordinates

I have a database filled with geographical coordinates (DB 2), like 45.062792, 8.892737 , and another that has other coordinates (DB 1).
I want to use one coordinate of DB 1 and then I want to calculate in a radius given by a user (like 10 km, 5 km, 500 mt) all the rows in DB 2 that are inside the radius previously given.
I'd like to calculate all the results without (e.g.) Google Maps or other online maps because there are limits over the usage and I want to be able to manage the results by php code on my server.
Use the Haversine formula using a lat/long coordinate and a radius as input. (I assume you have coordinates stored as separate lat and lon columns).
Your query roughly looks like this:
SELECT *, 3956 * 2 * ASIN(SQRT(POWER(SIN((#orig_lat - abs(tablename.lat)) * pi()/180 / 2),2) + COS(#orig_lat * pi()/180 ) * COS(
abs
(tablename.lat) * pi()/180) * POWER(SIN((#orig_lon – tablename.lon) * pi()/180 / 2), 2) ))
as distance
FROM tablename
HAVING distance < #radius
ORDER BY distance
LIMIT 10;
Replace #orig_lat with the given lat value and #orig_lon with the given lon value and #radius with a value in kilometers.
This will return all the coordinates from DB2 based on your coordinate from DB1 within the given radius.
More info here: http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

how to select zip codes in specific area/range?

I have a customer that requires that a user can add his/her zip code, then an administrator can select users in specific area range
for example:
user - zip code
1 - 24533
2 - 56924
3 - 35993
4 - 13435
admin's zip code is 39824
He needs to select people in area range of 5 kilometers by zipcode.
How can this happen using php or any other solutions?
Very simple
You just need the latitude and longitude of the zipcodes against which zipcode you want to get the zipcodes. If you not have longitude and latotude get it from google it is free. you can easily get latitude and longitude by zipcode.
For example you want to get the zipcodes round 100 mile of 'H8t'
i have a table of zipcodes having latitude and longitude info.
First i write a query to my table to get latitude and longitude
SELECT * FROM Tbl_Master_ZIPCodes WHERE ZIPcode ='H8T'
after getting latitude and longitude i write a query below to get all zipcodes in the range of 100 miles of 'H8T'
SELECT distinct zipcode,
3963 * (ACOS((SIN(45.456700/57.2958) * SIN(latitude/57.2958)) + (COS(45.456700/57.2958) * COS(latitude/57.2958) * COS(longitude/57.2958 - -73.712000/57.2958)))) AS distance
FROM Tbl_Master_ZIPCodes
WHERE 3963 * (ACOS((SIN(45.456700/57.2958) * SIN(latitude/57.2958)) + (COS(45.456700/57.2958) * COS(latitude/57.2958) * COS(longitude/57.2958 - -73.712000/57.2958)))) <= 100 Order by Distance
php/mysql zip code proximity search
But, personally, I like the zip code range and distance calculation solution for you. If you had lat and long I would suggestion another but this seems to be similar to what you need.
http://www.micahcarrick.com/php-zip-code-range-and-distance-calculation.html
If you are receiving address with the zip code, then you can query the Google geocode Api to get the geocode of these addresses.
Once you have these geocodes, calculating distance can be done using code sample from this site

Using PHP and MySQL to calculate geolocation distance

I have a php script and a database of geolocations.
At the moment my users can enter their postcode, using Yahaa I get their geo location.
Now that I have their geolocation I want to write a script that loads their local businesses in order of distance. I'm guessing to do this I need to load records from the database that have the most similar coordinates to the users current geolocation. I tried this code:
$sql = "SELECT * FROM dirlistings WHERE ABS(latitude - $lat) AND ABS(longitude - $long)";
However it just displays the results in normal order.
Have I missed anything?
I think you want an ORDER clause in there somewhere.
But what you really want is the Haversine formula: MySQL Great Circle Distance (Haversine formula)
It's more complicated than you think.
This is a great article that helped me a lot. Although its written in javascript, you easily change it to php.
http://www.movable-type.co.uk/scripts/latlong.html
Note that circle distance isn't going to be precise enough if you're talking about large distances (thousands of miles, for example), as the earth's surface isn't flat. If you need a better formula for geo-distance calculation, you can use something like this:
$dlat = ((double)$lat) / 57.29577951;
$dlon = ((double)$lon) / 57.29577951;
$sql = "SELECT *
FROM dirlistings
WHERE 3963.0 * acos(sin(latitude / 57.29577951) * sin($dlat) + cos(latitude / 57.29577951) * cos($dlat) * cos($dlon - longitude / 57.29577951)) < MAX_DIST
ORDER BY acos(sin(latitude / 57.29577951) * sin($dlat) + cos(latitude / 57.29577951) * cos($dlat) * cos($dlon - longitude / 57.29577951))
";
The distances here are in miles - make sure to specify correct max distance - and this formula will give very close results for distances of even ten thousand miles. Note though that such computation is quite time- and power-intensive and if you are not dealing with large distances (i.e. nothing more than a couple hundred miles), then you should use a quicker approximation.
You've missed quite a few things. In order to do what you're trying to do, you need to compute (using the Pythagorean theorem) the distance between two points, and then order by that distance.
You can calculate distance ( (lat - $lat)^2 + (lon - $lon)^2 )^0.5 via:
SQRT(
POW(latitude - ' . $lat . ',2)
+
POW(longitude - ' . $lon . ',2)
) AS distance
Then it's as simple as:
ORDER BY distance ASC
Try ORDER BY latitude, longitude at the end. That should do it or approximately do it.

Categories