Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active May 27, 2023 06:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dacr/4bd607523c8ddf30459b5a96d83baede to your computer and use it in GitHub Desktop.
Save dacr/4bd607523c8ddf30459b5a96d83baede to your computer and use it in GitHub Desktop.
geo conversions / published by https://github.com/dacr/code-examples-manager #0b3d43a7-3e8a-4bca-a768-3a28bd84c3ea/bfdf1d813f21789d7804ebf9a7ff9370e2bfb0eb
// summary : geo conversions
// keywords : scala, gps, dms, @testable
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 0b3d43a7-3e8a-4bca-a768-3a28bd84c3ea
// created-on : 2022-03-14T18:31:29+01:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// ---------------------
//> using scala "3.3.0"
//> using dep "org.scalatest::scalatest:3.2.16"
//> using objectWrapper
// ---------------------
import org.scalatest.*
import flatspec.*
import matchers.*
import scala.util.{Failure, Success, Try}
import org.scalatest.TryValues.*
import java.util.Locale
/*
- Positive latitudes are north of the equator
- Negative latitudes are south of the equator
- Positive longitudes are east of the Prime Meridian
- Negative longitudes are west of the Prime Meridian.
- Latitude and longitude are usually expressed in that sequence, latitude before longitude.
- Reference NSEW is mandatory to give the right value sign (because -0° is not valid of course)
*/
val dmsRE = """[-+]?(\d+)[°]\s*(\d+)['′]\s*(\d+(?:[.,]\d+)?)(?:(?:")|(?:'')|(?:′′)|(?:″))""".r
def convert(d: Double, m: Double, s: Double): Double = d + m / 60d + s / 3600d
def degreesMinuteSecondsToDecimalDegrees(
dms: String,
ref: String
): Try[Double] = Try {
val dd = dms.trim match {
case dmsRE(d, m, s) => convert(d.toDouble, m.toDouble, s.replaceAll(",", ".").toDouble)
}
if ("NE".contains(ref.trim.toUpperCase.head)) dd else -dd
}
class GeoTest extends AnyFlatSpec with should.Matchers {
"degrees minutes seconds" can "be decoded to decimal degrees" in {
val toTest = List(
("N", "1° 00′ 0″", 1.0d),
("N", "0° 06′ 0″", 0.1d),
("N", "0° 00′ 36″", 0.01d),
("N", "0° 00′ 0.036″", 0.00001),
("E", "30° 15' 50\"", 30.263888889d),
("S", "3°58'24\"", -3.9733333333333336d),
("S", "-3°58'24\"", -3.9733333333333336d),
("N", "48° 18' 57,67\"", 48.31601944444444d)
)
toTest.foreach((ref, dms, dd) => {
degreesMinuteSecondsToDecimalDegrees(dms, ref).success.value should equal(dd +- .000000001)
})
}
}
org.scalatest.tools.Runner.main(Array("-oDF", "-s", classOf[GeoTest].getName))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment