使用空间点类型在MySQL中存储经纬度值

38

使用的技术:MySQL 5.1 和 PHP 5.3

我正在为网站设计一个新的数据库。我正在考虑最好的方法来存储经纬度值。

过去,我一直使用 DECIMAL 并使用 PHP/MySQL select 的形式:

SQRT(POW(69.1 * (fld_lat - ( $lat )), 2) + POW(69.1 * (($lon) - fld_lon) * COS(fld_lat / 57.3 ), 2 )) AS distance

寻找最近匹配的地点。

开始阅读更多新技术,我在想是否应该使用空间扩展。http://dev.mysql.com/doc/refman/5.1/en/geometry-property-functions.html

然而,相关信息相当稀少,我有一个关于如何存储数据的问题。是否可以使用 POINT 作为数据类型而不是 DECIMAL?

此外,一旦存储为 POINT,如果我想在地图上绘制它,是否很容易从中获取纬度和经度值,还是我还需要将纬度和经度再次存储为 DECIMALS?

虽然这里的大多数帖子都说我应该使用 PostGIS,但我不想学习一个新的数据库!

跟进问题:

我已经尝试使用新的 POINT 类型。我已经能够使用以下方式添加纬度和经度值:

INSERT INTO spatialTable (placeName, geoPoint) VALUES( "London School of Economics", GeomFromText( 'POINT(51.514 -0.1167)' ));

我可以使用以下代码从数据库中获取纬度和经度数值:

SELECT X(geoPoint), Y(geoPoint) FROM spatialTable;

看起来一切都很好,但我需要解决距离计算的问题。显然MySQL有一个距离函数的占位符,但要等一段时间才会发布。在一些帖子中,我发现需要做类似下面的事情,但我认为我的代码略有问题:

SELECT
 placeName,
 ROUND(GLength(
  LineStringFromWKB(
   LineString(
    geoPoint, 
    GeomFromText('POINT(52.5177, -0.0968)')
  )
  )
))
AS distance
FROM spatialTable
ORDER BY distance ASC;
在这个例子中,geoPoint是使用上面的INSERT命令输入到数据库中的一个点。 GeomFromText('POINT(52.5177, -0.0968)' 是我想要计算距离的纬度和经度值。
更多后续:
我有点愚蠢地只把SQL语句中的ROUND部分放了进去,没有仔细想。把它拿掉后给我的结果是:
SELECT
placeName,
(GLength(
LineStringFromWKB(
  LineString(
    geoPoint, 
    GeomFromText('POINT(51.5177 -0.0968)')
  )
 )
))
AS distance
FROM spatialTable
ORDER BY distance ASC

这似乎给了我需要的正确距离。

我想目前唯一需要回答的问题是,是否我现在使用Spatial会让自己变得更加困难,还是为未来考虑...


2
未来证明:[你不需要它] (http://c2.com/xp/YouArentGonnaNeedIt.html) - Seth
非常有用和有益的信息。 - CodeChap
4
不,你确实需要它。如果你想使用MySQL并(快速)搜索靠近某个点的位置,则需要使用空间索引。 - ypercubeᵀᴹ
距离是以哪个单位计算的? - Nícolas Iensen
是哪个单位计算了距离? - Krunal Limbad
4个回答

8
我认为您应该尽可能使用最高级别的抽象。 如果您的数据是地理空间数据,则应使用地理空间对象。

但要小心,Mysql是最糟糕的地理空间数据库。对于点来说还可以,但它的所有多边形函数都是完全错误的 - 它们会将多边形更改为其边界矩形,然后执行答案。

最糟糕的例子就是如果您有一个代表日本的多边形,并且您问哪些地方在日本,那么符拉迪沃斯托克会出现在列表中!

Oracle和PostGIS没有这个问题。我期望MSSQL和任何使用JTS作为引擎的Java数据库也不会有这个问题。地理空间好,MySQL地理空间差。

只需在此处阅读 如何使用MySQL空间查询查找X半径内的所有记录?即可了解它在5.6.1中已经修复。

太好了!


事情已经发生了变化。当一个人使用依赖于计算“最小边界矩形”的MBR函数时,会出现mysql坏的多项式查询。自mysql 5.6以来,新的函数出现了。以“st_*”开头的函数表现更好。例如contains(g1, g2)st_contains(g1, g2)。更多信息可以在https://www.percona.com/blog/2013/10/21/using-the-new-spatial-functions-in-mysql-5-6-for-geo-enabled-applications/找到。 - oak
是的,我认为OpenGIS决定使用st_名称在数据库供应商之间更具可移植性。它们绝对应该被使用。但我认为它们在5.6中并不准确。 - Julian
请注意,在“5.6.23-72.1 - Percona Server (GPL)”上,st_contains函数似乎按预期工作。仅测试了附近(即:最多800公里距离)的坐标。也许对于远距离坐标,它的表现会有所不同。 - oak
在stackoverflow.com上深入搜索https://dev59.com/I1zUa4cB1Zd3GeqP1kUI,说5.6.1与您的经验相符。 - Julian

1

这是一件好事,因为您可以在查询中使用空间索引。例如,限制到一个边界框,以限制要与之比较的行数。


1

Mysql GIS yagni:

如果你没有GIS的经验,学习空间扩展实际上就像学习一个新的数据库,再加上一点数学和大量的缩略语。地图、投影、SRID、格式……你必须学习所有这些知识才能计算给定某个纬度/经度的点之间的距离吗?可能不需要,你将集成第三方GIS数据或处理比点更复杂的任何内容,你将使用哪个坐标系统?

回到yagni:尽可能简单地做事,在这种情况下,用php或简单的SQL实现你的代码。一旦你遇到障碍并决定需要空间,就阅读GIS系统、坐标系统、项目和约定。

到那时,你可能会想要使用PostGIS。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接