2012-01-04 21 views
7

沒有結果 - 我一直在摔跤約3個月左右,因爲我已經用盡了所有地理鄰近公式,我已經遇到過,我我沒有接近取得正確的結果,我想現在是時候尋求一些幫助。由於幾何鄰近公式(商店定位器)

的AIM

我設置了一個商店定位器的一個相當基本實現。用戶輸入他們的郵政編碼並從預定義的搜索半徑列表中進行選擇。 gmaps API爲該地址生成緯度/經度座標,並將它們傳遞給php腳本。在該腳本的用戶COORDS被查詢針對MySQL數據庫表(下面結構)

post_id int(11)        
post_type varchar(20)         
lat float(10,6)        
lng float(10,6) 

此查詢(後IDS)的結果被輸入到其生成包含地圖標記數據的XML一個WordPress查詢。 (WordPress的查詢使用post__in和posts_per_page -1以顯示由查詢

的問題

簡而言之產生的所有ID信息,haversine公式我遇到的每一個執行似乎導致在丟失的標記 - 特別是任何非常接近用戶的標記輸入的座標(不知道確切的,但我認爲它在大約500米以內)。這是一個很大的問題,如果用戶輸入他們的郵政編碼,並有一家商店非常接近到他們的位置它不會顯示出來

我已經嘗試過8個不同的forumla排列,我已經找到了fr在各種教程中獲得相同的結果。下面是我目前使用的提供除了那些非常接近用戶的所有標記的網站上輸入位置的公式:

$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']; 
} 

有1次的配方,我試過,被Mike佩利這裏建議:Geolocation SQL query not finding exact location

該公式似乎顯示非常接近用戶輸入位置的標記,但遺漏了應在給定半徑內顯示的其他標記。爲了消除混淆,這是我使用的代碼:

$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商店定位器,可以在這裏找到:http://www.bjornblog.com/web/jquery-store-locator-plugin

不適用於每個項目,但爲我的需要,它是完美的和工作!)

+0

是否有你沒有使用MySQL內置geospacial功能的原因? http://dev.mysql.com/doc/refman/5.0/en/creating-a-spatially-enabled-mysql-database.html – Kenneth 2012-01-05 17:46:16

+0

我很努力去理解代碼中的一些東西。爲什麼'HAVING'而不是'WHERE'? '(float)$ center_lat - ((int)$ radius/69)中的'int'和''以及另一個查詢中的'truncate'發生了什麼?在應用公式時請牢記這一事實:將赤道的一分經度定義爲海里。整數度是60海里。最後,嘗試擺脫'BETWEEN'並使用'WHERE a> = lat1 AND a <= lat2'來代替。它應該提供相同的查詢複雜性,並闡明瞭您搜索的範圍的包容性/排他性。 – 2012-01-06 01:49:06

+0

