9

I am looking for a function that returns a point (lat, long) between two points (where I also specify their lat, long) and that point is based on a distance percentage.

So, I specify Lat1, Lon1, Lat2, Lon2 and a % on the function and, it returns a point, for example, 20% distant from the first point to the second.

mignz
  • 3,648
  • 2
  • 20
  • 21
  • 1
    If you're working with the Google Maps api, have a look at [interpolate](https://developers.google.com/maps/documentation/javascript/reference#spherical) that calculates a point between two other points at some fraction (0-1) between them. – Christiaan Westerbeek Jan 04 '17 at 10:26

3 Answers3

13

Assuming the coordinate is a decimal number. You can use this equation.

function midpoint(lat1, long1, lat2, long2, per) {
     return [lat1 + (lat2 - lat1) * per, long1 + (long2 - long1) * per];
}

Return a new desired coordinate of [lat, long], based on the percentage (such as per=0.2 for 20%).

lguiel
  • 328
  • 7
  • 15
  • 6
    mid point of lat long is in general not the midpoint computed by this formula, because they are coordinates on a sphere, not a flat surface. – spirographer Nov 25 '15 at 02:21
  • 2
    Iguiel I thought of that too but it doesn't work like that. For P1(37.740675, -25.661043) and P2(37.738096, -25.669267), for example, 20% returns P3(7.5478771, -5,133031). That places me in Africa instead of the Azores Islands between P1 and P2. – mignz Nov 25 '15 at 02:31
  • 1
    Hi Swift-R, I see, I think there is a mistake in my equation, it should be: [lat1+(lat2-lat1)*per, long1+(long2-long1)*per] I will edit my answer. I hope it works for you. – lguiel Nov 25 '15 at 02:56
  • 1
    besides maps, this is actually a really nice function for other creative javascript related things - I took it for a test drive in this pen http://codepen.io/headwinds/pen/BLVXwa?editors=0010 – headwinds Oct 14 '16 at 00:49
  • 1
    This equation is NOT mathematically correct. Linearly interpolating lat/long is not proper spherical math. – Meekohi Jun 19 '18 at 19:19
4

Here is a reference that will help a lot (check the bottom)

http://www.movable-type.co.uk/scripts/latlong.html

Intermediate point

An intermediate point at any fraction along the great circle path between two points can also be calculated.

Formula:

a = sin((1−f)⋅δ) / sin δ
b = sin(f⋅δ) / sin δ
x = a ⋅ cos φ1 ⋅ cos λ1 + b ⋅ cos φ2 ⋅ cos λ2
y = a ⋅ cos φ1 ⋅ sin λ1 + b ⋅ cos φ2 ⋅ sin λ2
z = a ⋅ sin φ1 + b ⋅ sin φ2
φi = atan2(z, √x² + y²)
λi = atan2(y, x)

where f is fraction along great circle route (f=0 is point 1, f=1 is point 2), δ is the angular distance d/R between the two points.

spirographer
  • 630
  • 4
  • 18
  • I do not understand where the percentage comes in. – mignz Nov 25 '15 at 02:33
  • 1
    *f* is the fraction from point 1 to point 2. A percentage is a fraction where the denominator is 100. For example, if your percentage is 39%, then the fraction *f = 39/100*. – spirographer Nov 25 '15 at 03:16
4

Both answers might be useful for specific users, but I would like to point out some issues.

The solution by lguiel is correct for games or short distances, but does not work for geometric calculations on the earth as a globe.

The answer by spirographer is correct, but as it is very theoretical it might be too difficult to program.

I translated the second answer to a practical programming language, so that you can use it for your own projects.

Full code | Run online

// Original calculation from https://www.movable-type.co.uk/scripts/latlong.html
LatLng calculateIntermediatePoint(LatLng point1, LatLng point2, double perc) {
  //const φ1 = this.lat.toRadians(), λ1 = this.lon.toRadians();
  //const φ2 = point.lat.toRadians(), λ2 = point.lon.toRadians();
  double lat1 = degreesToRadians(point1.latitude);
  double lng1 = degreesToRadians(point1.longitude);
  double lat2 = degreesToRadians(point2.latitude);
  double lng2 = degreesToRadians(point2.longitude);

  //const Δφ = φ2 - φ1;
  //const Δλ = λ2 - λ1;
  double deltaLat = lat2 - lat1;
  double deltaLng = lng2 - lng1;

  //const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ/2) * Math.sin(Δλ/2);
  //const δ = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  double calcA = sin(deltaLat / 2) * sin(deltaLat / 2) +
      cos(lat1) * cos(lat2) * sin(deltaLng / 2) * sin(deltaLng / 2);
  double calcB = 2 * atan2(sqrt(calcA), sqrt(1 - calcA));

  //const A = Math.sin((1-fraction)*δ) / Math.sin(δ);
  //const B = Math.sin(fraction*δ) / Math.sin(δ);
  double A = sin((1 - perc) * calcB) / sin(calcB);
  double B = sin(perc * calcB) / sin(calcB);

  //const x = A * Math.cos(φ1) * Math.cos(λ1) + B * Math.cos(φ2) * Math.cos(λ2);
  //const y = A * Math.cos(φ1) * Math.sin(λ1) + B * Math.cos(φ2) * Math.sin(λ2);
  //const z = A * Math.sin(φ1) + B * Math.sin(φ2);
  double x = A * cos(lat1) * cos(lng1) + B * cos(lat2) * cos(lng2);
  double y = A * cos(lat1) * sin(lng1) + B * cos(lat2) * sin(lng2);
  double z = A * sin(lat1) + B * sin(lat2);

  //const φ3 = Math.atan2(z, Math.sqrt(x*x + y*y));
  //const λ3 = Math.atan2(y, x);
  double lat3 = atan2(z, sqrt(x * x + y * y));
  double lng3 = atan2(y, x);

  //const lat = φ3.toDegrees();
  //const lon = λ3.toDegrees();
  return LatLng(radiansToDegrees(lat3), radiansToDegrees(lng3));
}
James Cameron
  • 1,681
  • 25
  • 40