天天看點

Geo Distance Search with MySQL Presentation

1. 利用geohash封裝成内置資料庫函數的簡易方案;

a:mysql 内置函數方案,适合于已有業務,新增加lbs功能,增加經緯度字段方可,避免資料遷移

b:mongodb 内置函數方案,适合中小型應用,快速實作lbs功能,性能優于a(推薦)

方案a: (mysql spatial)

1、先履歷一張表:(mysql 5.0 以上 僅支援 myisam 引擎)

<code>1</code>

<code>create</code> <code>table</code> <code>address (</code>

<code>2</code>

<code> </code> 

<code>3</code>

<code>    </code><code>address </code><code>char</code><code>(80) </code><code>not</code> <code>null</code><code>,</code>

<code>4</code>

<code>5</code>

<code>    </code><code>address_loc point </code><code>not</code> <code>null</code><code>,</code>

<code>6</code>

<code>7</code>

<code>    </code><code>primary</code> <code>key</code><code>(address)</code>

<code>8</code>

<code>9</code>

<code>);</code>

空間索引:

<code>alter</code> <code>table</code> <code>address </code><code>add</code> <code>spatial </code><code>index</code><code>(address_loc);</code>

插入資料:(注:此處point(緯度,經度) 标準寫法)

<code>insert</code> <code>into</code> <code>address </code><code>values</code><code>(</code><code>'foobar street 12'</code><code>, geomfromtext(</code><code>'point(30.620076 104.067221)'</code><code>));</code>

<code>insert</code> <code>into</code> <code>address </code><code>values</code><code>(</code><code>'foobar street 13'</code><code>, geomfromtext(</code><code>'point(31.720076 105.167221)'</code><code>));</code>

查詢: 查找(30.620076,104.067221)附近 10 公裡

<code>01</code>

<code>select</code>  <code>*</code>

<code>02</code>

<code>    </code><code>from</code>    <code>address</code>

<code>03</code>

<code>    </code><code>where</code>   <code>mbrcontains</code>

<code>04</code>

<code>                    </code><code>(</code>

<code>05</code>

<code>                    </code><code>linestring</code>

<code>06</code>

<code>                            </code><code>(</code>

<code>07</code>

<code>                            </code><code>point</code>

<code>08</code>

<code>                                    </code><code>(</code>

<code>09</code>

<code>                                    </code><code>30.620076 + 10 / ( 111.1 / cos(radians(104.067221))),</code>

<code>10</code>

<code>                                    </code><code>104.067221 + 10 / 111.1</code>

<code>11</code>

<code>                                    </code><code>),</code>

<code>12</code>

<code>13</code>

<code>14</code>

<code>                                    </code><code>30.620076 - 10 / ( 111.1 / cos(radians(104.067221))),</code>

<code>15</code>

<code>                                    </code><code>104.067221 - 10 / 111.1</code>

<code>16</code>

<code>                                    </code><code>)</code>

<code>17</code>

<code>                            </code><code>),</code>

<code>18</code>

<code>                    </code><code>address_loc</code>

<code>19</code>

<code>                    </code><code>)</code>

方案b:

1、先建立一張簡單的表user,兩條資料如下:

<code>{</code>

<code>  </code><code>"_id"</code><code>: objectid(</code><code>"518b1f1a83ba88ca60000001"</code><code>),</code>

<code>  </code><code>"account"</code><code>: </code><code>"[email protected]"</code><code>,</code>

<code>  </code><code>"gps"</code><code>: [</code>

<code>    </code><code>104.067221,</code>

<code>    </code><code>30.620076</code>

<code>  </code><code>]</code>

<code>}</code>

<code>  </code><code>"_id"</code><code>: objectid(</code><code>"518b1dae83ba88d660000000"</code><code>),</code>

<code>  </code><code>"account"</code><code>: </code><code>"[email protected]"</code><code>,</code>

<code>    </code><code>104.07958,</code>

<code>    </code><code>30.653936</code>

其中,gps為二維數組,分别為經度,緯度

(注:此處必須按照(經度,緯度)順序存儲。我們平時表示經緯度,都是(緯度,精度),此處這種方式有木有很親民)

2、使用之前,先建立二維索引

//建立索引 最大範圍在經度-180~180

<code>db.user.ensureindex({</code><code>"gps"</code><code>:</code><code>"2d"</code><code>},{</code><code>"min"</code><code>:-180,</code><code>"max"</code><code>:180})</code>

//删除索引

<code>db.user.dropindex({</code><code>"gps"</code><code>:</code><code>"2d"</code><code>})</code>

3、mongodb有兩中方式可以查找附近的xxx;其中方案2)會傳回距離(推薦)

1)标準查詢,為地球經緯度查詢内置;參數一為查詢條件利用$near查找附近,參數二$maxdistance為經緯弧度(1° latitude = 111.12 kilometers)即 1/111.12,表示查找附近一公裡。

<code>db.user.find({ gps :{ $near : [104.065847, 30.657554] , $maxdistance : 1/111.12} })</code>

2)執行命名方式,模拟成一個圓球;參數一指定geonear方式和表名;參數二坐标,參數三是否為球形,參數四弧度(弧度=弧長/半徑 一千米的弧度1000/6378000),參數五指定球形半徑(地球半徑)

<code>db.runcommand({geonear:</code><code>'user'</code><code>, near:[104.065847, 30.657554], spherical:</code><code>true</code><code>, maxdistance:1000/6378000, distancemultiplier:6378000});</code>

2 利用谷歌方案

the sql statement that will find the closest 20 locations that are within a radius of 30 miles to the 78.3232, 65.3234 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 30 miles, orders the whole query by distance, and limits it to 20 results. to search by kilometers instead of miles, replace 3959 with 6371.

<code>select</code>

<code>  </code><code>id, (</code>

<code>    </code><code>3959 * acos (</code>

<code>      </code><code>cos ( radians(78.3232) )</code>

<code>      </code><code>* cos( radians( lat ) )</code>

<code>      </code><code>* cos( radians( lng ) - radians(65.3234) )</code>

<code>      </code><code>+ sin ( radians(78.3232) )</code>

<code>      </code><code>* sin( radians( lat ) )</code>

<code>    </code><code>)</code>

<code>  </code><code>) </code><code>as</code> <code>distance</code>

<code>from</code> <code>markers</code>

<code>having</code> <code>distance &lt; 30</code>

<code>order</code> <code>by</code> <code>distance</code>

<code>limit 0 , 20;</code>

this is using the google maps api v3 with a mysql backend which your already have.

<a href="https://developers.google.com/maps/articles/phpsqlsearch_v3#findnearsql">https://developers.google.com/maps/articles/phpsqlsearch_v3#findnearsql</a>