Getting radius in meters/km from latitudeDelta and longitudeDelta - mysql

I am requesting data from backend (laravel) based on a set distance between 2 points latitude and longitude eg. Lat= 78.3232 and Long = 65.3234 and distance = 30 miles/kilometers , get the rows within the 30 miles.
The problem I'm having is with distance.
I am using react-native-maps and the zoom in/out is based on the latitudeDelta and longitudeDelta and I don't know how to calculate the radius/distance of them when the user zooms in/out to be sent to the backend and get data based on the points and radius/distance
at the moment, I have this function on the client-side. to determine when to fetch new data when user changes the region. but it has a problem of hard-coded distance (5.0 KM)
const _onRegionChangeComplete = (onCompletedregion: Region) => {
if (initialRegion) {
const KMDistance = helper.distance(
initialRegion?.latitude,
initialRegion?.longitude,
onCompletedregion.latitude,
onCompletedregion.longitude,
"K"
);
if (KMDistance > 5.0) {
props.getNearByReports({ // request backend if distance difference more than 5 kilometers
latitude: onCompletedregion.latitude,
longitude: onCompletedregion.longitude
});
}
}
};
helper.distance // get the difference in distance between the initial points and after the user done changing the region
function distance(
lat1: number,
lon1: number,
lat2: number,
lon2: number,
unit: string
) {
var radlat1 = (Math.PI * lat1) / 180;
var radlat2 = (Math.PI * lat2) / 180;
var theta = lon1 - lon2;
var radtheta = (Math.PI * theta) / 180;
var dist =
Math.sin(radlat1) * Math.sin(radlat2) +
Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
dist = Math.acos(dist);
dist = (dist * 180) / Math.PI;
dist = dist * 60 * 1.1515;
if (unit == "K") {
dist = dist * 1.609344;
}
if (unit == "M") {
dist = dist * 0.8684;
}
return dist;
}
The distance value is hard coded in the backend and frontend (which shouldn't be) cuz when the user zooms in/out , the radius/distance should change too. I am unable to calculate the distance from latitudeDelta and longitudeDelta
// Backend query code (simplified)
SELECT
id, (
6371 * acos (
cos ( radians(78.3232) )
* cos( radians( lat ) )
* cos( radians( lng ) - radians(65.3234) )
+ sin ( radians(78.3232) )
* sin( radians( lat ) )
)
) AS distance
FROM markers
HAVING distance < 5 // hard-coded distance
ORDER BY distance
LIMIT 0 , 20;

latitudeDelta is the amount of degrees that are visible on the screen. 1 degree is always equal to approx. 69 miles, so you can calculate the diameter of the currently visible area in miles with this diameter = latitudeDelta * 69. This is assuming the screen is in portrait mode and, therefore, latitudeDelta is the larger value. If not, use longitudeDelta instead. I hope I understood your question correctly and this is helpful.

Related

passing longitude and latitude and distance as parameters in node js mysql query

Hello I wrote this long mysql query that takes user longitude ,latitude and meters as input and shows the locations within 100 meters.
However I always get a mysql error near ? when I type http://localhost:3002/closest/20.8877387/5.847748474/100 in the browser. Could someone give me a hint?
app.get('/closest/:lat/:lon/meters', (request, response) => {
const lat = request.params.lat;
const lon = request.params.lon;
pool.query('SELECT *, ( ( ( Acos(Sin(( WHERE lat =? * Pi() / 180 )) * Sin(( map.lat* Pi() / 180 )) + Cos (( WHERE lat =? * Pi() / 180 )) * Cos(( map.lat* Pi() / 180 )) * Cos (( ( WHERE lon = ? - map.lon ) * Pi() / 180 ))) ) * 180 / Pi () ) * 60 * 1.1515 * 1.609344 * 1000 ) AS `METERS` FROM base.map WHERE "METERS" <= ?',lat,lon, (error, result) => {
if (error) throw error;
response.send(result);
});
});
You don't seem to set the values of your ? parameters. You need to provide them as an array as the second parameter to .query(sqlStatement, parameterArray, callback). You have four ?s in your query, so the array you provide must have four elements. [lat, lat, lon, radius] correspond to the purpose of your four ?s. The ? in your query represent conditional parameters. You want
Your query says something like this.
SELECT bigFatFormula AS METERS FROM tbl WHERE METERS < someDistance
That WHERE clause doesn't work. You have to say
SELECT * FROM
(SELECT bigFatFormula AS METERS FROM tbl) result
WHERE METERS < someDistance
In your query's WHERE clause, your METERS alias isn't in scope. The nested query puts it in scope.
Your query contains a mess of nested WHERE clauses. That doesn't work.
Your get handler's first line needs to say this, with a colon on meters.
app.get('/closest/:lat/:lon/:meters', (request, response) => {
So, you want something like this.
const lat = Number(request.params.lat);
const lon = Number(request.params.lon);
const radius = Number(request.params.meters);
var q = '
SELECT * FROM (
SELECT *, ( ( ( Acos(Sin(( ? * Pi() / 180 ))
* Sin(( map.lat* Pi() / 180 ))
+ Cos (( lat =? * Pi() / 180 )) *
Cos(( map.lat* Pi() / 180 )) *
Cos ((( ? - map.lon ) * Pi() / 180 ))) )
* 180 / Pi () ) * 60 * 1.1515 * 1.609344 * 1000 )
AS METERS
FROM base.map
) resultset WHERE METERS <= ?';
pool.query(q, [lat, lat, lon, radius], (error, result) => {
I haven't debugged this.
Pro tip: Your query will be very slow if you have lots of rows in your table. Read this for a suggestion.
Pro tip: You might want to use a stored function for your lat/lng - to - distance function, to make your query easier to read for the next person. Here's a stable function.
Pro tip: It's often a good idea to debug complex queries like this using a SQL client program before trying to embed them into programs.

MySQL query that finds circles containing given coordinate [duplicate]

I've got a working PHP script that gets Longitude and Latitude values and then inputs them into a MySQL query. I'd like to make it solely MySQL. Here's my current PHP Code:
if ($distance != "Any" && $customer_zip != "") { //get the great circle distance
//get the origin zip code info
$zip_sql = "SELECT * FROM zip_code WHERE zip_code = '$customer_zip'";
$result = mysql_query($zip_sql);
$row = mysql_fetch_array($result);
$origin_lat = $row['lat'];
$origin_lon = $row['lon'];
//get the range
$lat_range = $distance/69.172;
$lon_range = abs($distance/(cos($details[0]) * 69.172));
$min_lat = number_format($origin_lat - $lat_range, "4", ".", "");
$max_lat = number_format($origin_lat + $lat_range, "4", ".", "");
$min_lon = number_format($origin_lon - $lon_range, "4", ".", "");
$max_lon = number_format($origin_lon + $lon_range, "4", ".", "");
$sql .= "lat BETWEEN '$min_lat' AND '$max_lat' AND lon BETWEEN '$min_lon' AND '$max_lon' AND ";
}
Does anyone know how to make this entirely MySQL? I've browsed the Internet a bit but most of the literature on it is pretty confusing.
From Google Code FAQ - Creating a Store Locator with PHP, MySQL & Google Maps:
Here's the SQL statement that will find the closest 20 locations that are within a radius of 25 miles to the 37, -122 coordinate. It calculates the distance based on the latitude/longitude of that row and the target latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) )
* cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin(radians(lat)) ) ) AS distance
FROM markers
HAVING distance < 25
ORDER BY distance
LIMIT 0 , 20;
$greatCircleDistance = acos( cos($latitude0) * cos($latitude1) * cos($longitude0 - $longitude1) + sin($latitude0) * sin($latitude1));
with latitude and longitude in radian.
so
SELECT
acos(
cos(radians( $latitude0 ))
* cos(radians( $latitude1 ))
* cos(radians( $longitude0 ) - radians( $longitude1 ))
+ sin(radians( $latitude0 ))
* sin(radians( $latitude1 ))
) AS greatCircleDistance
FROM yourTable;
is your SQL query
to get your results in Km or miles, multiply the result with the mean radius of Earth (3959 miles,6371 Km or 3440 nautical miles)
The thing you are calculating in your example is a bounding box.
If you put your coordinate data in a spatial enabled MySQL column, you can use MySQL's build in functionality to query the data.
SELECT
id
FROM spatialEnabledTable
WHERE
MBRWithin(ogc_point, GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))'))
If you add helper fields to the coordinates table, you can improve response time of the query.
Like this:
CREATE TABLE `Coordinates` (
`id` INT(10) UNSIGNED NOT NULL COMMENT 'id for the object',
`type` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'type',
`sin_lat` FLOAT NOT NULL COMMENT 'sin(lat) in radians',
`cos_cos` FLOAT NOT NULL COMMENT 'cos(lat)*cos(lon) in radians',
`cos_sin` FLOAT NOT NULL COMMENT 'cos(lat)*sin(lon) in radians',
`lat` FLOAT NOT NULL COMMENT 'latitude in degrees',
`lon` FLOAT NOT NULL COMMENT 'longitude in degrees',
INDEX `lat_lon_idx` (`lat`, `lon`)
)
If you're using TokuDB, you'll get even better performance if you add clustering
indexes on either of the predicates, for example, like this:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
You'll need the basic lat and lon in degrees as well as sin(lat) in radians, cos(lat)*cos(lon) in radians and cos(lat)*sin(lon) in radians for each point.
Then you create a mysql function, smth like this:
CREATE FUNCTION `geodistance`(`sin_lat1` FLOAT,
`cos_cos1` FLOAT, `cos_sin1` FLOAT,
`sin_lat2` FLOAT,
`cos_cos2` FLOAT, `cos_sin2` FLOAT)
RETURNS float
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
SQL SECURITY INVOKER
BEGIN
RETURN acos(sin_lat1*sin_lat2 + cos_cos1*cos_cos2 + cos_sin1*cos_sin2);
END
This gives you the distance.
Don't forget to add an index on lat/lon so the bounding boxing can help the search instead of slowing it down (the index is already added in the CREATE TABLE query above).
INDEX `lat_lon_idx` (`lat`, `lon`)
Given an old table with only lat/lon coordinates, you can set up a script to update it like this: (php using meekrodb)
$users = DB::query('SELECT id,lat,lon FROM Old_Coordinates');
foreach ($users as $user)
{
$lat_rad = deg2rad($user['lat']);
$lon_rad = deg2rad($user['lon']);
DB::replace('Coordinates', array(
'object_id' => $user['id'],
'object_type' => 0,
'sin_lat' => sin($lat_rad),
'cos_cos' => cos($lat_rad)*cos($lon_rad),
'cos_sin' => cos($lat_rad)*sin($lon_rad),
'lat' => $user['lat'],
'lon' => $user['lon']
));
}
Then you optimize the actual query to only do the distance calculation when really needed, for example by bounding the circle (well, oval) from inside and outside.
For that, you'll need to precalculate several metrics for the query itself:
// assuming the search center coordinates are $lat and $lon in degrees
// and radius in km is given in $distance
$lat_rad = deg2rad($lat);
$lon_rad = deg2rad($lon);
$R = 6371; // earth's radius, km
$distance_rad = $distance/$R;
$distance_rad_plus = $distance_rad * 1.06; // ovality error for outer bounding box
$dist_deg_lat = rad2deg($distance_rad_plus); //outer bounding box
$dist_deg_lon = rad2deg($distance_rad_plus/cos(deg2rad($lat)));
$dist_deg_lat_small = rad2deg($distance_rad/sqrt(2)); //inner bounding box
$dist_deg_lon_small = rad2deg($distance_rad/cos(deg2rad($lat))/sqrt(2));
Given those preparations, the query goes something like this (php):
$neighbors = DB::query("SELECT id, type, lat, lon,
geodistance(sin_lat,cos_cos,cos_sin,%d,%d,%d) as distance
FROM Coordinates WHERE
lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d
HAVING (lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d) OR distance <= %d",
// center radian values: sin_lat, cos_cos, cos_sin
sin($lat_rad),cos($lat_rad)*cos($lon_rad),cos($lat_rad)*sin($lon_rad),
// min_lat, max_lat, min_lon, max_lon for the outside box
$lat-$dist_deg_lat,$lat+$dist_deg_lat,
$lon-$dist_deg_lon,$lon+$dist_deg_lon,
// min_lat, max_lat, min_lon, max_lon for the inside box
$lat-$dist_deg_lat_small,$lat+$dist_deg_lat_small,
$lon-$dist_deg_lon_small,$lon+$dist_deg_lon_small,
// distance in radians
$distance_rad);
EXPLAIN on the above query might say that it's not using index unless there's enough results to trigger such. The index will be used when there's enough data in the coordinates table.
You can add
FORCE INDEX (lat_lon_idx)
to the SELECT to make it use the index with no regards to the table size, so you can verify with EXPLAIN that it is working correctly.
With the above code samples you should have a working and scalable implementation of object search by distance with minimal error.
I have had to work this out in some detail, so I'll share my result. This uses a zip table with latitude and longitude tables. It doesn't depend on Google Maps; rather you can adapt it to any table containing lat/long.
SELECT zip, primary_city,
latitude, longitude, distance_in_mi
FROM (
SELECT zip, primary_city, latitude, longitude,r,
(3963.17 * ACOS(COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude)))) AS distance_in_mi
FROM zip
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint, 50.0 AS r
) AS p
WHERE latitude
BETWEEN latpoint - (r / 69)
AND latpoint + (r / 69)
AND longitude
BETWEEN longpoint - (r / (69 * COS(RADIANS(latpoint))))
AND longpoint + (r / (69 * COS(RADIANS(latpoint))))
) d
WHERE distance_in_mi <= r
ORDER BY distance_in_mi
LIMIT 30
Look at this line in the middle of that query:
SELECT 42.81 AS latpoint, -70.81 AS longpoint, 50.0 AS r
This searches for the 30 nearest entries in the zip table within 50.0 miles of the lat/long point 42.81/-70.81 . When you build this into an app, that's where you put your own point and search radius.
If you want to work in kilometers rather than miles, change 69 to 111.045 and change 3963.17 to 6378.10 in the query.
Here's a detailed writeup. I hope it helps somebody. http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
SELECT *, (
6371 * acos(cos(radians(search_lat)) * cos(radians(lat) ) *
cos(radians(lng) - radians(search_lng)) + sin(radians(search_lat)) * sin(radians(lat)))
) AS distance
FROM table
WHERE lat != search_lat AND lng != search_lng AND distance < 25
ORDER BY distance
FETCH 10 ONLY
for distance of 25 km
I have written a procedure that can calculate the same,
but you have to enter the latitude and longitude in the respective table.
drop procedure if exists select_lattitude_longitude;
delimiter //
create procedure select_lattitude_longitude(In CityName1 varchar(20) , In CityName2 varchar(20))
begin
declare origin_lat float(10,2);
declare origin_long float(10,2);
declare dest_lat float(10,2);
declare dest_long float(10,2);
if CityName1 Not In (select Name from City_lat_lon) OR CityName2 Not In (select Name from City_lat_lon) then
select 'The Name Not Exist or Not Valid Please Check the Names given by you' as Message;
else
select lattitude into origin_lat from City_lat_lon where Name=CityName1;
select longitude into origin_long from City_lat_lon where Name=CityName1;
select lattitude into dest_lat from City_lat_lon where Name=CityName2;
select longitude into dest_long from City_lat_lon where Name=CityName2;
select origin_lat as CityName1_lattitude,
origin_long as CityName1_longitude,
dest_lat as CityName2_lattitude,
dest_long as CityName2_longitude;
SELECT 3956 * 2 * ASIN(SQRT( POWER(SIN((origin_lat - dest_lat) * pi()/180 / 2), 2) + COS(origin_lat * pi()/180) * COS(dest_lat * pi()/180) * POWER(SIN((origin_long-dest_long) * pi()/180 / 2), 2) )) * 1.609344 as Distance_In_Kms ;
end if;
end ;
//
delimiter ;
I can't comment on the above answer, but be careful with #Pavel Chuchuva's answer. That formula will not return a result if both coordinates are the same. In that case, distance is null, and so that row won't be returned with that formula as is.
I'm not a MySQL expert, but this seems to be working for me:
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance
FROM markers HAVING distance < 25 OR distance IS NULL ORDER BY distance LIMIT 0 , 20;
I thought my javascript implementation would be a good reference to:
/*
* Check to see if the second coord is within the precision ( meters )
* of the first coord and return accordingly
*/
function checkWithinBound(coord_one, coord_two, precision) {
var distance = 3959000 * Math.acos(
Math.cos( degree_to_radian( coord_two.lat ) ) *
Math.cos( degree_to_radian( coord_one.lat ) ) *
Math.cos(
degree_to_radian( coord_one.lng ) - degree_to_radian( coord_two.lng )
) +
Math.sin( degree_to_radian( coord_two.lat ) ) *
Math.sin( degree_to_radian( coord_one.lat ) )
);
return distance <= precision;
}
/**
* Get radian from given degree
*/
function degree_to_radian(degree) {
return degree * (Math.PI / 180);
}
calculate distance in Mysql
SELECT (6371 * acos(cos(radians(lat2)) * cos(radians(lat1) ) * cos(radians(long1) -radians(long2)) + sin(radians(lat2)) * sin(radians(lat1)))) AS distance
thus distance value will be calculated and anyone can apply as required.

