Menghasilkan lokasi acak di dekat sini?

30

Saya mencoba membuat lokasi acak di dekat lokasi saya. Yang saya inginkan adalah membuat pasangan garis lintang / bujur secara acak di dalam lingkaran 200 meter di sekitar lokasi saya.

Ini adalah rumus yang saya buat (dengan bantuan orang-orang di StackOverFlow): (Angka acak antara -1 dan 1) * radius + (bujur lama) = bujur baru dalam radius bujur lama

(Angka acak antara -1 dan 1) * radius + (lintang lama) = lintang baru dalam radius lintang lama

Masalahnya adalah bahwa ada sesuatu yang aneh terjadi dengan implementasi saya karena semua lokasi acak terlalu dekat dengan pusat lokasi saya, tampaknya rumus tersebut tidak mencakup seluruh jari-jari.

Adakah yang tahu apa yang salah dengan formula saya?

Diedit untuk menampilkan implementasi java saat ini:

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

Saya tidak yakin apa yang saya lakukan salah, karena lokasi baru dibuat di tengah laut.

Ada ide?

kulit pindle
sumber
Bagaimana Anda menerapkan formula ini? Bisakah Anda menyajikan bagian ini dari kode Anda? Mungkin masalah Anda di generator nomor Pseudorandom ?
Alex Markov
Sejauh pertanyaan terakhir berjalan, prosedur seperti ini menghadapi masalah seperti itu karena (i) jarak tidak tepat dikonversi ke derajat lintang atau bujur dan (ii) distorsi metrik sistem koordinat tidak diperhitungkan atau tidak tepat. Menggunakan sistem koordinat yang diproyeksikan alih-alih sistem koordinat geografis biasanya mengatasi kedua masalah ini. Melakukan hal itu akan memaparkan properti mendasar dari rumus Anda, yang mungkin atau mungkin tidak diinginkan: itu menghasilkan lokasi dalam persegi panjang di sekitar lokasi, bukan dalam lingkaran.
whuber
Terima kasih Alex, kode java diposting di stackoverflow: stackoverflow.com/questions/10682743/...
pindleskin
Re kode yang diedit: (i) random.nextInt(1001)/1000akan mengembalikan nilai lebih besar dari 1 sekitar 0,1% dari waktu Mengapa Anda tidak menggunakan random.nextDoubleatau random.nextFloat? (ii) Mengalikan x0dan y0oleh 1E6agak misterius; sepertinya tidak akan menghasilkan hasil yang benar.
Whuber
Benar, saya mengedit metode menggunakan nextDouble dan menyingkirkan 1E6. Sekarang, semua lokasi yang dihasilkan secara acak memiliki koordinat yang sama dengan lokasi saya. Terima kasih atas bantuannya, sepertinya saya akan menyelesaikannya dalam waktu dekat
pindleskin

Jawaban:

46

Ini rumit karena dua alasan: pertama, membatasi titik-titik pada lingkaran alih-alih persegi; kedua, memperhitungkan distorsi dalam perhitungan jarak.

Banyak GIS menyertakan kemampuan yang secara otomatis dan transparan menangani kedua komplikasi. Namun, tag di sini menunjukkan bahwa deskripsi algoritma-independen GIS mungkin diinginkan.

  1. Untuk menghasilkan titik secara seragam, acak, dan mandiri dalam lingkaran jari-jari r di sekitar lokasi (x0, y0), mulailah dengan menghasilkan dua nilai acak seragam independen u dan v dalam interval [0, 1). (Inilah yang disediakan oleh hampir setiap generator angka acak.) Hitung

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

    Titik acak yang diinginkan ada di lokasi (x + x0, y + y0).

  2. Saat menggunakan koordinat geografis (lat, lon), maka x0 (garis bujur) dan y0 (garis lintang) akan dalam derajat tetapi r kemungkinan besar berada dalam meter (atau kaki atau mil atau pengukuran linear lainnya). Pertama, konversikan jari-jari r menjadi derajat seolah-olah Anda berada di dekat khatulistiwa. Di sini, ada sekitar 111.300 meter dalam satu derajat.

    Kedua, setelah menghasilkan x dan y seperti pada langkah (1), sesuaikan koordinat x untuk penyusutan jarak timur-barat:

    x' = x / cos(y0)

    Titik acak yang diinginkan ada di lokasi (x '+ x0, y + y0). Ini adalah prosedur perkiraan. Untuk jari-jari kecil (kurang dari beberapa ratus kilometer) yang tidak melampaui kedua kutub bumi, biasanya akan sangat akurat sehingga Anda tidak dapat mendeteksi kesalahan apa pun bahkan ketika menghasilkan puluhan ribu titik acak di sekitar masing-masing pusat (x0, y0) .

whuber
sumber
2
Penjelasan hebat, itu yang perlu saya ketahui. Sekarang saya akan mengimplementasikannya. Terima kasih
kulit pindle
1
Saya mengedit pertanyaan untuk menunjukkan beberapa implementasi java dari formula
pindleskin
1
"ada sekitar 111.300 meter dalam derajat" hanya untuk catatan koma digunakan sebagai pemisah ribuan. radiusInDegrees = radius / 111300
RMalke
2
untuk lat, koordinat panjang seharusnya tidak Anda lakukan x '= x / cos (y0 * Pi / 180)
Aaron Stainback
2
Mind meledak @whuber, dan itu masuk akal. Cara lain untuk melihatnya, saya kira adalah membayangkan 55 jari-jari acak yang dihasilkan untuk jari-jari 20. Katakanlah setiap jari-jari acak seragam dan persis sama dengan 0 hingga 20, jadi 0, 2, 4, ..., 20 Jadi akan ada 5 titik dengan jari-jari, 5 jari-jari 2, dll. 5 titik dengan jari-jari 2 (sekitar lingkaran jari-jari 2) akan terlihat JAUH lebih dekat satu sama lain daripada 5 titik dengan jari-jari 20.
Aziz Javed
11

Diimplementasikan untuk 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
Lyra
sumber
10

Implementasi yang benar adalah:

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

Saya menghapus ketergantungan pada perpustakaan eksternal untuk membuatnya lebih mudah diakses.

atok
sumber
Usulan mengedit OP Per ini stackoverflow Q & A, di Jawa Math.cos () mengharapkan masukan dalam radian.
MikeJRamsey56
3

Jawaban yang diterima dan turunannya tidak bekerja untuk saya. Hasilnya sangat tidak akurat.

Implementasi yang benar dalam 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)
}

