2016-04-28 25 views
0

我試圖在半徑內生成一個點,並且出現錯誤的值。有人介意看看並告訴我我在做什麼錯誤的經度?這是張貼在一個不同的問題一個公式化的方法...在半徑內隨機生成一個latlng會產生一個超出範圍的點

public static Location generateLocationWithinRadius(Location myCurrentLocation) { 
    return getLocationInLatLngRad(1000, myCurrentLocation); 
} 

protected static Location getLocationInLatLngRad(double radiusInMeters, Location currentLocation) { 
    double x0 = currentLocation.getLatitude(); 
    double y0 = currentLocation.getLongitude(); 

    Random random = new Random(); 

    // Convert radius from meters to degrees 
    double radiusInDegrees = radiusInMeters/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); 

    double new_x = x/Math.cos(y0); 
    double new_y = y/Math.cos(x0); 
    double foundLatitude; 
    double foundLongitude; 
    boolean shouldAddOrSubtractLat = random.nextBoolean(); 
    boolean shouldAddOrSubtractLon = random.nextBoolean(); 
    if (shouldAddOrSubtractLat) { 
     foundLatitude = new_x + x0; 
    } else { 
     foundLatitude = x0 - new_x; 
    } 
    if (shouldAddOrSubtractLon) { 
     foundLongitude = new_y + y0; 
    } else { 
     foundLongitude = y0 - new_y; 
    } 
    Location copy = new Location(currentLocation); 
    copy.setLatitude(foundLatitude); 
    copy.setLongitude(foundLongitude); 
    return copy; 
} 

我還應該說,由於某種原因,有效點產生座標的統一的行看着他們的時候。

我認爲緯度正確處理,而經度不正確。

+1

顯示您的輸入,輸出和預期輸出。 – shmosel

+0

我也應該說我現在正在使用靜態值,但我會保持它們可調整並減少指針的數量 – KoalaKoalified

+0

lat lng在小數位數方面變化很大。因此,如果在半徑範圍內,您生成的隨機值在0.000x範圍內會有所不同。 –

回答

1

您的代碼似乎或多或少基於一個想法 which is presented at gis.stackexchange.comthis discussionthis discussion討論多一些存在。

如果我們根據這些討論仔細研究它,那麼它可能更有意義。

要將值限制爲圓形,它使用隨機化方向和距離的方法。首先,我們得到兩個隨機double值在0.0 ... 1.0:

double u = random.nextDouble(); 
double v = random.nextDouble(); 

隨着半徑以米爲單位和計算需要程度,它的轉換:

double radiusInDegrees = radiusInMeters/111000f; 

的程度與米比的赤道在這裏使用。 (維基百科表明111320米。)

要使隨機點的距離與一個平方根補償的更均勻的分佈:

w = r * sqrt(u) 

否則會有在點附近的量的統計偏差中心與遠離中心。1的平方根爲1,當然爲0,因此 將隨機雙的根乘以預期的最大值。半徑始終給出介於0和半徑之間的值。

那麼其他的隨機雙由2 * PI因爲有一個完整的圓2個* PI弧度乘以:

t = 2 * Pi * v 

我們現在有一個角度的地方介於0 ... 2 * PI即0 ... 360度。

然後,隨機的x和y座標的增量與使用隨機距離和隨機角度基本三角學計算:

x = w * cos(t) 
y = w * sin(t) 

[x,y]然後指向一些隨機距離w遠離朝向方向t原始座標。

然後經度線之間的變化的距離與三角(y0爲中心的y座標)來補償:如果cos()期望角度弧度

x' = x/cos(y0) 

以上y0需要被轉換爲弧度。在Java中它確實。

然後建議將這些增量值添加到原始座標中。 cossin對於整個圓的一半角度都是負值,所以只需添加即可。一些隨機點位於格林威治西部,赤道南部。如果要進行加法或減法,則不需要隨機化 。

