46

I have GPS information presented in the form:

36°57'9" N 110°4'21" W

I can use the javascript functions of Chris Veness to convert degrees, minutes and seconds to numeric degrees, but first need to parse the GPS info into the individual latitude and longitude strings (with NSEW suffixes). I have read related posts on stackoverflow, but am not a regex expert (nor a programmer) and need some help with the parsing function. What's the best way to parse this string into latitude and longitude for use in the conversion function?

The result of all this will be a Web link that one can click on to see a Google map representation of location.

  • 3
    What exactly is the format of the output you are looking for? Is it something like: "36 57 9 N" and "110 4 21 W" for the input you have given? Does the input always come in this form? – Sinan Taifour Jul 16 '09 at 21:10
  • For the inputs I've shown, I'd expect the output to be two values: 36.95250 -110.07250 And yes, the input is always in this form. –  Jul 16 '09 at 22:01
  • That should read, "two values: 36.95250 and -110.07250 (without the "and" of course). Sorry. –  Jul 16 '09 at 22:04

9 Answers9

75

To parse your input use the following.

function ParseDMS(input) {
    var parts = input.split(/[^\d\w]+/);
    var lat = ConvertDMSToDD(parts[0], parts[1], parts[2], parts[3]);
    var lng = ConvertDMSToDD(parts[4], parts[5], parts[6], parts[7]);
}

The following will convert your DMS to DD

function ConvertDMSToDD(degrees, minutes, seconds, direction) {
    var dd = degrees + minutes/60 + seconds/(60*60);

    if (direction == "S" || direction == "W") {
        dd = dd * -1;
    } // Don't do anything for N or E
    return dd;
}

So your input would produce the following:

36°57'9" N  = 36.9525000
110°4'21" W = -110.0725000

Decimal coordinates can be fed into google maps to get points via GLatLng(lat, lng) (Google Maps API)

Charlie Martin
  • 8,208
  • 3
  • 35
  • 41
Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • Perhaps a little better: var parts = input.split(/[^\d\w]+/). Adjust offsets by -1. – Inshallah Jul 16 '09 at 21:35
  • That should work! I'll give it a try tonight. Thanks to all who responded. This site is the best! –  Jul 16 '09 at 22:06
  • I'm dealing with this issue to, though have to cater for some alternative HMS representations: `2°28’14”N`, `50 44.63N`, `S 038° 18.429’`. I only show these here in case others need to think about them too. – Drew Noakes Oct 03 '10 at 14:21
  • ParseDMS("36°57'9\" N") gives me 360.95. I don't understand why. Besides, 36°57'9.0" N would not work. – Sadık Jan 07 '14 at 21:08
  • 3
    You're doing input.split() on the lat/long, so you're working with strings, so you should wrap degrees, minutes and seconds in parseInt() before attempting arithmetic operations on them. It was causing weird results for me. – Rolf Jan 06 '17 at 00:01
  • if you know beforehand that 60*60 is 3600 why to compute? – João Pimentel Ferreira Nov 19 '17 at 21:37
  • second unit can contain real number e.g. N44°34'59.2 – Sang Sep 19 '18 at 04:20
  • you'll get some f***** up results if you don't wrap it in floats. `var dd = parseFloat(degrees) + parseFloat(minutes) / 60 + parseFloat(seconds) / (60 * 60);` – EugenSunic Jun 22 '23 at 06:59
19

Corrected the above functions and made the output an object.

function ParseDMS(input) {
    var parts = input.split(/[^\d\w\.]+/);    
    var lat = ConvertDMSToDD(parts[0], parts[2], parts[3], parts[4]);
    var lng = ConvertDMSToDD(parts[5], parts[7], parts[8], parts[9]);

    return {
        Latitude : lat,
        Longitude: lng,
        Position : lat + ',' + lng
    }
}


function ConvertDMSToDD(degrees, minutes, seconds, direction) {   
    var dd = Number(degrees) + Number(minutes)/60 + Number(seconds)/(60*60);

    if (direction == "S" || direction == "W") {
        dd = dd * -1;
    } // Don't do anything for N or E
    return dd;
}
Peter John
  • 191
  • 1
  • 2
  • 1
    if you know beforehand that 60*60 is 3600 why to compute? – João Pimentel Ferreira Nov 19 '17 at 21:33
  • @JoãoPimentelFerreira It can be easier to quickly understand. Many people might realize there are 60 seconds in a minute and 60 minutes in an hour/degree, but realizing there are 3600 seconds in an hour/degree is slightly less obvious. – J.D. Sandifer Oct 09 '20 at 18:39
11

My tweaked version coerces the string parts into Numbers so that they can actually be added together rather than concatenated. It also handles decimal values which are common for the Seconds component:

function ParseDMS(input) {
    var parts = input.split(/[^\d\w\.]+/);
    var lat = ConvertDMSToDD(parts[0], parts[1], parts[2], parts[3]);
    var lng = ConvertDMSToDD(parts[4], parts[5], parts[6], parts[7]);
}

The following will convert your DMS to DD

function ConvertDMSToDD(degrees, minutes, seconds, direction) {
    var dd = Number(degrees) + Number(minutes)/60 + Number(seconds)/(60*60);

    if (direction == "S" || direction == "W") {
        dd = dd * -1;
    } // Don't do anything for N or E
    return dd;
}
matt burns
  • 24,742
  • 13
  • 105
  • 107
6

here is my take on this:

