Boxing The Compass
October 12, 2018
There are 360° in a circle but 16 compass points, so each compass point encompasses 22.5°, thus 11.25° left and right of the actual heading. So our function adds 11.25°, divides by 22.5°, rounds down, takes the result mod 16, and looks up the compass heading in a table:
(define (compass d)
(vector-ref '#(N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW)
(modulo (inexact->exact (floor (/ (+ d 11.25) 22.5))) 16)))
Here are some examples:
> (compass 70) ENE > (compass 230) SW
You can run the program at https://ideone.com/i3GlHe.
(defparameter *directions* ;; from heading 0 degree up. (N = 0, E = 90) '(n nne ne ene e ese se sse s ssw sw wsw w wnw nw nnw)) (defun direction (heading-degrees) (let* ((divisions (length *directions*)) (separation (/ 360 divisions)) (offset (/ separation 2))) (elt *directions* (truncate (mod (+ heading-degrees offset) 360) separation)))) (loop for d from 0 to 360 by 22 collect (list d (direction d))) ;; --> ((0 n) (22 nne) (44 ne) (66 ene) (88 e) (110 ese) (132 se) (154 sse) ;; (176 s) (198 ssw) (220 sw) (242 wsw) (264 w) (286 wnw) (308 nw) (330 nnw) (352 n))directions(
[n, nne, ne, ene,
e, ese, se, sse,
s, ssw, sw, wsw,
w, wnw, nw, nnw]).
compass(D, Result) :-
directions(Directions),
length(Directions, Divisions),
Separation is 360 / Divisions,
Offset is Separation / 2,
I is mod(floor((D + Offset) / Separation), Divisions) + 1,
nth(I, Directions, Result).
A Haskell version. It was difficult, but I resisted the temptation to do the 128 compass points…
import Text.Printf (PrintfType, printf) import System.Environment (getArgs, getProgName) import System.IO (IOMode(ReadMode), hGetContents, withFile) -- The 32 compass points defined in the Wikipedia page -- https://en.wikipedia.org/wiki/Points_of_the_compass#Compass_points. points :: [String] points = ["N", "NbE", "NNE", "NEbN", "NE", "NEbE", "ENE", "EbN", "E", "EbS", "ESE", "SEbE", "SE", "SEbS", "SSE", "SbE", "S", "SbW", "SSW", "SWbS", "SW", "SWbW", "WSW", "WbS", "W", "WbN", "WNW", "NWbW", "NW", "NWbN", "NNW", "NbW"] -- Convert degrees to a compass point index between 0 and 31. indexFromDegrees :: (RealFrac a, Integral b) => a -> b indexFromDegrees deg = (round (deg * 1000) + 5625) `mod` 360000 `div` 11250 -- Convert degrees to the name of a compass point. pointFromDegrees :: RealFrac a => a -> String pointFromDegrees deg = points !! indexFromDegrees deg printPointName :: PrintfType a => Double -> a printPointName deg = printf "%6.2f° %s\n" deg (pointFromDegrees deg) main :: IO () main = do args <- getArgs prog <- getProgName case args of [file] -> withFile file ReadMode $ \h -> do degs <- map read . lines <$> hGetContents h mapM_ printPointName degs _ -> putStrLn $ "Usage: " ++ prog ++ " path"Here’s a 32-point compass solution in Python.
def convert(degrees): compass_points = [ "N", "NbE", "NNE", "NEbN", "NE", "NEbE", "ENE", "EbN", "E", "EbS", "ESE", "SEbE", "SE", "SEbS", "SSE", "SbE", "S", "SbW", "SSW", "SWbS", "SW", "SWbW", "WSW", "WbS", "W", "WbN", "WNW", "NWbW", "NW", "NWbN", "NNW", "NbW" ] divisor = 360.0 / len(compass_points) idx = round(degrees / divisor) % len(compass_points) return compass_points[idx] print(convert(70)) print(convert(230))Output: