ConvertToDD.R 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. ConvertToDD <-
  2. function(XY, FileSep = NULL, LatColName, LongColName)
  3. {
  4. if(!is.object(XY) & !is.character(XY)) stop("XY must be an object in R or a file path character string.")
  5. if(is.object(XY)) XY<- data.frame(XY)
  6. if(is.character(XY)){
  7. if(!file.exists(XY)) stop("Character string input for XY argument does not resemble an existing file path.")
  8. if(is.null(FileSep)) stop("To load a file as input, you must also specify its delimiter (FileSep).")
  9. XY<- read.delim(XY, sep = FileSep)
  10. }
  11. DMS.lat <- as.character(XY[ ,which(names(XY) == LatColName)])
  12. DMS.long <- as.character(XY[ ,which(names(XY) == LongColName)])
  13. which.format.lat <- gregexpr("([^0-9.][0-9])", DMS.lat)
  14. which.format.long <- gregexpr("([^0-9.][0-9])", DMS.long)
  15. DM.or.DMS.lat <- rep(NA, nrow(XY))
  16. DM.or.DMS.long <- rep(NA, nrow(XY))
  17. for(i in 1:nrow(XY)){
  18. DM.or.DMS.lat[i] <- length(which.format.lat[[i]])
  19. DM.or.DMS.long[i] <- length(which.format.long[[i]])
  20. }
  21. if(any(DM.or.DMS.lat != DM.or.DMS.long)){
  22. stop("A coordinate has been recognised with inconsistent formatting between lat and long.
  23. Check for erroneous non-numeric characters. See the help page for advice on correct formats.")
  24. }
  25. if(any(DM.or.DMS.lat != 1 & DM.or.DMS.lat != 2)){
  26. stop("A coordinate has been found that does not match the required format for degrees minutes seconds or degrees minutes.
  27. Check for erroneous non-numeric characters. See the help page for advice on correct formats.")
  28. }
  29. DD.lat <- rep(NA, nrow(XY))
  30. DD.long <- rep(NA, nrow(XY))
  31. D.lat <- rep(NA, nrow(XY))
  32. D.long <- rep(NA, nrow(XY))
  33. M.lat <- rep(NA, nrow(XY))
  34. M.long <- rep(NA, nrow(XY))
  35. S.lat <- rep(NA, nrow(XY))
  36. S.long <- rep(NA, nrow(XY))
  37. D.point.lat <- regexpr("[^0-9][0-9]{1,2}[^0-9]", DMS.lat)
  38. D.point.long <- regexpr("[^0-9][0-9]{1,2}[^0-9]", DMS.long)
  39. for(i in 1:nrow(XY)){
  40. # For degrees minutes seconds coordinates.
  41. if(DM.or.DMS.lat[i] == 2){
  42. # Latitude
  43. D.lat[i] <- as.numeric(substr(DMS.lat[i], 1, D.point.lat[i]-1))
  44. M.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+1, D.point.lat[i]+attr(D.point.lat, "match.length")[i]-2))
  45. if(substr(DMS.lat[i], nchar(DMS.lat[i]), nchar(DMS.lat[i])) == 'N'){
  46. S.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+attr(D.point.lat, "match.length")[i], nchar(DMS.lat[i])-2))
  47. } else {
  48. if(substr(DMS.lat[i], nchar(DMS.lat[i]), nchar(DMS.lat[i])) == 'S'){
  49. D.lat[i] <- -D.lat[i]
  50. S.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+attr(D.point.lat, "match.length")[i], nchar(DMS.lat[i])-2))
  51. } else {
  52. S.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+attr(D.point.lat, "match.length")[i], nchar(DMS.lat[i])-1))
  53. }
  54. }
  55. # Calculate latitude decimal degrees.
  56. if(D.lat[i] >= 0){
  57. DD.lat[i] <- D.lat[i] + (M.lat[i] / 60) + (S.lat[i] / 3600)
  58. } else {
  59. DD.lat[i] <- -(S.lat[i] / 3600) - (M.lat[i] / 60) + D.lat[i]
  60. }
  61. if(substr(DMS.lat[i], nchar(DMS.lat[i]), nchar(DMS.lat[i])) == 'S' & D.lat[i] == 0){
  62. DD.lat[i] <- -DD.lat[i]
  63. }
  64. # Longitude
  65. D.long[i] <- as.numeric(substr(DMS.long[i], 1, D.point.long[i]-1))
  66. M.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+1, D.point.long[i]+attr(D.point.long, "match.length")[i]-2))
  67. if(substr(DMS.long[i], nchar(DMS.long[i]), nchar(DMS.long[i])) == 'E'){
  68. S.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+attr(D.point.long, "match.length")[i], nchar(DMS.long[i])-2))
  69. } else {
  70. if(substr(DMS.long[i], nchar(DMS.long[i]), nchar(DMS.long[i])) == 'W'){
  71. S.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+attr(D.point.long, "match.length")[i], nchar(DMS.long[i])-2))
  72. D.long[i] <- -D.long[i]
  73. } else {
  74. S.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+attr(D.point.long, "match.length")[i], nchar(DMS.long[i])-1))
  75. }
  76. }
  77. # Calculate longitude decimal degrees.
  78. if(D.long[i] >= 0){
  79. DD.long[i] <- D.long[i] + (M.long[i] / 60) + (S.long[i] / 3600)
  80. } else {
  81. DD.long[i] <- -(S.long[i] / 3600) - (M.long[i] / 60) + D.long[i]
  82. }
  83. if(substr(DMS.long[i], nchar(DMS.long[i]), nchar(DMS.long[i])) == 'W' & D.long[i] == 0){
  84. DD.long[i] <- -DD.long[i]
  85. }
  86. }
  87. # For degrees minutes coordinates.
  88. if(DM.or.DMS.lat[i] == 1){
  89. # Latitude
  90. D.lat[i] <- as.numeric(substr(DMS.lat[i], 1, D.point.lat[i]-1))
  91. if(substr(DMS.lat[i], nchar(DMS.lat[i]), nchar(DMS.lat[i])) == 'N'){
  92. M.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+1, nchar(DMS.lat[i])-2))
  93. } else {
  94. if(substr(DMS.lat[i], nchar(DMS.lat[i]), nchar(DMS.lat[i])) == 'S'){
  95. M.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+1, nchar(DMS.lat[i])-2))
  96. D.lat[i] <- -D.lat[i]
  97. } else {
  98. M.lat[i] <- as.numeric(substr(DMS.lat[i], D.point.lat[i]+1, nchar(DMS.lat[i])-1))
  99. }
  100. }
  101. # Calculate latitude decimal degrees.
  102. if(D.lat[i] >= 0){
  103. DD.lat[i] <- D.lat[i] + (M.lat[i] /60)
  104. } else {
  105. DD.lat[i] <- -(M.lat[i] / 60) + D.lat[i]
  106. }
  107. if(substr(DMS.lat[i], nchar(DMS.lat[i]), nchar(DMS.lat[i])) == 'S' & D.lat[i] == 0){
  108. DD.lat[i]<- -DD.lat[i]
  109. }
  110. # Longitude
  111. D.long[i] <- as.numeric(substr(DMS.long[i], 1, D.point.long[i]-1))
  112. if(substr(DMS.long[i], nchar(DMS.long[i]), nchar(DMS.long[i])) == 'E'){
  113. M.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+1, nchar(DMS.long[i])-2))
  114. } else {
  115. if(substr(DMS.long[i], nchar(DMS.long[i]), nchar(DMS.long[i])) == 'W'){
  116. M.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+1, nchar(DMS.long[i])-2))
  117. D.long[i] <- -D.long[i]
  118. } else {
  119. M.long[i] <- as.numeric(substr(DMS.long[i], D.point.long[i]+1, nchar(DMS.long[i])-1))
  120. }
  121. }
  122. # Calculate longitude decimal degrees.
  123. if(D.long[i] >= 0){
  124. DD.long[i] <- (D.long[i]) + ((M.long[i])/60)
  125. } else {
  126. DD.long[i] <- -((M.long[i])/60) + (D.long[i])
  127. }
  128. if(substr(DMS.long[i], nchar(DMS.long[i]), nchar(DMS.long[i])) == 'W' & D.long[i] == 0){
  129. DD.long[i] <- -DD.long[i]
  130. }
  131. }
  132. }
  133. # Checks that lat answers are going to be sensible before returning result.
  134. if(any(abs(D.lat) > 90)){
  135. cat("Invalid degrees of latitude entries:", "\n", XY[which(abs(D.lat) > 90), ], "\n")
  136. stop("Range of valid degrees is from -90 to 90.")
  137. }
  138. if(any(M.lat < 0 & M.lat > 60)){
  139. cat("Invalid minutes entries:", "\n", XY[which(M.lat > 0 & M.lat < 60), ], "\n")
  140. stop("Range of valid minutes is from 0 to 60.")
  141. }
  142. if(any(DM.or.DMS.lat == 2)){
  143. if(any(S.lat[which(DM.or.DMS.lat == 2)] < 0 & S.lat[which(DM.or.DMS.lat == 2)] > 60)){
  144. cat("Invalid seconds entries:", "\n",
  145. XY[which(S.lat[which(DM.or.DMS.lat == 2)] > 0 & S.lat[which(DM.or.DMS.lat == 2)] < 60), ], "\n")
  146. stop("Range of valid seconds is from 0 to 60.")
  147. }
  148. }
  149. # Checks that long answers are going to be sensible before returning result.
  150. if(any(abs(D.long) > 180)){
  151. cat("Invalid degrees of longitude entries:", "\n", XY[which(abs(D.long) > 180), ], "\n")
  152. stop("Range of valid degrees longitude is from -180 to 180.")
  153. }
  154. if(any(M.long < 0 & M.long > 60)){
  155. cat("Invalid minutes entries:", "\n", XY[which(M.long > 0 & M.long < 60), ], "\n")
  156. stop("Range of valid minutes is from 0 to 60.")
  157. }
  158. if(any(DM.or.DMS.lat == 2)){
  159. if(any(S.long[which(DM.or.DMS.lat == 2)] < 0 & S.long[which(DM.or.DMS.lat == 2)] > 60)){
  160. cat("Invalid seconds entries:", "\n",
  161. XY[which(S.long[which(DM.or.DMS.lat == 2)] > 0 & S.long[which(DM.or.DMS.lat == 2)] < 60), ], "\n")
  162. stop("Range of valid seconds is from 0 to 60.")
  163. }
  164. }
  165. # Final checks that -90 <= decimal lat <= 90 and -180 <= decimal long <= 180, and then return the result.
  166. lat.res.check <- all(abs(DD.lat) <= 90)
  167. long.res.check <- all(abs(DD.long) <= 180)
  168. if(!lat.res.check & !long.res.check){
  169. stop("It appears an invalid answer has been calculated. Check for values just beyond the valid ranges of lat and long.")
  170. } else {
  171. return(cbind(DD.lat, DD.long))
  172. }
  173. }