function parse_gps( input ) {

if( input.indexOf( 'N' ) == -1 && input.indexOf( 'S' ) == -1 &&
    input.indexOf( 'W' ) == -1 && input.indexOf( 'E' ) == -1 ) {
    return input.split(',');
}

var parts = input.split(/[°'"]+/).join(' ').split(/[^\w\S]+/);

var directions = [];
var coords = [];
var dd = 0;
var pow = 0;

for( i in parts ) {

    // we end on a direction
    if( isNaN( parts[i] ) ) {

        var _float = parseFloat( parts[i] );

        var direction = parts[i];

        if( !isNaN(_float ) ) {
            dd += ( _float / Math.pow( 60, pow++ ) );
            direction = parts[i].replace( _float, '' );
        }

        direction = direction[0];

        if( direction == 'S' || direction == 'W' )
            dd *= -1;

        directions[ directions.length ] = direction;

        coords[ coords.length ] = dd;
        dd = pow = 0;

    } else {

        dd += ( parseFloat(parts[i]) / Math.pow( 60, pow++ ) );

    }

}

if( directions[0] == 'W' || directions[0] == 'E' ) {
    var tmp = coords[0];
    coords[0] = coords[1];
    coords[1] = tmp;
}

return coords;

}

This function doesn't handle all types of lat / long types, but it handles the following formats:

-31,2222,21.99999
-31 13 13 13.75S, -31 13 13 13.75W
-31 13 13 13.75S -31 13 13 13.75W
-31 13 13 13.75W -31 13.75S
36°57'9" N 110°4'21" W
110°4'21" W 36°57'9"N

Which is what i needed.

4

I got some NaN's on this function and needed to do this (don't ask me why)

function ConvertDMSToDD(days, minutes, seconds, direction) {
    var dd = days + (minutes/60) + seconds/(60*60);
    dd = parseFloat(dd);
    if (direction == "S" || direction == "W") {
        dd *= -1;
    } // Don't do anything for N or E
    return dd;
}
4

I used \d+(\,\d+) and \d+(.\d+) because can have float numbers

My final function:

 convertDMSToDD: function (dms) {
     let parts = dms.split(/[^\d+(\,\d+)\d+(\.\d+)?\w]+/);
     let degrees = parseFloat(parts[0]);
     let minutes = parseFloat(parts[1]);
     let seconds = parseFloat(parts[2].replace(',','.'));
     let direction = parts[3];

     console.log('degrees: '+degrees)
     console.log('minutes: '+minutes)
     console.log('seconds: '+seconds)
     console.log('direction: '+direction)

     let dd = degrees + minutes / 60 + seconds / (60 * 60);

     if (direction == 'S' || direction == 'W') {
       dd = dd * -1;
     } // Don't do anything for N or E
     return dd;
   }
Gustavo Rozolin
  • 1,070
  • 2
  • 13
  • 21
3

Joe, the script you've mentioned already did what do you want. With it you can convert lat and long and put it into link to see location in Google map:

var url = "http://maps.google.com/maps?f=q&source=s_q&q=&vps=3&jsv=166d&sll=" + lat.parseDeg() + "," + longt.parseDeg()
Alex Pavlov
  • 630
  • 5
  • 14
0

Using Shannon Antonio Black's regex pattern (above), my solution is:

function convertLatLong(input) {

if(!( input.toUpperCase() != input.toLowerCase()) ) {   // if geodirection abbr. isn't exist, it should be already decimal notation
    return `${input}:the coordinate already seems as decimal`
}
const parts = input.split(/[°'"]+/).join(' ').split(/[^\w\S]+/); // thanks to Shannon Antonio Black for regEx patterns 
const geoLetters = parts.filter(el=> !(+el) ) 
const coordNumber = parts.filter(n=>(+n)).map(nr=>+nr)
const latNumber = coordNumber.slice(0,(coordNumber.length/2))
const longNumber = coordNumber.slice((coordNumber.length/2))
const reducer = function(acc,coord,curInd){
   return acc + (coord/Math.pow( 60, curInd++ ))
}
let latDec = latNumber.reduce(reducer)
let longDec = longNumber.reduce(reducer)

if(geoLetters[0].toUpperCase()==='S') latDec = -latDec // if the geodirection is S or W, decimal notation should start with minus
if(geoLetters[1].toUpperCase()==='W') longDec= -longDec 

const dec= [{
    ltCoord: latDec,
    geoLet:geoLetters[0]
},
{
    longCoord: longDec,
    geoLet: geoLetters[1]
}]

return dec
}

I think that is more simplified version with EC6

0

function convertRawCoordinatesIntoDecimal(input) {
    let grade = parseInt(input.substring(0, input.indexOf("°")));
    let rest = input.substring(input.indexOf("°") + 1);
    let minutes = parseInt(rest.substring(0, rest.indexOf("'")));
    let seconds = parseInt(rest.substring(rest.indexOf("'") + 1).split('"')[0]);
    return grade + (minutes + seconds / 60) / 60;
}

function getCoordinates(input) {
    let parts = input.split(" "); //element 0 is N and element 2 is W coordinates
    return {
        [parts[1]]: convertRawCoordinatesIntoDecimal(parts[0]),
        [parts[3]]: convertRawCoordinatesIntoDecimal(parts[2])
    };
}

let input = `36°57'9" N 110°4'21" W`; //a test input
console.log(getCoordinates(input));

Result:

{
  "N": 36.9525,
  "W": 110.0725
}

Explanation:

  • we split by " ", getting an array of strings of four elements
  • element 1 will be the name of the first coordinate and element 3 will be the name of the second coordinate
  • element 0 will be the value of the first coordinate and element 2 will be the value of the second coordinate
  • we dissect the elements into grade, minutes and seconds respectively, all numerical values
  • the formula is grade + (minutes + seconds / 60) / 60
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175