Генерувати випадкові місця поблизу?


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) метричні спотворення системи координат не враховуються або враховуються неправильно. Використання проектованої системи координат замість географічної системи координат зазвичай долає обидві ці проблеми. Це виявить основну властивість вашої формули, яка може бути або не бажаною: вона генерує розташування у прямокутнику навколо місця, а не в колі.
качан

Дякую Алекс, код Java розміщений на StackOverflow: stackoverflow.com/questions/10682743 / ...
pindleskin

Повторно відредагований код: (i) random.nextInt(1001)/1000поверне значення, що перевищує 1, приблизно 0,1% часу. Чому ви не використовуєте random.nextDoubleабо random.nextFloat? (Б) Множення x0і y0на 1E6досить таємниче; не здається, що це дасть правильні результати.
whuber

Правда, я редагував метод за допомогою nextDouble і позбувся 1E6. Тепер усі випадково генеровані місця мають ті самі координати, що і моє місцеположення. Дякую за допомогу, здається, що я скоро вирішу це питання
pindleskin

Відповіді:


46

Це хитро з двох причин: по-перше, обмеження точок колом замість квадрата; по-друге, облік спотворень у розрахунках відстані.

Багато ГІС включають можливості, які автоматично та прозоро обробляють обидва ускладнення. Однак теги тут говорять про те, що GIS-незалежний опис алгоритму може бути бажаним.

  1. Створювати точки рівномірно, випадковим чином і незалежно в колі радіуса r навколо місця (x0, y0), почніть з генерації двох незалежних рівномірних випадкових значень u та v в інтервалі [0, 1). (Це вам забезпечує майже кожен генератор випадкових чисел.) Обчисліть

    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 метрів в градусі.

    По-друге, після генерації х і y, як на кроці (1), регулюйте координату x для зменшення відстаней схід-захід:

    x' = x / cos(y0)

    Бажана випадкова точка знаходиться в місці (x '+ x0, y + y0). Це приблизна процедура. Для малих радіусів (менше декількох сотень кілометрів), які не простягаються на жоден полюс Землі, зазвичай це буде настільки точно, що ви не можете виявити жодної помилки, навіть коли генеруєте десятки тисяч випадкових точок навколо кожного центру (x0, y0) .


2
Чудове пояснення, що я повинен знати. Зараз я збираюся це здійснити. Спасибі
pindleskin

1
Я відредагував питання, щоб показати деяку реалізацію формули java
pindleskin

1
"Є близько 111 300 метрів в градусі", тільки для замітки кома використовується як тисяча роздільник. radiusInDegrees = радіус / 111300
RMalke

2
для лата, довгі координати, чи не слід робити x '= x / cos (y0 * Pi / 180)
Aaron Stainback,

2
Розум вибухнув @whuber, і це має сенс. Ще один спосіб поглянути на це, я думаю, уявити собі, що генерується 55 випадкових радіусів для радіуса 20. Скажімо, кожен випадковий радіус є рівномірним і точно рівним 0 або 20, тому 0, 2, 4, ..., 20 . Отже, буде 5 точок з радіусами, 5 радіусами 2 і т. Д. 5 балів з радіусами 2 (навколо кола радіуса 2) будуть виглядати НАШМО ближче один до одного, ніж 5 балів з радіусами від 20.
Азіз Джавед

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

Я усунув залежність від зовнішніх бібліотек, щоб зробити її більш доступною.


Пропонована редакція ОП Відповідно до цього питання щодо стаціонарного потоку, в 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 разів більше її висоти (на півночі Швеції). Якщо я зняв регулювання координати x з відповіді @ whuber, еліпс спотворюється в інший бік, у 8 разів перевищує його ширину.

Код у моїй відповіді був заснований на алгоритмах звідси

Нижче ви можете побачити дві jsfiddles, які показують проблему з розтягуючим еліпсом

Правильний алгоритм

Спотворений алгоритм


Опис Ваших проблем, що виникли, передбачає, що Ваша реалізація була неправильною.
whuber

Ви цілком можете мати рацію. Чи хотіли б ви поглянути на зроблені jsfiddles та скажіть, де я пішов не так.
Джуліан Манн

Я по порівнянні з Java відповідь аток в вище, і зробив це зміна у вашій jsfiddle з спотворюється алгоритму whuberPointAtDistance(): x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180)).
Метт

1
Незважаючи на мою корекцію, я мав набагато точніші результати з сутью Джуліана. Додавши мої виправлення до whuberPointAtDistance () та запустивши їх у суть із звітом про помилки, він показав помилку 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,603178188859 Відстань між точками - 0,603178188859 Відстань між точками - 0,63178188859 Відстань між точками - 0.503691568896 Відстань між точками - 0.175153349209 Відстань між точками - 0.195149463735 Відстань між пунктами - 0.424094009858 Відстань між пунктами - 0.286807741494 Відстань між точками - 0.558049206307 Відстань між точками - 0.498612171417 Відстань між точками - 0.047344718215

введіть тут опис зображення


0

Ви можете перевірити результати своїх розрахунків тут . Прокрутіть вниз до розділу під назвою "Точка призначення, задана відстань та опора від точки початку". Існує навіть проста формула JavaScript внизу, щоб реалізувати це. Вам все одно доведеться генерувати випадковий підшипник $ \ theta $ в радіанах (вимірюється за годинниковою стрілкою з півночі), хоча це має бути досить прямо вперед. Ці формули передбачають сферичну землю (хоча вона і еліпсоїдальна), що досить добре, оскільки вона створює помилки до 0,3%.


0

Реалізація для Swift

Отримання lat та lng від 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

приватний порожній малюнокПоліліне (подвійний лат, подвійний 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
Не могли б ви трохи розширити, як це вирішує завдання ОП та поясніть ваш код?
Мартін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.