经度/纬度find最接近的经度/纬度 – 复杂的SQL或复杂的计算

我有经纬度,我想从距离最近的纬度和经度的数据库中提取logging,如果这个距离比指定的距离长,那么不检索它。

表结构:

id latitude longitude place name city country state zip sealevel 

您需要将距离转换为经度和纬度,然后根据那些大致位于边界框中的条目进行过滤,然后执行更精确的距离filter。 这里有很好的论文解释了如何做到这一点:

http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

 SELECT latitude, longitude, SQRT( POW(69.1 * (latitude - [startlat]), 2) + POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance FROM TableName HAVING distance < 25 ORDER BY distance; 

其中[starlat][startlng]是开始测量距离的位置。

Google的解决scheme:

创build表

当你创buildMySQL表时,你要特别注意lat和lng属性。 利用Google地图目前的缩放function,您只需要精确到小数点后的6位数字。 为了将表格所需的存储空间保持在最小,可以指定lat和lng属性是大小为(10,6)的浮点数。 这将使字段存储小数点后六位数字,再加上小数点前四位数字,例如-123.456789度。 你的表也应该有一个id属性作为主键。

 CREATE TABLE `markers` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , `name` VARCHAR( 60 ) NOT NULL , `address` VARCHAR( 80 ) NOT NULL , `lat` FLOAT( 10, 6 ) NOT NULL , `lng` FLOAT( 10, 6 ) NOT NULL ) ENGINE = MYISAM ; 

填充表格

创build表之后,是时候用数据填充它了。 下面提供的样本数据是分散在美国的大约180个比萨饼。 在phpMyAdmin中,可以使用IMPORT选项卡导入各种文件格式,包括CSV(逗号分隔值)。 Microsoft Excel和Google Spreadsheet都导出为CSV格式,因此您可以通过导出/导入CSV文件轻松地将数据从电子表格传输到MySQL表格。

 INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Frankie Johnnie & Luigo Too','939 W El Camino Real, Mountain View, CA','37.386339','-122.085823'); INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Amici\'s East Coast Pizzeria','790 Castro St, Mountain View, CA','37.38714','-122.083235'); INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Kapp\'s Pizza Bar & Grill','191 Castro St, Mountain View, CA','37.393885','-122.078916'); INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Mountain View','570 N Shoreline Blvd, Mountain View, CA','37.402653','-122.079354'); INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Tony & Alba\'s Pizza & Pasta','619 Escuela Ave, Mountain View, CA','37.394011','-122.095528'); INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Oregano\'s Wood-Fired Pizza','4546 El Camino Real, Los Altos, CA','37.401724','-122.114646'); 

使用MySQL查找位置

要查找标记表中位于给定经度/纬度的特定半径范围内的位置,可以使用基于Haversine公式的SELECT语句。 Haversine公式通常用于计算球体上两对坐标之间的大圆距离。 维基百科给出了一个深入的math解释,并且与编程有关的公式的一个很好的讨论在Movable Type的网站上。

下面是SQL语句,它将查找距离37,-122坐标25英里范围内最近的20个位置。 它根据该行的纬度/经度和目标纬度/经度计算距离,然后仅要求距离值小于25的行,按距离sorting整个查询,并将其限制为20个结果。 以公里而不是英里search,用6371代替3959。

 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; 

https://developers.google.com/maps/articles/phpsqlsearch_v3#creating-the-map

这里是我用PHP实现的完整解决scheme。

此解决scheme使用http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL中介绍的Haversine公式。;

应该指出的是,Haversine公式在极点周围存在弱点。 这个答案显示如何实现vincenty大圆距离公式来解决这个问题,但是我select了使用Haversine,因为它足够我的目的。

我将纬度存储为DECIMAL(10,8),将经度存储为DECIMAL(11,8)。 希望这有助于!

