Cardinal direction algorithm in Java

余生颓废 提交于 2019-12-20 10:29:54

问题


This weekend I spend a few minutes thrashing together an algorithm that would take in a heading (in degrees) and return a String for the cardinal direction (I'm using it in an android compass application I'm using). What I ended up with was this:

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;
}

My question is, is this the best way of doing this? It must have been done many times before although I haven't done a search for examples on the web yet. Have any other people tried this and found a neater solution?

Edit for The Reverand's Thilo's, shinjin's and Chrstoffer's responses:

The Solution

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)) ];
}

回答1:


That's fine in most cases, though to make it optimized and (IMO) cleaner, what you could do is find a function to relate the input heading to one used in the map.

For example: (I'm pretty sure this is right, but you'll want to check it)

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

What this does is first x % 360 makes sure the heading is within a valid range. then

45 * round(.../45)

finds the closest multiple of 45.

Now change your map to be

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

So, now your algorithm becomes a fast mathemtical calculation rather than iterating through the map. Furthermore, you don't need as Hashtable here since it provides constructs for concurrency (if I remember correctly) and in your case it would actually cause a performance decrease.

Once again, the performance hit may be completely negligible for your needs.

Edit for Thilo's and shinjin's suggestions:

Instead of multiplying by 45, just keep the rest of the equation, which gives you values for 0-7, and make an array of your strings.

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

and you've got your problem solved in two lines.

One note: Modulus won't work correctly for negative numbers. If our input heading is negative, you'll need to make it positive first.




回答2:


Most of the answers here are off by 22.5 degrees for their 45 degree intervals, and map e.g. 0-45 as N, rather than [337.5-360],[0-22.5] to N. You need to offset before doing the math to correct for this.

Here's a solution that uses 22.5 degree intervals, such as you might see for wind directions:

  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:


You could maybe add 15 degrees up front to avoid North_1 and North_2.




回答4:


The previous examples are not accurate, here is a more accurate solution in 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];
}



回答5:


in java:

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

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

In "java" case u must need to create a Class.

in javascript:

var _directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];

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


来源:https://stackoverflow.com/questions/2131195/cardinal-direction-algorithm-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!