在附近生成随机位置?


30

我正在尝试在我的位置附近创建随机位置。我想要的是在围绕我的位置的200米圆内创建随机的纬度/经度对。

这是我想出的公式(在StackOverFlow的人们的帮助下):(-1和1之间的随机数)*半径+(旧经度)=旧经度范围内的新经度

(介于-1和1之间的随机数)*半径+(旧纬度)=旧纬度半径内的新纬度

问题是我的实现发生了一些奇怪的事情,因为所有随机位置都离我的位置中心太近了,看来该公式不能覆盖整个半径。

我的公式有什么问题的想法吗?

编辑以显示当前的Java实现:

public static Location getLocation(Location location, int radius) {
    Random random = new Random();

    // Convert radius from meters to degrees
    double radiusInDegrees = radius / METERS_IN_DEGREES;

    double x0 = location.getLongitude() * 1E6;
    double y0 = location.getLatitude() * 1E6;
    double u = random.nextInt(1001) / 1000;
    double v = random.nextInt(1001) / 1000;
    double w = radiusInDegrees * Math.sqrt(u);
    double t = 2 * Math.PI * v;
    double x = w * Math.cos(t);
    double y = w * Math.sin(t);

    // Adjust the x-coordinate for the shrinking of the east-west distances
    double new_x = x / Math.cos(y0);

    // Set the adjusted location
    Location newLocation = new Location("Loc in radius");
    newLocation.setLongitude(new_x + x0);
    newLocation.setLatitude(y + y0);

    return newLocation;
}

我不确定自己在做什么错,因为新位置是在海中创建的。

任何的想法?


您如何执行此公式?您可以显示这部分代码吗?可能是您在伪随机数生成器遇到的问题?
亚历克斯·马可夫

就最后一个问题而言,这样的过程会遇到这样的问题,因为(i)距离被错误地转换为纬度或经度,并且(ii)坐标系的度量失真没有被考虑或被错误地考虑了。使用投影坐标系而不是地理坐标系通常可以解决这两个问题。这样做将暴露您的公式的基本属性,这可能是您可能想要的,也可能不是您想要的:它会在一个位置周围的矩形内而不是圆形内生成位置。
ub

感谢亚历克斯,Java代码公布在计算器:stackoverflow.com/questions/10682743/...
pindleskin

重新编辑代码:(i)random.nextInt(1001)/1000将在大约0.1%的时间内返回大于1的值。为什么不使用random.nextDoublerandom.nextFloat?(ii)乘x0y01E6是相当神秘的;它似乎不会产生正确的结果。
ub

是的,我使用nextDouble编辑了方法,摆脱了1E6。现在,所有随机生成的位置都具有与我的位置相同的坐标。感谢您的帮助,看来我很快就会解决它

Answers:


46

这很棘手,原因有二:首先,将点限制为圆形而不是正方形。第二,考虑距离计算中的失真。

许多GIS包含自动透明地处理这两种复杂性的功能。但是,此处的标签表明,可能需要与GIS无关的算法描述。

  1. 在位置(x0,y0)周围的半径r 的圆内均匀,随机且独立地生成点,请首先在间隔[0,1)中生成两个独立的均匀随机值uv。(这是几乎每个随机数生成器都为您提供的内容。)计算

    w = r * sqrt(u)
    t = 2 * Pi * v
    x = w * cos(t) 
    y = w * sin(t)

    所需的随机点位于位置(x + x0,y + y0)。

  2. 使用地理(纬度,经度)坐标时, x0(经度)和y0(纬度)将以度为单位,r最有可能以米(或英尺或英里或其他线性测量值)为单位。首先,将半径r转换为度数,就好像您位于赤道附近一样。 在这里,一个学位大约有111,300米。

    其次,按照步骤(1)生成xy 之后,调整x坐标以缩小东西向的距离:

    x' = x / cos(y0)

    所需的随机点位于位置(x'+ x0,y + y0)。 这是一个大概的过程。 对于不延伸到地球两极的小半径(小于几百公里),通常如此精确,即使在每个中心周围生成数以万计的随机点(x0,y0),也无法检测到任何误差。


2
很棒的解释,这就是我需要知道的。现在,我要实施它。谢谢
pindleskin 2012年

1
我编辑了问题以显示该公式的一些Java实现
pindleskin 2012年

1
注意:“一个角度大约有111,300米”,逗号用作千位分隔符。radiusInDegrees = radius /
111300

2
对于纬度,长坐标,您不应该这样做x'= x / cos(y0 * Pi / 180)
亚伦·斯坦巴克

2
介意@whuber,这很有意义。我猜想用另一种方式看待它,可以想象生成一个半径为20的55个随机半径。假设每个随机半径是均匀的,并且精确地等于0到20,所以0、2、4,...,20因此,将有5个半径为2的半径,以此类推。半径为2的5个点(在半径2的圆周围)看起来比5个具有半径的点彼此更靠近20之
阿兹兹Javed

11

为Java语言实现:

var r = 100/111300 // = 100 meters
  , y0 = original_lat
  , x0 = original_lng
  , u = Math.random()
  , v = Math.random()
  , w = r * Math.sqrt(u)
  , t = 2 * Math.PI * v
  , x = w * Math.cos(t)
  , y1 = w * Math.sin(t)
  , x1 = x / Math.cos(y0)

newY = y0 + y1
newX = x0 + x1

10

正确的实现是:

public static void getLocation(double x0, double y0, int radius) {
    Random random = new Random();

    // Convert radius from meters to degrees
    double radiusInDegrees = radius / 111000f;

    double u = random.nextDouble();
    double v = random.nextDouble();
    double w = radiusInDegrees * Math.sqrt(u);
    double t = 2 * Math.PI * v;
    double x = w * Math.cos(t);
    double y = w * Math.sin(t);

    // Adjust the x-coordinate for the shrinking of the east-west distances
    double new_x = x / Math.cos(Math.toRadians(y0));

    double foundLongitude = new_x + x0;
    double foundLatitude = y + y0;
    System.out.println("Longitude: " + foundLongitude + "  Latitude: " + foundLatitude );
}

我删除了对外部库的依赖,以使其更易于访问。


建议的OP编辑根据 stackoverflow Q&A,在Java Math.cos()中,期望以弧度为单位的输入。
MikeJRamsey56

3

接受的答案和派生对我不起作用。结果非常不准确。

javascript中的正确实现:

function pointAtDistance(inputCoords, distance) {
    const result = {}
    const coords = toRadians(inputCoords)
    const sinLat =  Math.sin(coords.latitude)
    const cosLat =  Math.cos(coords.latitude)

    /* go a fixed distance in a random direction*/
    const bearing = Math.random() * TWO_PI
    const theta = distance/EARTH_RADIUS
    const sinBearing = Math.sin(bearing)
    const cosBearing =  Math.cos(bearing)
    const sinTheta = Math.sin(theta)
    const cosTheta =    Math.cos(theta)

    result.latitude = Math.asin(sinLat*cosTheta+cosLat*sinTheta*cosBearing);
    result.longitude = coords.longitude + 
        Math.atan2( sinBearing*sinTheta*cosLat, cosTheta-sinLat*Math.sin(result.latitude )
    );
    /* normalize -PI -> +PI radians (-180 - 180 deg)*/
    result.longitude = ((result.longitude+THREE_PI)%TWO_PI)-Math.PI

    return toDegrees(result)
}

function pointInCircle(coord, distance) {
    const rnd =  Math.random()
    /*use square root of random number to avoid high density at the center*/
    const randomDist = Math.sqrt(rnd) * distance
    return pointAtDistance(coord, randomDist)
}

全要点在这里

在公认的答案中,我发现点分布在一个椭圆形中,其宽度是其高度的1.5倍(在巴拿马)和8倍(在瑞典北部)。如果我从@whuber的答案中删除了x坐标调整,则椭圆会以其他方式变形,比其宽度高8倍。

我的答案中的代码基于此处的算法

在下面,您可以看到两个jsfiddles,它们显示了椭圆拉伸的问题

正确的算法

失真算法


您对问题的描述表明您的实现不正确。
ub

您可能是正确的。您是否愿意看一下我制作的jsfiddles并告诉我哪里出错了。
朱利安·曼

我ATOK的Java回答上述比较,并提出此更改您在distored算法的jsfiddle whuberPointAtDistance()x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180))
马特

1
尽管我进行了更正,但朱利安(Julian)的主旨仍然使我获得了更为准确的结果。将我的更正添加到whuberPointAtDistance()并使用错误报告在要点中运行它们,它在所有三种情况下均显示0.05%的错误(显着高于替代方法。)
Matt

1

