27

I want to convert my geographic coordinates from degrees to decimals, my data are as follows:

         lat     long
105252 30°25.264 9°01.331
105253 30°39.237 8°10.811
105255 31°37.760 8°06.040
105258 31°41.190 8°06.557
105259 31°41.229 8°06.622
105260 31°38.891 8°06.281

I have this code but I can not see why it is does not work:

convert<-function(coord){
tmp1=strsplit(coord,"°")
tmp2=strsplit(tmp1[[1]][2],"\\.")
dec=c(as.numeric(tmp1[[1]][1]),as.numeric(tmp2[[1]]))
return(dec[1]+dec[2]/60+dec[3]/3600) 
} 
don_convert=don1
for(i in 1:nrow(don1)){don_convert[i,2]=convert(as.character(don1[i,2]));              don_convert[i,3]=convert(as.character(don1[i,3]))}

The convert function works but the code where I am asking the loop to do the job for me does not work.

Any suggestion is apperciated.

Jochem
  • 3,295
  • 4
  • 30
  • 55
Homap
  • 2,142
  • 5
  • 24
  • 34
  • 3
    It looks like the convert function is expecting degrees, minutes, and seconds, but your input seems to have degrees and floating point minutes, with no seconds field. And it doesn't look like it would handle negative values properly. Not sure if either of those issues is related to your problem, though. – Jim Lewis Jan 18 '13 at 18:01

7 Answers7

29

Use the measurements package from CRAN which has a unit conversion function already so you don't need to make your own:

x = read.table(text = "
   lat     long
105252 30°25.264 9°01.331
105253 30°39.237 8°10.811
105255 31°37.760 8°06.040
105258 31°41.190 8°06.557
105259 31°41.229 8°06.622
105260 31°38.891 8°06.281",
header = TRUE, stringsAsFactors = FALSE)

Once your data.frame is set up then:

# change the degree symbol to a space
x$lat = gsub('°', ' ', x$lat)
x$long = gsub('°', ' ', x$long)

# convert from decimal minutes to decimal degrees
x$lat = measurements::conv_unit(x$lat, from = 'deg_dec_min', to = 'dec_deg')
x$long = measurements::conv_unit(x$long, from = 'deg_dec_min', to = 'dec_deg')

Resulting in the end product:

                    lat             long
105252 30.4210666666667 9.02218333333333
105253         30.65395 8.18018333333333
105255 31.6293333333333 8.10066666666667
105258          31.6865 8.10928333333333
105259         31.68715 8.11036666666667
105260 31.6481833333333 8.10468333333333
Gord Stephen
  • 965
  • 11
  • 15
CephBirk
  • 6,422
  • 5
  • 56
  • 74
  • 2
    This is probably the simplest solution (and you get other coordinate conversions for free as well). I would just add that the functionality has now been moved into the `measurements` package, and is deprecated in `birk`. – Gord Stephen Oct 17 '16 at 13:36
9

Try using the char2dms function in the sp library. It has other functions that will additionally do decimal conversion.

library("sp")
?char2dms
Noah
  • 1,404
  • 8
  • 12
  • Wrap the output in `as.numeric()` if you want to convert something like `"35d8'2.222\"E"` into `35.13395`. You will need the trailing compass direction. – seasmith Nov 24 '19 at 15:40
7

A bit of vectorization and matrix manipulation will make your function much simpler:

x <- read.table(text="
       lat     long
105252 30°25.264 9°01.331
105253 30°39.237 8°10.811
105255 31°37.760 8°06.040
105258 31°41.190 8°06.557
105259 31°41.229 8°06.622
105260 31°38.891 8°06.281",
                header=TRUE, stringsAsFactors=FALSE)

x

The function itself makes use of:

  • strsplit() with the regex pattern "[°\\.]" - this does the string split in one step
  • sapply to loop over the vector

Try this:

convert<-function(x){
  z <- sapply((strsplit(x, "[°\\.]")), as.numeric)
  z[1, ] + z[2, ]/60 + z[3, ]/3600
} 

