Java中的基准方向算法

17
这个周末,我花了几分钟时间匆忙编写了一个算法,它可以接收一个标题(以度为单位),并返回一个代表基本方向的字符串(我在使用安卓指南针应用时使用它)。最终我得到了这个代码:
private String headingToString(Float heading)
{
    String strHeading = "?";
    Hashtable<String, Float> cardinal = new Hashtable<String, Float>();
    cardinal.put("North_1", new Float(0));
    cardinal.put("Northeast", new Float(45));
    cardinal.put("East", new Float(90));
    cardinal.put("Southeast", new Float(135));
    cardinal.put("South", new Float(180));
    cardinal.put("Southwest", new Float(225));
    cardinal.put("West", new Float(270));
    cardinal.put("Northwest", new Float(315));
    cardinal.put("North_2", new Float(360));

    for (String key: cardinal.keySet())
    {
        Float value = cardinal.get(key);
        if (Math.abs(heading - value) < 30)
        {
            strHeading = key;
            if (key.contains("North_"))
            {
                strHeading = "North";
            }
            break;
        }
    }
    return strHeading;
}

我的问题是,这是最好的方法吗?这种情况肯定已经有很多人做过了,虽然我还没有在网上搜索示例。其他人尝试过这个方法并找到更简洁的解决方案吗?

针对The Reverand、Thilo、shinjin和Chrstoffer的反馈进行编辑:

解决方案

public static String headingToString2(double x)
{
    String directions[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"};
    return directions[ (int)Math.round((  ((double)x % 360) / 45)) ];
}
5个回答

30

在大多数情况下这很好,但为了使其优化和更加简洁(依我之见),您可以找到一个函数将输入的标题与地图中使用的标题相关联。

例如: (我非常确定这是正确的,但您需要检查一下)

45* (int)Math.round((  ((double)x % 360) / 45))

这段代码首先会使用x % 360来确保方向角度在有效范围内。

45 * round(.../45)

找到最接近45的倍数。

现在将您的地图更改为

  HashMap<Integer, String> map = new HashMap<Integer, String>()
  map.put(0, "North")
  map.put(45, "Northeast")
  etc...

因此,现在您的算法变成了一个快速的数学计算,而不是通过地图进行迭代。此外,在此处您不需要Hashtable,因为它提供了用于并发的构造(如果我记得正确),在您的情况下,它实际上会导致性能下降。

再次强调,对于您的需求来说,性能影响可能完全可以忽略不计。

针对Thilo和shinjin的建议进行编辑:

不要乘以45,只需保留方程式的其余部分,这将为您提供0-7的值,并制作一个字符串数组。

String directions[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"}
return directions[ (int)Math.round((  ((double)x % 360) / 45)) % 8 ]

在两行代码内解决问题。

需要注意的是,对于负数,取模运算将无法正确地工作。如果输入为负数,则需要先将其转换为正数。


1
非常好。再进一步,将另一个小转换添加到函数中,他就可以得到字符串数组的索引:String[] directions = { "N", "NE", "E" ... } - Thilo
1
如果你省略了乘以45的部分,那么你可以使用一个简单的数组代替哈希映射。 - shinjin
1
你需要在那个数组中添加一个额外的“N”。标题为337.5及以上将四舍五入为8。 - Christoffer Hammarström
10
这个解决方案中有两个拼写错误导致了不正确的结果和 IndexOutOfBoundsException: 1)数组中缺少“W”字符: String directions[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"} 2)索引 8 应该回绕到 0: return directions[(int)Math.round(((double)x % 360) / 45) % 8] - Andrey
@XCore 不,模8是正确的。有效索引为[0,7],0%8= 0,1%8= 1,...,7%8= 7,8%8= 0,正如所期望的那样。 - Andrey
显示剩余3条评论

10

这里大部分答案对于45度的间隔偏差为22.5度,并将例如0-45度映射为N,而非[337.5-360],[0-22.5]映射为N。在进行计算前,您需要进行偏移以纠正这个问题。

以下是一种解决方法,使用22.5度的间隔,例如风向的表示方式:

  private String formatBearing(double bearing) {
    if (bearing < 0 && bearing > -180) {
      // Normalize to [0,360]
      bearing = 360.0 + bearing;
    }
    if (bearing > 360 || bearing < -180) {
      return "Unknown";
    }

    String directions[] = {
      "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
      "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW",
      "N"};
    String cardinal = directions[(int) Math.floor(((bearing + 11.25) % 360) / 22.5)];
    return cardinal + " (" + formatBearing.format(bearing) + " deg)";
  }

3
你的方向数组中包含两个N,这正常吗? - Pak

0

之前的示例并不准确,这是一个更准确的JavaScript解决方案。

function getCardinalDirection(input) {
    var directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"];
    var index = Math.floor( ((input-22.5)%360) / 45 );
    return directions[index+1];
}

0

你可以在前面加上15度,以避免出现North_1和North_2。


-1

使用Java:

String _directions[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"};

public String getHeading(int hea) {
  return _directions[(int)Math.floor((hea % 360) / 45)];
}

在Java中,您必须创建一个类。
在JavaScript中:
var _directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];

function getDirection (hea) {
  return _directions[Math.floor((hea % 360) / 45)];
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接