showClosest.php

 <?PHP /** * Use the Haversine Formula to display the 100 closest matches to $origLat, $origLon * Only search the MySQL table $tableName for matches within a 10 mile ($dist) radius. */ include("./assets/db/db.php"); // Include database connection function $db = new database(); // Initiate a new MySQL connection $tableName = "db.table"; $origLat = 42.1365; $origLon = -71.7559; $dist = 10; // This is the maximum distance (in miles) away from $origLat, $origLon in which to search $query = "SELECT name, latitude, longitude, 3956 * 2 * ASIN(SQRT( POWER(SIN(($origLat - latitude)*pi()/180/2),2) +COS($origLat*pi()/180 )*COS(latitude*pi()/180) *POWER(SIN(($origLon-longitude)*pi()/180/2),2))) as distance FROM $tableName WHERE longitude between ($origLon-$dist/cos(radians($origLat))*69) and ($origLon+$dist/cos(radians($origLat))*69) and latitude between ($origLat-($dist/69)) and ($origLat+($dist/69)) having distance < $dist ORDER BY distance limit 100"; $result = mysql_query($query) or die(mysql_error()); while($row = mysql_fetch_assoc($result)) { echo $row['name']." > ".$row['distance']."<BR>"; } mysql_close($db); ?> 

./assets/db/db.php

 <?PHP /** * Class to initiate a new MySQL connection based on $dbInfo settings found in dbSettings.php * * @example $db = new database(); // Initiate a new database connection * @example mysql_close($db); // close the connection */ class database{ protected $databaseLink; function __construct(){ include "dbSettings.php"; $this->database = $dbInfo['host']; $this->mysql_user = $dbInfo['user']; $this->mysql_pass = $dbInfo['pass']; $this->openConnection(); return $this->get_link(); } function openConnection(){ $this->databaseLink = mysql_connect($this->database, $this->mysql_user, $this->mysql_pass); } function get_link(){ return $this->databaseLink; } } ?> 

./assets/db/dbSettings.php

 <?php $dbInfo = array( 'host' => "localhost", 'user' => "root", 'pass' => "password" ); ?> 

通过使用上面提到的“Geo-Distance-Search-with-MySQL”文章所build议的MySQL存储过程,可以提高性能。

我有一个约17,000个地方的数据库和查询执行时间为0.054秒。

以防万一你像我一样懒,这里有一个从这个和其他答案合并的解决scheme。

 set @orig_lat=37.46; set @orig_long=-122.25; set @bounding_distance=1; SELECT * ,((ACOS(SIN(@orig_lat * PI() / 180) * SIN(`lat` * PI() / 180) + COS(@orig_lat * PI() / 180) * COS(`lat` * PI() / 180) * COS((@orig_long - `long`) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS `distance` FROM `cities` WHERE ( `lat` BETWEEN (@orig_lat - @bounding_distance) AND (@orig_lat + @bounding_distance) AND `long` BETWEEN (@orig_long - @bounding_distance) AND (@orig_long + @bounding_distance) ) ORDER BY `distance` ASC limit 25; 

容易的一个;)

 SELECT * FROM `WAYPOINTS` W ORDER BY ABS(ABS(W.`LATITUDE`-53.63) + ABS(W.`LONGITUDE`-9.9)) ASC LIMIT 30; 

只需将坐标replace为您所需的坐标即可。 值必须以doubleforms存储。 这是一个正在运行的MySQL 5.x示例。

干杯

你正在寻找像沙丁胺那样的东西。 也看到这里 。

还有其他的,但这是最常被引用的。

如果您正在寻找更强大的function,您可能需要查看数据库GISfunction。 他们能够做出一些很酷的事情,例如告诉你一个点(城市)是否出现在给定的多边形(地区,国家,大陆)内。

试试这个,它显示最近的点提供的坐标(50公里内)。 它完美的作品:

 SELECT m.name, m.lat, m.lon, p.distance_unit * DEGREES(ACOS(COS(RADIANS(p.latpoint)) * COS(RADIANS(m.lat)) * COS(RADIANS(p.longpoint) - RADIANS(m.lon)) + SIN(RADIANS(p.latpoint)) * SIN(RADIANS(m.lat)))) AS distance_in_km FROM <table_name> AS m JOIN ( SELECT <userLat> AS latpoint, <userLon> AS longpoint, 50.0 AS radius, 111.045 AS distance_unit ) AS p ON 1=1 WHERE m.lat BETWEEN p.latpoint - (p.radius / p.distance_unit) AND p.latpoint + (p.radius / p.distance_unit) AND m.lon BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint)))) AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint)))) ORDER BY distance_in_km 

只要更改<table_name><userLat><userLon>

你可以在这里阅读更多关于这个解决scheme: http : //www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

根据文章Geo-Distance-Search-with-MySQL检查这段代码:

例如:find距我目前位置10英里的最近的10家酒店:

 #Please notice that (lat,lng) values mustn't be negatives to perform all calculations set @my_lat=34.6087674878572; set @my_lng=58.3783670308302; set @dist=10; #10 miles radius SELECT dest.id, dest.lat, dest.lng, 3956 * 2 * ASIN(SQRT(POWER(SIN((@my_lat -abs(dest.lat)) * pi()/180 / 2),2) + COS(@my_lat * pi()/180 ) * COS(abs(dest.lat) * pi()/180) * POWER(SIN((@my_lng - abs(dest.lng)) * pi()/180 / 2), 2)) ) as distance FROM hotel as dest having distance < @dist ORDER BY distance limit 10; #Also notice that distance are expressed in terms of radius. 
 simpledb.execSQL("CREATE TABLE IF NOT EXISTS " + tablename + "(id INTEGER PRIMARY KEY AUTOINCREMENT,lat double,lng double,address varchar)"); simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2891001','70.780154','craftbox');"); simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2901396','70.7782428','kotecha');");//22.2904718 //70.7783906 simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2863155','70.772108','kkv Hall');"); simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.275993','70.778076','nana mava');"); simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2667148','70.7609386','Govani boys hostal');"); double curentlat=22.2667258; //22.2677258 double curentlong=70.76096826;//70.76096826 double curentlat1=curentlat+0.0010000; double curentlat2=curentlat-0.0010000; double curentlong1=curentlong+0.0010000; double curentlong2=curentlong-0.0010000; try{ Cursor c=simpledb.rawQuery("select * from '"+tablename+"' where (lat BETWEEN '"+curentlat2+"' and '"+curentlat1+"') or (lng BETWEEN '"+curentlong2+"' and '"+curentlong1+"')",null); Log.d("SQL ", c.toString()); if(c.getCount()>0) { while (c.moveToNext()) { double d=c.getDouble(1); double d1=c.getDouble(2); } } } catch (Exception e) { e.printStackTrace(); } 

这听起来像你想做一个最近的邻居search与一些距离的约束。 就我所知,SQL不支持这样的任何事情,而且您需要使用替代数据结构,例如R树或kd树 。

find最近的用户我的:

以米为单位的距离

基于Vincenty的公式

我有用户表:

 +----+-----------------------+---------+--------------+---------------+ | id | email | name | location_lat | location_long | +----+-----------------------+---------+--------------+---------------+ | 13 | xxxxxx@xxxxxxxxxx.com | Isaac | 17.2675625 | -97.6802361 | | 14 | xxxx@xxxxxxx.com.mx | Monse | 19.392702 | -99.172596 | +----+-----------------------+---------+--------------+---------------+ 

SQL:

 -- my location: lat 19.391124 -99.165660 SELECT (ATAN( SQRT( POW(COS(RADIANS(users.location_lat)) * SIN(RADIANS(users.location_long) - RADIANS(-99.165660)), 2) + POW(COS(RADIANS(19.391124)) * SIN(RADIANS(users.location_lat)) - SIN(RADIANS(19.391124)) * cos(RADIANS(users.location_lat)) * cos(RADIANS(users.location_long) - RADIANS(-99.165660)), 2) ) , SIN(RADIANS(19.391124)) * SIN(RADIANS(users.location_lat)) + COS(RADIANS(19.391124)) * COS(RADIANS(users.location_lat)) * COS(RADIANS(users.location_long) - RADIANS(-99.165660)) ) * 6371000) as distance, users.id FROM users ORDER BY distance ASC 

地球半径:6371000(米)

听起来你应该只使用PostGIS,SpatialLite,SQLServer2008或Oracle Spatial。 他们都可以用空间SQL为你解答这个问题。

您应该尝试以下内容: http : //en.wikipedia.org/wiki/Great-circle_distance http://code.google.com/apis/maps/articles/phpsqlsearch.html

在极端的情况下,这种方法失败了,但是为了performance,我跳过了三angular学并简单地计算了对angular线的平方。

这个问题并不是非常困难,但是如果你需要优化,这个问题会变得更加复杂。

我的意思是,你的数据库中有100个位置,还是1亿? 这是一个很大的区别。

如果位置数量很less,只需要执行 – >,将它们从SQL中移出并放入代码中

 Select * from Location 

一旦你把它们变成代码,用Haversine公式计算每个经纬度和你原来的距离,并对它进行sorting。