Php MySQL大圆距离(哈弗公式)
我有一个可以工作的PHP脚本,它获取经度和纬度值,然后将它们输入到MySQL查询中。我想单独做一个。以下是我当前的PHP代码:Php MySQL大圆距离(哈弗公式),php,mysql,great-circle,Php,Mysql,Great Circle,我有一个可以工作的PHP脚本,它获取经度和纬度值,然后将它们输入到MySQL查询中。我想单独做一个。以下是我当前的PHP代码: 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 = '$cus
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 ";
}
有没有人知道如何让这一切变得完整?我浏览了一下互联网,但大部分关于它的文献都很混乱 来自:
下面的SQL语句将查找距离37,-122坐标25英里半径内最近的20个位置。它基于该行的纬度/经度和目标纬度/经度计算距离,然后仅要求距离值小于25的行,按距离对整个查询排序,并将其限制为20个结果。要按公里而不是英里搜索,请将3959替换为6371
选择id,(3959*acos(弧度(37))*cos(弧度(纬度))
*cos(弧度(lng)-弧度(-122))+sin(弧度(37))*sin(弧度(纬度)))作为距离
从标记
距离小于25的
按距离排序
限值0,20;
$greatCircleDistance=acos(cos($latitude0)*cos($latitude1)*cos($longitude0-$longitude1)+sin($latitude0)*sin sin($latitude1))代码>
以弧度表示纬度和经度
所以
这是您的SQL查询
要获得以公里或英里为单位的结果,请将结果乘以地球的平均半径(3959
miles,6371
Km或3440
海里)
您在示例中计算的是一个边界框。
如果将坐标数据放在中,则可以使用来查询数据
SELECT
id
FROM spatialEnabledTable
WHERE
MBRWithin(ogc_point, GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))'))
我写了一个程序,可以计算相同的,
但是您必须在相应的表中输入纬度和经度
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 ;
如果将帮助器字段添加到坐标表中,则可以缩短查询的响应时间
像这样:
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`)
)
如果您使用的是TokuDB,那么如果添加集群,您将获得更好的性能
例如,在任一谓词上建立索引,如下所示:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
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
每个点都需要基本的纬度和经度,以及以弧度表示的sin(lat)、以弧度表示的cos(lat)*cos(lon)和以弧度表示的cos(lat)*sin(lon)。
然后创建一个mysql函数,如下所示:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
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
这给了你距离
不要忘记在lat/lon上添加索引,这样边界框可以帮助搜索,而不是减慢搜索速度(索引已经添加到上面的CREATE TABLE查询中)
给定一个只有lat/lon坐标的旧表,您可以设置一个脚本来更新它,如下所示:(php使用meekrodb)
然后优化实际查询,使其仅在真正需要时进行距离计算,例如,从内部和外部包围圆(椭圆)。
为此,您需要为查询本身预先计算几个指标:
// 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));
考虑到这些准备工作,查询如下(php):
$neights=DB::query(“选择id、type、lat、lon、,
大地距离(sin_lat,cos_cos,cos_sin,%d,%d)作为距离
从哪个坐标系
lat介于%d和%d之间,lon介于%d和%d之间
有(lat介于%d和%d之间,lon介于%d和%d之间)或距离我认为我的javascript实现可以很好地参考:
/*
* 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);
}
/*
*检查第二坐标是否在精度范围内(米)
*并相应地返回
*/
函数checkWithinBound(坐标1、坐标2、精度){
var距离=3959000*Math.acos(
数学cos(度到弧度(坐标2.lat))*
数学cos(度到弧度(坐标1.lat))*
数学(
度到弧度(坐标1.lng)-度到弧度(坐标2.lng)
) +
数学sin(度到弧度(坐标2.lat))*
数学sin(度到弧度(坐标1.lat))
);
返回距离我不能对上述答案发表评论,但请注意@Pavel Chuchuva的答案。如果两个坐标相同,则该公式不会返回结果。在这种情况下,距离为空,因此该行不会按原样返回该公式
我不是MySQL专家,但这似乎对我有用:
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;
选择id,(3959*acos(cos(弧度(37))*cos(弧度(纬度))*cos(弧度(lng)-弧度(-122))+sin(弧度(37))*sin(弧度(纬度)))作为距离
距离小于25或距离为空的标记的距离限制为0,20;
我必须详细地解决这个问题,所以我将分享我的结果。这使用了一个带有纬度和经度表的zip
表。它不依赖于谷歌地图;相反,你可以将它适应任何包含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
这将在zip
表中搜索距离lat/long point 42.81/-70.81 50.0英里范围内最近的30个条目。当您将其构建到应用程序中时,您将在其中输入自己的点和搜索半径
如果要以公里而不是英里为单位工作,请在查询中将69
更改为111.045
,并将3963.17
更改为6378.10
这是一篇详细的文章。我希望它能帮助一些人。SELECT*,(
6371*acos(弧度(搜索纬度))*cos(弧度(纬度))*
cos(弧度(lng)-弧度(搜索lng))+sin(弧度(搜索纬度))*sin(弧度(纬度)))
)作为距离
从桌子上
其中lat!=搜索lat和lng!=搜索lng和距离<25
按距离排序
只卖10英镑
对于25公里的距离以Mysql计算距离
SELECT (6371 * acos(cos(radians(lat2)) * cos(radians(lat1) ) * cos(radians(long1) -radians(long2)) + sin(radians(lat2)) * sin(radians(lat1)))) AS distance
因此,将计算距离值,任何人都可以根据需要进行应用。sql语句非常好。但是,我可以在何处将我的坐标传递到该语句中?我看不到有任何坐标通过替换37和-122与您的坐标。如果有数百万个位置,我想知道这对性能的影响(+数千名访问者)…您可以缩小查询范围以获得更好的性能
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;
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
SELECT 42.81 AS latpoint, -70.81 AS longpoint, 50.0 AS r
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
SELECT (6371 * acos(cos(radians(lat2)) * cos(radians(lat1) ) * cos(radians(long1) -radians(long2)) + sin(radians(lat2)) * sin(radians(lat1)))) AS distance