Inti penuh di sini

Dalam jawaban yang diterima - saya menemukan bahwa titik didistribusikan dalam elips dengan lebarnya 1,5 kali tinggi (di Panama) dan 8 kali tingginya (di utara Swedia). Jika saya menghapus penyesuaian x coord dari jawaban @ whuber elips terdistorsi dengan cara lain, 8 kali lebih tinggi dari lebarnya.

Kode dalam jawaban saya didasarkan pada algoritma dari sini

Di bawah ini Anda dapat melihat dua jsfiddles yang menunjukkan masalah dengan elips peregangan

Algoritma yang benar

Algoritma terdistorsi

Julian Mann
sumber
Deskripsi Anda tentang masalah yang Anda miliki menunjukkan implementasi Anda tidak benar.
whuber
Anda mungkin benar. Maukah Anda melihat-lihat jsfiddles yang saya buat dan memberi tahu saya di mana kesalahan saya.
Julian Mann
Aku dibandingkan dengan jawaban Java Atok di atas, dan membuat perubahan ini untuk jsfiddle Anda dari algoritma distored di whuberPointAtDistance(): x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180)).
Matt
1
Meskipun ada koreksi, saya mendapat hasil yang lebih akurat dengan intisari Julian. Menambahkan koreksi saya ke whuberPointAtDistance () dan menjalankannya di intinya dengan laporan kesalahan, itu menunjukkan kesalahan 0,05% dalam ketiga skenario (secara signifikan lebih tinggi dari alternatif.)
Matt
1

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

Keluaran

Jarak antara titik adalah 0.288044147914 Jarak antara titik adalah 0.409557451806 Jarak antara titik adalah 0.368260305716 Jarak antara titik adalah 0.340720560546 Jarak antara titik adalah 0.453773334731 Jarak antara titik adalah 0.460608754561 Jarak antara titik adalah 0.497188825576 Jarak antara titik adalah 0.497188825576 Jarak antara titik adalah 0.497188825576. Jarak antara titik adalah 0.503691568896 Jarak antara titik adalah 0.175153349209 Jarak antara titik adalah 0.195149463735 Jarak antara titik adalah 0.424094009858 Jarak antara titik adalah 0.286807741494 Jarak antara titik adalah 0.558049206307 Jarak antara titik adalah 0.498612171417 Jarak antara titik adalah 0.498612171417 Jarak antara titik adalah 0.498612171417

masukkan deskripsi gambar di sini

Alex Punnen
sumber
0

Anda dapat memeriksa hasil perhitungan Anda di sini . Gulir ke bawah ke bagian yang disebut "Titik tujuan diberi jarak dan bantalan dari titik awal". Bahkan ada formula JavaScript sederhana di bagian bawah untuk mengimplementasikannya. Anda masih perlu menghasilkan bantalan acak $ \ theta $ dalam radian (diukur searah jarum jam dari utara), meskipun itu harus cukup lurus ke depan. Rumus-rumus ini mengasumsikan bumi bulat (meskipun berbentuk ellipsoidal), yang cukup baik karena menghasilkan kesalahan hingga 0,3%.

Dimitriy V. Masterov
sumber
0

Implementasi untuk Swift

Dapatkan lat dan lng dari geoencoder dan meneruskannya ke fungsi ini

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

Dalam contoh saya di atas saya mendapatkan garis lintang dan garis bujur dari geoencoding nama negara, karena setiap kali, nama negara memberikan garis lintang dan garis bujur yang sama, yang juga di tengah negara, jadi saya perlu keacakan.

Pulkit
sumber
-1

private void drawPolyline (lat ganda, lng ganda) {

         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));
Astaga
sumber
5
Bisakah Anda mengembangkan sedikit tentang bagaimana ini memecahkan masalah OP dan menjelaskan kode Anda dengan cepat?
Martin