MySQL空间数据库–查询点到多点间的最短路径

20 五月, 2011 (10:05) | 存储, 性能, 数据库 繁体 English    DeliciOus    分享到新浪微博
作者: H.E. | 您可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
网址: http://www.javabloger.com/article/mysql-spatial-database.html
豆瓣读书 向你推荐有关 存储性能数据库、 类别的图书。

当SNS产品加入LBS的技术将会让移动互联网领域更加丰富多彩,例如:大众点评,街旁,盛大切客 这些运行在智能手机端的应用,当用户拿出手机就可以根据你当前的所在地向你推荐一些有用的信息,例如:附近的美食,商铺,周边生活信息,等。

攻城师们,你有没有想过这些应用背后的技术实现呢?手机端获得当前的坐标后是怎么进行计算和查询返回附件的结果呢?

用Java程序可以实现Dijkstra算法获得点与多点之间最短路径的计算结果,但是我个人认为是一种暴力的方法,开发的简化程度和计算的执行效率不会非常高。
参考资料:http://baike.baidu.com/view/7839.htm

接着再往下想,用到数据库技术是必然,但不会把节点的坐标信息存储到数据库普通的字段中进行查询,如果和Dijkstra算法相比不会简化工作量也不会提高性能,但使用到MySQL中空间数据库的概念就会简化很多也会得到性能的提升,开源的MySQL Spatial空间索引机制就可以对点到多点之间的距离计算,类似的Spatial Database还有,PostGIS,SpatiaLite。

我的废话:
在android手机上获得当前坐标后,将数据整好录入android中的SQLite数据库也可以获得当前点对多点的最短路径,也就是说在地理数据不会更新的场景下完全可以采用android手机上的数据库完成这项工作,没有必要非要利用服务器端的Spatial Database完成最短路径的计算。

MySQL空间数据几种主要类型:
     – GEOMETRY  Geometry是层次结构的根类。它是一种非实例化类,但具有很多属性,这些属性对由任何Geometry子类创建的所有几何值来说是共同的。
     – POINT   代表坐标空间中单个位置的几何类,他的属性包含 X-坐标值,Y-坐标值。
     – LINESTRING  具有线段的坐标,由每个连续的点对(两点)定义。如果仅包含两点,LineString为Line。 如果它既是简单的也是封闭的,LineString为LinearRing。
     – POLYGON  它由单个外部边界以及0或多个内部边界定义,其中,每个内部边界定义为Polygon中的1个孔。例如:在地区地图上,Polygon对象可表示森林。
     – MULTIPOINT  MultiPoint是一种由Point元素构成的几何对象集合。这些点未以任何方式连接或排序。
     – MULTILINESTRING  MultiLineString是一种由 LineString元素构成的MultiCurve几何对象集合,例如:河流体系或高速路系统。
     – MULTIPOLYGON  MultiPolygon是一种由Polygon元素构成的几何对象集合。在地区地图上,MultiPolygon可表示湖泊系统。
     – GEOMETRYCOLLECTION   他是由1个或多个任意类几何对象构成的几何对象。GeometryCollection中的所有元素必须具有相同的空间参考系(即相同的坐标系).
以上几种的类型依赖关系,如图所示:
xyz

了解过上述一些基本知识,下面来创建一张商户表,并且包含定义的空间数据库的POINT字段:
  Create table shop (
     shop_id int(3) primary key,
     Location POINT,
     Shop_na vachar(100),
     Shop_info vachar(300)
     );

插入几条商家的门店信息,其中采用GeomFromText方法将坐标的数据库插入POINT字段中,例如:
insert into shop values (‘XXX’,’,GeomFromText(‘POINT(1 1)’),’XX店’,’ '其他信息');
下面将根据客户当前所在位置在MySQL中查询,搜索出在当前位置附近的一定范围内的门店,并且可以做到按距离由近到远排列显示出来,从让用户而找到离他最近的门店。
把客户当前所在位置可设成变量 ,例如:set @center=GeomFromText(‘POINT(10 10)’);

再把要找到最近门店可以缩小搜索范围 设半径,添加搜索条件
例:set @radius=30;
WHERE SQRT(POW( ABS( X(location) – X(@center)), 2) + POW( ABS(Y(location) – Y(@center)), 2 )) < @radius

最近门店搜索,完整的SQL示例:
SELECT shop_id,shop_na, SQRT(POW( ABS( X(Location) – X(@center)), 2) + POW(ABS(Y(Location) – Y(@center)), 2 )) AS distance
FROM shop WHERE SQRT(POW( ABS( X(location) – X(@center)), 2) + POW( ABS(Y(location) – Y(@center)), 2 )) < @radius
order by distance;

