Generazione di posizioni casuali nelle vicinanze?


30

Sto cercando di creare posizioni casuali nelle vicinanze della mia posizione. Quello che voglio è creare coppie casuali di latitudine / longitudine all'interno di un cerchio di 200 metri che circonda la mia posizione.

Questa è la formula che mi è venuta in mente (con l'aiuto delle persone su StackOverFlow): (Numero casuale compreso tra -1 e 1) * raggio + (vecchia longitudine) = nuova longitudine nel raggio della vecchia longitudine

(Numero casuale compreso tra -1 e 1) * raggio + (vecchia latitudine) = nuova latitudine nel raggio della vecchia latitudine

Il fatto è che qualcosa di strano sta accadendo con la mia implementazione perché tutte le posizioni casuali sono troppo vicine al mio centro di localizzazione, sembra che la formula non copra l'intero raggio.

Qualche idea di cosa potrebbe esserci di sbagliato nella mia formula?

Modificato per mostrare l'attuale implementazione 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;
}

Non sono sicuro di cosa sto facendo di sbagliato, perché le nuove posizioni sono create in mezzo al mare.

Qualche idea?


Come si implementa questa formula? Puoi presentare questa parte del tuo codice? Potrebbe essere il tuo problema con il generatore di numeri Pseudorandom ?
Alex Markov,

Per quanto riguarda la domanda finale, procedure come questa incontrano tali problemi perché (i) le distanze vengono convertite in modo errato in gradi di latitudine o longitudine e (ii) la distorsione metrica del sistema di coordinate non viene presa in considerazione o non viene spiegata correttamente. L'uso di un sistema di coordinate proiettato anziché di un sistema di coordinate geografiche di solito aggira entrambi questi problemi. In questo modo si esporrà una proprietà fondamentale della formula, che potrebbe o non potrebbe essere desiderabile: genera posizioni all'interno di un rettangolo attorno a una posizione, non all'interno di un cerchio.
whuber

Grazie Alex, il codice Java è pubblicato su StackOverflow: stackoverflow.com/questions/10682743/...
pindleskin

Per quanto riguarda il codice modificato: (i) random.nextInt(1001)/1000restituirà un valore maggiore di 1 circa lo 0,1% delle volte. Perché non stai usando random.nextDoubleo random.nextFloat? (ii) Moltiplicare x0e y0da 1E6è piuttosto misterioso; non sembra che produrrà risultati corretti.
whuber

È vero, ho modificato il metodo usando nextDouble e mi sono sbarazzato di 1E6. Ora, tutte le posizioni generate casuali hanno le stesse coordinate della mia posizione. Grazie per l'aiuto, sembra che lo risolverò presto
pindleskin

Risposte:


46

Ciò è complicato per due motivi: primo, limitare i punti a un cerchio anziché a un quadrato; in secondo luogo, tenendo conto delle distorsioni nei calcoli della distanza.

Molti GIS includono funzionalità che gestiscono in modo automatico e trasparente entrambe le complicazioni. Tuttavia, i tag qui suggeriscono che potrebbe essere desiderabile una descrizione indipendente da GIS di un algoritmo.

  1. Per generare punti in modo uniforme, casuale e indipendente all'interno di un cerchio di raggio r attorno a una posizione (x0, y0), iniziare generando due valori casuali uniformi indipendenti u e v nell'intervallo [0, 1). (Questo è ciò che ti offre quasi ogni generatore di numeri casuali.) Calcola

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

    Il punto casuale desiderato è nella posizione (x + x0, y + y0).

  2. Quando si usano le coordinate geografiche (lat, lon), allora x0 (longitudine) e y0 (latitudine) saranno in gradi ma r molto probabilmente sarà in metri (o piedi o miglia o qualche altra misura lineare). Innanzitutto, converti il ​​raggio r in gradi come se ti trovassi vicino all'equatore. Qui ci sono circa 111.300 metri in un grado.

    In secondo luogo, dopo generando x ed y come nello stadio (1), regolare la coordinata x per la contrazione delle distanze est-ovest:

    x' = x / cos(y0)

    Il punto casuale desiderato è nella posizione (x '+ x0, y + y0). Questa è una procedura approssimativa. Per piccoli raggi (meno di qualche centinaio di chilometri) che non si estendono su nessuno dei due poli della terra, di solito sarà così preciso che non è possibile rilevare alcun errore anche quando si generano decine di migliaia di punti casuali attorno a ciascun centro (x0, y0) .


2
Grande spiegazione whuber, questo è quello che dovevo sapere. Ora lo implementerò. Grazie
pindleskin

1
Ho modificato la domanda per mostrare un po 'di implementazione Java della formula
pindleskin,

1
"ci sono circa 111.300 metri in un grado" solo per notare che la virgola viene utilizzata come separatore delle migliaia. radiusInDegrees = radius / 111300
RMalke

2
per lat, coordinate lunghe non dovresti fare x '= x / cos (y0 * Pi / 180)
Aaron Stainback

2
Mente soffiata @whuber, e ha senso. Un altro modo di vederlo credo sia immaginare che vengano generati 55 raggi casuali per un raggio di 20. Supponiamo che ogni raggio casuale sia uniforme ed esattamente uguale a 0-20, quindi 0, 2, 4, ..., 20 Quindi ci saranno 5 punti con un raggio di, 5 di raggi di 2, ecc. I 5 punti con un raggio di 2 (attorno a un cerchio di raggio 2) appariranno MOLTO più vicini l'uno rispetto ai 5 punti con un raggio di 20.
Aziz Javed,

11

Implementato per 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

L'implementazione corretta è:

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

Ho rimosso la dipendenza da librerie esterne per renderla più accessibile.


Modifica proposta di OP Secondo questo Q&A StackOverflow, in Java Math.cos () si aspettano input in radianti.
MikeJRamsey56,

3

La risposta accettata e i derivati ​​non hanno funzionato per me. I risultati sono stati molto imprecisi.

Implementazione corretta in 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)
}

