0

I am trying to get the coordinates of a point, that is on a set distance from a starting position, but the end result is wrong.

First, I calculate the angle between the starting position and the desired destination:

private func calculateAngleBetweenLocations(currentLocation: CLLocationCoordinate2D, targetLocation: CLLocationCoordinate2D) -> Double {
    let fLat = self.degreesToRadians(currentLocation.latitude);
    let fLng = self.degreesToRadians(currentLocation.longitude);
    let tLat = self.degreesToRadians(targetLocation.latitude);
    let tLng = self.degreesToRadians(targetLocation.longitude);
    let deltaLng = tLng - fLng

    let y = sin(deltaLng) * cos(tLat)
    let x = cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(deltaLng)

    let bearing = atan2(y, x)

    return self.radiansToDegrees(bearing)
}

Then, I calculate the new point, given a distance:

private func coordinatesForMovement(endLocation: CLLocationCoordinate2D, distance: Double) -> CLLocationCoordinate2D {
    let angle = self.calculateAngleBetweenLocations(self.currentLocation, targetLocation: endLocation)
    let x = self.currentLocation.latitude + distance * cos(angle)
    let y = self.currentLocation.longitude + distance * sin(angle)

    return CLLocationCoordinate2D(latitude: x, longitude: y)
}

And this is the result (the feet are the starting position, the blue marker is the destination and the red marker is where the new calculated point is). I've tried passing the distance in meters and kilometers and every other floating point position, but never got the correct result. Any ideas?

smeshko
  • 1,184
  • 13
  • 27
  • `coordinatesForMovement` seems to be mixing dimensions. `currentCoordinate.latitude` is an angle (in degrees?), `distance` is a length, and `cos()` is dimensionless. So the program is adding an angle to a length, which is always going to be wrong. Angles add to angles, and lengths add to lengths. – emrys57 Feb 18 '16 at 16:50
  • Ok, so how do I fix it? – smeshko Feb 18 '16 at 23:42

1 Answers1

5

Ok, after some more digging, I found this answer, which resolves my problem. Here is my complete solution in swift:

internal func moveToLocation(location: CLLocationCoordinate2D, distance: Double) {
    let angle = self.calculateAngleBetweenLocations(self.currentLocation, targetLocation: location)
    let newLocation = self.coordinates(self.currentLocation, atDistance: distance, atAngle: angle)

    self.moveUser(newLocation: newLocation) 
}

private func coordinates(startingCoordinates: CLLocationCoordinate2D, atDistance: Double, atAngle: Double) -> CLLocationCoordinate2D {
    let distanceRadians = atDistance / 6371
    let bearingRadians = self.degreesToRadians(atAngle)
    let fromLatRadians = self.degreesToRadians(startingCoordinates.latitude)
    let fromLonRadians = self.degreesToRadians(startingCoordinates.longitude)

    let toLatRadians = asin(sin(fromLatRadians) * cos(distanceRadians) + cos(fromLatRadians) * sin(distanceRadians) * cos(bearingRadians))
    var toLonRadians = fromLonRadians + atan2(sin(bearingRadians) * sin(distanceRadians) * cos(fromLatRadians), cos(distanceRadians) - sin(fromLatRadians) * sin(toLatRadians));

    toLonRadians = fmod((toLonRadians + 3 * M_PI), (2 * M_PI)) - M_PI

    let lat = self.radiansToDegrees(toLatRadians)
    let lon = self.radiansToDegrees(toLonRadians)

    return CLLocationCoordinate2D(latitude: lat, longitude: lon)
}

private func calculateAngleBetweenLocations(currentLocation: CLLocationCoordinate2D, targetLocation: CLLocationCoordinate2D) -> Double {
    let fLat = self.degreesToRadians(currentLocation.latitude);
    let fLng = self.degreesToRadians(currentLocation.longitude);
    let tLat = self.degreesToRadians(targetLocation.latitude);
    let tLng = self.degreesToRadians(targetLocation.longitude);
    let deltaLng = tLng - fLng

    let y = sin(deltaLng) * cos(tLat)
    let x = cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(deltaLng)

    let bearing = atan2(y, x)

    return self.radiansToDegrees(bearing)
}

private func degreesToRadians(x: Double) -> Double {
    return M_PI * x / 180.0
}

private func radiansToDegrees(x: Double) -> Double {
    return x * 180.0 / M_PI
}
Community
  • 1
  • 1
smeshko
  • 1,184
  • 13
  • 27