35

I'm trying to generate some points at random distances away from a fixed point using GPS.

How can I add distance in meters to a GPS coordinate? I've looked at UTM to GPS conversion but is there a simpler method to achieve this?

I'm working on Android platform just in case.

Cheers, fgs

NoWar
  • 36,338
  • 80
  • 323
  • 498
fgs
  • 353
  • 1
  • 3
  • 4

9 Answers9

64
  • P0(lat0,lon0) : initial position (unit : degrees)
  • dx,dy : random offsets from your initial position in meters

You can use an approximation to compute the position of the randomized position:

 lat = lat0 + (180/pi)*(dy/6378137)
 lon = lon0 + (180/pi)*(dx/6378137)/cos(lat0)

This is quite precise as long as the random distance offset is below 10-100 km

Edit: of course in Java Math.cos() expects radians so do use Math.cos(Math.PI/180.0*lat0) if lat0 is in degrees as assumed above.

Stéphane
  • 6,920
  • 2
  • 43
  • 53
  • 2
    Hi, is 6378137 the radius of the earth? Thanks! – fgs May 15 '10 at 16:36
  • 1
    Yes (in meters as always). More precisely it is the semi-major axis of the WGS84 ellipsoid, so the radius of the Earth at the equator. – Stéphane May 15 '10 at 18:59
  • 3
    This wasn't quite working for me. The longitude offset was always double what it should have been. Change it to the following to correct:` lat = lat0 + (180/pi)*(dy/6378137) lon = lon0 + (180/pi/2)*(dx/6378137)/cos(lat0) ` – GeekyMonkey Mar 09 '11 at 11:20
  • mmm... My formulas seem right, and they work fine on my test data... Can you give me your input data and maybe your code? – Stéphane Apr 08 '11 at 07:52
  • 4
    I'd probably use the mean radius of 6371.0 KM instead of the radius at the equator. Odds are the area you're referencing is between the semi-major and semi-minor axes. If you need more accuracy, use the Vincenty formula for ellipsoids, http://www.movable-type.co.uk/scripts/latlong-vincenty.html. – Phil Jan 10 '14 at 18:34
  • 1
    What to do with a offset of 500-550km ? – user3408380 Mar 22 '15 at 21:46
15

To take a square I'm using this:

 private double[] getBoundingBox(final double pLatitude, final double pLongitude, final int pDistanceInMeters) {

    final double[] boundingBox = new double[4];

    final double latRadian = Math.toRadians(pLatitude);

    final double degLatKm = 110.574235;
    final double degLongKm = 110.572833 * Math.cos(latRadian);
    final double deltaLat = pDistanceInMeters / 1000.0 / degLatKm;
    final double deltaLong = pDistanceInMeters / 1000.0 / degLongKm;

    final double minLat = pLatitude - deltaLat;
    final double minLong = pLongitude - deltaLong;
    final double maxLat = pLatitude + deltaLat;
    final double maxLong = pLongitude + deltaLong;

    boundingBox[0] = minLat;
    boundingBox[1] = minLong;
    boundingBox[2] = maxLat;
    boundingBox[3] = maxLong;

    return boundingBox;
}

This returns an array with 4 coordinates, with them you can make a square with your original point in center.

Rob
  • 45,296
  • 24
  • 122
  • 150
Bruno Pinto
  • 2,013
  • 3
  • 23
  • 33
6

If you want to go east or north or west or south you can use this:

@SuppressLint("DefaultLocale")
public static double go_mock_loc(double xx_lat,double xx_long,double xx_dinstance,String Direction)
{
//  double xx_lat= 45.815005; 
//  double xx_long= 15.978501;

//  int xx_dinstance=500;

    int equator_circumference=6371000;
    int polar_circumference=6356800;

    double m_per_deg_long =  360 / polar_circumference;
    double rad_lat=(xx_lat* (Math.PI) / 180);
    double m_per_deg_lat = 360 / ( Math.cos(rad_lat) * equator_circumference);

    double deg_diff_long = xx_dinstance * m_per_deg_long;
    double deg_diff_lat  = xx_dinstance * m_per_deg_lat; 


    double xx_north_lat = xx_lat + deg_diff_long;
    //double xx_north_long= xx_long;
    double xx_south_lat = xx_lat - deg_diff_long;
    //double xx_south_long= xx_long;

    //double xx_east_lat = xx_lat;
    double xx_east_long= xx_long + deg_diff_lat;  
    //double xx_west_lat = xx_lat;
    double xx_west_long= xx_long - deg_diff_lat;

    if (Direction.toUpperCase().contains("NORTH")) {
        return xx_north_lat;
    } else if (Direction.toUpperCase().contains("SOUTH"))
    {
        return xx_south_lat;
    } else if (Direction.toUpperCase().contains("EAST"))
    {
        return xx_east_long;
    } else if (Direction.toUpperCase().contains("WEST"))
    {
        return xx_west_long;
    }
    else 
        return 0; 

}
CompEng
  • 7,161
  • 16
  • 68
  • 122
5

A detailed outline is given at http://www.movable-type.co.uk/scripts/latlong.html.

If you, somewhere, need to interconvert longitude/latitude to UTM coordinates (the ones used in GPS) you may want to have a look at http://www.uwgb.edu/dutchs/UsefulData/UTMFormulas.htm

2

I found that solution of @Bogdan Khrystov is very well. So here is C# version of his solution.

public enum GeoDirection
{
   NORTH = 1, SOUTH = 2, EAST = 3, WEST = 4
}            
    
public static Tuple<double, double> AddDistanceInMeters(double latitude, double longitude, int distanceInMeters, GeoDirection direction)
{
  var equatorCircumference = 6371000;
  var polarCircumference = 6356800;
    
  var mPerDegLong = 360 / (double)polarCircumference;
  var radLat = latitude * Math.PI / 180;
  var mPerDegLat = 360 / (Math.Cos(radLat) * equatorCircumference);
    
  var degDiffLong = distanceInMeters * mPerDegLong;
  var degDiffLat = distanceInMeters * mPerDegLat;
    
  var xxNorthLat = latitude + degDiffLong;
  var xxSouthLat = latitude - degDiffLong;
  var xxEastLong = longitude + degDiffLat;
  var xxWestLong = longitude - degDiffLat;
    
  switch (direction)
  {
    case GeoDirection.NORTH:
       return new Tuple<double, double>(xxNorthLat, longitude);
    case GeoDirection.SOUTH:
       return new Tuple<double, double>(xxSouthLat, longitude);
    case GeoDirection.EAST:
       return new Tuple<double, double>(latitude, xxEastLong);
    case GeoDirection.WEST:
       return new Tuple<double, double>(latitude, xxWestLong);
    default:
       return null;
    }
}
NoWar
  • 36,338
  • 80
  • 323
  • 498
  • 1
    This didn't work for me, instead of bounding box "radius" 2km it gave me 12.6km. I used this bounding box algorithm instead: https://stackoverflow.com/a/14315990/379279 – xhafan May 17 '22 at 11:49
1

rewrite @Ersin Gülbahar answer in Kotlin:

object LocationUtil {

enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

fun addDistanceInMeters(
    latitude: Double,
    longitude: Double,
    distanceInMeters: Int,
    direction: Direction
): Pair<Double, Double> {
    val equatorCircumference = 6371000
    val polarCircumference = 6356800

    val mPerDegLong = (360 / polarCircumference.toDouble())
    val radLat = latitude * Math.PI / 180
    val mPerDegLat = 360 / (Math.cos(radLat) * equatorCircumference)

    val degDiffLong = distanceInMeters * mPerDegLong
    val degDiffLat = distanceInMeters * mPerDegLat

    val xxNorthLat = latitude + degDiffLong
    val xxSouthLat = latitude - degDiffLong
    val xxEastLong = longitude + degDiffLat
    val xxWestLong = longitude - degDiffLat

    return when (direction) {
        Direction.NORTH -> Pair(xxNorthLat, longitude)
        Direction.SOUTH -> Pair(xxSouthLat, longitude)
        Direction.EAST -> Pair(latitude, xxEastLong)
        Direction.WEST -> Pair(latitude, xxWestLong)
    }
}

}

0

This code splits the line between two coordinates in n segments. Replace the delta calculation by your fixed distance

 @Override
public void split(Coordinates p1, Coordinates p2, int segments) {
    double φ1 = Math.toRadians(p1.getLat());
    double λ1 = Math.toRadians(p1.getLon());
    double φ2 = Math.toRadians(p2.getLat());
    double λ2 = Math.toRadians(p2.getLon());


    double xDelta = (φ2 - φ1) / segments;
    double yDelta = (λ2 - λ1) / segments;
    for (int i = 0; i < segments; i++){
        double x = φ1 + i * xDelta;
        double y = λ1 + i * yDelta;
        double xc = Math.toDegrees(x);
        double yc = Math.toDegrees(y);
        System.out.println(xc+","+yc);
    }
}
jpereira
  • 648
  • 7
  • 12
0

Combining answers from @Ersin Gülbahar and @Stéphane above, I came up with this solution in Flutter/Dart:

import 'dart:math' as math;
enum Direction { north, south, east, west }

double moveCoordinate(
    double latitude, double longitude, double distanceToMoveInMeters, Direction directionToMove) {
  const earthEquatorRadius = 6378137;
  final latitudeOffset = (180 / math.pi) * (distanceToMoveInMeters / earthEquatorRadius);
  final longitudeOffset = (180 / math.pi) *
      (distanceToMoveInMeters / earthEquatorRadius) /
      math.cos(math.pi / 180 * latitude);

  switch (directionToMove) {
    case Direction.north:
      return latitude + latitudeOffset;
    case Direction.south:
      return latitude - latitudeOffset;
    case Direction.east:
      return longitude + longitudeOffset;
    case Direction.west:
      return longitude - longitudeOffset;
  }

  return 0;
}
jaredbaszler
  • 3,941
  • 2
  • 32
  • 40
0

This works, tested. The code is C# but you can easily change it to another language

 private PointLatLng NewPositionBasedOnDistanceAngle(PointLatLng org, double distance, double bearing)
 {
    double rad = bearing * Math.PI / 180; //to radians
    double lat1 = org.Lat * Math.PI / 180; //to radians
    double lng1 = org.Lng * Math.PI / 180; //to radians
    double lat = Math.Asin(Math.Sin(lat1) * Math.Cos(distance / 6378137) + Math.Cos(lat1) * Math.Sin(distance / 6378137) * Math.Cos(rad));
    double lng = lng1 + Math.Atan2(Math.Sin(rad) * Math.Sin(distance / 6378137) * Math.Cos(lat1), Math.Cos(distance / 6378137) - Math.Sin(lat1) * Math.Sin(lat));
    return new PointLatLng(lat * 180 / Math.PI, lng * 180 / Math.PI); // to degrees  
 }