其中涉及的数学函数SQRT(x):表示求一个数x的平方根。POW(x,y):包含两个参数表示求x的y次幂。ABS(x):表示求数X的绝对值。整个SQRT(POW( ABS( X(Location) – X(@center)), 2) + POW(ABS(Y(Location) – Y(@center)), 2 ))这个SQL语句实现的是一个算术表达式
http://public.bay.livefilestore.com/y1po7ENYXgBlsmmLKp2_WlYd_iiXZhsAAIyqniUqqAkWrJYinExgS5_YBDIcI_vwVg8AEe5Fjh0NLwvbWlAapZpIA/x_y_z_1.png?psid=1
即两点间的直线距离。
比如说现在有两个点坐标A(x1,y1),B(x2,y2) 要求线段AB长度 就是用http://public.bay.livefilestore.com/y1po7ENYXgBlsmmLKp2_WlYd_iiXZhsAAIyqniUqqAkWrJYinExgS5_YBDIcI_vwVg8AEe5Fjh0NLwvbWlAapZpIA/x_y_z_1.png?psid=1这个公式去计算。把A看成当前位置B看成一个门店,不就是相当于计算当前位置到门店这两个点的距离吗。坐标点有了带进去就行,等于现在只要能用函数把这个公式表示出来就可以了。
所以用到这三个函数:
SQRT(x):表示求一个数x的平方根。就相当于那个根号。√x
POW(x,y):包含两个参数表示求x的y次幂
例如pow(2,3)就表示23,那么POW((X1-X2),2)就相当于〖(x1-x2)〗^2
ABS(x):表示求数X的绝对值。|x|  ABS(x1-x2)就等于|x1-x2|.

根据那个公式组合起来就行了
整个SQRT(POW( ABS( X(Location) – X(@center)), 2) + POW(ABS(Y(Location) – Y(@center)), 2))这句话就是用来表示这个公式的
http://public.bay.livefilestore.com/y1pM_5Xtwtl4QeSaP8qXtHUJyDToYypy1K3UmyZVxM_6_E64Xad_C0AlmQDWWE_ncb8ap6FRZfjQX2jWD4eGJMe8w/x_y_z_2.png?psid=1,
这个公式计算得出来的值就是两点间的直线距离。

参考资料:
http://dev.mysql.com/doc/refman/5.1/zh/spatial-extensions-in-mysql.html
http://en.wikipedia.org/wiki/Spatial_database

口水:
 以上部分内容来自 NJ-AMT 实习生余珊的分析报告。

–end–

豆瓣读书  向你推荐有关 存储 性能 数据库、 类别的图书。



Creative Commons License
本文由J2ee企业顾问-黄毅创作,并已采用创作共用署名2.5中国大陆版许可证授权。

评论

Comment from 904943950
Time 2011年05月21日 at 9:19 上午

晕,太难了,大学函数都出来了…

Comment from 煮稀饭
Time 2011年05月22日 at 9:11 上午

我还是来晚了,写的不错真好啊

Comment from 健尔马
Time 2011年05月22日 at 9:15 上午

还好吧,如果文章长点就更好了

Comment from 真味
Time 2011年05月24日 at 12:51 下午

搜索过来的,这个还是挺有帮助的,谢了

Comment from imbiss
Time 2011年05月27日 at 6:55 下午

我用过mysql 的Geometry地理函数,最后在实际中放弃了,主要是效率问题。
我的解决办法是直接把lat lng信息录入数据库,
然后比较这两个参数即可。缺点是,地图模型是方形的,而不是圆形的。按照半径搜索比圆形模型大一些。
例子 http://www.raumobil.de

Comment from Ken
Time 2011年05月29日 at 8:05 下午

我觉得确实是这样的,写的不错。

Comment from 锦州客
Time 2011年05月30日 at 1:56 上午

好久没来了,继续支持大大

Comment from 姓名
Time 2011年07月8日 at 10:11 下午

如果只有100条左右的数据,这种查询是可行的,稍大一点的数据,这种方式会导致效率极低,支持imbiss 的做法,先以中心点+半径算出一个bounds,然后判断坐标点是否和bounds有intersection的关系。

Comment from millken
Time 2011年08月12日 at 11:20 上午

做过类似服务,不过后来还是放弃mysql的point来存储数据,效率实在不行。

用mongo吧,方便且高效。

Comment from zuaa
Time 2011年10月24日 at 5:25 下午

这样子的计算的结果和实际的结果还是有比较大的误差,可以尝试结合solr的位置感知搜索

Comment from being
Time 2012年01月12日 at 3:26 下午

为啥不用MySQL的空间距离计算函数distance(g1, g2)?

评论

评论也是有版权的!




3260