118

Given an existing point in lat/long, distance in (in KM) and bearing (in degrees converted to radians), I would like to calculate the new lat/long. This site crops up over and over again, but I just can't get the formula to work for me.

The formulas as taken the above link are:

lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))

lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))

The above formula is for MSExcel where-

asin          = arc sin()   
d             = distance (in any unit)   
R             = Radius of the earth (in the same unit as above)  
and hence d/r = is the angular distance (in radians)  
atan2(a,b)    = arc tan(b/a)  
θ is the bearing (in radians, clockwise from north);  

Here's the code I've got in Python.

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = 52.20472 * (math.pi * 180) #Current lat point converted to radians
lon1 = 0.14056 * (math.pi * 180) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
             math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
                     math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

print(lat2)
print(lon2)

I get

lat2 = 0.472492248844 
lon2 = 79.4821662373
6
  • 1
    @GWW I was getting an answer that didn't make sense. The reason it didn't make sense because because I wasn't converting the answers back to degrees. Code changed and included in the original post as an edit.
    – David M
    Commented Aug 28, 2011 at 17:05
  • 6
    You should simply submit your edit as an answer, and accept that answer, to make it more clear that you resolved your own problem. Otherwise, SO will penalise you for leaving an question unresolved, making it slightly more likely that future users will not bother to answer your questions.
    – Cerin
    Commented Oct 12, 2011 at 21:35
  • You will get better precision and results if you use numpy objects. Commented Oct 16, 2011 at 11:52
  • shouldn't that be "lat1 = 52.20472 * (math.pi */180)"? Commented Nov 20, 2019 at 10:53
  • Why should the latitude change if the bearing is 90 degrees? Isn’t that just moving along the longitudinal?
    – danjampro
    Commented Sep 30, 2021 at 22:05

15 Answers 15

135

Needed to convert answers from radians back to degrees. Working code below:

from math import asin, atan2, cos, degrees, radians, sin

def get_point_at_distance(lat1, lon1, d, bearing, R=6371):
    """
    lat: initial latitude, in degrees
    lon: initial longitude, in degrees
    d: target distance from initial
    bearing: (true) heading in degrees
    R: optional radius of sphere, defaults to mean radius of earth

    Returns new lat/lon coordinate {d}km from initial, in degrees
    """
    lat1 = radians(lat1)
    lon1 = radians(lon1)
    a = radians(bearing)
    lat2 = asin(sin(lat1) * cos(d/R) + cos(lat1) * sin(d/R) * cos(a))
    lon2 = lon1 + atan2(
        sin(a) * sin(d/R) * cos(lat1),
        cos(d/R) - sin(lat1) * sin(lat2)
    )
    return (degrees(lat2), degrees(lon2),)

lat = 52.20472
lon = 0.14056
distance = 15
bearing = 90
lat2, lon2 = get_point_at_distance(lat, lon, distance, bearing)

# lat2  52.20444 - the lat result I'm hoping for
# lon2  0.36056 - the long result I'm hoping for.

print(lat2, lon2)
# prints "52.20451523755824 0.36067845713550956"
6
41

The geopy library supports this:

import geopy
from geopy.distance import VincentyDistance

# given: lat1, lon1, b = bearing in degrees, d = distance in kilometers

origin = geopy.Point(lat1, lon1)
destination = VincentyDistance(kilometers=d).destination(origin, b)

lat2, lon2 = destination.latitude, destination.longitude

Found via https://stackoverflow.com/a/4531227/37610

2
35

This question is known as the direct problem in the study of geodesy.

This is indeed a very popular question and one that is a constant cause of confusion. The reason is that most people are looking for a simple and straight-forward answer. But there is none, because most people asking this question are not supplying enough information, simply because they are not aware that:

  1. Earth is not a perfect sphere, since it is flattened/compressed by it poles
  2. Because of (1) earth does not have a constant Radius, R. See here.
  3. Earth is not perfectly smooth (variations in altitude) etc.
  4. Due to tectonic plate movement, a geographic point's lat/lon position may change by several millimeters (at least), every year.

