【问题标题】:Get results that fall within marker radiuses from database从数据库中获取落在标记半径内的结果
【发布时间】:2012-07-20 20:44:23
【问题描述】:

2012 年 11 月 16 日更新

我想再次提出这个问题,提供新的赏金以获得可靠、良好的解决方案。似乎只有解决方案 (shubhansh's answer) 现在无法有效工作。我会解释原因。

首先,这是我有半径和人的实时地图,半径在red,人在blue

如您所见,这张地图中有 two 人,半径为 eight,基本上我只得到 @ 的人987654332@,但我没有收到 Person B,我猜 SQL 没有正确拾取它,我需要它从人的半径和标记半径。

看起来拾取的内容在半径内,而不是与半径重叠的那些,我需要它能够拾取任何相互重叠的半径的任何结果。

我正在寻找比 shubhansh 的答案更精确的 SQL。您可以阅读下文以了解我究竟需要该查询来采取行动并找到准确的人。

数据,

+-----------+-----------+--------+
| latitude  | longitude | radius |
+-----------+-----------+--------+
| 51.517395 | -0.053129 | 5.6    |
| 51.506607 | -0.116129 | 0.7    |
+-----------+-----------+--------+

请注意radius 以公里为单位。

+-----------+-----------+-----+
| latitude  | longitude | km  |
+-----------+-----------+-----+
| 51.502117 | -0.103340 | 0.3 |
| 51.498913 | -0.120850 | 0.7 |
| 51.496078 | -0.108919 | 0.7 |
| 51.496506 | -0.095873 | 0.7 |
| 51.503399 | -0.090723 | 0.7 |
| 51.508049 | -0.100336 | 0.7 |
| 51.508797 | -0.112610 | 0.7 |
| 51.505535 | -0.125227 | 0.7 |
| 51.502331 | -0.108061 | 0.7 |
+-----------+-----------+-----+

我当前使用的 SQL:

SELECT ppl.latitude,
       ppl.longitude,
       ppl.radius
FROM 
(
    people ppl
),
(
    SELECT latitude, longitude 
    FROM radiuses
) AS radius
WHERE (POW((ppl.longitude - radius.longitude) * 111.12 * COS(ppl.latitude), 2) + POW((ppl.longitude - radius.longitude) * 111.12, 2)) <= 4
GROUP BY ppl.id

可用于测试查询的 MySQL 数据,

INSERT INTO radiuses (id, latitude, longitude, km) VALUES ('1', '51.502117', '-0.103340', '0.3'), ('2', '51.498913', '-0.120850', '0.7'), ('3', '51.496078', '-0.108919', '0.7'), ('4', '51.496506', '-0.095873', '0.7'), ('5', '51.503399', '-0.090723', '0.7'), ('6', '51.508049', '-0.100336', '0.7'), ('7', '51.508797', '-0.112610', '0.7'), ('8', '51.505535', '-0.125227', '0.7'), ('9', '51.502331', '-0.108061', '0.7');

INSERT INTO people (id, latitude, longitude, radius) VALUES ('1', '51.517395', '-0.053129', '5.6'), ('2', '51.506607', '-0.116129', '0.7');

旧总结

注意:所有经纬度都是随机制作的。

我有一个地图小程序,用户可以将他的 半径 放置在 lat/lng 位置,半径为 1 公里。

现在,有另一个用户可以将他的半径放在地图上的任何位置,每个位置的半径为 1 公里(与上面的用户相同)。

像这样用户A是红色的,用户B是蓝色的。

基本上用户A将他的半径存储在一个如下所示的表中:

+-----------+---------+-----------+-----------+
| radius_id | user_id | latitude  | longitude |
+-----------+---------+-----------+-----------+
|         1 |       1 | 81.802117 | -1.110035 |
|         2 |       1 | 81.798272 | -1.144196 |
|         3 |       1 | 81.726782 | -1.135919 |
+-----------+---------+-----------+-----------+

用户 B 将他的半径存储在另一个表格中,如下所示 - (注意:每个帐户只能存储 1 个坐标):

+---------+-----------+-----------+
| user_id | latitude  | longitude |
+---------+-----------+-----------+
|       6 | 81.444126 | -1.244910 |
+---------+-----------+-----------+

我希望能够在地图图片中提取那些落在定义半径内的用户,即使半径圆圈是接触的。只有标记 C 能够拾取单个半径,而 AB 不能。

我确信这是可能的,但我不知道如何在 MySQL 中提出这种系统。

我在 Google Developers 网站上发现它很接近,但不仅仅是我需要的性能。

编辑:我找到了一个更好的,这是非常接近的,但仍然不是我想要的,因为当我在表中有多个时,它使用 1 个经纬度坐标.

【问题讨论】:

标签: mysql math markers


【解决方案1】:

要解决这个问题,您需要了解圆方程,就像这样 对于任何点 (x,y) 落在以 (x1, y1) 为圆心且半径为 r 单位的圆内是

(x-x1)^2 + (y - y1)^2 <= r^2

where a^b = a to the power b

在您的情况下,用户 B 的(纬度、经度)是圆心,用户 A 的(纬度、经度)是点 (x,y),半径 = 2 公里。

但基本问题是将纬度转换为经度,所以这是解决方案,1 度 = 111.12 公里。因此,为了保持等式两边的单位相同,我们将其转换为 Kms

所以我们的最终方程变为:

((x-x1)*111.12)^2 + ((y-y1)*111.12)^2 = 4      (=2^2) 

相同的 SQL 语句应该是这样的

SELECT A.user_id, A.radius_id, A.latitude, A.logitude
FROM UserA AS A, 
     (SELECT user_id, latitude, longitude 
       FROM UserB 
       WHERE user_id = 8) AS B
WHERE (POW((A.latitude-B.latitude)*111.12, 2) + POW((A.longitude - B.longitude)*111.12, 2)) <= 4
/* **Edit** Here I have used (A.longitude - B.longitude)*111.12, for more accurate results one can replace it with (A.longitude - B.longitude)*111.12*cos(A.latitude)) or (A.longitude - B.longitude)*111.12*cos(B.latitude)) 

And, as i have suggested in the comments that first filter some records based on approximation, so whether one uses A.latitude or B.latitude it will not make much difference */

希望这会有所帮助...

【讨论】:

  • 您可以分解出 111.12^2。然后将双方除以该数量。 WHERE (POW((A.latitude-B.latitude), 2) + POW((A.longitude - B.longitude), 2))
  • 1 度 = 111.12 公里,仅在赤道;当您向两极移动时,纬度保持相对恒定,但经度接近于零,因此您离赤道越远,这个解决方案就会变得越来越不准确。
  • 我认为上面给出的模型很好。如果需要有关距离的更准确结果,他们可以使用此处给出的公式。 [movable-type.co.uk/scripts/latlong.html ] 并在给定的查询中进行适当的调整
  • 为了加快查询速度,我建议您先过滤掉解决方案,只需过滤经纬度 +- 2/112.12 度,然后在结果公式上应用距离公式,以加快查询速度结果。
  • @shubhansh 我可以将定义的公里数放入表UserB 中名为km 的列中吗?它们都不是1km,那么我应该把它们放在哪里?另外,我有点不明白您通过过滤来加快速度是什么意思,究竟我将如何将它们过滤掉它们将距离公式应用于结果?
【解决方案2】:

问题的核心是“我如何知道两个圆圈是否重叠”。答案是“如果它们的中心之间的距离小于它们的半径之和”。所以你要找的是如何确定两点之间的距离。

另一个答案是将纬度和经度视为组成笛卡尔平面。他们没有(当您从赤道接近两极时,经度趋于零)。现在,作为一个近似值,它可能适用于您的解决方案,具体取决于您的解决方案所需的准确性。另一方面,如果您需要非常准确,则需要 Haversine 公式。这里有一个很好的描述如何在 MySQL 中实现它:

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

从该演示文稿的幻灯片 7 中,您有以下公式:

3956*2*ASIN(SQRT(POWER(SIN((orig.lat-dest.lat)*pi()/180/2),2)+
    COS(orig.lat*pi()/180)*COS(dest.lat*pi()/180)*
    POWER(SIN((orig.lon-dest.lon)*pi()/180/2),2)))

注意第一个数字是以英里为单位的地球平均半径;将其更改为 6371 公里。

您如何使用此计算出的距离将取决于您的帖子中未包含的详细信息,例如您要处理的点数、地理分布、任何性能要求,以及数据是否是静态的或不断更新。

我提到这些是因为性能会成为一个问题,尤其是当您有大量数据和/或数据不断更新时(例如基于手机 GPS 数据的用户位置)。

您可以帮助解决性能问题的一种方法是使用正方形而不是圆形,并使用 1 度 = 111.12 公里的近似值。这样你就可以自动剔除任何明显相距很远的点。然后,您只需为感兴趣区域内的少数点计算 Haversine 公式。

我希望这有助于为您指明正确的方向。

【讨论】:

    【解决方案3】:

    几何图形的要点是,如果两个圆的中心之间的距离小于它们的半径之和,则两个圆会重叠。由于我们正在进行比较,我们可以使用距离的平方,因为这样可以避免平方根运算。原来,每个半径固定为1,两个半径之和为2,和的平方为4。

    原始问题和新问题之间存在很大差异。第一个你有固定半径的圆,第二个你有不同半径的圆。比较表达式[...distance^2...] &lt;= 4 中的常量4 需要替换,因为这是原始固定半径的产物。要实现这一点,请将km 字段添加到查询中。您应该检查一下,您没有在 WHERE 过滤器中使用 ppl.radius,因此更改该值不会改变您的查询结果也就不足为奇了。

    SELECT ppl.latitude, ppl.longitude, ppl.radius
    FROM 
      ( people ppl ),
      ( SELECT latitude, longitude, km FROM radiuses ) AS B
    WHERE [...distance^2...] <= POW( ppl.radius + B.km, 2)
    

    我应该说,这个问题的理解时间比它应该理解的要长得多,因为你将实体——那不是一个人——称为“半径”,而实际上你有一个属性应该在两个不同的实体上被称为“半径”。因此,将其他实体命名为具有描述性的名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      • 2015-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多