Inconsistency in Calculating Distance Between Two Lat/Long Points

I have an inconsistency between calculations of distance with SQL consult, and CLLocation. How can I get the real distance?
Distance using this Swift code: 334.599618308747 km
var latitude = 19.395039;
var longitude = -99.156203;
var fromLocation = CLLocation(latitude: self.latitude , longitude: self.longitude)
var toLocation = CLLocation(latitude: latitudeDestion , longitude: longitudeDestinaton)
let distance = fromLocation.distanceFromLocation(toLocation)
Distance SQL : 207.91730456420444 km
SELECT id_gasolineria,
( 3959 * acos( cos( radians(19.395039) ) * cos( radians( gasolinerias.latitud ) )
* cos( radians(gasolinerias.longitud) - radians(-99.156203)) + sin(radians(19.395039))
* sin( radians(gasolinerias.latitud)))) AS distance
FROM gasolinerias
ORDER BY distance;
How is the actual distance obtained using SQL?
There is no (real) inconsistency. The leading factor in the
Haversine formula
is the earth radius, and 3959 in your SQL formula is the (approximate)
radius in miles, therefore the result is 207.9 miles,
which is 334.6 kilometer.
If you replace 3959 with 6371 (approx. earth radius in kilometer)
then you should get the same result as with your Swift code.

