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