@Kenneth - 我沒有使用地理空間查詢有幾個原因。首先,我使用[地理數據存儲插件](http://wordpress.org/extend/plugins/geo-data-store)來創建和維護我的標記數據表。這個插件生成我上面顯示的表結構。其次,我創建商店定位器類型地圖時遇到的絕大多數教程似乎都推薦了一種類似於所列的表結構。也許這些並不是最好的理由,但是我已經用目前的設置走到了這一步,我很確定我想要做的事情應該是可能的。 – FourStacks 2012-01-06 08:51:07

回答

0

想一點橫向我想出了一種解決方案來解決缺少標記的問題。我發佈的兩個等式原來給出了正確的結果,但是每個都遺漏了接近目標的標記或搜索半徑的邊緣

這不是很優雅,但我想到運行兩個方程並生成2個數組,然後我合併(刪除任何重複項)會給我所有我想要的標記。這確實有效(顯然性能受到影響,但它不是一個高流量的應用程序),所以我會暫時處理這個問題,但如果任何人有一個解決方案,我仍然會採取更實際的解決方案!

0

下面是我在自己的地緣接近的計算成功地使用了一段時間的解決方案:

/** 
* 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 
*/ 
+0

感謝發佈代碼@Fleep。不幸的是,雖然它一般工作,我仍然結束了相同的缺少標記問題。雖然欣賞嘗試! – FourStacks 2012-01-07 13:38:11

2

編輯 這個位置取景器出現往往不夠,我已經寫在上面的文章。

http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

原貼

讓我們一度與haversine公式處理所有,是放入存儲函數開始,所以我們可以瞭解它的粗糙細節忘了。注:這整個解決方案是在法定英里。

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 
"; 

請注意關於此的一些事情。

首先,它在SQL中而不是在PHP中進行邊界框計算。除了將所有計算都保存在一個環境中之外,沒有什麼好的理由。 (radius/69)radius法定里程數的度數。

其次,它沒有根據緯度擺弄縱向邊界框的大小。相反,它使用了一個更簡單但稍大的邊界框。這個邊界框會捕獲一些額外的記錄,但是距離測量會消除它們。對於典型的郵政編碼/商店查找應用程序,性能差異可以忽略不計。如果你正在搜索更多的記錄(例如所有電線杆的數據庫),它可能並不那麼微不足道。

第三,它使用嵌套查詢來執行距離消除,以避免必須爲每個項目運行多次距離函數。

第四,它按距離ASCENDING命令。這意味着您的零距離結果應該首先顯示在結果集中。通常首先列出最近的事物是有意義的。

第五,它始終使用FLOAT而不是DOUBLE。這有一個很好的理由。半直角距離公式並不完美,因爲它使地球是一個完美球體的近似值。該近似值恰好與FLOAT數字的epsilon的精確度大致相同。所以DOUBLE是這個問題的欺騙性數字矯枉過正。 (不要使用這種半馬賽克配方來做停車場排水等土木工程工作,否則你會得到大的水坑,幾英寸深,我承諾)。這對商店查找應用程序來說很好。

第六,你一定要爲你的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); 
+0

感謝您的非常詳細的答覆Ollie。在實現它來測試它時遇到了一些麻煩。對不起,我很痛苦,但我對MySQL存儲過程並不熟悉(我可以用PhpMyAdmin來解決MySQL數據庫問題,但這是我的MySQL知識枯竭的地方)。看看有關使用此GUI創建存儲函數的一些教程,但沒有找到任何指導我完成的教程。這個格式的公式是以這種格式存在,還是可以包含在其餘的php查詢中? – FourStacks 2012-01-07 13:44:51

+0

感謝您編輯Ollie - 感謝您將其轉換爲php以及迄今爲止所有的幫助。不幸的是,它似乎沒有產生任何ID值。加倍檢查了代碼,以確保我沒有做任何愚蠢的事情 - 看不到任何東西。也嘗試了您在sprintf中更改類型說明符的建議,但沒有任何更改。不知道代碼中的小寫'a'是有意的還是錯字,但是無論如何它都沒有產生任何結果。 – FourStacks 2012-01-08 17:54:00

+0

我已經授予這個答案的賞金,因爲它是最完整和充分解釋的答案(儘管最終沒有真正使用它 - 請參閱我自己的答案,下面我現在滾動)。這就是說這個問題產生了一些非常好的答案,我相信下面列出的公式可能適用於其他人不同設置的項目,因此它們都值得一看。 – FourStacks 2012-01-13 12:05:19

0

你可以試試我的課程http://www.phpclasses.org/package/6202-PHP-Generate-points-of-an-Hilbert-curve.html。它使用harvesine公式和希爾伯特曲線來計算quadkey。然後您可以從左至右搜索quadkey。鍵的每個位置都是怪物曲線上的一個點。 Nick的空間索引四叉樹希爾伯特曲線博客可以找到更好的曲線解釋。這就像使用mysql的空間索引擴展,但你有更多的控制。您可以使用z曲線或摩爾曲線,也可以更改外觀。

0

這是從工作生產系統的代碼,

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 

使用不同的距離表現公式,但是對於一個商店定位器的差異是最小的。