【问题标题】:Relative cardinal direction of two coordinates两个坐标的相对基本方向
【发布时间】:2016-01-30 17:48:07
【问题描述】:

定义了以下枚举:

public enum Direction
{
    North,
    South,
    East,
    West,
    Northeast,
    Northwest,
    Southeast,
    Southwest,
    Undefined
}

给定二维空间中的两组坐标,我想确定点 2 到 1 的相对基本方向。

例子:

  • P1(1,1) 和 P2(0,1) 返回 Direction.North,因为 P2 位于 P1 的北边
  • P1(1,1) 和 P2(5,4) 返回 Direction.Southeast
  • P1(1,1) 和 P2(1,1) 返回 Direction.Undefined

我目前的方法涉及一堆条件,即

if (P1.X == P2.X)
{
    // either North, South or Undefined
    if (P1.Y < P2.Y)
        return Direction.South;
    else if (P1.Y > P2.Y)
        return Direction.North,
    else
        return Direction.Undefined;
}
else if (P1.Y == P2.Y)
{
    ...
}
else 
{
    ...
}

我正在寻找一种更短更优雅的解决方案。

【问题讨论】:

  • 使用矢量P2 - P1 的角度(使用arctan2()),然后执行if(-15° &lt;= angle &lt;= 15°) return Direction.North 之类的操作? (即,将整个事物拆分为 numDirections 切片,然后检查角度是否在一个区间内)。另外我不明白为什么如果P1.X &lt; P2.X 为真(意味着点P1 位于P2 的左侧),为什么它会返回南方向?
  • 它是在标准笛卡尔坐标中定义的吗?如果是这样,那么第一个样本不应该是 Direction.West,第二个是 Direction.Northeast 吗?
  • @PiotrWolkowski 显然,OP 沿着负 X 轴向北,而不是更常见的正 Y 轴。这很奇怪,但“标准笛卡尔坐标”中没有任何东西可以指示北方的方向。
  • 我们可以假设您需要一个 45 度楔形用于八个方向中的每一个吗?

标签: c# algorithm


【解决方案1】:

我的 3 美分 - 我正在等待改进

这里是枚举:

public enum Direction
{
    North = 0,
    South = 4,
    East = 6,
    West = 2,
    Northeast = 7,
    Northwest = 1,
    Southeast = 5,
    Southwest = 3,
    Undefined = -1
}

转换如下:

public static Direction GetDirection(Point p1, Point p2) {
    double angle = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
    angle += Math.PI;
    angle /= Math.PI / 4;
    int halfQuarter = Convert.ToInt32(angle);
    halfQuarter %= 8;
    return (Direction)halfQuarter;
}

但是它不会返回Direction.Undefined,因为

如果 y 为 0,x 为 0,则 θ = 0。

(来自https://msdn.microsoft.com/library/system.math.atan2(v=vs.110).aspx

【讨论】:

  • 对于Direction.Undefined 位,您可以检查p1-p2 是否足够接近零向量(通过定义一个小ε,如果坐标是浮点值。如果他们'重新整数,然后p1-p2 == (0,0)可以直接检查。否则,很好的解决方案。
【解决方案2】:

假设你有 P1 和 P2,使用变换 P -&gt; P - P1 将 P1 移动到 2D 空间的原点。

然后计算向量(0,0) - P2' 之间的角度,其中P2' 是变换P2 点(P2' = P2 - P1) 和X 轴。

使用该角度选择方向(每个方向都有360/8 角度宽度)。

两个向量之间的角度可以使用dot product来计算

【讨论】:

    【解决方案3】:

    让我们打破 360 度的 8 个扇区,每个扇区 45 度。 所以我们只需要找出向量 (p2 - p1) 属于 8 个扇区中的哪一个:

    static Direction GetDirection(Point start, Point end)
    {
    
        double dx = end.X - start.X;
        double dy = end.Y - start.Y;
    
    
        if (Math.Abs(dx) > Math.Abs(dy))
        {
            if (Math.Abs(dy / dx) <= tan_Pi_div_8)
            {
                return dx > 0 ? Direction.East : Direction.West;     
            }
    
            else if (dx > 0)
            {
                return dy > 0 ? Direction.Northeast : Direction.Southeast;
            }
            else 
            {
                return dy > 0 ? Direction.Northwest : Direction.Southwest;
            }
        }
    
        else if (Math.Abs(dy) > 0)
        {
            if (Math.Abs(dx / dy) <= tan_Pi_div_8)
            {
                return dy > 0 ? Direction.North : Direction.South;
            }
            else if (dy > 0)
            {
                return dx > 0 ? Direction.Northeast : Direction.Northwest;
            }
            else 
            {
                return dx > 0 ? Direction.Southeast : Direction.Southwest;
            }
        }
        else 
        {
            return Direction.Undefined;
        }
    
    
    }
    
    static readonly double tan_Pi_div_8= Math.Sqrt(2.0) - 1.0;
    

    Working sample

    【讨论】:

      【解决方案4】:

      当坐标不完全指向其中一个方向时,此解决方案将正确返回您的 Direction.Undefined,并且它通过仅检查角度 (see unit circle) 来消除嵌套的跟随控制语句。请注意,我假设点类型的XY 属性定义为int,但这应该可以正常工作,即使它们被定义为浮点类型值(例如,float、decimal 或 double)。

      static Direction GetDirection(Point p1, Point p2)
      {
          double rad = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
      
          // Ajust result to be between 0 to 2*Pi
          if (rad < 0)
              rad = rad + (2 * Math.PI);
      
          var deg = rad * (180 / Math.PI);
      
          if (deg == 0)
              return Direction.East;
          else if (deg == 45)
              return Direction.Northeast;
          else if (deg == 90)
              return Direction.North;
          else if (deg == 135)
              return Direction.Northwest;
          else if (deg == 180)
              return Direction.West;
          else if (deg == 225)
              return Direction.Southwest;
          else if (deg == 270)
              return Direction.South;
          else if (deg == 315)
              return Direction.Southeast;
          else
              return Direction.Undefined;
      }
      

      一个简单的测试...

      Direction dir;
      
      dir = GetDirection(new Point(0, 0), new Point(1, 0));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(1, 1));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(0, 1));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(-1, 1));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(-1, 0));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(-1, -1));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(0, -1));
      Console.WriteLine(dir);
      dir = GetDirection(new Point(0, 0), new Point(1, -1));
      Console.WriteLine(dir);
      

      输出...

      East
      Northeast
      North
      Northwest
      West
      Southwest
      South
      Southeast
      

      请注意,如果Point 类型将XY 属性定义为浮点类型,那么类似下面的内容将返回Direction.Undefined,因为它并不完全指向东方,所以希望这符合您的意图。 .

      // "Almost" pointing east...
      dir = GetDirection(new Point(0, 0), new Point(1, 0.001));
      

      输出...

      Undefined
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-13
        • 1970-01-01
        • 1970-01-01
        • 2018-05-19
        • 1970-01-01
        • 1970-01-01
        • 2015-04-08
        • 1970-01-01
        相关资源
        最近更新 更多