sort zip codes by distance - php

So, I'm working on a search form for my site. I've run into a road block. Seems others have too but I've been unable to find an answer for my question, at least one that I understand.
I found a PHP function online that grabs up all the zip codes within a specified radius. I edited the sql query so that I wasn't grabbing every zip code within that radius, but ones that are associated with a user account. The function works and grabs the zip codes I'm after. This function returns an array with the zip codes. I would like to sort the zip codes by distance from nearest to farthest, and apparently I'm no where near smart enough to figure out how to do it.
What I've Tried:
I found another PHP function that will calculate the distance between 2 sets of coordinates(lat,lon2, lat2, lon2). I created my own function that loops through my zip codes array and runs each zip through the calculateDistance function. Just seems to be a big mess. I believe I have all the pieces just not sure how to put it all together. Any help would be greatly appreciated.
Here is the zipcodeRadius function I'm using:
// get all the zipcodes within the specified radius - default 20
function zipcodeRadius($lat, $lon, $radius)
{
$radius = $radius ? $radius : 20;
$dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
$sql = 'SELECT DISTINCT zipcodes.ZIP,
members.*,
location.*
FROM members
LEFT JOIN location
ON members.id = location.user_id
LEFT JOIN zipcodes
ON location.zip = zipcodes.ZIP
WHERE activated="1"
AND (3958*3.1415926*sqrt((Latitude-'.$lat.')*(Latitude-'.$lat.') + cos(Latitude/57.29578)*cos('.$lat.'/57.29578)*(Longitude-'.$lon.')*(Longitude-'.$lon.'))/180) <= '.$radius.';';
$result = mysqli_query($dbc, $sql);
// get each result
$zipcodeList = array();
while($row = mysqli_fetch_array($result))
{
array_push($zipcodeList, $row['ZIP']);
}
return $zipcodeList;
}
Here is the calculateDistance function
function calculateDistance($latitude1, $longitude1, $latitude2, $longitude2) {
$theta = $longitude1 - $longitude2;
$miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
$miles = acos($miles);
$miles = rad2deg($miles);
$miles = $miles * 60 * 1.1515;
return $miles;
}
And, Here's my seemingly useless function:
$matches = join(',',$zipCodeArray);
function sort_zip_by_distance($matches, $lat, $lon){
$dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
$queryDistance = "SELECT * FROM zipcodes WHERE ZIP IN ($matches)";
$resultDistance = mysqli_query($dbc, $queryDistance)
or die("Unable to query the database");
while($rowDistance = mysqli_fetch_array($resultDistance)){
$miles = calculateDistance($lat, $lon, $rowDistance['Latitude'], $rowDistance['Longitude']);
echo '<p>ZIP: ' . $rowDistance['ZIP'] . ' | Lat: ' . $rowDistance['Latitude'] . ' | Lon: ' . $rowDistance['Longitude'] . ' | Miles: ' . $miles . '</p><br />';
$distanceTotal = array();
array_push($distanceTotal, $rowDistance['ZIP'], $miles);
}
return $distanceTotal;
}

You can do arbitrary math in order by clauses, but you'd most likely to actually RETURN the distances found, so why not do
SELECT *, long_ugly_distance_calc_here AS distance
...
ORDER BY distance DESC

Related

GEO Postcode more countrys

It is about my site, it is a ad portal and 3 geodata are installed in the system: Germany, Switzerland and Austria.
When I look for an advertisement in Germany, everything works correctly, I'm looking for zip code 68259 and a radius of 30 km. The results are correct, it shows all ads from 68259 Mannheim and the radius of 30 km.
Problem: The problem exists when I search in Switzerland or Austria: I search for the postal code 6000 Lucerne 1 PF and a radius of 30 km ... the results are wrong, I also find ads from Munich or Frankfurt which correspond to 300-500 km radius! I think the mistake is somewhere here in the GEO Post code ! Any Help?
$this->search_code = htmlspecialchars($key);
$this->search_radius = intval($_GET['search-area']);
// Germany Postcode
preg_match('/\b((?:0[1-46-9]\d{3})|(?:[1-357-9]\d{4})|(?:[4][0-24-9]\d{3})|(?:[6][013-9]\d{3}))\b/is', $this->search_code, $output);
if(!empty($output[0])){
$this->search_code = $output[0];
}else{
// Switzerland, Austria Postcode
preg_match('/\d{4}/', $this->search_code, $at_ch);
if(!empty($at_ch[0])){
$this->search_code = $at_ch[0];
}
}
if( is_numeric($this->search_code) ){
$this->dbValue = 'geo_plz'; }else{
$this->dbValue = 'geo_ort'; }
//query->build
$get = $this->db->db->query("
SELECT geo_lg AS lon, geo_plz, geo_bg AS lat
FROM geo
WHERE ".$this->dbValue." = '".$this->search_code."'
");
$row = $get->fetch();
$lon = $row['lon'] / 180 * M_PI;
$lat = $row['lat'] / 180 * M_PI;
$plz = $row['geo_plz'];
//radius->query->search
$query_boot = "SELECT geo_plz AS plz, (".$this->earthRadius." * SQRT(2*(1-cos(RADIANS(geo_bg)) *
cos(".$lat.") * (sin(RADIANS(geo_lg)) *
sin(".$lon.") + cos(RADIANS(geo_lg)) *
cos(".$lon.")) - sin(RADIANS(geo_bg)) * sin(".$lat.")))) AS Distance
FROM geo WHERE
".$this->earthRadius." * SQRT(2*(1-cos(RADIANS(geo_bg)) *
cos(".$lat.") * (sin(RADIANS(geo_lg)) *
sin(".$lon.") + cos(RADIANS(geo_lg)) *
cos(".$lon.")) - sin(RADIANS(geo_bg)) * sin(".$lat."))) <= ".$this->search_radius."
ORDER BY Distance
";
$getString = $this->db->db->query($query_boot);
$rowString = $getString->fetchAll();
foreach( $rowString as $key => $value ){
$dataPackageString[] = $value['plz'];
}
$resultListener = "";
if(!empty($dataPackageString)) {
$listener = join(',', $dataPackageString);
$listener = str_replace(',', '|', $listener);
$resultListener = "AND ( postleitzahl REGEXP '$listener|$plz' OR stadt LIKE '%" . $this->search_code . "%' )";
}else{
$resultListener = "AND ( postleitzahl = '".$plz."' OR stadt LIKE '%".$this->search_code."%' )";
}
return $resultListener;

Find shortest distance between 2 coordinates with 1 set stored in a table

I have a MySQL table with the following attributes:
store_name
lat
lon
I have 1500 records.
I need to find the closest store to the user (based on IP).
I have figured the IP translation part using a 3rd party API. I know how to calculate the distance between 2 coordinates. However, I don't know how to return the store name that is the closest.
My function to calculate distance
function distance($lat1, $lon1, $lat2, $lon2)
{
$minus = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1))*cos(deg2rad($lat2)) * cos(deg2rad($minus));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
return $miles;
}
I can find the distance between user and each store.
Note: $lat1 and $lat1 are obtained by a 3rd party API service.
$store = mysql_query("SELECT * FROM store") or die(mysql_error());
while($row=mysql_fetch_assoc($store))
{
$lat2 = $row["lat"];
$lon2 = $row["lon"];
$distance = distance($lat1, $lon1, $lat2,$lon2);
}
How do I calculate run the distance function and store the distance value in an array and then sort the distances to find the nearest store? Is this the correct methodology? And how do I proceed with the array code? I have no experience with arrays in PHP.
Thank you.
You could just do the math on the database side, order your result set by distance and select the one with the lowest value.
And I took the liberty of updating your database access methods. Don't, under any circumstances, use mysql_* functions.
$lat1 = 123.245;
$lon1 = 48.123;
$dbhost = "localhost";
$dbname = "database";
$username = "user";
$password = "pass";
$db = new PDO("mysql:host=$dbhost;dbname=$dbname", $username, $password);
$query = "SELECT *, DEGREES(ACOS(SIN(RADIANS(?)) * SIN(RADIANS(`lat`)) + COS(RADIANS(?)) * COS(RADIANS(`lat`)) * COS(RADIANS(? - `lon`)))) * 60 * 1.1515 AS distance
FROM store
ORDER BY distance ASC
LIMIT 1";
$stmt = $db->prepare($query);
$stmt->execute(array($lat1, $lat1, $lon1));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($result);

Compare one query with multiple results in PHP

