Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Queries to find places within a given lat/lng

So I'm trying to display lists of places within a range of the given lat/lng. I have no problem with this:

Places within one mile (list of places...)

Using something like

SELECT * FROM places WHERE lat < $latmax AND lat > $latmin AND lng < $lngmax AND lng > $lngmin

But then I want to list places within two miles, BUT not within one mile -- that is, I don't want to repeat the results from the first query.

Here's one version of what I've tried:

$milesperdegree = 0.868976242 / 60.0 * 1.2;

// 1 mile -- this works
$degrees = $milesperdegree * 1;
$latmin = $lat - $degrees;
$latmax = $lat + $degrees;
$lngmin = $lng - $degrees;
$lngmax = $lng + $degrees;

$query = "SELECT * FROM places WHERE lat < $latmax AND lat > $latmin AND lng < $lngmax AND lng > $lngmin";

// 2 miles -- this doesn't work
$degrees_2 = $milesperdegree * 2;
$latmin_2 = $lat - $degrees_2;
$latmax_2 = $lat + $degrees_2;
$lngmin_2 = $lat - $degrees_2;
$lngmax_2 = $lat + $degrees_2;

$query = "SELECT * FROM places WHERE ";
$query .= "lat BETWEEN $latmax AND $latmax_2 AND lng BETWEEN $lngmax AND $lngmax_2 OR ";
$query .= "lat BETWEEN $latmin AND $latmin_2 AND lng BETWEEN $lngmin AND $lngmin_2 OR ";
$query .= "lat BETWEEN $latmax AND $latmax_2 AND lng BETWEEN $lngmin AND $lngmin_2 OR ";
$query .= "lat BETWEEN $latmin AND $latmin_2 AND lng BETWEEN $lngmax AND $lngmax_2";

That's not doing it. I'm guessing it's just some logic I can't wrap my head around on Sunday afternoon, but I'm probably doing something else wrong too. Any input is greatly appreciated.

like image 917
nickfindley Avatar asked Feb 27 '11 19:02

nickfindley


People also ask

How do I find LAT LNG address?

On the Google Maps app, you can't pull up a context menu. To find the address for your coordinates, hold your finger down over the red pin that shows your coordinates. Then, after 1-3 seconds, the address will pop up on the bottom of your screen.

How do you find the distance between two latitude longitude points in MySQL query?

Heres is MySQL query and function which use to get distance between two latitude and longitude and distance will return in KM. SELECT getDistance($lat1,$lng1,$lat2,$lng2) as distance FROM your_table. Almost a decade later, this function gives THE SAME results as Google Maps distance measurement.


2 Answers

We implement it more or less like the code below (disclaimer: I snipped this out of a file and deleted the code that was irrelevant to the problem at hand. I didn't run this, but you should be able to get the idea.

$maxLat = $city->latitude + ($max_distance / 69); // 69 Miles/Degree
$minLat = $city->latitude - ($max_distance / 69);

$maxLon = $city->longitude + ($max_distance / (69.172 * cos($city->latitude * 0.0174533)));
$minLon = $city->longitude - ($max_distance / (69.172 * cos($city->latitude * 0.0174533)));

// Simplify terms to speed query
$originLatRadCos = cos($city->latitude * 0.0174533);
$originLatRadSin = sin($city->latitude * 0.0174533);
$originLonRad = $city->longitude * 0.0174533;

$city_distance_query = "
SELECT city_id, 
  3963 * acos(($originLatRadSin * sin( latitude * 0.0174533)) + 
  ($originLatRadCos * cos(latitude * 0.0174533) * cos((longitude * 0.0174533) -
  $originLonRad))) AS distanceFromOrigin
FROM cities
WHERE
 latitude < $maxLat AND latitude > $minLat AND longitude < $maxLon AND longitude > $minLon";

The rest of the query

SELECT cities.city_name, CityDistance.distanceFromOrigin,
FROM cities 
INNER JOIN ($city_distance_query) AS CityDistance ON CityDistance.city_id=cities.city_id
WHERE (distanceFromOrigin < $distance OR distanceFromOrigin IS NULL) 
like image 122
Roger W. Avatar answered Oct 04 '22 04:10

Roger W.


I think you're missing some brackets and have the logical operators a bit mixed up. How about this.

$query  = "SELECT * FROM places WHERE ";
$query .= "((lat BETWEEN $latmin_2 AND $latmax_2) AND NOT (lat BETWEEN $latmin AND $latmax)) AND ";
$query .= "((lng BETWEEN $lngmin_2 AND $lngmax_2) AND NOT (lng BETWEEN $lngmin AND $lngmax)) AND ";

EDIT

To solve the circle/square problem:

$query  = "SELECT * FROM places WHERE ";
$query .= "(POW((lat - $lat) * $avgMilesPerLatDeg,2) + ".
           "POW((lng - $lng) * $avgMilesPerLngDeg,2) BETWEEN 1 AND 4)";
// the four at the end is 2 squared

I would suggest using this approach if your app is not large-scale (geographically speaking) and the averages produce acceptable results. Calculating the real distance takes longer to compute and the difference might not be that big. This is up to you and your application's goal.

like image 27
Czechnology Avatar answered Oct 04 '22 04:10

Czechnology