2012-07-20 69 views
20

更新2012年11月16日

我想再次提出这个问题,提供一个新的奖励以获得一个坚实的,良好的解决方案。看来只有解决方案(shubhansh's answer)现在不能有效工作。我会解释为什么。获取落在数据库标记半径内的结果

首先,这是现场地图我有半径和人民,半径在red和人民都在blue

enter image description here

正如你所看到的,有two人在这个地图eight半径,基本上我只得到这是Person A的人,但我没有得到Person B ,我猜测SQL没有正确地拾取它,我需要它从人的半径和标记半径精确而准确。

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

我在寻找一个精确而准确的SQL比shubhansh的答案。您可以阅读以下内容,了解我如何确切地查询查询并采集准确的人员。

的数据,PEOPLE

+-----------+-----------+--------+ 
| 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'); 

旧总结

注意:所有的经度和纬度都是随机产生的。

我有一个地图applet,用户可以将他的半径放置在半径为1km的经纬度位置。

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

像这样用户A是红色并且用户B是蓝色。

enter image description here

基本上用户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 | 
+---------+-----------+-----------+ 

我希望能够在地图图片中选取落在所定义的半径范围内的用户,即使半径圆正在触摸。当AB不需要时,只有标记C才能够拾取单个半径。

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

我在Google Developers网站上发现了它,但它并不仅仅是我需要的功能。

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

+0

这可能是一个答案,给你一个提示http://stackoverflow.com/questions/11502469/find-records-with-lattitude-and-logintude/11502530#11502530你只是添加一个'WHERE距离<1234'到查询。 – fdomig 2012-07-24 10:35:13

回答

6

几何图形的最重要的一点是,两个圆重叠的,如果他们的中心之间的距离小于其半径的总和。由于我们正在做比较,所以我们可以使用距离的平方,因为这样可以避免平方根操作。在原来,每一个半径固定为1,两个半径的总和为2,和的平方是4

有原来的问题和新问题之间有很大的区别。首先你有固定半径的圆,第二个你有不同半径的圆。在比较表达[...distance^2...] <= 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) 

我应该说,这个问题花了更长的时间来了解比它应该有,因为你调用实体that's - 不一个人一个“半径”,当你真的有一个对两个不同实体应该称之为“半径”的财产。所以将其他实体命名为描述性的。

14

为了解决这一点,你需要理解圆的方程,它是这样的 对于任何点(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公里。因此,要保持单位同一方程的两侧,我们将其转换成公里

所以我们最后的公式变为:

((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 */ 

希望这会有所帮助...

+0

你可以分解出111.12^2。然后用这个数量除以双方。在哪里(POW((A.latitude-B.latitude),2)+ POW((A.longitude-B.longitude),2))≤4/(111.12^2)。 – walrii 2012-07-20 22:20:35

+0

@walrii是的,这是可以做到的...... – Shubhansh 2012-07-20 22:23:59

+2

仅在赤道一度= 111.12公里;当你朝着两极移动时,纬度保持相对恒定,但经度接近零,所以这种解决方案在离赤道更远的地方会变得越来越不准确。 – 2012-07-20 22:44:59

7

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

另一个答案是将纬度和经度视为包含笛卡尔平面。他们不这样做(当你从赤道接近极点时,经度趋于零)。现在,作为一个近似值,它可能适用于您的解决方案,取决于解决方案所需的准确度。另一方面,如果你需要这个非常准确,你需要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数据用户的位置)。您可以使用性能问题帮助

一种方法是使用方形而不是圆形,并使用一个度=111.12公里的逼近。这样,您可以自动选择显然彼此远离的任何点。然后,您只需计算Haversine公式只适用于感兴趣区域内的少数几个点。

我希望这是在指点你在正确的方向有帮助。