Therefore there are many different assumptions used in the various geometric models that apply differently, depending on your needed accuracy. So to answer the question you need to consider to what accuracy you would like to have your result.

Some examples:

  • I'm just looking for an approximate location to the nearest few kilometers for small ( < 100 km) distances of in latitudes between 0-70 deg N|S. (Earth is ~flat model.)
  • I want an answer that is good anywhere on the globe, but only accurate to about a few meters
  • I want a super accurate positioning that is valid down to atomic scales of nanometers [nm].
  • I want answers that is very fast and easy to calculate and not computationally intensive.

So you can have many choices in which algorithm to use. In addition each programming language has it's own implementation or "package" multiplied by number of models and the model developers specific needs. For all practical purposes here, it pays off to ignore any other language apart javascript, since it very closely resemble pseudo-code by its nature. Thus it can be easily converted to any other language, with minimal changes.

Then the main models are:

  • Euclidian/Flat earth model: good for very short distances under ~10 km
  • Spherical model: good for large longitudinal distances, but with small latitudinal difference. Popular model:
    • Haversine: meter accuracy on [km] scales, very simple code.
  • Ellipsoidal models: Most accurate at any lat/lon and distance, but is still a numerical approximation that depend on what accuracy you need. Some popular models are:
    • Lambert: ~10 meter precision over 1000's of km.
    • Paul D.Thomas: Andoyer-Lambert approximation
    • Vincenty: millimeter precision and computational efficiency
    • Kerney: nanometer precision

References:

2
  • Great answer! Thank you for listing out all of the different strategies. Would it be possible to determine the altitude of the target if we know the current point's LLA (lat, lon, alt) and if we know the bearing and distance to the target? With these formulas, it seems like we can gather the target's latitude and longitude, but I haven't yet found anything for altitude. Also, say if we also knew the current point's reported elevation angle to the target. Would that play a factor at all?
    – FriskySaga
    Commented Jul 9 at 16:38
  • 1
    @FriskySaga I think your best option is to chose a model and then you have to perform a bit of trigonometry for going from a shell at r=R to a higher altitude one, at r=(R + z).
    – not2qubit
    Commented Jul 13 at 11:20
17

May be a bit late for answering, but after testing the other answers, it appears they don't work correctly. Here is a PHP code we use for our system. Working in all directions.

PHP code:

lat1 = latitude of start point in degrees

long1 = longitude of start point in degrees

d = distance in KM

angle = bearing in degrees

function get_gps_distance($lat1,$long1,$d,$angle)
{
    # Earth Radious in KM
    $R = 6378.14;

    # Degree to Radian
    $latitude1 = $lat1 * (M_PI/180);
    $longitude1 = $long1 * (M_PI/180);
    $brng = $angle * (M_PI/180);

    $latitude2 = asin(sin($latitude1)*cos($d/$R) + cos($latitude1)*sin($d/$R)*cos($brng));
    $longitude2 = $longitude1 + atan2(sin($brng)*sin($d/$R)*cos($latitude1),cos($d/$R)-sin($latitude1)*sin($latitude2));

    # back to degrees
    $latitude2 = $latitude2 * (180/M_PI);
    $longitude2 = $longitude2 * (180/M_PI);

    # 6 decimal for Leaflet and other system compatibility
   $lat2 = round ($latitude2,6);
   $long2 = round ($longitude2,6);

   // Push in array and get back
   $tab[0] = $lat2;
   $tab[1] = $long2;
   return $tab;
 }
