I'm using MySQLi with mysqlnd driver, and using a function to get long/lat coordinates and calculate the distance between them, however I'm having issues with strange numbers and errors regarding "expected double, string given".
In my database, the long/lat values are stored as "Decimal (18,15)". I then use a MySQLi query to retireve this from the database, and store them in a PHP Variable like so:
$latitudeTo = $postcoderow[latitude];
$float_latitudeTo = floatval($latitudeTo);
$longitudeTo = $postcoderow[longitude];
$float_longitudeTo = floatval($longitudeTo);
The other set of long/lat are the same (just using different names).
However, the postcodes will be only from the UK, and using the following PHP Function
function calculateDistance($float_latitudeFrom, $float_longitudeFrom, $float_latitudeTo, $float_longitudeTo, $earthMeanRadius = 3440) {
$deltaLatitude = deg2rad($float_latitudeTo - $float_latitudeFrom);
$deltaLongitude = deg2rad($float_longitudeTo - $float_longitudeFrom);
$a = sin($deltaLatitude / 2) * sin($deltaLatitude / 2) +
cos(deg2rad($float_latitudeFrom)) * cos(deg2rad(float_latitudeTo)) *
sin($deltaLongitude / 2) * sin($deltaLongitude / 2);
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
return $earthMeanRadius * $c;
}
//Function call with the coordinates.
$miles = calculateDistance($float_latitudeFrom, $float_longitudeFrom, $float_latitudeTo, $float_longitudeTo, $earthMeanRadius = 3440);
I'm getting values of 3000+ miles returned (I just echo '.$miles.')
How do I store $float_latitudeTo... etc as doubles, as they are decimals from the database, however are converted to a string which is causing errors. I think the PHP Function itself is fine, just how I'm parsing the values.
Thanks for your time!
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
So I am trying to get the distance between two point based on Latitude and Longitude calculated and printed to the screen, it 'works' but the correct answer is way off. And by way off I mean 187 kms off in my particular case. I am not sure why, and I feel as though I am doing something really silly but I can't seem to locate the problem. Here's what I have so far:
/**
* Calculates Geographical Distance from Latitude and Longitude Pairs
*
* #param array $pair1 Array of first Pair
* #param array $pair2 Array of second Pair
*
* #return string
*/
private function _calculateGeographicalDistanceFromLatLng($pair1, $pair2)
{
$pi80 = M_PI / 180;
$pair1[0] *= $pi80;
$pair1[1] *= $pi80;
$pair2[0] *= $pi80;
$pair2[1] *= $pi80;
$r = 6372.797; // radius of Earth in km
$dlat = $pair2[0] - $pair1[0];
$dlng = $pair2[1] - $pair1[1];
$a = sin($dlat / 2) * sin($dlat / 2) + cos($pair1[0]) * cos($pair2[0]) * sin($dlng / 2) * sin($dlng / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$km = $r * $c / 1000;
if ($this->_unit == "all") {
$miles = $km * 0.621371192;
return array("KM" => round($km, 2), "Mile" => round($miles, 2));
} elseif ($this->_unit == "mile") {
$miles = $km * 0.621371192;
return round($miles, 2);
} else {
return round($km, 2);
}
}
When I try and have it echo the correct answer with different options, the answer is absolutely way off.
$df = new distanceAdvice("Geographic");
$result = $df->findDistance(array("53.219383", "6.566502"), array("52.090737", "5.121420"));
if (isset($result['error'])) {
echo $result['error']['msg'];
} else {
echo "The geographical distance between the two points based on Latitude and Longitude is: " . $result . " Kilometer.<br />";
}
According to the documentation, to calculate distance between 2 points you should use computeDistanceBetween(LatLngFrom, LatLngTo)
Google handled all those Mercator Projection stuff for you so I guess, rather than writing your own, you should use this API.
I know your pain about this. I've had to encode this great circle distance formula into Excel VBA for some NASA geolocation work I've volunteered for. There is confusing information about the proper formula to use on the web when you do a google search. There is the Haversine formula, and the Spherical Law of Cosines formula. Also, the ATAN2 formula is implemented slightly differently at times [some libraries do atan2(dy, dx) while other libraries (like Excel) do atan2(dx, dy]].
For the Haversine formula (see https://en.wikipedia.org/wiki/Haversine_formula), try changing the line below. The Haversine formula is not supposed to use ATAN2, and unfortunately some first results on Google searches provide the wrong formula:
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
with
$c = 2 * asin(sqrt($a));
That is the proper Haversine formula. There may be an edge case that needs to be accounted for ... my formula in Excel included:
$c = 2 * asin(min(1,sqrt($a)));
And a source for that came from the U.S. Census (though its link is no longer valid) so here is a posting that references it:
http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Also, different distance calculators you will find online use different Earth radius values, since the earth is actually not round; even the term "sea level" is not consistent and "round" across the entire Earth. So you may still find your distance calculation is slightly different from whatever you are using as a reference just because of different Earth radius values.
For a project, I'm storing location points in my MySQL database. The latitude and longitude data for each point is stored as a varchar. It must remain a varchar for reasons I won't get in to. For each new point, I'm trying to perform a search to see if there's already any point in my database within a .000300 latitude/longitude radius of the new point.
My PHP code is as follows: ($lat and $long are the new point's latitude and longitude)
$lat1 = $lat - 0.000300;
$lat2 = $lat + 0.000300;
$long1 = $long - 0.000300;
$long2 = $long + 0.000300;
$sql = "SELECT * FROM `DataBase`.`DataBase_data` WHERE `DataBase_data`.`Type`='Point' AND `DataBase_data`.`Lat`>'$lat1' AND `DataBase_data`.`Lat`<'$lat2' AND `DataBase_data`.`Long`>'$long1' AND `DataBase_data`.`Long`<'$long2'";
$query = mysql_query($sql);
echo mysql_error();
The code works correctly when both latitude and longitude are positive numbers. However, when one (or both) of them are negative numbers, the search doesn't work. I think it has something to do with doing a "greater than" and "less than" comparison with a varchar containing a - sign, but I'm not positive. Any ideas about how to fix the problem would be greatly appreciated.
This is your query
SELECT d.*
FROM `DataBase`.`DataBase_data` d
WHERE d.`Type`='Point' AND d.`Lat`> '$lat1' AND d.`Lat`<'$lat2' AND
d.`Long`>'$long1' AND d.`Long`<'$long2';
My first suggestion is to do the comparison as numbers. You can convert to a number easily in MySQL by adding 0:
SELECT d.*
FROM `DataBase`.`DataBase_data` d
WHERE d.`Type` = 'Point' AND (d.`Lat` + 0) > $lat1 AND (d.`Lat` + 0) < $lat2 AND
(d.`Long` + 0) > $long1 AND (d.`Long` + 0) < $long2;
Does this fix your problem?
i've to calculate the distance between
(40.851774999999996,14.268123999999998)
and each coordinates into results of an sql query:
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $key => $value) {
echo "distance = ". calculateDistance("40.851774999999996","14.268123999999998",$value['lat'],$value['lng'])."<br>";
}
Where calculateDistance is
function calculateDistance($targetLat,$targetLng,$currentLat,$currentLng){
$r = 6371; // km
$dLat = $targetLat-$currentLat;
$dLng = $targetLng-$currentLng;
$a = sin($dLat/2)*sin($dLat/2) + sin($dLng/2)*sin($dLng/2);
$c = 2*atan2(sqrt($a), sqrt(1-$a));
return $r*$c;
}
it gives me strange result like:
distance = NAN //-> NAN???
distance = 3392.8405117312 // TOO MUCH!
distance = 3392.8405117312 // TOO MUCH!
...
Where is the problem? can someone help me to fix it? :)
According to this answer:
Calculate distance between two latitude-longitude points? (Haversine formula)
You don't convert from degrees to radians.
You formula is incorrect:
They say:
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
You wrote:
$a = sin($dLat/2)*sin($dLat/2) + sin($dLng/2)*sin($dLng/2);
The cosine is missing in your code.
You need to convert degrees to radians before using it in sin function.
$radians = $degrees * (M_PI/180);
Look at this function, too. It looks a little bit different.
You are referring to the Haversine formula:
http://en.wikipedia.org/wiki/Haversine_formula
There are plenty of examples and code snippets in these pages:
http://rosettacode.org/wiki/Haversine_formula
http://www.codecodex.com/wiki/Calculate_distance_between_two_points_on_a_globe
I've used this snippet in my code, which works very well:
http://www.codecodex.com/wiki/Calculate_distance_between_two_points_on_a_globe#PHP
I'm trying to get make a query which gives me a list of stores sorted by how far they are from the current location. I'm working in php and using MySQL for my database.
To calculate the distance between 2 stores, I use the longitudes and latitudes from the 2 stores and derive the distance from it. This is contained in a self-defined function distance($lat1, $lng1, $lat2, $lng2). The result of this function is the distance in km.
I want to use this function to create an extra column in my query result so I can sort all the stores from the one most behind my current location to the one most far from my current location. Both functions are declared in the same file, but I do not get any result. Is it possible to call a function in the SELECT clause by declaring it the way I did?
function getSortedStores($cur_lat, $cur_lng)
{
$query = "SELECT Store.ID, Store.Name, distance($cur_lat, $cur_lng, Address.Latitude, Address.Longitude) AS Distance FROM Store INNER JOIN Address ON Store.ID=Address.StoreID ORDER BY Distance";
$result = mysql_query($query);
return $result;
}
function distance($lat1, $lng1, $lat2, $lng2)
{
$toRadians = M_PI / 180;
$lat1 *= $toRadians;
$lng1 *= $toRadians;
$lat2 *= $toRadians;
$lng2 *= $toRadians;
$r = 6371; // mean radius of Earth in km
$dlat = $lat2 - $lat1;
$dlng = $lng2 - $lng1;
$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$km = $r * $c;
$km = round($km, 1);
return $km;
}
you can't use php function in mysql. for more detail about mysql User-Defined Function
see this
http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html
You can't mix PHP and mySQL in that way, you can either do the calculation in mySQL with their math functions, or select the raw data and do the calculations based on the result set in PHP. But you cannot call a PHP function inside a mySQL query.
Alternatively, and assuming that your stores are not mobile. You can create a simple table table to store the distances between all of your stores. It takes up a little extra storage, but can potentially save you a fair bit of CPU cycles in the end.
TABLE distances
store1_id INT PK
store2_id INT PK
distance FLOAT
SELECT distance
FROM distances
WHERE (store1_id = $store1 AND store2_id = $store2)
OR (store1_id = $store2 AND store1_id = $store2)
LIMIT 1
I need to convert coordinates in the following form:
N42-53.9°
W072-16.2°
Into something that is like the following:
-90.7311
0.346944
A php function would be greatly appreciated - or just a formula would also be nice enough.
I found an online JS calculator and a PHP solution:
<?php
function DMStoDEC($deg,$min,$sec)
{
// Converts DMS ( Degrees / minutes / seconds )
// to decimal format longitude / latitude
return $deg+((($min*60)+($sec))/3600);
}
function DECtoDMS($dec)
{
// Converts decimal longitude / latitude to DMS
// ( Degrees / minutes / seconds )
// This is the piece of code which may appear to
// be inefficient, but to avoid issues with floating
// point math we extract the integer part and the float
// part by using a string function.
$vars = explode(".",$dec);
$deg = $vars[0];
$tempma = "0.".$vars[1];
$tempma = $tempma * 3600;
$min = floor($tempma / 60);
$sec = $tempma - ($min*60);
return array("deg"=>$deg,"min"=>$min,"sec"=>$sec);
}
?>
Do it with simple mathematics:
arcsecond is 1⁄3,600 of a degree
arcminute is 1/60 of a degree
S,E negative, N,W positive
example: S 23° 25' 33.8: -1 * 23+25/60+33.8/3600 = -23,426055555555555555555555555556°