Xah Lee has reintroduced a challenge from last year. Given a string of latitude/longitude is degrees, minutes, seconds, write a function that returns them as signed decimal numbers. That is,
"37°26′36.42″N 06°15′14.28″W" → (37.44345 -6.253966666666667)
I remember looking at this challenge last year and thinking it wasn't very interesting but when I started thinking about it this time, I realized that there are a couple of twists that do make it interesting.
Here's my solution in Elisp:
(require 'cl) (defun decimalize-lat-lon (lat-lon) "Decimalize latitude longitude \"37°26′36.42″N 06°15′14.28″W\" --> (37.44345 -6.253966666666667)" (let (result) (dolist (ll (split-string lat-lon " +")) (destructuring-bind (deg min sec dir) (split-string ll "[°′″]") (if (string= "" dir) (error "malformed lat-lon %s" ll)) (let ((factor (if (member dir '("S" "W")) -1 1))) (push (* factor (+ (string-to-number deg) (/ (string-to-number min) 60.0) (/ (string-to-number sec) 3600.0))) result)))) (reverse result)))
Elisp purists might complain about my using
push from the Common Lisp package but they're convenient and don't do anything that can't be done a bit more verbosely in pure Emacs Lisp. The real workhorse in this function is the Elisp
split-string function. It gets used twice: once to split the original string into latitude and longitude and once to break the latitude and longitude into their constituent parts.
The check on whether
dir is the empty string really checks for any missing constituents and is just a sanity check. I didn't bother rounding the numbers to any particular number of decimal places or making the function a command. Either of those are easily done if the user needs them.