6
  • 2
    Look good, but i think the requestor would like to have something in python. Wrong? Commented Aug 19, 2015 at 11:11
  • may be better named get_gps_coord or similar. You're not getting the distance, you supply that to the func. But thanks for this, it's exactly what I was looking for. Many searches return calculating distance between coords (false positives). Thanks!
    – Dev Null
    Commented Mar 6, 2017 at 23:27
  • Awesome! Thanks for your contribution!
    – jkamizato
    Commented Nov 20, 2018 at 20:14
  • 3
    6,378.14 km seems to be the maximum radius of Earth. The average is about 6,371.0 km, which may allow for more accurate calculations.
    – JVE999
    Commented Feb 28, 2020 at 19:24
  • Thanks for saving me a little time. Commented Jun 29, 2021 at 17:47
14

I ported answer by Brad to vanilla JS answer, with no Bing maps dependency

https://jsfiddle.net/kodisha/8a3hcjtd/

    // ----------------------------------------
    // Calculate new Lat/Lng from original points
    // on a distance and bearing (angle)
    // ----------------------------------------
    let llFromDistance = function(latitude, longitude, distance, bearing) {
      // taken from: https://stackoverflow.com/a/46410871/13549 
      // distance in KM, bearing in degrees
    
      const R = 6378.1; // Radius of the Earth
      const brng = bearing * Math.PI / 180; // Convert bearing to radian
      let lat = latitude * Math.PI / 180; // Current coords to radians
      let lon = longitude * Math.PI / 180;
    
      // Do the math magic
      lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
      lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance / R) - Math.sin(lat) * Math.sin(lat));
    
      // Coords back to degrees and return
      return [(lat * 180 / Math.PI), (lon * 180 / Math.PI)];
    
    }
    
    let pointsOnMapCircle = function(latitude, longitude, distance, numPoints) {
      const points = [];
      for (let i = 0; i <= numPoints - 1; i++) {
        const bearing = Math.round((360 / numPoints) * i);
        console.log(bearing, i);
        const newPoints = llFromDistance(latitude, longitude, distance, bearing);
        points.push(newPoints);
      }
      return points;
    }
    
    const points = pointsOnMapCircle(41.890242042122836, 12.492358982563019, 0.2, 8);
    let geoJSON = {
      "type": "FeatureCollection",
      "features": []
    };
    points.forEach((p) => {
      geoJSON.features.push({
        "type": "Feature",
        "properties": {},
        "geometry": {
          "type": "Point",
          "coordinates": [
            p[1],
            p[0]
          ]
        }
      });
    });
    
    document.getElementById('res').innerHTML = JSON.stringify(geoJSON, true, 2);

In addition, I added geoJSON export, so you can simply paste resulting geoJSON to: http://geojson.io/#map=17/41.89017/12.49171 to see the results instantly.

Result: geoJSON Screenshot

4
  • The geojson's map is very helpful for me to target a location in a map Commented Nov 2, 2018 at 3:12
  • Thank you @kodisha, your fiddle help me a lot! Commented Oct 21, 2020 at 0:42
  • Same as my comment in the previous answer, I think the last part of the longitude computation might be wrong, since variable lat is already updated before calculating lon, i.e. the term Math.sin(lat) * Math.sin(lat) is not actually using both the old and the new latitudes, respectively.
    – afp
    Commented Oct 25, 2021 at 18:28
  • can do this with distance in decimal degrees ? Commented Jul 14, 2022 at 15:57
9

Quick way using geopy

from geopy import distance
#distance.distance(unit=15).destination((lat,lon),bering) 
#Exemples
distance.distance(nautical=15).destination((-24,-42),90) 
distance.distance(miles=15).destination((-24,-42),90)
distance.distance(kilometers=15).destination((-24,-42),90) 
2
  • 3
    Without stating the method you're using for calculation, the answer is basically useless.
    – not2qubit
    Commented Jan 5, 2019 at 19:50
  • 2
    @not2qubit Whether @plinio-bueno-andrade-silva was aware or not, geopy.distance.distance currently uses geodesic. geopy And to be more specific, the ellipsoidal model used by default is the WGS-84 ellipsoid, "which is the most globally accurate."
    – hlongmore
    Commented Jan 29, 2019 at 23:33
