Get 5 rows from table - php

I want to fetch 5 rows from table whose "lat" value is nearest to 30. I am developing Google MAP app, where i need the 5 nearest location from Data Base.
My table looks like that,

MySQL provides a Math function that turns negative numbers into absolute values. By using that, you can get the five closest locations whether their lat is slightly lower or higher than 30:
ORDER BY ABS(lat - 30) ASC LIMIT 5
The ASC is optional as it is the default sorting order in all DBMS (thanks Gordon).

On a map "nearest location" should be based on the distance of two points, otherwise lat 30.00, long 75.00 will be the "nearest" location.
A quite exact calculation of the distance between two points (latitude/longitude) is based on the Haversine formula:
DEGREES(ACOS(COS(RADIANS(ref_latitude)) *
COS(RADIANS(latitude)) *
COS(RADIANS(ref_longitude) - RADIANS(longitude)) +
SIN(RADIANS(ref_latitude)) *
SIN(RADIANS(latitude)))) AS distance
latitude = `lat`
longitude = `long`
ref_latitude & ref_longitude = the point you want to find the nearest locations from
`DOUBLE` should be used for calculation
This results in degrees, multiply by 111.195 for an approximate distance in kilometers or by 69.093 for miles.
As you want near locations you might go for a more simple calculation using the Pythagorean theorem
sqrt(power(lat-ref_latitude, 2) +
power((lng-ref_longitude)*cos(radians(ref_latitude)), 2))
Again multiply by 111.195 for kilometers or by 69.093 for miles.
Now simply ORDER BY this distance.
And instead of comparing to all rows in your database you should restrict the number of rows to compare, e.g.
WHERE latitude BETWEEN ref_latitude - 0.2 and ref_latitude + 0.2
AND longitude BETWEEN ref_longitude - 0.2 and ref_longitude + 0.2
Btw, some DBMSes support geospatial extensions like distance functions or geospatial indexes.

As a note, if you want to do this efficiently and you have an index on lat, then the following more complex query should perform better:
select t.*
from ((select t.*
from table t
where lat <= 30
order by lat desc
limit 5
) union all
(select t.*
from table t
where lat > 30
order by lat asc
limit 5
)
) t
order by abs(lat - 30)
limit 5;
The two subqueries can use an index on lat to avoid sorting. The outer query is then only sorting 10 rows.

Related

Selecting MySQL results within a distance (in miles); incorrect results

