Reputation: 2893
I have a database which contains postcodes along with their geo coordinates. The data looks similar to the following
id | Postcode | Longitude | Latitude |
-------------------------------------------------
1 | W12 7GF | 51.51527 | -0.08816 |
-------------------------------------------------
2 | SW16 6GF | 51.51528 | -0.15960 |
-------------------------------------------------
3 | W1 4FT | 51.51528 | -0.11590 |
-------------------------------------------------
What I first do is take in a postcode (I have hardcoded it for now).
$sql = "SELECT * FROM postcodes WHERE `postcode` = 'W14 6TY'";
Once I execute this, I get the Longitude and Latitude for that postcode. I also set a couple more variables.
$lat1 = $row['Latitude'];
$lon1 = $row['Longitude'];
$d = 5;
$r = 3959;
Now what I do is get all other postcodes within a 5 mile radius of the above postcode. To do this, I do
$latN = rad2deg(asin(sin(deg2rad($lat1)) * cos($d / $r) + cos(deg2rad($lat1)) * sin($d / $r) * cos(deg2rad(0))));
$latS = rad2deg(asin(sin(deg2rad($lat1)) * cos($d / $r) + cos(deg2rad($lat1)) * sin($d / $r) * cos(deg2rad(180))));
$lonE = rad2deg(deg2rad($lon1) + atan2(sin(deg2rad(90)) * sin($d / $r) * cos(deg2rad($lat1)), cos($d / $r) - sin(deg2rad($lat1)) * sin(deg2rad($latN))));
$lonW = rad2deg(deg2rad($lon1) + atan2(sin(deg2rad(270)) * sin($d / $r) * cos(deg2rad($lat1)), cos($d / $r) - sin(deg2rad($lat1)) * sin(deg2rad($latN))));
$query = "SELECT * FROM postcodes WHERE (Latitude <= $latN AND Latitude >= $latS AND Longitude <= $lonE AND Longitude >= $lonW) AND (Latitude != $lat1 AND Longitude != $lon1) ORDER BY Latitude, Longitude ASC LIMIT 30";
$result2 = $conn->query($query);
As you can see I limit the results because I do not want hundreds returned. Lastly, I output the data
echo "<div class='container'>";
echo "<div class='row'>";
echo "<div class='col-md-12 col-sm-12 col-xs-12'>";
echo "<table class=\"table table-striped\">";
echo "<tr><th>Postcode</th><th>Latitude</th><th>Longitude</th><th>Miles, Point A To B</th></tr>\n";
while ($row = $result2->fetch_assoc()) {
echo "<tr><td>$row[Postcode]</td><td>$row[Latitude]</td><td>$row[Longitude]</td>";
echo "<td>".acos(sin(deg2rad($lat1)) * sin(deg2rad($row['Latitude'])) + cos(deg2rad($lat1)) * cos(deg2rad($row['Latitude'])) * cos(deg2rad($row['Longitude']) - deg2rad($lon1))) * $r."</td>";
echo "</tr>\n";
}
echo "</table>\n<br />\n";
echo "</div>";
echo "</div>";
echo "</div>";
As you can see in the output, I also calculate the Miles, Point A To B. I cant do this at a database query level because I need to do all that maths on the resulting geo-coordinates.
At the moment, the data is ordered by Latitude and Longitude. Because these numbers do not make much sense, the output looks a bit funny.
My question is, would it be possible to order the output based on the smallest to highest number of miles between points? I presume I would need to remove the limit (so it can work on all outputs), but not sure if I can do this because I do not calculate this until after the query.
Any advice appreciated.
Thanks
Upvotes: 0
Views: 68
Reputation: 33935
E.g.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,Postcode VARCHAR(12) NOT NULL
,Longitude DECIMAL(8,5)
,Latitude DECIMAL(8,5) NOT NULL
);
INSERT INTO my_table VALUES
(1 ,'W12 7GF',51.51527,-0.08816),
(2 ,'SW16 6GF',51.51528,-0.15960),
(3 ,'W1 4FT',51.51528,-0.11590),
(4 ,'W14 8UX',51.49645,-0.20975);
SELECT y.*
, ROUND(geo_distance_km(x.latitude,x.longitude,y.latitude,y.longitude),2) dist
FROM my_table x
JOIN my_table y
ON y.id <> x.id
WHERE x.postcode = 'W14 8UX';
+----+----------+-----------+----------+-------+
| id | Postcode | Longitude | Latitude | dist |
+----+----------+-----------+----------+-------+
| 1 | W12 7GF | 51.51527 | -0.08816 | 13.69 |
| 2 | SW16 6GF | 51.51528 | -0.15960 | 5.96 |
| 3 | W1 4FT | 51.51528 | -0.11590 | 10.65 |
+----+----------+-----------+----------+-------+
Obviously, I've left a crucial bit out of this answer. I wonder if you can figure that bit out for yourself.
Upvotes: 2