3

lon1 and lat1 in degrees

brng = bearing in radians

d = distance in km

R = radius of the Earth in km

lat2 = math.degrees((d/R) * math.cos(brng)) + lat1
long2 = math.degrees((d/(R*math.sin(math.radians(lat2)))) * math.sin(brng)) + long1

I implemented your algorithm and mine in PHP and benchmarked it. This version ran in about 50% of the time. The results generated were identical, so it seems to be mathematically equivalent.

I didn't test the python code above so there might be syntax errors.

1
  • Not working. From North to South, result is correct but it's wrong in "East-West" direction.
    – Peter
    Commented Nov 14, 2014 at 20:01
3

I ported the Python to Javascript. This returns a Bing Maps Location object, you can change to whatever you like.

getLocationXDistanceFromLocation: function(latitude, longitude, distance, bearing) {
    // distance in KM, bearing in degrees

    var R = 6378.1,                         // Radius of the Earth
        brng = Math.radians(bearing)       // Convert bearing to radian
        lat = Math.radians(latitude),       // Current coords to radians
        lon = Math.radians(longitude);

    // Do the math magic
    lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
    lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance/R)-Math.sin(lat)*Math.sin(lat));

    // Coords back to degrees and return
    return new Microsoft.Maps.Location(Math.degrees(lat), Math.degrees(lon));

},
2
  • Please post functional code, including what it need to run. I.e. this seem to be dependent on Microsoft.Maps. Where to find/ how to install this?
    – not2qubit
    Commented Jan 26, 2018 at 19:44
  • You would only use Bing (Microsoft) Maps if your program uses Bing maps. Just take the Math.degrees(lat) and Math.degrees(lon) values and do with them whatever you need to for your application. Commented Jan 26, 2018 at 21:03
2

Thanks to @kodisha, here is a Swift version, but with improved and more precise calculation for Earth radius:

extension CLLocationCoordinate2D {
  
  func earthRadius() -> CLLocationDistance {
    let earthRadiusInMetersAtSeaLevel = 6378137.0
    let earthRadiusInMetersAtPole = 6356752.314
    
    let r1 = earthRadiusInMetersAtSeaLevel
    let r2 = earthRadiusInMetersAtPole
    let beta = latitude
    
    let earthRadiuseAtGivenLatitude = (
      ( pow(pow(r1, 2) * cos(beta), 2) + pow(pow(r2, 2) * sin(beta), 2) ) /
        ( pow(r1 * cos(beta), 2) + pow(r2 * sin(beta), 2) )
    )
    .squareRoot()
    
    return earthRadiuseAtGivenLatitude
  }
  
  func locationByAdding(
    distance: CLLocationDistance,
    bearing: CLLocationDegrees
  ) -> CLLocationCoordinate2D {
    let latitude = self.latitude
    let longitude = self.longitude
    
    let earthRadiusInMeters = self.earthRadius()
    let brng = bearing.degreesToRadians
    var lat = latitude.degreesToRadians
    var lon = longitude.degreesToRadians
    
    lat = asin(
      sin(lat) * cos(distance / earthRadiusInMeters) +
        cos(lat) * sin(distance / earthRadiusInMeters) * cos(brng)
    )
    lon += atan2(
      sin(brng) * sin(distance / earthRadiusInMeters) * cos(lat),
      cos(distance / earthRadiusInMeters) - sin(lat) * sin(lat)
    )
    
    let newCoordinate = CLLocationCoordinate2D(
      latitude: lat.radiansToDegrees,
      longitude: lon.radiansToDegrees
    )
    
    return newCoordinate
  }
}

extension FloatingPoint {
  var degreesToRadians: Self { self * .pi / 180 }
  var radiansToDegrees: Self { self * 180 / .pi }
}
2
  • 1
    I think the last part of the longitude computation might be wrong, since variable lat is already updated before calculating lon, i.e. the term sin(lat) * sin(lat) is not actually using both the old and the new latitudes, respectively.
    – afp
    Commented Oct 25, 2021 at 18:26
  • @afp I agree, that's def an error but an easy fix.
    – Eric
    Commented Jul 12, 2022 at 3:42
