【问题标题】:Calculating distance between two X, Y coordinates in PHP在PHP中计算两个X,Y坐标之间的距离
【发布时间】:2011-01-02 11:04:55
【问题描述】:

我正在为一个游戏编写一个工具,该工具涉及计算 500 个单位宽的环形平面上两个坐标之间的距离。也就是说,[0,0] 到 [499,499] 是有效坐标,并且 [0,0] 和 [499,499] 也是紧挨着的。

目前,在我的应用程序中,我正在比较具有 [X,Y] 位置的城市与他们预先配置的用户自己的 [X,Y] 位置之间的距离。

为此,我找到了这个算法,哪种有效:

Math.sqrt ( dx * dx + dy * dy );

因为按距离对分页列表进行排序是一件很有用的事情,所以我在 MySQL 查询中实现了这个算法,并使用我的 SELECT 语句的以下部分使其可用于我的应用程序:

SQRT( POW( ( ".strval($sourceX)." - cityX ) , 2 ) + POW( ( ".strval($sourceY)." - cityY ) , 2 ) ) AS distance

这适用于许多计算,但没有考虑到 [0,0] 和 [499,499] 彼此相邻这一事实。

鉴于 0 和 499 相邻,我有什么方法可以调整此算法以生成准确的距离?

【问题讨论】:

标签: php mysql algorithm math coordinate-systems


【解决方案1】:

我假设您的意思是环绕坐标而不是球形。就像一张扁平的纸,两端神奇地相互连接。

这意味着对于大小为 500x500 的地图,x(或 y)方向的距离最多为 250。(如果超过 250 步,我们最好向后走 500-x 步。)

解决这个问题的简单方法是

dx = Math.abs(dx);
dy = Math.abs(dy);
if (dx > 250)
  dx = 500 - dx;
if (dy > 250)
  dy = 500 - dy;
distance = Math.sqrt ( dx * dx + dy * dy );

【讨论】:

  • 是的,这样说更准确。
  • 看看能不能把它移植到 MySQL。
  • 不幸的是,我不能使用它,因为我需要在 MySql 中完成计算,以便我可以像页面上的其他列一样充分利用该列。跨度>
  • @Umopepisdn - 我认为你最好用 PHP 计算距离。 MySQL 不是为做这类事情而设计的。如果您只是想要第 20 个最近的城市;有更好的方法来获得它们,你可以为此提出一个新问题。
【解决方案2】:

更新(圆环):

好的,从您自己的 cmets 看来,您确实指的是圆环 - 甜甜圈的表面 - 而不是球体。 (这是一个很大的不同,您应该编辑您的问题:将其称为球体是错误的。)

对此,答案相当简单——您给出的笛卡尔公式或多或少是正确的。但是,您需要环绕距离,以便任何大于或等于 250=500/2 的值都被转换为 0 到 250 之间。

所以答案是这样的(我根本不懂 PHP,所以这可能需要修改语法)

dx1 = min(dx, 500-dx)
dy1 = min(dy, 500-dy);
distance = Math.sqrt(dx1*dx1 + dy1*dy1);

(假设您已将 dx 和 dy 定义为差值的绝对值。)

刚刚找到this routine,它在一个封装良好的函数中实现了相同的计算。

原始答案(球体):

您还没有解释您的 (x,y) 坐标如何映射到球体上的点!

(字面意思)有无数种选择,每种选择对应一个不同的map projection,而且每种选择的公式都不同。请注意,无论您做出何种选择,这两个坐标的含义是非常不同的。

例如,如果您的 (x,y) 确实是经度和纬度,则有很多固定公式(即半正弦),但您必须首先将经度的 0->499 转换为 0->360 度和 -90-> 90 度的纬度。 (请注意,lon 和 lat 在球体上的行为非常不同!)

但我强调,任何您所做的选择都会使您在 (x,y) 中绘制时得到的平面几何图形与球体上的实际外观有所不同。