Evening,
I have a PHP script that is selecting results and attempting to filter those results based on a certain distance in miles. I have a database of results, which includes longitude and latitude data collected from Google Maps.
My query currently looks like this...
$stmt = $pdo->prepare("SELECT *,
(((acos(sin(('$latitude'*pi()/180))
* sin((latitude*pi()/180))
+ cos(('$latitude'*pi()/180))
* cos((latitude*pi()/180))
* cos((('$longitude' - longitude)*pi()/180))))
* 180/pi()) * 60 * 1.1515) AS distance
FROM directory
HAVING distance <= '$distance'
ORDER by distance ASC");
The query is returning data. However, it does not seem to filter accurately. My $distance variable is currently set to 10, so I am hoping only results within 10 miles will be returned.
Does anyone know what is going wrong here?
Thanks,
Luke
MySQL supports spatial functions since version 5.7, which relief you from the burden of typing such complicated formula. You can, for example, use st_distance_sphere() like so:
select d.*,
st_distance_sphere(point(latitude, longitude), point(:latitude, :longitude)) as distance
from directory d
having distance <= :distance * 1609.344
order by distance
st_distance_sphere() returns a value in meters, so this uses an approximation to convert the input miles value to meters.
Note that this uses named query parameters (denoted by the starting :) rather than concatenating variables in the query string: this is both safer and more efficient.

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

Calculating a distances using eastings and northings UK (ordnance survey)

I have downloaded the ordnance survey which contains the postcode eastings and northings of all UK postcodes and I would like to perform a mySQL query which calculates and shows the results of a given distance.
Firstly I need to match up all records in properties to the postcode table to show each properties easting and northing joined into the results.
I then need to calculate the distance between the given postcode and each property record and display only the ones which fall within the given distance.
I have found lots of examples for using long/lat but I cannot seem to find any good examples for using the ordance survey easting and northing system. According to the ordenance survey guide I should be getting an answer in metres for using the full 6 digits.
I am also getting this error:
errorErrormessage: BIGINT UNSIGNED value is out of range in '(((mjm.pc.Eastings - 436421) ^ 2) + ((mjm.pc.Northings - 291786) ^ 2))'
When trying to test the query:
$query = "
SELECT * , SQRT( (pc.Eastings - {$e1})^2 + (pc.Northings - {$n1})^2) AS distance
FROM properties p, postcodes pc
WHERE concat( p.POSTCODE1, p.POSTCODE2 )= pc.Postcode HAVING distance <= 10
LIMIT 0 , 30
";
Can someone help me understand what I need to do to make this work please?
^ is the XOR operator, not the square. Can you try this?
SQRT( POW(pc.Eastings - {$e1}, 2) +
POW(pc.Northings - {$n1}, 2)) as distance

Select Distinct Rows from MySQL database through PHP/PDO [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to prevent SQL injection in PHP?
Select distinct rows from MySQL Database
Hi I have a phonegap application that stores latitude, longitude, address and severity when a road surface deformation (such as pothole and speedbump) is detected.
So far saving these in the database through a PHP PDO is not a problem at all. The PDO is designed in a way that if the pothole being reported has already been reported 10 times (checks the database for any entries within a 15 meter range), then it would not be reported (i.e inserted in the database again). Also, loading the surface deformations is not a problem either, i am using the Haversine formula to do that, where I pass the latitude and longitude of the user and get the values within a certain distance.
$stmt = $dbh->prepare("
SELECT
lat, lng,
( 6378160 * acos( cos( radians(?) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(?) ) + sin( radians(?) ) * sin( radians( lat ) ) ) ) AS distance
FROM myTable
HAVING distance > 0
ORDER BY distance
LIMIT 0 , 30
");
The issue I have is that since the same pothole can be reported 10 times, I am ending up having the same pothole reported back to the application for charting on a map 10 times. What I need to do is, get the list of potholes that are within a certain distance from the user (done using the haversine formula), and then out of this list, filter the potholes so that I only get distinct potholes rather than the same pothole being returned back 10 times. Anyone has any idea how I can make such filtering? Can anyone tell me how is it posssible to do this in PHP/PDO or point me to some similar tutorial if available?
Here is what I need to do in brief: say I am near pothole A and Pothole B, and say I have 6 reports for pothole A, and 8 reports for pothole B (and so on) in the database. By using the haversine formula I get all the values of the reports for pothole A and pothole B (ie 14 results). What I need is rather I get the midpoint of the reports for pothole A and midpoint of the reports for Pothole B (using: http://www.geomidpoint.com/calculation.html) and return back 2 results (one for A and one for B) rather than 14 results.
You have to separate potholes and reports. First query what potholes are around GROUP BY lat, lng and then query one report for each pothole with LIMIT 1.
$rows = dbh_query("SELECT id FROM ... ");
foreach ($rows as $row)
{
dbh_query("SELECT ... WHERE id = :id LIMIT 1", array('id' => $row['id']));
}
I think you have 2 options to elaborate. Unfortunately both implies some complications.
1) Instead of writing multiple observations, you should always clusterize them at writing time. For example, if you support space granularity of 10 meters, then every time a new measurement arrives that is less then in 10 meters from existing record, you do not add a new pothole, but change average values (latitude, longitude, counter) in the nearest existing record. This way you'll end up with 2 records for potholes A and B for your example, so you can use DISTINCT query.
2) For every request, you can fetch all records in 15 meter range from existing table and create a temporary table on them for calculating a probability density function along the road axe. Again, this requires to choose a granularity which can be simulated as decimal accuracy in the ROUND function. For example, if you would have a stored function for calculating distance between current point and existing records, you could write:
INSERT INTO `temppdf` (dist, pothole_id)
SELECT FROM `maintable`
ROUND(distance(#current_lat, #current_lon, maintable.lat, maintable.lon), -1), pothole_id
WHERE distance(#current_lat, #current_lon, maintable.lat, maintable.lon) < 15;
Then you can query temppdf for rows with maximum counters for every pothole, something like this:
SELECT pothole, MAX(cnt) as `peak` FROM
(SELECT DISTINCT pothole, COUNT(dist) as cnt FROM `temppdf` GROUP BY pothole, dist) as `subq`
GROUP BY pothole;
The potholes with counters larger than a threshold are the result.

Categories