2013年5月12日星期日

MySQL和GIS半正矢公式,兩點之間的距離。

Original post: http://anothermysqldba.blogspot.com/2013/05/mysql-gis-haversine-formula-and.html


MySQL是沒有的,首先想到的,當人們認為的GIS數據庫。 下面列出的數據庫有:

  • 神諭
  • 微軟SQL Server
  • IBM DB2
  • IBM的Informix
  • PostgreSQL的

http://webhelp.esri.com/arcgisserver/9.3/java/index.htm#地理數據庫/ types_of_geodatabases.htm的
http://webhelp.esri.com/arcgisserver/9.3/java/index.htm#geodatabases/determ-1479045992.htm


MySQL已經被與GIS工作了一段時間,現在,它可以追溯到到MySQL 4.1:
馬克蒙德的職位上面是一個偉大的GIS看看。 本博客文章將更多的精力放在公式,但我想指出,馬克的好貼。

空間索引的使用MyISAM存儲引擎,所以,如果你是在MySQL 5.5或以上記住這一點,因為InnoDB是默認的存儲引擎。

> CREATE TABLE geom (
-> lat float(10,7) NOT NULL,
-> lon float(10,7) NOT NULL,
-> g GEOMETRY NOT NULL,
-> SPATIAL INDEX(g)
-> ) ENGINE=Innodb;
ERROR 1464 (HY000): The used table type doesn't support SPATIAL indexes
> CREATE TABLE geom (
-> lat float(10,7) NOT NULL,
-> lon float(10,7) NOT NULL,
-> g GEOMETRY NOT NULL,
-> SPATIAL INDEX(g)
-> ) ENGINE=MyISAM; 

因此,我與馬克的架構設計填充在中國一些城市的緯度/經度。
通過收集這些數據是這裡http://www.infoplease.com/ipa/A0001769.html

CREATE TABLE china (
cityname varchar(50) DEFAULT NULL,
lat float(10,7) NOT NULL,
lon float(10,7) NOT NULL,
g GEOMETRY NOT NULL,
SPATIAL INDEX(g)
) ENGINE=MyISAM; 

INSERT INTO china VALUES ('Beijing', 39.55, 116.25, GeomFromText('POINT(39.55 116.25)'));
INSERT INTO china VALUES ('Canton', 23.7, 113.15, GeomFromText('POINT(23.7 113.15)'));
INSERT INTO china VALUES ('Chongqing', 29.46, 106.34, GeomFromText('POINT(29.46, 106.34)'));
INSERT INTO china VALUES ('Hong Kong', 22.20, 114.11, GeomFromText('POINT( 22.20 114.11)'));
INSERT INTO china VALUES ('Shanghai', 31.10, 121.28, GeomFromText('POINT(31.10 121.28)'));

只是為了確保它的所有工作....

select lat, lon from china;
+------------+-------------+
| lat | lon |
+------------+-------------+
| 39.5499992 | 116.2500000 |
| 23.7000008 | 113.1500015 |
| 22.2000008 | 114.1100006 |
| 31.1000004 | 121.2799988 |
+------------+-------------+


因此,讓我們檢查從北京到香港的距離。
就讓我們來看看在網上告訴我們,它是1963公里,但讓我們檢查我們的數據。


現在是時間挖成的距離公式的世界.. 我不是一個地理信息系統重點DBA,我承認沒有問題。 我喜歡更多的答案我遇到數學公式的範圍內,否則我會使用以上人員表信息。 相反,它是為你只是一個參考。 我拿了看看在廣泛辯論的方式來計算兩個緯度/長點之間的距離。 你會發現很多不同的功能,程序等在線計算。

首先,我設置了一些變量,因為我想測試公式

SET @lat1 =39.55;
SET @long1 =116.25;
SET @lat2 =22.20;
SET @long2 =114.11; 

例如:

這個網站有一個距離的功能。 我已經加入以確保與分隔符選項在你的地方,你可以剪切和粘貼。 但它的工作原理? 順便說一句,我也更新了地球的半徑值3959。
http://www.sqlexamples.info/SPAT/mysql_distance.htm

delimiter //
CREATE FUNCTION fn_distance
(p_x1 FLOAT, p_y1 FLOAT, p_x2 FLOAT, p_y2 FLOAT)
RETURNS FLOAT
DETERMINISTIC
BEGIN
DECLARE v_dist FLOAT;
DECLARE A FLOAT; DECLARE B FLOAT;
DECLARE C FLOAT; DECLARE D FLOAT;
/*
returns distance calculation between two points in LAT-LONG coordinates
*/

SET v_dist = 0;

-- convert to radians
SET A = p_x1 / 57.29577951;
SET B = p_y1 / 57.29577951;
SET C = p_x2 / 57.29577951;
SET D = p_y2 / 57.29577951;

IF (A = C && B = D) THEN
SET v_dist = 0;
ELSEIF ((sin(A)*sin(C)+cos(A)*cos(C)*cos(B - D)) > 1) THEN
SET v_dist = 3959 * acos(1);
ELSE
SET v_dist = 3959 *acos(sin(A)*sin(C) + cos(A)*cos(C)*cos(B - D));
END IF;

SET v_dist = v_dist * 1.609;

/* return distance in km. */
RETURN v_dist;