1

Also late but for those who might find this, you will get more accurate results using the geographiclib library. Check out the geodesic problem descriptions and the JavaScript examples for an easy introduction to how to use to answer the subject question as well as many others. Implementations in a variety of languages including Python. Far better than coding your own if you care about accuracy; better than VincentyDistance in the earlier "use a library" recommendation. As the documentation says: "The emphasis is on returning accurate results with errors close to round-off (about 5–15 nanometers)."

1

Just interchange the values in the atan2(y,x) function. Not atan2(x,y)!

1

I ported the answer from @David M to java if anyone wanted this... I do get a slight different result of 52.20462299620793, 0.360433887489931

    double R = 6378.1;  //Radius of the Earth
    double brng = 1.57;  //Bearing is 90 degrees converted to radians.
    double d = 15;  //Distance in km

    double lat2 = 52.20444; // - the lat result I'm hoping for
    double lon2 = 0.36056; // - the long result I'm hoping for.

    double lat1 = Math.toRadians(52.20472); //Current lat point converted to radians
    double lon1 = Math.toRadians(0.14056); //Current long point converted to radians

    lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
            Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));

    lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
            Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

    lat2 = Math.toDegrees(lat2);
    lon2 = Math.toDegrees(lon2);

    System.out.println(lat2 + ", " + lon2);
1
  • Probably this is the most correct answer, since it correctly uses the old and new latitudes, respectively, when calculating the last term of the lon2 expression, i.e. Math.sin(lat1)*Math.sin(lat2). Hence the slightly different result.
    – afp
    Commented Oct 25, 2021 at 18:29
0

Here is a PHP version based on Ed Williams Aviation Formulary. Modulus is handled a little different in PHP. This works for me.

function get_new_waypoint ( $lat, $lon, $radial, $magvar, $range )
{

   // $range in nm.
   // $radial is heading to or bearing from    
   // $magvar for local area.

   $range = $range * pi() /(180*60);
   $radial = $radial - $magvar ;

   if ( $radial < 1 )
     {
        $radial = 360 + $radial - $magvar; 
     }
   $radial =  deg2rad($radial);
   $tmp_lat = deg2rad($lat);
   $tmp_lon = deg2rad($lon);
   $new_lat = asin(sin($tmp_lat)* cos($range) + cos($tmp_lat) * sin($range) * cos($radial));
   $new_lat = rad2deg($new_lat);
   $new_lon = $tmp_lon - asin(sin($radial) * sin($range)/cos($new_lat))+ pi() % 2 * pi() -  pi();
   $new_lon = rad2deg($new_lon);

   return $new_lat." ".$new_lon;

}
2
  • Could you explain a couple of the variables? $range and $magvar could use a bit more exposition for the novice readers like (me:)
    – Tommie C.
    Commented Aug 27, 2018 at 18:59
  • Please see my answer and link to the formula that it uses and to what accuracy we can expect.
    – not2qubit
    Commented Jan 27, 2019 at 17:16
0

For whoever is interested in a Java solution here is my code: I noticed that the initial solution needs some tweaks in order to return a proper longitude value, especially when the point is at one of the poles. Also a round operation is sometimes required as the results on 0 latitude / longitude seem to slightly shift away from 0. For small distances, rounding will help in this regard.