I have two text input. Like This:
So, I have some kind of dynamic ajax search. I pass the input data and make two different mysql select. Something like this:
Table finalmap - SELECT 1
id -------- latitud-----longitud---
1 | 6.2523915 | -75.5737028 |
2 | 6.2640349 | -75.5990783 |
3 | 6.2642411 | -75.5999791 |
4 | 6.2638461 | -75.5982590 |
-------------------------------------
Table finalmap - SELECT 2
id -------- latitud-----longitud---
6 | 6.262669 | -75.596799 |
7 | 6.258019 | -75.598001 |
8 | 6.253668 | -75.599374 |
9 | 6.250724 | -75.602335 |
-------------------------------------
So, I want to compare every single "latitud and longitud field" with all the "latitud" and "longitud" fields of the SELECT2:
I have this Php, I have to make some improvements but can say that it worked:
<?php
$buscar = $_POST['b'];
$buscarcarrera = $_POST['c'];
$whatIWant = substr($buscar, strpos($buscar, "Calle") + 5);
$whatIWant2 = substr($buscarcarrera, strpos($buscarcarrera, "Carrera") + 5);
$vacio = "Calle50A";
$vacioc = "Carrera50A";
if (preg_match('/[A-Za-z]/', $whatIWant))
{
buscar($buscar, "", $buscarcarrera, "");
}
else
{
buscar($buscar, $vacio, $buscarcarrera, $vacioc);
}
function buscar($b, $exclusion, $buscarcarrera, $exclusion2)
{
$con = mysql_connect('localhost', 'root', '');
mysql_select_db('map', $con);
$sql = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $b . "%' AND calle not in ('$exclusion')", $con);
$contar = mysql_num_rows($sql);
if ($contar == 0)
{
echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
}
else
{
while ($row = mysql_fetch_array($sql))
{
$nombre = $row['calle'];
$id = $row['id'];
$lat = $row['latitud'];
$lon = $row['longitud'];
}
}
$sql2 = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $buscarcarrera . "%' AND calle not in ('$exclusion2')", $con);
$contar2 = mysql_num_rows($sql2);
if ($contar2 == 0)
{
echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
}
else
{
while ($row2 = mysql_fetch_array($sql2))
{
$nombre2 = $row2['calle'];
$id2 = $row2['id'];
$lat2 = $row2['latitud'];
$lon2 = $row2['longitud'];
}
}
}
function distance($lat1, $lon1, $lat2, $lon2, $unit)
{
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
$unit = strtoupper($unit);
if ($unit == "K")
{
return ($miles * 1.609344);
}
else
if ($unit == "N")
{
return ($miles * 0.8684);
}
else
{
return $miles;
}
}
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "M") . " Miles<br />";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilometers<br />";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "N") . " Nautical Miles<br />";
?>
Then: how could compare each of the values ​​using the function to determine the proximity between the coordinates (using my distance() function)?. My problem is I don't know how to compare distance between each point of the 1st query with each point of the 2nd query, with every possible combinations.
I want to have something like this function compare (lat1,lon1,lat2,lon2);
(lat1,lon1,lat3,lon3),(lat1,lon1,lat4,lon4),(lat1,lon1,lat5,lon5),(lat1,lon1,lat6,lon6) and so on.
Thank you very much in advance for any help given
Your logic is completely wrong and you are way complexifying what you are looking for. It s much simpler than you think.
First: in order to drastically speed up your script and queries as well as protecting it from any attacks, use PDO prepared statements and security protocols. Mysql none prepared statements are deprecated for more than a decade !!!
My experience (25 years of coding) shows me to never ever trust what s coming into your server so I am a little phobic about security....
This tutorial explains the first basic steps you need to do and explains you this script as it should be (copy past all codes as they are).
Note, Using a local IP address instead of Localhost will speed up mysql connections by 2 to 5 depending on dev platform (lamp,wamp etc...)
set execution parameters
ini_set('memory_limit', '64M'); // memory limit used by script
set_time_limit(0); // no time limit
Establish Your secured mysql connection
try {
$con = new PDO('mysql:host=127.0.0.1;dbname=map;charset=UTF8','root','password');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
Establish a second level security protocol to check if it s really an ajax call
$isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) AND
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
if (!$isAjax) {
$user_error = 'Access denied - not an AJAX request from allowed computer...';
trigger_error($user_error, E_USER_ERROR);
exit;
}
Second: Use the distance function INSIDE your mysql query in order to properly sort the results you are looking for. Please note the mysql distance used here is different from the one you are currently using (faster and more precise).
It will gives you the distance between reference points from table 1 and all others from Table 2 in km, ordered by distance (closest as first row).
We suppose you get your results from your ajax like this:
if($isAjax){
$param4 = '%' . $_POST['b']. '%'; // MYSQL LIKE bind param buscar
$param5 = '(' . $_POST['c'] . ')'; //MYSQL NOT IN bind param buscarcarrera
}
We suppose you get your results from first query as this (simplified):
$sql1_array = array(
array('id' => 1, 'Lati' => 6.2523915, 'Longi' => -75.5737028),
array('id' => 2, 'Lati' => 6.2640349, 'Longi' => -75.5990783)
);
(note: always use "lati" and "longi" to avoid collision with PHP function like "long")
Your prepared statement:
$sql2 = "SELECT *, ( 6371 * acos( cos(radians(?))
* cos(radians(Latitude)) * cos(radians(Longitude) - radians(?)) +
sin( radians(?) ) * sin(radians(Latitude)))) AS distance
FROM finalmap WHERE calle LIKE ? AND calle NOT IN ?
ORDER BY distance LIMIT 10";
$stmt = $con->prepare($sql2);
Third: Execute a loop with each query1 result to get the distances compared with table 2. In the loop, you bind your parameters to your prepared stmt. This will drastically speed up your queries as connection with database is persistent and the query is prepared with PDO (compiled and in memory).
foreach ($sql1_array as $key => $value) {
$stmt->bindParam(1, $value['Lati'], PDO::PARAM_INT);
$stmt->bindParam(2, $value['Longi'], PDO::PARAM_INT);
$stmt->bindParam(3, $value['Lati'], PDO::PARAM_INT);
$stmt->bindParam(4, $param4, PDO::PARAM_STR);
$stmt->bindParam(5, $param5, PDO::PARAM_STR);
$stmt->execute();
$count = $stmt->rowCount();
if ($count >= 1) {
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
/* $result ordered by distance */
|**************************************|
|field 0 | field 1 | field 2 | distance|
|**************************************|
| 1 | a1 | a2 | 0.00123 | 1 meters
| 2 | b1 | b2 | 0.01202 | 12 meters
| 3 | c1 | c2 | 0.23453 | 234 meters
| 4 | d1 | d2 | 1.58741 | 1km and 587 meters
|**************************************|
I think you are all set ! Enjoy.
You should do something like this :
function buscar($b, $exclusion, $buscarcarrera, $exclusion2)
{
$nombre1 = array();
$id1= array();
$lat1= array();
$lon1= array();
$nombre2 = array();
$id2= array();
$lat2= array();
$lon2= array();
$con = mysql_connect('localhost', 'root', '');
mysql_select_db('map', $con);
$sql = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $b . "%' AND calle not in ('$exclusion')", $con);
$contar = mysql_num_rows($sql);
if ($contar == 0)
{
echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
} else {
while ($row = mysql_fetch_array($sql))
{
if(array_key_exists('calle',$row) && array_key_exists('id',$row) && array_key_exists('latitud',$row) && array_key_exists('longitud',$row)) {
// ignore data with a missing field
$nombre1[] = $row['calle'];
$id1[] = $row['id'];
$lat1[] = $row['latitud'];
$lon1[] = $row['longitud'];
}
}
}
$sql2 = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $buscarcarrera . "%' AND calle not in ('$exclusion2')", $con);
$contar2 = mysql_num_rows($sql2);
if ($contar2 == 0)
{
echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
} else {
while ($row2 = mysql_fetch_array($sql2))
{
if(array_key_exists('calle',$row2) && array_key_exists('id',$row2) && array_key_exists('latitud',$row2) && array_key_exists('longitud',$row2)) {
// ignore data with a missing field
$nombre2[] = $row2['calle'];
$id2[] = $row2['id'];
$lat2[] = $row2['latitud'];
$lon2[] = $row2['longitud'];
}
}
}
$nbData1 = count($nombre1);
$nbData2 = count($nombre2);
if($nbData1 > 0 && $nbData2 > 0) {
// there are data in both 1st and 2nd query
$distances = array();
// loop on all data from 1st query
for($i1 = 0; $i1 < $nbData1; $i1++) {
// loop on all data from 2nd query
for($i2 = $i1; $i2 < $nbData2; $i2++) {
// storing result of the distance
$distances[] = array('i1' => $i1,
'i2' => $i2,
'dist' => array('M' => distance($lat1[$i1], $lon1[$i1], $lat2[$i2], $lon2[$i2], 'M'),
'K' => distance($lat1[$i1], $lon1[$i1], $lat2[$i2], $lon2[$i2], 'K'),
'N' => distance($lat1[$i1], $lon1[$i1], $lat2[$i2], $lon2[$i2], 'N')
)
)
}
}
}
foreach($distances as $distance) {
// use each result
$i1 = $distance['i1'];
$lat1 = $distance['lat1'];
$lon1 = $distance['lon1'];
$i2 = $distance['i1'];
$lat2 = $distance['lat2'];
$lon2 = $distance['lon2'];
$dist = $distance['dist'];
$distM = $dist['M'];
$distK = $dist['K'];
$distN = $dist['N'];
echo "Have fun with theses data !\n";
}
}
So, storing the content of your rows.
Then parse all your data, to calculate all possible distances.
Then use the result as you wish.
This is much better to process distance calculation in the 2nd query, and this is faster, so the distance() function not needed. Also it seems that your formula is not correct.
$buscar = $_POST['b'];
$buscarcarrera = $_POST['c'];
$whatIWant = substr($buscar, strpos($buscar, "Calle") + 5);
$whatIWant2 = substr($buscarcarrera, strpos($buscarcarrera, "Carrera") + 5);
$vacio = "Calle50A";
$vacioc = "Carrera50A";
if (preg_match('/[A-Za-z]/', $whatIWant))
{
buscar($buscar, "", $buscarcarrera, "");
}
else
{
buscar($buscar, $vacio, $buscarcarrera, $vacioc);
}
function buscar($b, $exclusion, $buscarcarrera, $exclusion2)
{
$con = mysql_connect('localhost', 'root', '');
mysql_select_db('map', $con);
$sql = mysql_query("SELECT * FROM finalmap WHERE calle LIKE '%" . $b . "%' AND calle not in ('$exclusion')", $con);
$contar = mysql_num_rows($sql);
if ($contar == 0)
{
echo "No se han encontrado resultados para '<b>" . $b . "</b>'.";
}
else
{
while ($row = mysql_fetch_array($sql))
{
$nombre = $row['calle'];
$id = $row['id'];
$lat = $row['latitud'];
$lon = $row['longitud'];
$dLat = '((latitud-'.$lat.')*PI()/180)';
$dLong = '((longitud-'.$lon.')*PI()/180)';
$a = '(sin('.$dLat.'/2) * sin('.$dLat.'/2) + cos('.$lat.'*pi()/180) * cos(map_coords_1*pi()/180) * sin('.$dLong.'/2) * sin('.$dLong.'/2))';
$sub_sql = '2*atan2(sqrt('.$a.'), sqrt(1-'.$a.'))';
$results = mysql_query(
"SELECT DISTINCT
id, calle, " . $sub_sql . " AS distance FROM finalmap
WHERE
calle LIKE '%" . $buscarcarrera . "%' AND calle not in ('$exclusion2')
ORDER BY
distance
", $con);
if ($results == 0)
{
echo "No se han encontrado resultados para '<b>" . $buscarcarrera . "</b>'.";
}
else
{
while ($row2 = mysql_fetch_array($results)) {
echo "Calle: " . $row2['calle'] . " Miles - " . $row2['distance']*3956;
echo "Calle: " . $row2['calle'] . " Kilometers - " . $row2['distance']*6367;
echo "Calle: " . $row2['calle'] . " Nautical Miles - " . $row2['distance']*3435;
}
}
}
}
}
I'm giving it a first try.
$output_array = array();
$i = 0;
while ($row = mysql_fetch_array($sql))
{
$j = 0;
while ($row2 = mysql_fetch_array($sql2))
{
$out_array[$i][$j][0] = $row2['latitud'] - $row['latitud']
$out_array[$i][$j][1] = $row2['longitud'] - $row['longitud']
++$j;
}
++$i;
}
print "<pre>";
print_r($out_array);
print "</pre>";
Preview (with random numbers) :
Array
(
[0] => Array
(
[0] => Array
(
[0] => 6
[1] => 9
)
[1] => Array
(
[0] => 6
[1] => 9
)
[2] => Array
(
[0] => 6
[1] => 9
)
............
............
............
Array[0][0][0] == difference between 1st result latitud(table 1) AND 1st result latitud(table 2)
Array[0][1][0] == difference latitud (1st res table1) AND (2nd res table2)
Same case for longitud.
I think cross join of table will do your work.
Let say first table is Table1 and second is Table2.
So the query:
Select Table1.id, Table1.latitud, Table1.longitude,
Table2.id, Table2.latitud, Table2.longitude
from
Table1 , Table2
This will give you all combination which you required.
I think that INNER JOIN will work. I'm just confused that you will provide the table-1 long and lat value or not.So I 'm writing both the options.Let's say first table is table1 and second is table2
When you will provide the lat and long value of table 1SELECT * FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.latitude=t2.latitud OR t2.longitude=t1.longitude WHERE t1.latitude='#enter the value#' AND t1.longitude='#enter the value#'
It will provide the filter result of the equal value in both tableSELECT * FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.latitude=t2.latitud OR t2.longitude=t1.longitude
Let me know if needs anything else.Thanking you.
You can easily generate the necessary result set in SQL without having to write a bunch of PHP code though.
It would also make thing a lot easier if you would actually show what you want your results to look like as a table. I'm confused as to what you're actually trying to do. My assumption is that you want to match the two queries when either the latitudes match exactly OR the longitudes match exactly.
Update: I found the SQL buried in your PHP code. Something like this should work:
"SELECT t1.latitud AS lat1, t1.longitud AS lon1,
t2.latitud AS lat2, t2.longitud AS lon2
FROM finalmap AS t1
INNER JOIN finalmap AS t2 ON t1.latitud=t2.latitud OR t1.longitud=t2.longitud
WHERE t1.calle LIKE '%" . $b . "%' AND t1.calle not in ('$exclusion')
AND t2.calle LIKE '%" . $buscarcarrera . "%' AND t2.calle NOT IN ('exclusion2');"
If you want all results from SELECT 1 even if there are no matches in SELECT 2 change the INNER JOIN to a LEFT JOIN.
I also noticed you're checking if fields have values after the query is returned. You can do those checks in the WHERE clause on the SQL query that way you don't get any results you don't actually want. It just depends on what a "no-value" looks like (NULL? empty string? etc.) as to how you would check that.
You can take Cartesian product of both tables so that you can find all possible combinations.
Otherwise, take two loops like
for(int i=0;i < n; i++)
for(int j=0; j< m; j++)
someProcess(a[i][0], a[i][1], b[j][0], b[j][1]);
And this will give you all possible combinations.
Your function should then have logic for geocoding/reverse geocoding to calculate distance.
I hope this helps!!
I'd support this answer, but add the other major point:
Fourth: do not use distance calculation and sorting with customly coded haversine formula neither in PHP, nor in MySQL. MySQL has spatial functions support for a long time. Moreover, when dealing with GIS it's better to move to PostgreSQL + PostGIS extension
Fifth: Utilize MySQL or any other RDBMS spatial indexes when sorting by distance. I doubt you can code anything more efficient with your "every-to-every distance comparison" approach.
Solution: review this answer and apply the following queries to your DB table:
ALTER TABLE `finalmap` ENGINE=MYISAM; -- changing your storage engine to MyISAM. It supports spatial indexes in MySQL
ALTER TABLE `finalmap` ADD `pt` POINT NOT NULL; -- adding POINT() spatial datatype for zip cetner. Eventually, you may remove the old lat/lng decimal columns
UPDATE `finalmap` SET `pt` = POINT(latitud,longitud);
ALTER TABLE `finalmap` ADD SPATIAL INDEX(`pt`);
Then, assuming you get a coordinates in input (let's say (6.25,-75.6)) that you need to compare to all records in your table and find the closest one, you need:
SELECT *, ST_Distance_Sphere(`pt`,POINT('6.25','-75.6')) as `dst`
FROM `fnialmap`
ORDER BY `dst`
LIMIT 1;
And, finally, sixth: please name your columns and variables in English. It may be so that someone else supports your code in the future and they have to deal with "latitud" and "longitud"

Distance calculation based on multiple locations (Lat,Lon) including starting/ finishing point

I would like to calculate the total distance of driving beetween multiple locations (loop), including the distance (starting point (garage) - first location sarting point) and (last location finishig point - finishing point (garage)).
Example:
(Garage + D1) + (D1 + D2) + (D2 + E1) + (E1 + E2) + E2 + Garage)
I'm having a problem with the correct looping. Here's my simplified code:
<?
$driver = 5;
$result2 = mysql_query("SELECT * FROM test WHERE id='$driver' LIMIT 1") or die(mysql_error());
while($row2 = mysql_fetch_array( $result2 )) {
$lon=$row2['lon'];
$lat=$row2['lat'];
echo "$lon, $lat";
}
$result = mysql_query("SELECT * FROM test1 WHERE driver='$driver'") or die(mysql_error());
while($row = mysql_fetch_array( $result )) {
$lon1=$row['lon1'];
$lat1=$row['lat1'];
$lon2=$row['lon2'];
$lat2=$row['lat2'];
////////// distance between driver address and starting address
$distancecalc = (3958*3.1415926*sqrt(($lat-$lat1)*($lat-$lat1) + cos($lat/57.29578)*cos($lat1/57.29578)*($lon-$lon1)*($lon-$lon1))/180);
////////// distance between statring address and finishing address - multiple adsresses
$distancecalc1 = $distancecalc1 + (3958*3.1415926*sqrt(($lat2-$lat1)*($lat2-$lat1) + cos($lat2/57.29578)*cos($lat1/57.29578)*($lon2-$lon1)*($lon2-$lon1))/180);
////////// distance between finishing address and driver address
$distancecalc2 = (3958*3.1415926*sqrt(($lat2-$lat)*($lat2-$lat) + cos($lat2/57.29578)*cos($lat/57.29578)*($lon2-$lon)*($lon2-$lon))/180);
$distancetotal = $distancecalc + $distancecalc1 +$distancecalc2;
echo "$distancecalc<br>
$distancecalc1<br>
$distancecalc2<br>";
}
echo "$distancetotal";
?>
I tried some things (mostly if... ) and also with more database requests, but I'm still having the problem of avoiding multiple calculations, and I also strongly believe there is way to code it to make it easier and clearer.
I would appreciate some help on this one.
Thank you very much.
Here's the function you need:
function distance ($lat1, $lon1, $lat2, $lon2) {
return (3958*3.1415926*sqrt(($lat2-$lat1)*($lat2-$lat1) + cos($lat2/57.29578)*cos($lat1/57.29578)*($lon2-$lon1)*($lon2-$lon1))/180);
}
The function body is exactly the same as the formula you used in-line, so I don't understand why you needed help with this.
The number 6367 - is the radius of the Earth in kilometers
DELIMITER $$
DROP FUNCTION IF EXISTS geodist $$
CREATE FUNCTION geodist (
src_lat DECIMAL(9,6), src_lon DECIMAL(9,6),
dst_lat DECIMAL(9,6), dst_lon DECIMAL(9,6)
) RETURNS DECIMAL(6,2) DETERMINISTIC
BEGIN
SET #dist := 6367 * 2 * ASIN(SQRT(
POWER(SIN((src_lat - ABS(dst_lat)) * PI()/180 / 2), 2) +
COS(src_lat * PI()/180) *
COS(ABS(dst_lat) * PI()/180) *
POWER(SIN((src_lon - dst_lon) * PI()/180 / 2), 2)
));
RETURN #dist;
END $$
DELIMITER ;
Ok. I worked it out with help of mac_gyver (php freaks). All calculations are done as i wish. My code below:
<?
include "connectdb.php";
$driver = 5;
$datestamp = '2013/05/07';
$result2 = mysql_query("SELECT * FROM drivers WHERE id='$driver' LIMIT 1") or die(mysql_error());
while($row2 = mysql_fetch_array( $result2 )) {
$lon=$row2['lon'];
$lat=$row2['lat'];
}
$result = mysql_query("SELECT * FROM quotedb WHERE moveday='$datestamp' AND driver='$driver' AND cleared='Not Cleared' AND status='Done' ORDER BY moveday, timeday") or die(mysql_error());
$distance = 0; // accumulate the distance
$first_pass = true; // flag to detect the first row inside the loop
while($row = mysql_fetch_assoc( $result )) {
$lon2a=$lon2;
$lat2a=$lat2;
$lon1=$row['lon1'];
$lat1=$row['lat1'];
$lon2=$row['lon2'];
$lat2=$row['lat2'];
// calculate the distance from the Garage to the first point of the first row
if($first_pass){
$distance += (3958*3.1415926*sqrt(($lat-$lat1)*($lat-$lat1) + cos($lat/57.29578)*cos($lat1/57.29578)*($lon-$lon1)*($lon-$lon1))/180);
$first_pass = false;
}
// calculate the distance for each row (segment) in the route
$distance += (3958*3.1415926*sqrt(($lat2-$lat1)*($lat2-$lat1) + cos($lat2/57.29578)*cos($lat1/57.29578)*($lon2-$lon1)*($lon2-$lon1))/180);
if ( $lon2a == "" or $lat2a =="" ) {
} else {
// calculate the distance from the second point of the first row to the first point of the next row
$distance += (3958*3.1415926*sqrt(($lat2a-$lat1)*($lat2a-$lat1) + cos($lat2a/57.29578)*cos($lat1/57.29578)*($lon2a-$lon1)*($lon2a-$lon1))/180);
}
}
// calculate the distance from the second point of the last row to the Garage
$distance += (3958*3.1415926*sqrt(($lat2-$lat)*($lat2-$lat) + cos($lat2/57.29578)*cos($lat/57.29578)*($lon2-$lon)*($lon2-$lon))/180);
echo "$distance<br>
";
?>
Still think there is a place for improvement in the code. Will aplay Haversine method for calculations. Do you guys have some suggestion to improve this piece of code ... thx
Impoved version:
<?
include "connectdb.php";
$driver = 5;
$datestamp = '2013/05/07';
$result2 = mysql_query("SELECT * FROM drivers WHERE id='$driver' LIMIT 1") or die(mysql_error());
while($row2 = mysql_fetch_array( $result2 )) {
$garage_lon=$row2['lon'];
$garage_lat=$row2['lat'];
}
$result = mysql_query("SELECT * FROM quotedb WHERE moveday='$datestamp' AND driver='$driver' AND cleared='Not Cleared' AND status='Done' ORDER BY moveday, timeday") or die(mysql_error());
function calculate_distance($lon1, $lat1, $lon2, $lat2) {
return (3958 * 3.1415926 * sqrt(($lat2 - $lat1) * ($lat2 - $lat1) + cos($lat2 / 57.29578) * cos($lat1 / 57.29578) * ($lon2 - $lon1) * ($lon2 - $lon1)) / 180);}
$previous_lon = $garage_lon;
$previous_lat = $garage_lat;
$distance = 0; // accumulate the distance
while($row = mysql_fetch_assoc( $result ))
{
$lon1 = $row['lon1'];
$lat1 = $row['lat1'];
$lon2 = $row['lon2'];
$lat2 = $row['lat2'];
if ( $previous_lon && $previous_lat )
{
// calculate the distance from the second point of the first row to the first point of the next row
$distance += calculate_distance($lon1, $lat1, $previous_lon, $previous_lat);
}
// calculate the distance for each row (segment) in the route
$distance += calculate_distance($lon1, $lat1, $lon2, $lat2);
$previous_lon = $lon2;
$previous_lat = $lat2;
}
// calculate the distance from the second point of the last row to the Garage
$distance += calculate_distance($garage_lon, $garage_lat, $lon2, $lat2);
$distance = round($distance,0);
echo "$distance<br>
";
?>

PHP / Mysql - ORDER BY When Using Temp Tables

I've been trying to get this to work using ORDER BY and LIMIT, but it will output everything that was put in. The ORDER BY and LIMIT does not seem to work:
$lat1 = 37.349418;
$lon1 = -121.896286;
$distance = 25;
$q = "SELECT * FROM cityname WHERE feature_class = 'A' OR feature_class = 'P'";
$r = mysql_query($q) or die(mysql_error());
while ($row = mysql_fetch_array($r)) {
$lat = trim($row["latitude"]);
$lon = trim($row["longitude"]);
$name = $row["name"];
$pop = $row["population"];
$miles = distance($lat, $lon, $lat1, $lon1, "m");
$milesrounded = round($miles, 2);
if ($miles < $distance) {
if ($pop > 0) {
$q2 = "CREATE TEMPORARY TABLE IF NOT EXISTS `templocation4` (
`cityname` varchar(75) NOT NULL,
`distance` double NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1";
$r2 = mysql_query($q2) or die(mysql_error());
$q1 = "INSERT INTO templocation4 (cityname, distance) VALUES ('$name', '$milesrounded')";
$r1 = mysql_query($q1) or die(mysql_error());
$q3 = "SELECT MIN(distance) FROM templocation4 GROUP BY distance DESC LIMIT 10";
$r3 = mysql_query($q3) or die(mysql_error());
while ($row1 = mysql_fetch_array($r3)) {
echo $row1["cityname"];
echo " ";
echo $row1["distance"];
echo "<br>";
}
$q5 = "DROP TABLE templocation4";
$r5 = mysql_query($q5) or die(mysql_error());
}
}
}
The table cityname has > 250K entries and I'm trying to sort it down to the closest city name based on the latitude / longitude that a user has input.
Thanks.
There's an error here:
$q3 = "SELECT MIN(distance) FROM templocation4 GROUP BY distance DESC LIMIT 10";
$r3 = mysql_query($q3) or die(mysql_error());
while ($row1 = mysql_fetch_array($r3)) {
echo $row1["cityname"]; // Where is cityname defined???
echo " ";
echo $row1["distance"]; // Where is distance defined???
echo "<br>";
}
$r3 only has one result column (which you haven't given a name). Are you sure you are using the correct variable because there's no way that your code should work as you have posted it.
Also your variable naming is really awful. Use meaningful names instead of $q1, $q2, etc...
Please check with Logic once again. "GROUP BY distance" will return single records from list of concern records. For e.g. if there are 100 users at 10 miles distance, your query will fetch 1st record found for 10 miles. this will NOT return all 100 records.
$q1 = "INSERT INTO templocation4 (cityname, distance) VALUES ('$name', '$milesrounded')";
$r1 = mysql_query($q1) or die(mysql_error());
$q3 = "SELECT MIN(distance) FROM templocation4 GROUP BY distance DESC LIMIT 10";
$r3 = mysql_query($q3) or die(mysql_error());
Need to rework with your need and logic.
//determine distance function
function distance($lat, $lon, $lat1, $lon1, $unit) {
$theta = $lon - $lon1;
$dist = sin(deg2rad($lat)) * sin(deg2rad($lat1)) + cos(deg2rad($lat)) * cos(deg2rad($lat1)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
$unit = strtoupper($unit);
if ($unit == "K") {
return ($miles * 1.609344);
} else if ($unit == "N") {
return ($miles * 0.8684);
} else {
return $miles;
}
}
//sample latitude for testing purposes
$lat1 = 37.349418;
//sample longitude for testing purposes
$lon1 = -121.896286;
//sample distance for testing purposes
$distance = 25;
//query to select only a or p feature class
$query = "SELECT * FROM cityname WHERE feature_class = 'A' OR feature_class = 'P'";
$result = mysql_query($query) or die(mysql_error());
//create the temporary table - if it does not exist
$createtemporarytablequery = "CREATE TEMPORARY TABLE IF NOT EXISTS `templocation4` (
`cityname` varchar(75) NOT NULL,
`distance` double NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1";
$resultofcreatetemporarytablequery = mysql_query($createtemporarytablequery) or die(mysql_error());
while ($row = mysql_fetch_array($result)) {
//gets latitude of city in database
$lat = trim($row["latitude"]);
//gets longitude of city in database
$lon = trim($row["longitude"]);
//gets cityname
$name = $row["name"];
//gets population of aforementioned cityname
$pop = $row["population"];
//determines distance from sample latitude and longitude from the latitude and longitude of cities in the cityname database
$miles = distance($lat, $lon, $lat1, $lon1, "m");
//round the miles to the 2nd decimal place
$milesrounded = round($miles, 2);
//determine if the city meets the request
if ($miles < $distance) {
//make sure its a populated city
if ($pop > 0) {
//insert stuff into temporary table
$insertstuffintotemporarytable = "INSERT INTO templocation4 (cityname, distance) VALUES ('$name', '$milesrounded')";
$resultofinsertstuffintotemporarytable = mysql_query($insertstuffintotemporarytable) or die(mysql_error());
}
}
}
//retrieve the closest 10 cities from the temporary table
$retrieve10closestcities = "SELECT * FROM templocation4 GROUP BY distance ASC LIMIT 10";
$resultofretrieving10closestcities = mysql_query($retrieve10closestcities) or die(mysql_error());
//determine how many results there are
$numrows = mysql_num_rows($resultofretrieving10closestcities);
//are there more than 0 results?
if ($numrows > 0) {
//loops through array
while ($row1 = mysql_fetch_array($resultofretrieving10closestcities)) {
echo $row1["cityname"];
echo " ";
echo $row1["distance"];
echo "<br>";
}
} else {
//echos no results found
echo "No results found";
}
//drop temporary table
$droptable = "DROP TABLE templocation4";
$resultofdroptable = mysql_query($droptable) or die(mysql_error());
And the answer is:
San Jose 0.7
Buena Vista 2.24
Burbank 2.65
Santa Clara 3.25
Fruitdale 3.33
Alum Rock 3.97
East Foothills 4.85
Campbell 5.21
Seven Trees 5.41
Milpitas 5.48

Categories