Tạo địa điểm ngẫu nhiên gần đó?


30

Tôi đang cố gắng tạo các vị trí ngẫu nhiên gần vị trí của tôi. Những gì tôi muốn là tạo các cặp vĩ độ / kinh độ ngẫu nhiên trong một vòng tròn 200 mét bao quanh vị trí của tôi.

Đây là công thức tôi đã đưa ra (với sự giúp đỡ của mọi người tại StackOverFlow): (Số ngẫu nhiên giữa -1 và 1) * radius + (kinh độ cũ) = kinh độ mới trong bán kính của kinh độ cũ

(Số ngẫu nhiên giữa -1 và 1) * radius + (vĩ độ cũ) = vĩ độ mới trong bán kính của vĩ độ cũ

Có một điều kỳ lạ đang xảy ra với việc triển khai của tôi vì tất cả các vị trí ngẫu nhiên đều ở quá gần trung tâm vị trí của tôi, có vẻ như công thức không bao gồm toàn bộ bán kính.

Bất kỳ ý tưởng về những gì có thể sai với công thức của tôi?

Đã chỉnh sửa để hiển thị triển khai java hiện tại:

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;
}

Tôi không chắc chắn những gì tôi đang làm sai, bởi vì các địa điểm mới được tạo ra giữa biển.

Bất kỳ ý tưởng?


Làm thế nào để bạn thực hiện công thức này? Bạn có thể trình bày phần này của mã của bạn? Có thể là vấn đề của bạn trong trình tạo số Pseudorandom ?
Alex Markov

Theo như câu hỏi cuối cùng, các quy trình như thế này gặp phải những vấn đề như vậy bởi vì (i) khoảng cách được chuyển đổi không chính xác thành độ vĩ độ hoặc kinh độ và (ii) độ méo hệ mét của hệ tọa độ không được tính hoặc được tính sai. Sử dụng hệ tọa độ dự kiến ​​thay vì hệ tọa độ địa lý thường khắc phục được cả hai vấn đề này. Làm điều đó sẽ làm lộ ra một thuộc tính cơ bản của công thức của bạn, điều này có thể hoặc không thể mong muốn: nó tạo ra các vị trí trong một hình chữ nhật xung quanh một vị trí, không phải trong một vòng tròn.
whuber

Cảm ơn Alex, mã java được đăng tại stackoverflow: stackoverflow.com/questions/10682743/
mẹo

Re mã đã chỉnh sửa: (i) random.nextInt(1001)/1000sẽ trả về giá trị lớn hơn 1 khoảng 0,1% thời gian. Tại sao bạn không sử dụng random.nextDoublehay random.nextFloat? (ii) Nhân x0y0bằng 1E6là khá bí ẩn; có vẻ như nó sẽ không tạo ra kết quả chính xác.
whuber

Đúng, tôi đã chỉnh sửa phương thức bằng nextDouble và loại bỏ 1E6. Bây giờ, tất cả các vị trí được tạo ngẫu nhiên có cùng tọa độ với vị trí của tôi. Cảm ơn sự giúp đỡ, có vẻ như tôi sẽ sớm giải quyết nó
pindleskin

Câu trả lời:


46

Điều này là khó khăn vì hai lý do: thứ nhất, giới hạn các điểm vào một vòng tròn thay vì hình vuông; thứ hai, chiếm các biến dạng trong tính toán khoảng cách.

Nhiều GIS bao gồm các khả năng tự động và minh bạch xử lý cả hai biến chứng. Tuy nhiên, các thẻ ở đây cho thấy rằng một mô tả độc lập về thuật toán của GIS có thể được mong muốn.

  1. Để tạo các điểm đồng đều, ngẫu nhiên và độc lập trong một vòng tròn bán kính r quanh một vị trí (x0, y0), hãy bắt đầu bằng cách tạo hai giá trị ngẫu nhiên thống nhất độc lập uv trong khoảng [0, 1). (Đây là những gì hầu như mọi trình tạo số ngẫu nhiên cung cấp cho bạn.) Tính toán

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

    Điểm ngẫu nhiên mong muốn là tại vị trí (x + x0, y + y0).

  2. Khi sử dụng (lat, lon) tọa độ địa lý, x0 thì (longitude) và y0 (vĩ độ) sẽ được ở độ nhưng r rất có thể sẽ được tính bằng mét (hoặc chân hoặc dặm hoặc một số đo tuyến tính khác). Đầu tiên, chuyển đổi bán kính r thành độ như thể bạn đang ở gần đường xích đạo. Ở đây, có khoảng 111.300 mét trong một mức độ.

    Thứ hai, sau khi tạo xy như ở bước (1), hãy điều chỉnh tọa độ x để thu hẹp khoảng cách đông-tây:

    x' = x / cos(y0)

    Điểm ngẫu nhiên mong muốn là tại vị trí (x '+ x0, y + y0). Đây là một thủ tục gần đúng. Đối với bán kính nhỏ (dưới vài trăm km) không kéo dài trên cả hai cực của trái đất, thông thường sẽ rất chính xác, bạn không thể phát hiện bất kỳ lỗi nào ngay cả khi tạo ra hàng chục nghìn điểm ngẫu nhiên quanh mỗi tâm (x0, y0) .


2
Whuber giải thích tuyệt vời, đó là những gì tôi cần biết. Bây giờ tôi sẽ thực hiện nó. Cảm ơn
pindleskin

1
Tôi đã chỉnh sửa câu hỏi để hiển thị một số cách thực hiện java của công thức
pindleskin

1
"có khoảng 111.300 mét trong một độ" chỉ cần lưu ý dấu phẩy được sử dụng như dấu phân cách nghìn. radiusInDegrees = radius / 111300
RMalke

2
đối với lat, tọa độ dài không nên làm x '= x / cos (y0 * Pi / 180)
Aaron Stainback

2
Tâm trí thổi @whuber, và nó có ý nghĩa. Một cách khác để xem xét tôi đoán là tưởng tượng 55 bán kính ngẫu nhiên được tạo ra cho bán kính 20. Giả sử mỗi bán kính ngẫu nhiên là đồng nhất và chính xác bằng 0 đến 20, do đó 0, 2, 4, ..., 20 Vì vậy, sẽ có 5 điểm với bán kính là 5 điểm bán kính 2, v.v ... 5 điểm có bán kính 2 (xung quanh một vòng tròn bán kính 2) sẽ trông gần nhau hơn 5 điểm với bán kính của 20.
Aziz Javed

11

Được triển khai cho 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

Việc thực hiện đúng là:

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 );
}

