MySQL 대원 거리(Haversine 공식)
경도와 Latitude 값을 가져와 MySQL 쿼리에 입력하는 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 = '$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 ";
}
이거 완전히 MySQL 만드는 법 아는 사람 있어?인터넷을 좀 둘러보긴 했지만 대부분의 문헌은 꽤 혼란스러워요.
Google 코드 FAQ - PHP, MySQL 및 Google 맵을 사용한 스토어 로케이터 작성:
다음은 37,122 좌표에서 반경 25마일 이내에 있는 가장 가까운 20개 지점을 찾는 SQL 문입니다.해당 행의 위도/경도 및 목표 위도/경도를 기준으로 거리를 계산한 다음 거리 값이 25 미만인 행만 요청하고 거리별로 전체 쿼리를 정렬한 후 결과를 20개로 제한합니다.마일 대신 킬로미터 단위로 검색하려면 3959를 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));
위도와 경도를 라디안으로 표시합니다.
그렇게
SELECT
acos(
cos(radians( $latitude0 ))
* cos(radians( $latitude1 ))
* cos(radians( $longitude0 ) - radians( $longitude1 ))
+ sin(radians( $latitude0 ))
* sin(radians( $latitude1 ))
) AS greatCircleDistance
FROM yourTable;
SQL 쿼리입니다.
또는 마일(km 또 는 는 마 마 to to to to to to to to to to to to to to to to to to to to to to to to to to to to to to to to to of of of of of)을.3959
거리, 거리,6371
또는 km 는 km3440
□□□□□□□□★
이 예에서 계산하고 있는 것은 경계 상자입니다.공간적으로 활성화된 MySQL 열에 좌표 데이터를 넣으면 MySQL의 빌트인 기능을 사용하여 데이터를 조회할 수 있습니다.
SELECT
id
FROM spatialEnabledTable
WHERE
MBRWithin(ogc_point, GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))'))
좌표 테이블에 도우미 필드를 추가하면 조회의 응답 시간을 단축할 수 있습니다.
다음과 같이 합니다.
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);
각 포인트에 대해 기본 위도 및 위도와 라디안에서의 sin(lat), 라디안에서의 cos(lat)*cos(lon), 라디안에서의 cos(lat)*sin(lon)이 필요합니다.다음으로 다음과 같은 mysql 함수를 만듭니다.
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 쿼리에 이미 추가되었습니다).
INDEX `lat_lon_idx` (`lat`, `lon`)
lat/lon 좌표만 있는 오래된 테이블을 지정하면 다음과 같이 갱신하는 스크립트를 설정할 수 있습니다(meekrodb를 사용하여 php).
$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']
));
}
그런 다음 실제 조회를 최적화하여 원(원, 타원형)을 안쪽과 바깥쪽에서 바운딩하는 등 실제로 필요한 경우에만 거리 계산을 수행합니다.그러기 위해서는 쿼리 자체에 대해 다음과 같은 몇 가지 메트릭을 미리 계산해야 합니다.
// 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).
$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);
위의 쿼리에 대한 설명에서는 이러한 결과를 트리거할 수 없는 한 인덱스를 사용하지 않는다고 할 수 있습니다.좌표 테이블에 충분한 데이터가 있을 때 인덱스가 사용됩니다.테이블 크기에 관계없이 인덱스를 사용하도록 SELECT에 FORCE INDEX(lat_lon_idx)를 추가하여 올바르게 동작하고 있는지 확인할 수 있습니다.
위의 코드샘플을 사용하면 최소한의 오차로 거리별 객체 검색을 실행할 수 있고 확장 가능한 구현이 가능합니다.
저는 이 문제를 좀 더 자세히 풀어야 했기 때문에 결과를 공유하겠습니다.은 「」를 합니다.zip
와 식사하다.latitude
★★★★★★★★★★★★★★★★★」longitude
를 참조할 수 있습니다.Google 지도에 의존하지 않고 위도/길이 표로 변경할 수 있습니다.
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
30개의 엔트리에서 됩니다.zip
위도/장도 42.81/-70.81에서 50.0마일 이내에 있는 테이블입니다.이것을 앱에 짜넣으면, 거기에 독자적인 포인트와 검색 반경을 넣을 수 있습니다.
하십시오.69
로로 합니다.111.045
and and 。3963.17
로로 합니다.6378.10
를 참조해 주세요.
여기 자세한 내용이 있습니다.도움이 됐으면 좋겠어요.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
25km 거리 동안
동일하게 계산할 수 있는 절차를 작성했습니다만, 각각의 표에 위도와 경도를 입력해야 합니다.
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 ;
위 답변에 대해서는 코멘트는 할 수 없지만, @Pavel Chuchuva의 답변에 주의해 주십시오.두 좌표가 같은 경우 이 공식은 결과를 반환하지 않습니다.이 경우 거리는 null이므로 해당 행은 현재 공식으로 반환되지 않습니다.
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;
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);
}
거리 계산(Mysql)
SELECT (6371 * acos(cos(radians(lat2)) * cos(radians(lat1) ) * cos(radians(long1) -radians(long2)) + sin(radians(lat2)) * sin(radians(lat1)))) AS distance
따라서 거리 값이 계산되고 필요에 따라 누구나 신청할 수 있습니다.
언급URL : https://stackoverflow.com/questions/574691/mysql-great-circle-distance-haversine-formula
'source' 카테고리의 다른 글
PHP에서 문자열을 정수로 변환하는 가장 빠른 방법 (0) | 2022.10.01 |
---|---|
Maria에서 동시에 작업하는 프로세스를 처리하는 방법DB (0) | 2022.10.01 |
프로그램에서 JVM 버전을 찾는 방법 (0) | 2022.10.01 |
도커 파일에 mysql을 설치하시겠습니까? (0) | 2022.10.01 |
Android 버튼 객체 내에서 텍스트 주변의 내부 패딩을 줄이려면 어떻게 해야 합니까? (0) | 2022.10.01 |