【问题标题】:Order results by proximity (with coordinates & radius)按邻近度排序结果(带有坐标和半径)
【发布时间】:2012-07-09 08:28:43
【问题描述】:

给定一个包含 4 个圆的数据库,其中每个圆都有一个半径和一个地理定位中心:

id | radius | latitude | longitude
---+--------+----------+----------
 1 |      3 |    40.71 |    100.23
 2 |     10 |    50.13 |    100.23
 3 |     12 |    39.92 |    100.23
 4 |      4 |    80.99 |    100.23

注意:为了简单起见,每个圆圈的经度都是相同的。

假设我们在圈2,我想根据latitude/longitude坐标和每个圈的radius找到附近的每个圈。

例如,根据经纬度坐标,我们有这样的顺序:

  1. 圈 1(因为接近:9.42 <- 50.13 - 40.71
  2. 第 3 圈(因为靠近:10.21 <- 50.13 - 39.92
  3. 第 4 圈(因为靠近:30.86 <- 80.99 - 50.13

但是根据经纬度坐标和每个圆的半径,我们应该有:

  1. 第 3 圈(因为靠近:1.79 <- 12 - 10.21
  2. 圈 1(因为接近:6.42 <- 9.42 - 3
  3. 第 4 圈(因为靠近:26.86 <- 30.86 - 4

在 SQL 中有没有一种简单的方法可以做到这一点?

【问题讨论】:

  • 你想在 MySQL 还是 PostgreSQL 中做这个?
  • 我更喜欢使用 PostgreSQL。但即使只有 MySQL,我也会很高兴 ;)
  • 您打算如何计算这些顺序?由于您认为半径很重要,因此您不仅关心圆心之间的距离,因此请扩大。
  • 你只是指中心之间的gc距离除以所考虑圆的半径吗? (不太确定这个计算有什么用,但它会产生你要求的顺序)
  • 实际上顺序将根据我们的圆的地理定位中心与我们要列出的每个其他圆的圆周的近点(根据半径)之间的距离来计算。所以我们可以这样说:圆的周长和考虑的圆之间的 gc 距离。

标签: mysql sql postgresql geolocation neo4j


【解决方案1】:

postgresql 的 contrib 中提供的 cubeearthdistance 扩展可以处理这个问题,至少可以产生近似的答案。具体来说,他们假设地球是一个简单的球体,这使数学变得容易得多。

通过这些扩展,您可以像这样生成圆 2 和其他圆之间的距离:

select circle.id,
       earth_distance(ll_to_earth(circle.latitude, circle.longitude),
                      ll_to_earth(x.latitude, x.longitude))
 from circle,
      circle x
 where x.id = 2 and circle.id <> x.id
 order by 2;

修正圆半径应该只涉及从上面的距离中减去x.radiuscircle.radius,尽管您需要考虑半径的单位。默认情况下,earth_distance 将计算以米为单位的值。

现在,除了扫描整个圆列表并计算每个圆的距离,然后对它们进行排序和限制之外,让查询执行其他操作,这更具挑战性。有几种方法:

  • 使用 cube 的 gist 索引功能,因此您可以创建索引以在围绕任何圆的中心的特定框内进行搜索,从而减少要考虑的圆列表。
  • 在任何时候编辑圆时,预先计算每个圆与所有其他圆之间的距离,并使用触发器在单独的表格中维护此计算。

第二个选项基本上以:

开头
create table circle_distance as
select a.id as a_id, b.id as b_id,
 earth_distance(ll_to_earth(a.latitude, a.longitude),
                ll_to_earth(b.latitude, b.longitude))
 from circle a, circle b
 where a.id <> b.id;
alter table circle_distance add unique(a_id, b_id);
create index on circle_distance(a_id, earth_distance);

然后是一些相当繁琐的函数来删除/插入circle_distance 中的相关行,由circle 上的触发器调用。这意味着您可以:

select b_id from earth_distance where a_id = $circle_id order by earth_distance limit $n

此查询将能够使用(a_id,earth_distance) 上的索引进行快速扫描。

【讨论】:

  • 感谢您的工作,@araqnid。我将学习 PostgreSQL,以便能够使用您出色的解决方案。问候。
【解决方案2】:

我建议查看 PostGIS Geography 数据类型及其相关函数(例如:ST_Distance)而不是重新发明轮子

【讨论】:

  • 感谢您的提示。但是,我在 Heroku 上,该插件目前处于私人测试版 (addons.heroku.com/spacialdb)。如果我们能从头开始做就太好了……
  • 我不确定您的半径与纬度值的关系如何。它们是不同的测量值,所以我不确定您是否可以在纬度上添加/减去半径并有意义......
  • 当然,如果经度不变,答案很简单... select t1.* from #t t1, (select latitude as lat from #t where id=@id) v where id@id order by abs(lat- case when latitude
【解决方案3】:
【解决方案4】:

我建议你以下:

创建 1 个表格用于计算相对于起始圆的相对距离

例如:

id | calc1  | calc2    
---+--------+----------
 1 |  9.42  |    1.97
 3 |  10.21 |    6.42
 4 |  30.86 |   62.86

Calc1 是没有半径的计算 calc2 是半径计算

然后创建一个存储过程,它在运行时首先删除表,然后用正确的数据填充它,然后从目标表中读取结果

Intrudoction to store procedures You will allso need cursor for this

【讨论】:

  • 我想知道我们是否可以分两步完成:首先我们得到calc1,感谢子查询,然后我们找到calc2 以便订购结果。
  • ofc calc2 只是来自 calc1 的数学运算,但我喜欢在结果端没有太多计算的 aproch,因为那样你需要在每个输出上都做它,然后你把它交给一些菜鸟程序员来做他把事情搞砸了,然后每个输出都不一样,但这是我在工作中习惯的事情
猜你喜欢
  • 2014-11-06
  • 1970-01-01
  • 1970-01-01
  • 2014-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-10
相关资源
最近更新 更多