private static final double EARTH_RADIUS = 6371; // average earth radius

    /**
     * Returns the coordinates of the point situated at the distance specified, in
     * the direction specified. Note that the value is an approximation, not an
     * exact result.
     *
     * @param startPointLatitude
     * @param startPointLongitude
     * @param distanceInKm
     * @param bearing:            0 means moving north, 90 moving east, 180 moving
     *                            south, 270 moving west. Max value 360 min value 0;
     * @return new point location
     */
    public static LocationDTO getPointAt(double startPointLatitude, double startPointLongitude, double distanceInKm,
            double bearing) {
        if (Math.abs(startPointLatitude) > 90) {
            throw new BadRequestException(ExceptionMessages.INVALID_LATITUDE);
        } else if (Math.abs(startPointLatitude) == 90) {
            startPointLatitude = 89.99999 * Math.signum(startPointLatitude); // we have to do this conversion else the formula doesnt return the correct longitude value 
        }
        if (Math.abs(startPointLongitude) > 180) {
            throw new BadRequestException(ExceptionMessages.INVALID_LONGITUDE);
        }
        double angularDistance = distanceInKm / EARTH_RADIUS;
        bearing = deg2rad(bearing);
        startPointLatitude = deg2rad(startPointLatitude);
        startPointLongitude = deg2rad(startPointLongitude);
        double latitude = Math.asin(Math.sin(startPointLatitude) * Math.cos(angularDistance)
                + Math.cos(startPointLatitude) * Math.sin(angularDistance) * Math.cos(bearing));
        double longitude = startPointLongitude
                + Math.atan2(Math.sin(bearing) * Math.sin(angularDistance) * Math.cos(startPointLatitude),
                        Math.cos(angularDistance) - Math.sin(startPointLatitude) * Math.sin(latitude));
        longitude = (rad2deg(longitude) + 540) % 360 - 180; // normalize longitude to be in -180 +180 interval 
        LocationDTO result = new LocationDTO();
        result.setLatitude(roundValue(rad2deg(latitude)));
        result.setLongitude(roundValue(longitude));
        return result;
    }

    private static double roundValue(double value) {
        DecimalFormat df = new DecimalFormat("#.#####");
        df.setRoundingMode(RoundingMode.CEILING);
        return Double.valueOf(df.format(value));
    }


    // This function converts decimal degrees to radians
    private static double deg2rad(double deg) {
        return (deg * Math.PI / 180.0);
    }

    // This function converts radians to decimal degrees
    private static double rad2deg(double rad) {
        return (rad * 180.0 / Math.PI);
    }
0

Very late to the party, but here is answer in R for anyone interested. Only change I've made is that I've set the radius to metres, so d needs to be set to meters too.

get_point_at_distance <- function(lon, lat, d, bearing, R = 6378137) {
  # lat: initial latitude, in degrees
  # lon: initial longitude, in degrees
  # d: target distance from initial point (in m)
  # bearing: (true) heading in degrees
  # R: mean radius of earth (in m)
  # Returns new lat/lon coordinate {d} m from initial, in degrees
  ## convert to radians
  lat1 <- lat * (pi/180)
  lon1 <- lon * (pi/180)
  a <- bearing * (pi/180)
  ## new position
  lat2 <- asin(sin(lat1) * cos(d/R) + cos(lat1) * sin(d/R) * cos(a))
  lon2 <- lon1 + atan2(
    sin(a) * sin(d/R) * cos(lat1),
    cos(d/R) - sin(lat1) * sin(lat2)
  )
  ## convert back to degrees
  lat2 <- lat2 * (180/pi)
  lon2 <- lon2 * (180/pi)
  ## return
  return(c(lon2, lat2))
}

lat = 52.20472
lon = 0.14056
distance = 15000
bearing = 90
get_point_at_distance(lon = lon, lat = lat, d = distance, bearing = bearing)
# [1]  0.3604322 52.2045157
3
  • The OP is clearly tagged with Python, so no need to provide more answers in other languages.
    – not2qubit
    Commented Jun 21, 2023 at 15:57
  • 1
    So we should ask new questions for every language someone wants this implemented in?
    – KaanKaant
    Commented Jun 22, 2023 at 9:19
  • Yes, that's correct. Unless the question say specifically any language and is not already marked with a language tag.
    – not2qubit
    Commented Jun 22, 2023 at 19:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.