所以隨機點將在(x'+x0, y+y0)

我不知道爲什麼你的代碼有:

double new_y = y/Math.cos(x0); 

而像說我們可以忽視shouldAddOrSubtractLatshouldAddOrSubtractLon

在我看來,x是指從左到右或從西到東的東西。即使經度線從南到北,這也是經度值增長的方式。所以我們使用x作爲經度,y作爲緯度。

那麼剩下的是什麼呢?喜歡的東西:

protected static Location getLocationInLatLngRad(double radiusInMeters, Location currentLocation) { 
    double x0 = currentLocation.getLongitude(); 
    double y0 = currentLocation.getLatitude(); 

    Random random = new Random(); 

    // Convert radius from meters to degrees. 
    double radiusInDegrees = radiusInMeters/111320f; 

    // Get a random distance and a random angle. 
    double u = random.nextDouble(); 
    double v = random.nextDouble(); 
    double w = radiusInDegrees * Math.sqrt(u); 
    double t = 2 * Math.PI * v; 
    // Get the x and y delta values. 
    double x = w * Math.cos(t); 
    double y = w * Math.sin(t); 

    // Compensate the x value. 
    double new_x = x/Math.cos(Math.toRadians(y0)); 

    double foundLatitude; 
    double foundLongitude; 

    foundLatitude = y0 + y; 
    foundLongitude = x0 + new_x; 

    Location copy = new Location(currentLocation); 
    copy.setLatitude(foundLatitude); 
    copy.setLongitude(foundLongitude); 
    return copy; 
} 
+0

非常感謝!這工作很好 – KoalaKoalified

1

我很難爲您提供純粹的Android解決方案,因爲我從來沒有使用過這些API。不過,我相信你可以很容易地調整這個解決方案,在現有點的給定半徑內生成一個隨機點。

該問題已在二維空間中解決,但很容易擴展以支持高度。

請看看下面的代碼。它提供了一個LocationGenerator以及我自己的Location實現和單元測試,證明它的工作原理。

我的解決方案是基於求解圓式(x-a)^2 + (y-b)^2 = r^2

package my.test.pkg; 

import org.junit.Test; 

import java.util.Random; 

import static org.junit.Assert.assertTrue; 

public class LocationGeneratorTest { 
    private class Location { 
     double longitude; 
     double latitude; 

     public Location(double longitude, double latitude) { 
      this.longitude = longitude; 
      this.latitude = latitude; 
     } 
    } 

    private class LocationGenerator { 
     private final Random random = new Random(); 

     Location generateLocationWithinRadius(Location currentLocation, double radius) { 
      double a = currentLocation.longitude; 
      double b = currentLocation.latitude; 
      double r = radius; 

      // x must be in (a-r, a + r) range 
      double xMin = a - r; 
      double xMax = a + r; 
      double xRange = xMax - xMin; 

      // get a random x within the range 
      double x = xMin + random.nextDouble() * xRange; 

      // circle equation is (y-b)^2 + (x-a)^2 = r^2 
      // based on the above work out the range for y 
      double yDelta = Math.sqrt(Math.pow(r, 2) - Math.pow((x - a), 2)); 
      double yMax = b + yDelta; 
      double yMin = b - yDelta; 
      double yRange = yMax - yMin; 
      // Get a random y within its range 
      double y = yMin + random.nextDouble() * yRange; 

      // And finally return the location 
      return new Location(x, y); 
     } 
    } 

    @Test 
    public void shoulRandomlyGeneratePointWithinRadius() throws Exception { 
     LocationGenerator locationGenerator = new LocationGenerator(); 
     Location currentLocation = new Location(20., 10.); 
     double radius = 5.; 
     for (int i=0; i < 1000000; i++) { 
      Location randomLocation = locationGenerator.generateLocationWithinRadius(currentLocation, radius); 
      try { 
       assertTrue(Math.pow(randomLocation.latitude - currentLocation.latitude, 2) + Math.pow(randomLocation.longitude - currentLocation.longitude, 2) < Math.pow(radius, 2)); 
      } catch (Throwable e) { 
       System.out.println("i= " + i + ", x=" + randomLocation.longitude + ", y=" + randomLocation.latitude); 
       throw new Exception(e); 
      } 
     } 

    } 
} 

注: 這只是一個通用的解決方案,以獲得一個圓圈內隨機點在中心(A,B)和半徑r可以用來解決你的問題,而不是你可以直接使用的解決方案。您很可能需要將其調整到您的使用案例。

我相信這是一個自然的解決方案。

問候

+0

我應該使用什麼單位的半徑?我試過Meters和Kilometers,但是我在當前位置和採購地點之間的距離很遠。 – KoalaKoalified

+0

半徑顯然必須以度爲單位,因爲它是以經度值爲單位添加/減去經度值。所以它必須從米轉換爲度才能調用該函數。我想知道在這些計算中角度/米比例在南北方向變化的含義是什麼。 (也許它工作的很好,我沒有檢查。) –

+0

@KoalaKoalified在我的算法中,我沒有附加任何距離單位的概念,它們只是三個雙精度值:經度,緯度,半徑。當我實施它時,我的假設是他們只是用同一個單位表示的所有長度。根據您的使用情況,您可以以米或公里表示。如果您的代碼使用度數,我認爲將它們轉換爲米或公里的數量並不會有任何問題,具體取決於您的需求 請記住,我的位置類只是爲了使發生器工作而編寫的,可能與完全不同您正在使用的位置。 – Julian

0

經度和緯度採用橢球座標所以對於大半徑(百米)使用這種方法的錯誤會成爲sinificant。一種可能的技巧是轉換爲笛卡爾座標,做半徑隨機化,然後再轉換回長半徑的橢球座標。我已經使用ibm的this java library測試了幾公里,並取得了巨大的成功。比那可能更長,但最終半徑會隨着地球顯示其球形性質而下降。

相關問題