在Python中

# Testing simlation of generating random points 
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA

def create_random_point(x0,y0,distance):
    """
            Utility method for simulation of the points
    """   
    r = distance/ 111300
    u = np.random.uniform(0,1)
    v = np.random.uniform(0,1)
    w = r * np.sqrt(u)
    t = 2 * np.pi * v
    x = w * np.cos(t)
    x1 = x / np.cos(y0)
    y = w * np.sin(t)
    return (x0+x1, y0 +y)

fig = plt.figure()
ax = host_subplot(111, axes_class=AA.Axes)

#ax.set_ylim(76,78)
#ax.set_xlim(13,13.1)
ax.set_autoscale_on(True)

latitude1,longitude1 = 13.04738626,77.61946793  
ax.plot(latitude1,longitude1,'ro')

for i in range(1,20):
    x,y = create_random_point(latitude1,longitude1 ,500 )
    ax.plot(x,y,'bo')
    dist = haversine(x,y,latitude1,longitude1)
    print "Distance between points is " ,dist    # a value approxiamtely less than 500 meters   


plt.show()

输出量

点之间的距离是0.288044147914点之间的距离是0.409557451806点之间的距离是0.368260305716点之间的距离是0.340720560546点之间的距离是0.453773334731点之间的距离是0.460608754561点之间的距离是0.497188825576点之间的距离是0.603178188859点之间的距离是0.628898384307点之间的距离是0.416297587754点之间的距离是0.503691568896点之间的距离是0.175153349209点之间的距离是0.195149463735点之间的距离是0.424094009858点之间的距离是0.286807741494点之间的距离是0.558049206307点之间的距离是0.498612171417点之间的距离是0.047344718215点之间的距离是0.484232497086

在此处输入图片说明


0

您可以在此处检查计算结果。向下滚动到“目标点给定距离和起始点的方位”部分。甚至在底部有一个简单的JavaScript公式即可实现此目的。您仍然需要生成一个以弧度为单位的随机轴承$ \ theta $(从北向顺时针方向测量),尽管这应该很简单。这些公式假设使用球形地球(尽管它是椭圆形的),但由于其产生的误差高达0.3%,已经足够了。


0

Swift的实现

从geoencoder获取经纬度并将其传递给此函数

func generateRandomLocation(lat: CLLocationDegrees, lng: CLLocationDegrees){
    let radius : Double = 100000 // this is in meters so 100 KM
    let radiusInDegrees: Double = radius / 111000
    let u : Double = Double(arc4random_uniform(100)) / 100.0
    let v : Double = Double(arc4random_uniform(100)) / 100.0
    let w : Double = radiusInDegrees * u.squareRoot()
    let t : Double = 2 * Double.pi * v
    let x : Double = w * cos(t)
    let y : Double = w * sin(t)

    // Adjust the x-coordinate for the shrinking of the east-west distances
    //in cos converting degree to radian
    let new_x : Double = x / cos(lat * .pi / 180 )

    processedLat = new_x + lat
    processedLng = y + lng

    print("The Lat are :- ")
    print(processedLat)
    print("The Lng are :- ")
    print(processedLng)
}

在上面的示例中,我通过对国家/地区名称进行地理编码来获得纬度和经度,因为每次国家/地区名称都具有相同的纬度和经度,在国家/地区中也是如此,因此我需要随机性。


-1

private void drawPolyline(double lat,double lng){

         double Pi=Math.PI;

         double lt=lat;
         double ln=lng;

        //Earth’s radius, sphere
         double R=6378137;

         double dn = 50;
         double de = 50;

         //Coordinate offsets in radians
         double dLat = dn/R;
         double dLon = de/(R*Math.cos(Pi*lat/180));

        //OffsetPosition, decimal degrees
        double lat2 = lt + dLat * 180/Pi;
        double lon2 = ln + dLon * 180/Pi ;



            //12.987859, 80.231038
            //12.987954, 80.231252

        double lat3 = lt - dLat * 180/Pi;
        double lon3 = ln - dLon * 180/Pi ;

            LatLng origin=new LatLng(lt, lon3);

            LatLng dest=new LatLng(lt, lon2);




          Polyline line = googleMap.addPolyline(new PolylineOptions()
         .add(origin, dest)
         .width(6)
         .color(Color.RED));

5
您能否扩展一下如何解决OP问题并简要说明您的代码?
马丁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.