END;
//
delimiter ;

> SELECT fn_distance (@lat1, @long1, @lat2 , @long2) AS dist_km;
+-------------------+
| dist_km |
+-------------------+
| 1939.5457763671875 |
+-------------------+

另一個查詢測試顯示 

> SELECT ( GLength( LineString(( PointFromWKB( POINT( @lat1, @long1 ))), ( PointFromWKB( POINT( @lat2, @long2 ) ))))) * 100 AS distance;
+--------------------+
| distance |
+--------------------+
| 1748.1478770401545 |
+--------------------+ 

然而,另一個在這裡找到http://www.posteet.com/view/1555 : 

set log_bin_trust_function_creators=TRUE;
DELIMITER |
CREATE FUNCTION GeoDistKM( lat1 FLOAT, lon1 FLOAT, lat2 FLOAT, lon2 FLOAT ) RETURNS float
BEGIN
DECLARE pi, q1, q2, q3 FLOAT;
DECLARE rads FLOAT DEFAULT 0;
SET pi = PI();
SET lat1 = lat1 * pi / 180;
SET lon1 = lon1 * pi / 180;
SET lat2 = lat2 * pi / 180;
SET lon2 = lon2 * pi / 180;
SET q1 = COS(lon1-lon2);
SET q2 = COS(lat1-lat2);
SET q3 = COS(lat1+lat2);
SET rads = ACOS( 0.5*((1.0+q1)*q2 - (1.0-q1)*q3) );
RETURN 6378.388 * rads;
END;
|
DELIMITER ;
select geodistkm(
 set log_bin_trust_function_creators=TRUE;
DELIMITER |
CREATE FUNCTION GeoDistKM( lat1 FLOAT, lon1 FLOAT, lat2 FLOAT, lon2 FLOAT ) RETURNS float
BEGIN
DECLARE pi, q1, q2, q3 FLOAT;
DECLARE rads FLOAT DEFAULT 0;
SET pi = PI();
SET lat1 = lat1 * pi / 180;
SET lon1 = lon1 * pi / 180;
SET lat2 = lat2 * pi / 180;
SET lon2 = lon2 * pi / 180;
SET q1 = COS(lon1-lon2);
SET q2 = COS(lat1-lat2);
SET q3 = COS(lat1+lat2);
SET rads = ACOS( 0.5*((1.0+q1)*q2 - (1.0-q1)*q3) );
RETURN 6378.388 * rads;
END;
|
DELIMITER ;
select geodistkm(
 @ LAT1,@龍1,@ LAT2龍 ) as distance;
+----------------------------------------+
| distance |
+----------------------------------------+
| 1942.0909423828125 |
+----------------------------------------+ 
) as distance;
+----------------------------------------+
| distance |
+----------------------------------------+
| 1942.0909423828125 |
+----------------------------------------+ 


然而,這仍然是錯誤的。 因為距離北京 ​​康景與他的1963公里1224.9英里,根據這個網站:http://www.timeanddate.com/worldclock/distances.html?n=102 。 因此,第一個和最後的結果是非常接近的。 

因此,浪費了大量的時間後,是的,我不得不承認,我還是想用數學公式,我可以很容易比較的結果。 

大圓距離d兩點之間的坐標{LAT1,lon1}和{LAT2,lon2}由下式給出: 
D = ACOS(SIN(LAT1)*罪(LAT2)+ COS(LAT1)* COS(LAT2)* COS(lon1 lon2)) 
> SELECT ACOS(
-> SIN(@lat1) * SIN(@lat2) + COS(@lat1) * COS(@lat2) * COS( @long1 - @long2 )
-> ) *1000 as Aviation_forumula_DISTANCE;
+----------------------------+
| Aviation_forumula_DISTANCE |
+----------------------------+
| 1923.0473470093848 |
+----------------------------+


另一個公式是半正矢公式 
> SELECT 3956* 2 * ASIN ( SQRT (POWER(SIN((@lat1 - @lat2)*pi()/180 / 2),2) + COS(@lat1 * pi()/180) * COS(@lat2 *pi()/180) * POWER(SIN((@long1 - @long2) *pi()/180 / 2), 2) ) ) as Haversine_Formula_distance;
+----------------------------+
| Haversine_Formula_distance |
+----------------------------+
| 1204.5222518763514 |
+----------------------------+ 

你又一次看到非常不同的結果。 半正矢 公式預期更好的結果 
所以上場後我就去與 GeoDistKM 功能與這些公式,因為它似乎我是最接近的 正矢 公式,我認為是正確的公式時使用的正確實施。 其次是密切的第二航空公式,我寫的基於威廉姆斯信息。 

雖然1942年不是1963年的結果,我通過網上搜索收集,誰是說,他們計算正確。 地球的曲率和緯度,離子在兩極緊密聯繫起來,將允許一些不同的公式中的錯誤。 所以我會堅持目前這個: 

select geodistkm( @ LAT1,@龍1,@ LAT2龍 ) as distance;
+----------------------------------------+
| distance |
+----------------------------------------+
| 1942.0909423828125 |
+----------------------------------------+
 ) as distance;
+----------------------------------------+
| distance |
+----------------------------------------+
| 1942.0909423828125 |
+----------------------------------------+