1. 利用GeoHash封裝成內置數據庫函數的簡易方案;
A:Mysql 內置函數方案,適合於已有業務,新增加LBS功能,增加經緯度字段方可,避免數據遷移
B:Mongodb 內置函數方案,適合中小型應用,快速實現LBS功能,性能優於A(推薦)
方案A: (MySQL Spatial)
1、先簡歷一張表:(MySQL 5.0 以上 僅支持 MyISAM 引擎)
CREATE TABLE address ( address CHAR(80) NOT NULL, address_loc POINT NOT NULL, PRIMARY KEY(address) );
空間索引:
ALTER TABLE address ADD SPATIAL INDEX(address_loc);
插入數據:(注:此處Point(緯度,經度) 標准寫法)
INSERT INTO address VALUES('Foobar street 12', GeomFromText('POINT(30.620076 104.067221)')); INSERT INTO address VALUES('Foobar street 13', GeomFromText('POINT(31.720076 105.167221)'));
查詢: 查找(30.620076,104.067221)附近 10 公里
SELECT * FROM address WHERE MBRContains ( LineString ( Point ( 30.620076 + 10 / ( 111.1 / COS(RADIANS(104.067221))), 104.067221 + 10 / 111.1 ), Point ( 30.620076 - 10 / ( 111.1 / COS(RADIANS(104.067221))), 104.067221 - 10 / 111.1 ) ), address_loc )
方案B:
1、先建立一張簡單的表user,兩條數據如下:
{ "_id": ObjectId("518b1f1a83ba88ca60000001"), "account": "simplephp1@163.com", "gps": [ 104.067221, 30.620076 ] } { "_id": ObjectId("518b1dae83ba88d660000000"), "account": "simplephp6@163.com", "gps": [ 104.07958, 30.653936 ] }
其中,gps為二維數組,分別為經度,緯度
(注:此處必須按照(經度,緯度)順序存儲。我們平時表示經緯度,都是(緯度,精度),此處這種方式有木有很親民)
2、使用之前,先建立二維索引
//建立索引 最大范圍在經度-180~180
db.user.ensureIndex({"gps":"2d"},{"min":-180,"max":180})
//刪除索引
db.user.dropIndex({"gps":"2d"})
3、Mongodb有兩中方式可以查找附近的XXX;其中方案2)會返回距離(推薦)
1)標准查詢,為地球經緯度查詢內置;參數一為查詢條件利用$near查找附近,參數二$maxDistance為經緯弧度(1° latitude = 111.12 kilometers)即 1/111.12,表示查找附近一公里。
db.user.find({ gps :{ $near : [104.065847, 30.657554] , $maxDistance : 1/111.12} })
2)執行命名方式,模擬成一個圓球;參數一指定geoNear方式和表名;參數二坐標,參數三是否為球形,參數四弧度(弧度=弧長/半徑 一千米的弧度1000/6378000),參數五指定球形半徑(地球半徑)
db.runCommand({geoNear:'user', near:[104.065847, 30.657554], spherical:true, maxDistance:1000/6378000, distanceMultiplier:6378000});
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.
3959是地球半徑的英里,6371是地球半徑的千米:http://baike.baidu.com/view/758812.htm
SELECT id, ( 3959 * acos ( cos ( radians(78.3232) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(65.3234) ) + sin ( radians(78.3232) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 30 ORDER BY distance LIMIT 0 , 20;
This is using the Google Maps API v3 with a MySQL backend which your already have.
https://developers.google.com/maps/articles/phpsqlsearch_v3#findnearsql
轉自:http://gis.stackexchange.com/a/31629
3 其他
Anyways, here’s the PHP formula for calculating the distance between two points (along with Mile vs. Kilometer conversion) rounded to two decimal places:
function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'Mi') { $theta = $longitude1 - $longitude2; $distance = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta))); $distance = acos($distance); $distance = rad2deg($distance); $distance = $distance * 60 * 1.1515; switch($unit) { case 'Mi': break; case 'Km' : $distance = $distance * 1.609344; } return (round($distance,2)); }
It’s also possible to use MySQL to do a calculation to find all records within a specific distance. In this example, I’m going to query MyTable to find all the records that are less than or equal to variable $distance (in Miles) to my location at $latitude and $longitude:
$qry = "SELECT *,(((acos(sin((".$latitude."*pi()/180)) * sin((`Latitude`*pi()/180))+cos((".$latitude."*pi()/180)) * cos((`Latitude`*pi()/180)) * cos(((".$longitude."- `Longitude`)*pi()/180))))*180/pi())*60*1.1515) as distance FROM `MyTable` WHERE distance >= ".$distance."
For Kilometers:
$qry = "SELECT *,(((acos(sin((".$latitude."*pi()/180)) * sin((`Latitude`*pi()/180))+cos((".$latitude."*pi()/180)) * cos((`Latitude`*pi()/180)) * cos(((".$longitude."- `Longitude`)*pi()/180))))*180/pi())*60*1.1515*1.609344) as distance FROM `MyTable` WHERE distance >= ".$distance."