지리적 근접 공식(스토어 로케이터)으로 인해 결과 누락
좋아요 - 저는 약 3개월 동안 이것과 계속해서 씨름해 왔고 제가 만난 모든 지리적 근접 공식을 다 써버렸기 때문에 올바른 결과를 얻는 데 더 가까워지지 않았기 때문에 저는 도움을 요청할 때라고 생각했습니다.
목표
상점 위치 추적기의 상당히 기본적인 구현을 설정하고 있습니다.사용자는 우편 번호를 입력하고 사전 정의된 검색 반경 목록에서 선택합니다.gmaps API는 이 주소에 대한 lat/long 좌표를 생성하여 php 스크립트로 전달합니다.이 스크립트에서 사용자 좌표는 mysql 데이터베이스 테이블(아래 구조)에 대해 쿼리됩니다.
post_id int(11)
post_type varchar(20)
lat float(10,6)
lng float(10,6)
이 쿼리(포스트 ID)의 결과는 지도 마커 데이터가 포함된 XML을 생성하는 워드프레스 쿼리에 입력됩니다.(wordpress query는 post__in 및 post_per_page -1을 사용하여 쿼리에 의해 생성된 모든 ID에 대한 정보를 표시합니다.
문제
간단히 말해서, 제가 본 Haversine 공식의 모든 구현은 마커를 누락시키는 결과를 초래하는 것 같습니다. 특히 입력된 좌표에 매우 가까운 마커입니다(정확하게는 모르지만 약 500m 이내인 것 같습니다).이는 사용자가 우편번호를 입력하고 위치에서 매우 가까운 상점이 있으면 표시되지 않는 큰 문제입니다.
저는 다양한 튜토리얼에서 발굴한 포룸라의 8가지 다른 순열을 동일한 결과로 시도했습니다.다음은 입력한 위치에 매우 가까운 사용자를 제외한 모든 마커를 제공하는 사이트에서 현재 사용 중인 수식입니다.
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
// Calculate square radius search
$lat1 = (float) $center_lat - ( (int) $radius / 69 );
$lat2 = (float) $center_lat + ( (int) $radius / 69 );
$lng1 = (float) $center_lng - (int) $radius / abs( cos( deg2rad( (float) $center_lat ) ) * 69 );
$lng2 = (float) $center_lng + (int) $radius / abs( cos( deg2rad( (float) $center_lat ) ) * 69 );
$sqlsquareradius = "
SELECT
post_id, lat, lng
FROM
wp_geodatastore
WHERE
lat BETWEEN ".$lat1." AND ".$lat2."
AND
lng BETWEEN ".$lng1." AND ".$lng2."
"; // End $sqlsquareradius
// Create sql for circle radius check
$sqlcircleradius = "
SELECT
t.post_id,
3956 * 2 * ASIN(
SQRT(
POWER(
SIN(
( ".(float) $center_lat." - abs(t.lat) ) * pi() / 180 / 2
), 2
) + COS(
".(float) $center_lat." * pi() / 180
) * COS(
abs(t.lat) * pi() / 180
) * POWER(
SIN(
( ".(float) $center_lng." - t.lng ) * pi() / 180 / 2
), 2
)
)
) AS distance
FROM
(".$sqlsquareradius.") AS t
HAVING
distance <= ".(int) $radius."
ORDER BY distance
"; // End $sqlcircleradius
$result = mysql_query($sqlcircleradius);
$row = mysql_fetch_array( $result );
while($row = mysql_fetch_array( $result )) {
// the contents of each row
$post_ids[] = $row['post_id'];
}
마이크 펠리가 여기서 제안한 공식이 하나 있습니다: 지리 위치 SQL 쿼리가 정확한 위치를 찾지 못합니다.
이 공식은 입력된 위치에 있는 사용자와 매우 가깝지만 주어진 반경 내에 표시되어야 하는 다른 마커를 놓친 마커를 보여주는 것처럼 보였습니다.혼란을 해소하기 위해 사용한 코드는 다음과 같습니다.
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
$sql = "
SELECT post_id, lat, lng,
truncate((degrees(acos( sin(radians(lat))
* sin(radians(".$center_lat."))
+ cos(radians(lat))
* cos(radians(".$center_lat."))
* cos(radians(".$center_lng." - lng) ) ) )
* 69.09*1.6),1) as distance
FROM wp_geodatastore HAVING distance <= ".$radius." ORDER BY distance desc
"; // End $sqlcircleradius
$result = mysql_query($sql);
$row = mysql_fetch_array( $result );
while($row = mysql_fetch_array( $result )) {
// Print out the contents of each row
$post_ids[] = $row['post_id'];
}
요청
기본적으로 저는 이 코드 블록 중 어느 것도 올바른 마커를 표시하지 않는 이유를 알고 싶습니다.코드에 대한 개선 사항을 제안하거나 제가 놓쳤을 수 있는 리소스를 알려줄 수 있는 사람이 있다면 매우 좋을 것입니다.
편집
제 PSudeo 답변이 효과가 있다고 생각했지만, 알고 보니 여전히 문제가 있었습니다.저는 이제 아주 다른 방법을 택하게 되었고 여기에서 찾을 수 있는 매우 좋은 jquery store locator를 사용하고 있습니다: http://www.bjornblog.com/web/jquery-store-locator-plugin .
모든 프로젝트에 효과가 있는 것은 아니지만, 제가 필요로 하는 경우에는 완벽합니다(그리고 효과가 있습니다!
편집 이 위치 검색기는 제가 기사를 썼을 정도로 자주 나옵니다.
http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
원본 게시물
먼저 하버신 공식을 한 번만 다루는 것으로 시작하겠습니다. 저장된 함수에 넣음으로써 세세한 부분까지 잊어버릴 수 있습니다.참고: 이 전체 솔루션은 법령 마일 단위입니다.
DELIMITER $$
CREATE
FUNCTION distance(lat1 FLOAT, long1 FLOAT, lat2 FLOAT, long2 FLOAT)
RETURNS FLOAT
DETERMINISTIC NO SQL
BEGIN
RETURN (3959 * ACOS(COS(RADIANS(lat1))
* COS(RADIANS(lat2))
* COS(RADIANS(long1) - RADIANS(long2))
+ SIN(RADIANS(lat1))
* SIN(RADIANS(lat2))
));
END$$
DELIMITER ;
이제 경계 상자에서 검색한 다음 거리 함수와 거리별 순서로 검색을 세분화하는 쿼리를 작성하겠습니다.
질문의 PHP 코드를 기반으로 합니다.
정다하추로 합니다.$radius
반경이 당신의 반경이고,$center_lat
,$center_lng
참조점입니다.
$sqlsquareradius = "
SELECT post_id, lat, lng
FROM
(
SELECT post_id, lat, lng,
distance(lat, lng, " . $center_lat . "," . $center_lng . ") AS distance
FROM wp_geodatastore
WHERE lat >= " . $center_lat . " -(" . $radius . "/69)
AND lat <= " . $center_lat . " +(" . $radius . "/69)
AND lng >= " . $center_lng . " -(" . $radius . "/69)
AND lng <= " . $center_lng . " +(" . $radius . "/69)
)a
WHERE distance <= " . $radius . "
ORDER BY distance
";
이에 대해 몇 가지 사항에 주목하십시오.
첫째, 경계 상자 계산을 PHP가 아닌 SQL로 수행합니다.모든 계산을 한 환경에 보관하는 것 외에는 그럴 만한 이유가 없습니다. (radius / 69)
는 의 도 입다니수로 표시된 의 수입니다.radius
법정 마일
둘째, 위도를 기준으로 한 세로 경계 상자의 크기를 조정하지 않습니다.대신에 더 간단하지만 약간 너무 큰 경계 상자를 사용합니다.이 경계 상자는 몇 개의 추가 레코드를 포착하지만 거리 측정을 통해 레코드가 제거됩니다.일반적인 우편 번호/스토어 파인더 앱의 경우 성능 차이는 무시할 수 있습니다.더 많은 레코드(예: 모든 전신주의 데이터베이스)를 검색하는 경우 이는 그리 사소한 일이 아닐 수 있습니다.
셋째, 각 항목에 대해 거리 함수를 두 번 이상 실행하지 않도록 중첩된 쿼리를 사용하여 거리 제거를 수행합니다.
넷째, 거리별 Ascending 순서입니다.즉, 거리가 0인 결과가 결과 집합에 먼저 표시되어야 합니다.일반적으로 가장 가까운 것을 먼저 나열하는 것이 합리적입니다.
다섯째, 그것은 사용합니다.FLOAT
DOUBLE
그럴 만한 충분한 이유가 있습니다.하버진 거리 공식은 완벽하지 않습니다. 지구가 완벽한 구라는 근사치를 내기 때문입니다.의 됩니다.FLOAT
숫자들그렇게DOUBLE
이 문제에 대한 기만적인 수치 과잉 살상입니다. (주차장 배수와 같은 토목 공사 작업에 이 Haversine 공식을 사용하지 마십시오. 그렇지 않으면 몇 인치 깊이의 큰 웅덩이가 생길 것입니다. 약속드립니다.)그것은 가게를 찾는 사람들에게 좋습니다.
여섯 번째, 당신은 분명히 당신을 위한 색인을 만들고 싶을 것입니다.lat
기둥.위치 표가 자주 변경되지 않으면 사용자에 대한 인덱스를 만드는 데 도움이 됩니다.lng
열도 표시합니다.하지만 당신의lat
인덱스는 쿼리 성능 향상의 대부분을 제공합니다.
마지막으로 저장 프로시저와 SQL을 테스트했지만 PHP는 테스트하지 않았습니다.
참고 자료: http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL 또한 의료 시설에 대한 근접 검색기를 사용한 경험이 있습니다.
편집 ---------------------
저장 프로시저를 정의할 수 있는 사용자 인터페이스가 없으면 성가신 작업입니다.어쨌든 PHP를 사용하면 sprintf 호출에서 번호가 매겨진 매개 변수를 사용할 수 있으므로 이렇게 중첩된 전체 문을 생성할 수 있습니다.참고: %$1f 등이 필요할 수 있습니다.당신은 이것으로 실험을 해야 할 것입니다.
$sql_stmt = sprintf ("
SELECT post_id, lat, lng
FROM
(
SELECT post_id, lat, lng,
(3959 * ACOS(COS(RADIANS(lat))
* COS(RADIANS(%$1s))
* COS(RADIANS(lng) - RADIANS(%$2s))
+ SIN(RADIANS(lat))
* SIN(RADIANS(%$1s))
))
AS distance
FROM wp_geodatastore
WHERE lat >= %$1s -(%$3s/69)
AND lat <= %$1s +(%$3s/69)
AND lng >= %$2s -(%$3s/69)
AND lng <= %$2s +(%$3s/69)
)a
WHERE distance <= %$3s
ORDER BY distance
",$center_lat,$center_lng, $radius);
지리적 근접성 계산에서 한동안 성공적으로 사용한 솔루션은 다음과 같습니다.
/**
* This portion of the routine calculates the minimum and maximum lat and
* long within a given range. This portion of the code was written
* by Jeff Bearer (http:return true;//www.jeffbearer.com).
*/
$lat = somevalue; // The latitude of our search origin
$lon = someothervalue; // The longitude of our search origin
$range = 50; // The range of our search, in miles, of your zip
// Find Max - Min Lat / Long for Radius and zero point and query only zips in that range.
$lat_range = $range / 69.172;
$lon_range = abs($range / (cos($lon) * 69.172));
$min_lat = number_format($lat - $lat_range, '4', '.', '');
$max_lat = number_format($lat + $lat_range, '4', '.', '');
$min_lon = number_format($lon - $lon_range, '4', '.', '');
$max_lon = number_format($lon + $lon_range, '4', '.', '');
/* Query for matching zips:
SELECT post_id, lat, lng
FROM wp_geodatastore
WHERE
lat BETWEEN $min_lat AND $max_lat
AND lng BETWEEN $min_lon AND $max_lon
*/
이것은 작동하는 생산 시스템의 코드입니다.
6371.04 * acos(cos(pi()/2-radians(90-wgs84_lat)) * cos(pi()/2-radians(90-$lat)) * cos(radians(wgs84_long)-radians($lon)) + sin(pi()/2-radians(90-wgs84_lat)) * sin(pi()/2-radians(90-$lat))) as distance
다른 거리 공식을 사용하지만 스토어 로케이터의 경우 차이가 작습니다.
조금 옆으로 생각하면서 사라진 마커의 문제에 대한 일종의 해결책을 생각해냈습니다.내가 게시한 두 방정식은 원래 정확한 결과를 제공했지만 각각 목표에 근접하거나 검색 반경의 가장자리에 있는 마커를 놓쳤습니다.
그다지 우아하지는 않지만 두 방정식을 모두 실행하고 두 배열을 생성한 다음 결합(중복 제거)하면 원하는 모든 마커를 얻을 수 있을 것이라고 생각했습니다.이것은 효과적입니다(분명히 성능은 좋지만 트래픽이 많은 애플리케이션은 아닙니다). 그래서 당분간은 이것으로 작업할 것입니다. 하지만 누군가 있다면 저는 여전히 더 실용적인 해결책을 찾고 있습니다!
제 수업은 http://www.phpclasses.org/package/6202-PHP-Generate-points-of-an-Hilbert-curve.html 에서 해보실 수 있습니다.그것은 쿼드키를 계산하기 위해 하버사인 공식과 힐버트 곡선을 사용합니다.그런 다음 쿼드 키를 왼쪽에서 오른쪽으로 검색할 수 있습니다.키의 모든 위치는 몬스터 곡선의 점입니다.곡선에 대한 더 나은 설명은 Nick의 공간 인덱스 쿼드트리 힐버트 곡선 블로그에서 찾을 수 있습니다.mysql의 공간 인덱스 확장을 사용하는 것과 비슷하지만 더 많은 제어 권한을 가집니다.Z 곡선 또는 무어 곡선을 사용하거나 모양을 변경할 수 있습니다.
언급URL : https://stackoverflow.com/questions/8727717/missing-results-due-to-geo-proximity-formula-store-locator
'programing' 카테고리의 다른 글
여러 data.frame을 여러 Excel 워크시트로 쉽게 내보낼 수 있는 방법 (0) | 2023.06.12 |
---|---|
createReducer 함수를 사용할 때 생산을 위해 angular+ngrx 8을 빌드하는 동안 오류가 발생했습니다. (0) | 2023.06.12 |
수직 gem_vline을 클래스 날짜의 x축으로 가져오는 방법은 무엇입니까? (0) | 2023.06.07 |
루비에서 현재 작업 디렉터리의 절대 경로를 얻는 방법은 무엇입니까? (0) | 2023.06.07 |
.NET의 저장 프로시저에서 오라클 출력 매개 변수를 반환하는 방법 (0) | 2023.06.07 |