Convert real distance to Google Map distance

I have a marker on a Google Map (Android) and I want to move the marker to a new position given bearing and distance.
//lat, lng in degrees. Bearing in degrees. Distance in Km
private LatLng newPostion(Double lat,Double lng,Double bearing,Double distance) {
Double R = 6371; // Earth Radius in Km
Double lat2 = Math.asin(Math.sin(Math.PI / 180 * lat) * Math.cos(distance / R) + Math.cos(Math.PI / 180 * lat) * Math.sin(distance / R) * Math.cos(Math.PI / 180 * bearing));
Double lon2 = Math.PI / 180 * lng + Math.atan2(Math.sin( Math.PI / 180 * bearing) * Math.sin(distance / R) * Math.cos( Math.PI / 180 * lat ), Math.cos(distance / R) - Math.sin( Math.PI / 180 * lat) * Math.sin(lat2));
Double newLat = 180 / Math.PI * lat2;
Double newLng = 180 / Math.PI * lon2;
LatLng newLatLng = new LatLng(newLat, newLng);
return newLatLng;
}
LatLng newPos = newPostion(myMark.getPosition().latitude,myMark.getPosition().longitude, 90.0,3000.0);
Distance is calculated speed * time.
distance = speed * time
Let's say the marker represents a car and the car goes 100 km/h and I need to update the marker every 5 seconds.
distance = 100 * (5/60)
but the problem is showing distance on Google map because Google maps are scaled. Using distance calculated above to move the marker will move the marker much further.
Can anyone help me to convert real distance to Google map distance?
In your distance calculation, you're mixing seconds with minutes (5 seconds, 60 minutes). It should be:
distance = 100 km * (5 sec / 3600 sec)