Try it:

convert(x$long)
[1] 9.108611 8.391944 8.111111 8.254722 8.272778 8.178056

Disclaimer: I didn't check your math. Use at your own discretion.

Andrie
  • 176,377
  • 47
  • 447
  • 496
  • As the comment on the question notes, the input is degrees and decimal minutes not degree, minute and seconds. This answer works if you split only on the `°` and remove the `z[3, ]/3600` part. – nniloc Sep 10 '20 at 16:32
2

Thanks for answers by @Gord Stephen and @CephBirk. Sure helped me out. I thought I'd just mention that I also found that measurements::conv_unit doesn't deal with "E/W" "N/S" entries, it requires positive/negative degrees. My coordinates comes as character strings "1 1 1W" and needs to first be converted to "-1 1 1".
I thought I'd share my solution for that.

df <- c("1 1 1E", "1 1 1W", "2 2 2N","2 2 2S")  
measurements::conv_unit(df, from = 'deg_min_sec', to = 'dec_deg')
[1] "1.01694444444444" NA                 NA                 NA  
Warning message:
In split(as.numeric(unlist(strsplit(x, " "))) * c(3600, 60, 1),  :
  NAs introduced by coercion

ewns <- ifelse( str_extract(df,"\\(?[EWNS,.]+\\)?") %in% c("E","N"),"+","-")
dms <- str_sub(df,1,str_length(df)-1)
df2 <- paste0(ewns,dms)

df_dec <- measurements::conv_unit(df2, 
                                  from = 'deg_min_sec', 
                                  to = 'dec_deg'))
df_dec
[1] "1.01694444444444"  "-1.01694444444444" "2.03388888888889"  "-2.03388888888889"
as.numeric(df_dec)
[1]  1.016944 -1.016944  2.033889 -2.033889
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Robbes
  • 135
  • 8
1

Have a look at the command degree in the package OSMscale.

ascripter
  • 5,665
  • 12
  • 45
  • 68
S Schmidt
  • 41
  • 1
  • 7
  • 1
    this was the answer that worked better for me. Thanks a lot! Take a look at some examples from the help file: ``` d <- read.table(header=TRUE, sep=",", text=" lat, long 52.366360, 13.024181 -32.599203, -55.809601") degree(lat, long, data=d) # You can use the degree symbol and escaped quotation mark (\") as well. degree("52'21'58.9'N", "13'1'27.1'E") ``` – Charles Santana Sep 28 '18 at 21:51
0

As Jim Lewis commented before it seems your are using floating point minutes. Then you only concatenate two elements on

dec=c(as.numeric(tmp1[[1]][1]),as.numeric(tmp2[[1]]))

Having degrees, minutes and seconds in the form 43°21'8.02 which as.character() returns "43°21'8.02\"", I updated your function to

convert<-function(coord){
  tmp1=strsplit(coord,"°")
  tmp2=strsplit(tmp1[[1]][2],"'")
  tmp3=strsplit(tmp2[[1]][2],"\"")
  dec=c(as.numeric(tmp1[[1]][1]),as.numeric(tmp2[[1]][1]),as.numeric(tmp3[[1]]))
  c<-abs(dec[1])+dec[2]/60+dec[3]/3600
  c<-ifelse(dec[1]<0,-c,c)
  return(c)
}

adding the alternative for negative coordinates, and works great for me . I still don't get why char2dms function in the sp library didn't work for me.

Thanks

AleRuete
  • 313
  • 3
  • 7
0

Another less elegant option using substring instead of strsplit. This will only work if all your positions have the same number of digits. For negative co-ordinates just multiply by -1 for the correct decimal degree.

x$LatDD<-(as.numeric(substring(x$lat, 1,2))
+ (as.numeric(substring(x$lat, 4,9))/60))
x$LongDD<-(as.numeric(substring(x$long, 1,1))
       + (as.numeric(substring(x$long, 3,8))/60))