近くのランダムな場所を生成していますか?


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)座標系のメトリックの歪みが考慮されないか、誤って考慮されるため、このような問題が発生します。通常、地理座標系の代わりに投影座標系を使用すると、これらの両方の問題を回避できます。これを行うと、数式の基本的なプロパティが公開されます。これは、望ましい場合と望ましくない場合があります。円の中ではなく、場所の周りの四角形の中に場所を生成します。
whuber

おかげでアレックスは、Javaコードはstackoverflowので掲示される:stackoverflow.com/questions/10682743/...
pindleskin

編集したコードについて:(i)random.nextInt(1001)/1000約0.1%の時間で1より大きい値を返します。random.nextDoubleまたはを使用しないのはなぜrandom.nextFloatですか?(ii)乗算x0y0バイ1E6はかなり不可解です。正しい結果が得られるとは思えません。
whuber

確かに、nextDoubleを使用してメソッドを編集し、1E6を取り除きました。これで、ランダムに生成された場所はすべて、私の場所と同じ座標になります。助けてくれてありがとう、私はすぐにそれをいつでも解決しようとしています
ピンドルスキン

回答:


46

これには2つの理由があります。1つ目は、ポイントを正方形ではなく円形に制限することです。第二に、距離計算の歪みを考慮します。

多くのGISには、両方の合併症を自動的かつ透過的に処理する機能が含まれています。ただし、ここのタグは、GISに依存しないアルゴリズムの記述が望ましいことを示唆しています。

  1. 位置(x0、y0)を中心とする半径rの円内に一様に、ランダムに、独立してポイントを生成するには、区間[0、1 )で2つの独立した一様ランダム値uおよびvを生成します。(これはほとんどすべての乱数ジェネレーターが提供するものです。)計算

    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を度に変換します。 ここでは、1度に約111,300メートルあります。

    次に、ステップ(1)でxyを生成した、東西距離の縮小に合わせてx座標を調整します。

    x' = x / cos(y0)

    目的のランダムポイントは位置(x '+ x0、y + y0)にあります。 これはおおよその手順です。 地球のどちらの極にも及ばない小さな半径(数百キロメートル未満)の場合、通常は非常に正確であるため、各中心(x0、y0)の周りに何万ものランダムな点を生成する場合でもエラーを検出できません。


2
素晴らしい説明whuber、それは私が知る必要があったものです。今、私はそれを実装するつもりです。ありがとう
ピンドルスキン

1
私は式のいくつかのJava実装を表示するために、質問を編集しました
pindleskin

1
「1度に約111,300メートルあります」コンマが千の区切り文字として使用されていることに注意してください。radiusInDegrees = radius / 111300
RMalke

2
緯度、
経度の

2
@whuberを吹き飛ばして、それは理にかなっています。別の見方をすれば、半径20に対して55のランダムな半径が生成されると想像することです。各ランダムな半径が均一で、0〜20のいずれかに等しいとすると、0、2、4、...、20 。したがって、半径が5のポイント、半径が2の5ポイントなどがあります。半径が2の5ポイント(半径2の円の周り)は、半径が5ポイントよりも互いに近くに見えます。 20の
アジズJaved

11

Javascript用に実装:

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 16

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倍大きく歪んでいます。

私の答えのコードは、ここからのアルゴリズムに基づいていまし

以下に、伸縮楕円の問題を示す2つのjsfiddleがあります。

正しいアルゴリズム

歪んだアルゴリズム


発生した問題の説明は、実装が正しくないことを示唆しています。
whuber

あなたは正しいかもしれません。私が作ったjsfiddleを見て、どこが間違っていたのか教えてください。
ジュリアンマン

私は上記のATOKのJavaの回答と比較して、そして中distoredアルゴリズムのあなたのjsfiddleにこの変更を行いましたwhuberPointAtDistance()x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180))
マット

1
私の訂正にもかかわらず、私はジュリアンの要点ではるかに正確な結果を得ました。(whuberPointAtDistanceに私の修正を追加)し、エラーレポートと要旨でそれらを実行している、それはすべての3つのシナリオでは0.05%の誤差を示した(代替よりも大幅に高い。)
マット・

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の実装

ジオエンコーダーから緯度と経度を取得し、この関数に渡す

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の問題をどのように解決するかについて少し拡張して、コードを簡単に説明してください。
マーティン14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.