DMS To Decimal Degree Conversions And More

Page content

Working with satellite data I often have to convert a coordinate value or an angle value from the DMS notation (degree@minute’second") to a decimal degree in the floating point number format and vice versa.

DMS to Decimal Degree

Of course, I want to do this directly in an Emacs buffer, so I wrote a simple function that uses deg and hms functions provided by the built-in superb calc package.

(defun dms2deg (p1 p2)
  "Converts a region from the DMS (dd@mm'ss\")
  to a decimal degree.
  P1,P2 are the beginning and the end of the region
  (selection) respectively."
  (interactive "r")
  (let (s)
    (if (region-active-p)
        (progn
          (setq s (calc-eval
              (concat "deg(" (buffer-substring-no-properties p1 p2) ")")))
          (delete-active-region)
          (insert s))
      (message "No active region!"))
    )
  )

I’ve bound this function to the Ctrl+F6 key chord (C-f6 in the Emacs notation) globally. Of course, you can use your own key combination.

(global-set-key [(control f6)] #'dms2deg)

Now I can select a region in a buffer, press C-f6 and replace the selection with its decimal value, so 30@15'54" becomes 30.265.

Decimal Degree to DMS

The inverse function (bound to S-f6 or Shift+F6) replaces a floating point with the corresponding DMS string.

(defun deg2dms (p1 p2)
  "Converts a region from a decimal degree
  to the DMS format (dd@ mm' ss\").
  P1,P2 - beginning and end of the region
  (selection) respectively."
  (interactive "r")
  (let (s)
    (if (region-active-p)
        (progn
          (setq s (calc-eval
              (concat "hms(" (buffer-substring-no-properties p1 p2) ")")))
          (delete-active-region)
          (insert s))
      (message "No active region!"))
    )
  )

(global-set-key [(shift f6)] #'deg2dms)

Now if you select a region in a buffer and then press S-f6, your 30.265 becomes 30@ 15' 54."

Enhanced Version

After a while, I decided to enhance the first conversion function by adding an ‘inline calculator’ feature. It works quite simple: if a selected region contains the ‘@’ character, the region is considered as a coordinate (angle) value in the DMS notation, and it should be replaced with its decimal value. Otherwise, the region is considered as an arithmetic expression (in terms of the Emacs' calc) and its calculated value will be inserted just after the region.

(defun avs/calc-region (p1 p2)
  "Calculates a region or converts DMS to decimal.
   P1,P2 - beginning and end of the region
   respectively."
  (interactive "r")
  (if (region-active-p)
      (let ((b (buffer-substring-no-properties p1 p2)))
        (cond ((string-match-p "@" b) ; convert DMS to decimal ...
               (delete-active-region)
               (insert (calc-eval (concat "deg(" b ")"))))
              (t ; ... otherwise calculate a region
               (goto-char (region-end))
               (pop-mark)
               (insert " = " (calc-eval b))))
        )
    (message "No active region!"))
  )

(global-set-key [(f6)] 'avs/calc-region)

The enhanced function is bound to f6. So now if you press F6, 15@45'20" becomes 15.7555555556, and ((350.0 / 7) + 48.314) becomes ((350.0 / 7) + 48.314) = 98.314.

Conclusion

Emacs' calc is great.

Next

As a next step, I want to implement DMS and Decimal degree conversions to the DD:MM:SS format used by the excellent Generic Mapping Tools (GMT) toolbox.