Somma completa qui

Nella risposta accettata, ho scoperto che i punti sono distribuiti in un'ellisse con la sua larghezza 1,5 volte la sua altezza (a Panama) e 8 volte la sua altezza (nel nord della Svezia). Se ho rimosso la regolazione x coord dalla risposta di @ whuber, l'ellisse viene distorta nell'altro modo, 8 volte più in alto della sua larghezza.

Il codice nella mia risposta era basato su algoritmi da qui

Sotto puoi vedere due jsfiddles che mostrano il problema con l'ellisse di stretching

Algoritmo corretto

Algoritmo distorto


La descrizione dei problemi riscontrati suggerisce che l'implementazione non è corretta.
whuber

Potresti avere ragione. Ti andrebbe di dare un'occhiata alle jsfiddles che ho fatto e dirmi dove ho sbagliato.
Julian Mann,

Ho confrontato con la risposta Java di ATOK sopra, e ho fatto questo cambiamento al tuo jsfiddle dell'algoritmo distored in whuberPointAtDistance(): x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180)).
Matt

1
Nonostante la mia correzione, ho ottenuto risultati molto più precisi con l'essenza di Julian. Aggiungendo le mie correzioni a whuberPointAtDistance () ed eseguendole in sintesi con il rapporto di errore, ha mostrato un errore dello 0,05% in tutti e tre gli scenari (significativamente più alto dell'alternativa.)
Matt

1

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

Produzione

La distanza tra i punti è 0.288044147914 La distanza tra i punti è 0.409557451806 La distanza tra i punti è 0.368260305716 La distanza tra i punti è 0.340720560546 La distanza tra i punti è 0.453773334731 La distanza tra i punti è 0.460608754561 La distanza tra i punti è 0.497188825576 La distanza tra i punti è 0.603 La distanza tra i punti è 0.503691568896 La distanza tra i punti è 0.175153349209 La distanza tra i punti è 0.195149463735 La distanza tra i punti è 0.424094009858 La distanza tra i punti è 0.286807741494 La distanza tra i punti è 0.558049206307 La distanza tra i punti è 0.498612171417 La distanza tra i punti è 0.047344718284

inserisci qui la descrizione dell'immagine


0

Puoi controllare i risultati dei tuoi calcoli qui . Scorri verso il basso fino alla sezione chiamata "Punto di destinazione data distanza e direzione dal punto di partenza". C'è anche una semplice formula JavaScript in fondo per implementare questo. Dovrai comunque generare un rilevamento casuale $ \ theta $ in radianti (misurato in senso orario da nord), anche se dovrebbe essere piuttosto semplice. Queste formule assumono una terra sferica (sebbene sia ellissoidale), che è abbastanza buona in quanto produce errori fino allo 0,3%.


0

Implementazione per Swift

Ottenere lat e lng da geoencoder e passarlo a questa funzione

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

Nel mio esempio sopra ottengo la latitudine e le lunghezze dalla geoencodifica del nome del paese, poiché ogni volta il nome del paese dà la stessa latitudine e le stesse lunghezze, anche quello nel mezzo del paese, quindi avevo bisogno di casualità.


-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
Potresti espandere un po 'su come questo risolve i problemi dei PO e spiegare il tuo codice in modo errato?
Martin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.