import squants.space.{Radians, Meters} import squants.{Angle, Length} object Geospatial { case class Longitude(angle: Angle) { require( (Radians(-Math.PI) <= angle) && (angle < Radians(Math.PI)), "angle.inRadians must be greater than or equal to -Math.PI and less than Math.PI" ) } case class Latitude(angle: Angle) { require( (Radians(-(Math.PI * 0.5d)) <= angle) && (angle < Radians(Math.PI * 0.5d)), "angle.inRadians must be greater than or equal to -(Math.PI * 0.5d) and less than (Math.PI * 0.5d)" ) } case class Distance(length: Length) { require( Meters(0.0d) <= length, "length.inMeters must be greater than or equal to 0.0d" ) } case class Bearing(angle: Angle) { require( (Radians(0.0d) <= angle) && (angle < Radians(Math.PI * 2.0d)), "angle.inRadians must be greater than or equal to 0.0d and less than (Math.PI * 2.0d)" ) } case class Coordinate(longitude: Longitude, latitude: Latitude) def calculateDistance( coordinate1: Coordinate, coordinate2: Coordinate ): (Distance, Bearing, Bearing) = { def calculateDistanceUsingLegacyCodeRifeWithDoubles( coordinate1LongitudeInRadians: Double, coordinate1LatitudeInRadians: Double, coordinate2LongitudeInRadians: Double, coordinate2LatitudeInRadians: Double ): (Double, Double, Double) = { //Legacy code encapsulated here only works with meters and radians //returns (distance, initial bearing, final bearing) (1.0d, 1.0d, 2.0d) //TODO: replace with real calculation results } val (coordinate1InRadians, coordinate2InRadians) = ( (coordinate1.longitude.angle.toRadians, coordinate1.latitude.angle.toRadians), (coordinate2.longitude.angle.toRadians, coordinate2.latitude.angle.toRadians) ) val (distanceInMeters, bearingInitialInRadians, bearingFinalInRadians) = calculateDistanceUsingLegacyCodeRifeWithDoubles( coordinate1InRadians._1, coordinate1InRadians._2, coordinate2InRadians._1, coordinate2InRadians._2 ) ( Distance(Meters(distanceInMeters)), Bearing(Radians(bearingInitialInRadians)), Bearing(Radians(bearingFinalInRadians)) ) } def calculateCoordinate( coordinate1: Coordinate, bearingInitial: Bearing, distance: Distance ): Coordinate = { def calculateCoordinateUsingLegacyCodeRifeWithDoubles( coordinate1Longitude: Double, coordinate1Latitude: Double, bearingInitialInRadians: Double, distanceInMeters: Double ): (Double, Double) = { //Legacy code encapsulated here only works with meters and radians //returns (longitude, latitude) (-1.0d, 1.0d) //TODO: replace with real calculation results } val (coordinate1InRadians, bearingInitialInRadians, distanceInMeters) = ( (coordinate1.longitude.angle.toRadians, coordinate1.latitude.angle.toRadians), bearingInitial.angle.toRadians, distance.length.toMeters ) val (longitude, latitude) = calculateCoordinateUsingLegacyCodeRifeWithDoubles( coordinate1InRadians._1, coordinate1InRadians._2, bearingInitialInRadians, distanceInMeters ) Coordinate(Longitude(Radians(longitude)), Latitude(Radians(latitude))) } }