Tôi đã loại bỏ sự phụ thuộc vào các thư viện bên ngoài để làm cho nó dễ tiếp cận hơn.


Đề xuất chỉnh sửa OP Theo Q & A stackoverflow này , trong Java Math.cos () mong đợi các đầu vào tính bằng radian.
MikeJRamsey56

3

Câu trả lời được chấp nhận và dẫn xuất không làm việc cho tôi. Kết quả rất không chính xác.

Thực hiện đúng trong 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)
}

Toàn bộ ý chính ở đây

Trong câu trả lời được chấp nhận - tôi thấy rằng các điểm được phân phối theo hình elip với chiều rộng gấp 1,5 lần chiều cao của nó (ở Panama) và 8 lần chiều cao của nó (ở phía bắc của Thụy Điển). Nếu tôi loại bỏ điều chỉnh tọa độ x khỏi câu trả lời của @ whub thì hình elip bị biến dạng theo cách khác, cao hơn 8 lần so với chiều rộng của nó.

Mã trong câu trả lời của tôi dựa trên các thuật toán từ đây

Dưới đây bạn có thể thấy hai jsfiddles cho thấy vấn đề với hình elip kéo dài

Thuật toán đúng

Thuật toán bị bóp méo


Mô tả của bạn về các vấn đề bạn đã đề nghị thực hiện là không chính xác.
whuber

Bạn cũng có thể đúng. Bạn có quan tâm để có một cái nhìn vào jsfiddles tôi đã thực hiện và cho tôi biết tôi đã sai ở đâu.
Julian Mann

Tôi đã so sánh với câu trả lời Java của atok ở trên và đã thực hiện thay đổi này cho jsfiddle của bạn về thuật toán bị lỗi trong whuberPointAtDistance(): x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180)).
Matt

1
Mặc dù đã điều chỉnh nhưng tôi đã có kết quả chính xác hơn nhiều với ý chính của Julian. Thêm các chỉnh sửa của tôi vào whuberPointAtDistance () và chạy chúng trong ý chính với báo cáo lỗi, nó cho thấy lỗi 0,05% trong cả ba kịch bản (cao hơn đáng kể so với giải pháp thay thế.)
Matt

1

Trong 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()

Đầu ra

Khoảng cách giữa các điểm là 0,28804414714, khoảng cách giữa các điểm là 0,2880441471414, Khoảng cách giữa các điểm là 0,2880441, khoảng cách giữa các điểm là 0,409557451806 của khoảng cách giữa các điểm là 0,409557451806 của khoảng cách giữa các điểm là 0,685565757, khoảng cách giữa các điểm là 0,68260305716 Khoảng cách giữa các điểm là 0,682603057, khoảng cách giữa các điểm là 0,68260305716 Khoảng cách giữa các điểm là 0,503691568896 Khoảng cách giữa các điểm là 0,175153349209 Khoảng cách giữa các điểm là 0,195149463735 Khoảng cách giữa các điểm là 0,424094009858 Khoảng cách giữa các điểm là 0,28807741494 Khoảng cách giữa các điểm là 0,558049206307 Khoảng cách giữa các điểm là 0,4980

nhập mô tả hình ảnh ở đây


0

Bạn có thể kiểm tra kết quả tính toán của bạn ở đây . Cuộn xuống phần được gọi là "Điểm đến cho khoảng cách và mang từ điểm bắt đầu". Thậm chí còn có một công thức JavaScript đơn giản ở phía dưới để thực hiện điều này. Bạn vẫn sẽ cần tạo một vòng bi ngẫu nhiên $ \ theta $ tính bằng radian (được đo theo chiều kim đồng hồ từ phía bắc), mặc dù điều đó sẽ khá dễ dàng. Các công thức này giả định một trái đất hình cầu (mặc dù nó là hình elip), đủ tốt vì nó tạo ra sai số lên đến 0,3%.


0

Triển khai cho Swift

Lấy lat và lng từ trình mã hóa địa lý và chuyển nó đến hàm này

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)
}

Trong ví dụ của tôi ở trên, tôi nhận được vĩ độ và kinh độ từ mã hóa địa lý tên của quốc gia, vì mỗi lần, tên quốc gia cho cùng một vĩ độ và kinh độ, cũng ở giữa đất nước, vì vậy tôi cần sự ngẫu nhiên.


-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
Bạn có thể mở rộng một chút về cách giải quyết vấn đề OP và giải thích mã của bạn không?
Martin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.