18

I receive the latitude and longitude from GPS with this format:

Latitude : 78°55'44.29458"N

I need convert this data to:

latitude: 78.9288888889

I found this code here: link

import re

def dms2dd(degrees, minutes, seconds, direction):
    dd = float(degrees) + float(minutes)/60 + float(seconds)/(60*60);
    if direction == 'E' or direction == 'S':
        dd *= -1
    return dd;

def dd2dms(deg):
    d = int(deg)
    md = abs(deg - d) * 60
    m = int(md)
    sd = (md - m) * 60
    return [d, m, sd]

def parse_dms(dms):
    parts = re.split('[^\d\w]+', dms)
    lat = dms2dd(parts[0], parts[1], parts[2], parts[3])
 
    return (lat)

dd = parse_dms("78°55'44.33324"N )

print(dd)

It is working for for this format

dd = parse_dms("78°55'44.33324'N" )

but it is not working for my datafromat. Can anyone help me to solve this problem?

clifgray
  • 4,313
  • 11
  • 67
  • 116
bikuser
  • 2,013
  • 4
  • 33
  • 57

10 Answers10

23

Here's my one liner (fine, fine – maybe it's two lines) :)

import re
lat = '''51°36'9.18"N'''
deg, minutes, seconds, direction =  re.split('[°\'"]', lat)
(float(deg) + float(minutes)/60 + float(seconds)/(60*60)) * (-1 if direction in ['W', 'S'] else 1)

This outputs 51.60255

Inti
  • 3,443
  • 1
  • 29
  • 34
16

The function above dms2dd is incorrect.

Actual (With error):

if direction == 'E' or direction == 'N':
        dd *= -1

Corrected Condition:

if direction == '**W**' or direction == '**S**':
        dd *= -1
General Grievance
  • 4,555
  • 31
  • 31
  • 45
10

The problem is that the seconds 44.29458 are split at ..

You could either define the split characters directly (instead of where not to split):

>>> re.split('[°\'"]+', """78°55'44.29458"N""")
['78', '55', '44.29458', 'N']

or leave the regular expression as it is and merge parts 2 and 3:

dms2dd(parts[0], parts[1], parts[2] + "." + parts[3], parts[4])

Update:

Your method call dd = parse_dms("78°55'44.33324"N ) is a syntax error. Add the closing " and escape the other one. Or use tripple quotes for the string definition:

parse_dms("""78°55'44.29458"N""")
Falko
  • 17,076
  • 13
  • 60
  • 105
  • It is still not working when I add the second option to the code – bikuser Nov 30 '15 at 11:36
  • Um, if you call your function like `parse_dms("78°55'44.33324"N )` that's a syntax error. You need to pass the string like I did: `parse_dms("""78°55'44.29458"N""")`. Alternatively you can escape the quotes like `"78°55'44.29458\"N"` or `'78°55\'44.29458"N'`. For more details you should look into the syntax for defining strings in Python. – Falko Nov 30 '15 at 11:38
  • ahh okey Now I got it. Thanks Falko :) – bikuser Nov 30 '15 at 11:43
  • If I have an array containing large number of such coordinates then how do I convert by this function? – bikuser Nov 30 '15 at 11:52
4

I know this is an old question, but for whoever is following along, just thought I'd point out that you seem to have incorrect logic in your dms2dd() function regarding the sign of your decimals. You have:

if direction == 'E' or direction == 'N':
    dd *= -1

But it should only be negative if the direction is West (W) of the Prime Meridian or South (S) of the equator. So it should rather be:

if direction == 'W' or direction == 'S':
    dd *= -1

Here's a quote from a thorough guide: https://www.ubergizmo.com/how-to/read-gps-coordinates/

The coordinate for the line of latitude represents north of the Equator because it is positive. If the number is negative, it represents south of the Equator.

[...] The coordinate for the line of longitude represents east of the Prime Meridian because it is positive. If the number is negative, it represents west of the Prime Meridian.

Isaac Asante
  • 435
  • 1
  • 5
  • 21
4

You can use this module https://pypi.org/project/dms2dec/

Convert DMS to Decimal.

Usage

from dms2dec.dms_convert import dms2dec

dms2dec('''36°44'47.69"N''') # converts to dec
dms2dec('''3° 2'33.53"E''') # converts to dec
Victor Stanescu
  • 451
  • 3
  • 9
2

I have slightly modified the re:

parts = re.split('[^\d\w\.]+', dms)

And as @Falko advised to make it work, you can either use double double quotes or escape your quotes characters

parse_dms("53°19\'51.8\"N")
Lili
  • 21
  • 1
0

For multiple coordinates, you can read them in using pandas. Formatting is important - there should not be any white spaces. White spaces can be removed using the replace function. The outputs can be easily saved as a text file or spreadsheet. I just printed them for validation and rounded the decimals locations out to 4.

### read input file
df = pd.read_excel('dms.xlsx')

n = len(df)

for i in range(n):
  Lat_d = round(parse_dms(df.Lat[i].replace(" ", "")),4)
  Long_d = round(parse_dms(df.Long[i].replace(" ", "")),4)
  print(Lat_d, Long_d)
bfree67
  • 669
  • 7
  • 6
0

You can use the function clean_lat_long() from the library DataPrep if your data is in a DataFrame. Install DataPrep with pip install dataprep.

from dataprep.clean import clean_lat_long
df = pd.DataFrame({"Latitude": ["78°55'44.29458''N", "51°36'9.18''N"]})

df2 = clean_lat_long(df, lat_col="Latitude")
            Latitude  Latitude_clean
0  78°55'44.29458''N         78.9290
1      51°36'9.18''N         51.6026
victoria55
  • 225
  • 2
  • 6
  • This looked good but unfortunately when installing it with anaconda it wants to massively downgrade the pandas library, it seems it's not compatible with pandas 2 – Richard Whitehead Jun 18 '23 at 13:33
0

You could simply use pygeodesy which is useful for many other functions anyway, like finding mid-points of coordinates, accurate distance and bearing calculations.

from pygeodesy import parse3llh, fstr

x = parse3llh('000° 00′ 05.31″W, 51° 28′ 40.12″ N')
print (fstr(x, prec=6))

Result

51.477811, -0.001475, 0.0
BBSysDyn
  • 4,389
  • 8
  • 48
  • 63
0

https://github.com/medo-mi/dms-to-dd

import re

#Degrees Minutes Seconds to Decimal Degrees
def dms_dd(dd):
    dd = f"""{dd}"""
    dd = re.sub('[^a-zA-Z0-9. ]', '', dd)
    dd = dd.split(" ")
    return round(float(dd[0])+(float(dd[1])/60)+(float(dd[2])/3600), 8)
medo-mi
  • 11
  • 2