how to get accurate or (near accurate) distance between two places? Mysql/PHP

SELECT postcode, lat, lng,
truncate(
(degrees(acos
(sin(radians(lat))
*
sin( radians('.$latitude.'))
+
cos(radians(lat))
*
cos( radians('.$latitude.'))
*
cos( radians(lng - ('.$longitude.')))
)
)
* 69.172), 2)
as distance
FROM myData
This query calculates distance (in miles). But when I check distance for same lat and longitude at google maps my result doesnt match. If the distance is around 10 miles then my result is a bit accurate, but over that it gets wrong (for example, my result showed 13 miles and google showed 22 miles for same post code values)
I got this query from http://forums.mysql.com/read.php?23,3868,3868#msg-3868
How can I get it accurate. Any ideas?
Thanks for help.
UPDATE
I tried #Blixt code in PHP. Picked up 2 sample postcodes and their lats longs
//B28 9ET
$lat1 = 52.418819;
$long1 = -1.8481053;
//CV5 8BX
$lat2 = 52.4125573;
$long2 = -1.5407743;
$dtr = M_PI / 180;
$latA = $lat1 * $dtr;
$lonA = $long1 * $dtr;
$latB = $lat2 * $dtr;
$lonB = $long2 * $dtr;
$EarthRadius = 3958.76; //miles
echo $distance = $EarthRadius * acos(cos($latA) * cos($latB) * cos($lonB - $lonA) + sin($latA) * sin($latB));
Results:
My app - 12.95 miles
Google - 17.8 miles
Any ideas how to get it right?
Have a look at this source code. When I tested it against various other measurement services it seemed to get the same results. It's C# but the math should be easy enough to convert.
Here are the relevant pieces:
public const double EarthRadius = 6371.0072; // Kilometers
//public const double EarthRadius = 3958.76; // Miles
/* ... */
const double dtr = Math.PI / 180;
double latA = this.Latitude * dtr;
double lonA = this.Longitude * dtr;
double latB = other.Latitude * dtr;
double lonB = other.Longitude * dtr;
return GpsLocation.EarthRadius * Math.Acos(Math.Cos(latA) * Math.Cos(latB) * Math.Cos(lonB - lonA) + Math.Sin(latA) * Math.Sin(latB));
Note: The Earth is not perfectly spherical, so the constants used may differ. There is no easy way to make the measurements truly exact. See Wikipedia.