(最后,如果你真的是说顶边和底边一样,右边和左边一样,那么你可能有一个圆环而不是一个球体!)

【讨论】:

  • 这几乎是正确的答案。是的,我正在使用圆环。
【解决方案3】:

如果您知道两点的纬度和经度 - 您可以使用haversine formula 计算球体上两点之间的距离。

但据我了解,您需要对几乎对映点准确的公式。 Haversine 公式在这里失败。在这种情况下,您需要Vincenty's formula,即使在对映的情况下也是准确的。

http://en.wikipedia.org/wiki/Great-circle_distance#Formulae

【讨论】:

    【解决方案4】:

    听起来您只是在使用“平铺”的特殊有限笛卡尔空间。在这种情况下,每个对象都没有唯一的位置。对于所有可能的整数值 i 和 j 而不是 (x, y) 它是 (x + i*w, y + j*h) ,其中 w 和 h 分别是“窗口”的宽度和高度。

    显然距离不是唯一的,但最小距离是所有 i, j 上的 min(d(p1,p2)))。

    如果您的坐标被包裹,那么您只需要计算 i=-1,0,1 和 j=-1,0,1 然后取最小的一个。

    【讨论】:

      【解决方案5】:

      该通用算法适用于直角坐标系或球坐标系中非常排序的距离,但不适用于球坐标系。

      我认为更好的方法是基于纬度和经度,如下所示:

      http://jan.ucc.nau.edu/~cvm/latlongdist.html

      MySQL 内置了地理编码功能。为什么不使用它?

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

      【讨论】:

      • 嗯,这种方法似乎可行。通读ppt;谢谢!如果我解决了一个问题,我会发布我的解决方案。
      • 我尝试采用 Haversine 公式,因为它似乎可能相关,并将其放入我的查询中以查看它的表现。结果大相径庭。这是新的查询:
      • SELECT cityX, cityY, distance, distance2 FROM ( SELECT cityX, cityY, SQRT( POW( ( 266 - cityX ) , 2 ) + POW( ( 182 - cityY ) , 2 ) ) AS distance, 3956 *2 * ASIN( SQRT( POWER( SIN( ( 370 - cityX ) * PI( ) /180 /2 ) , 2 ) + COS( 370 * PI( ) /180 ) * COS( cityX * PI( ) /180 ) * POWER( SIN( ( 293 - cityY ) * PI( ) /180 /2 ) , 2 ) ) ) AS distance2 FROM City ) AS SmartCity ORDER BY distance DESC LIMIT 0 , 20
      • 以下是旧算法和新算法之间的一些前后对比:
      • 最近的:[168,125] 113.371 到 Haversine 的 824.860 [359,282] 136.561 到 Haversine 的 1071.594
      【解决方案6】:

      虽然这里的一些答案非常接近,但问题最终还是通过这个 SELECT 片段解决了:

      SQRT( POW( LEAST( ABS($sourceXstr-cityX), ( 500 +LEAST($sourceXstr,cityX)-GREATEST($sourceXstr,cityX))) , 2 ) + POW( LEAST( ABS($sourceYstr-cityY), ( 500 +LEAST($sourceYstr,cityY)-GREATEST($sourceYstr,cityY))) , 2 ) ) AS distance

      【讨论】:

        【解决方案7】:

        如果两个坐标位于 OP 未要求的两端不相交的二维平面中,我正在写答案。但它可能会在将来对某人有所帮助。

        如果您的点位于二维平面中,则点 (x1, y1) 和 (x2, y2) 之间的距离由 勾股定理 给出:

        d = squareroot( square(x2 - x1) + square(y2 - y1) )
        

        在 PHP 中,

        $x1 = $y1 = 2;
        $x2 = $y2 = 5;
        $distance = sqrt( pow(($x2-$x1),2) + pow(($y2-$y1),2) );
        echo $distance;              // 4.2426406871193
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-12
          • 2018-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多