근처에서 임의의 위치를 ​​생성 하시겠습니까?


30

내 위치 근처에 임의의 위치를 ​​만들려고합니다. 내가 원하는 것은 내 위치를 둘러싼 200m 원 안에 임의의 위도 / 경도 쌍을 만드는 것입니다.

이것은 (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;
}

새로운 위치가 바다 한가운데서 만들어지기 때문에 내가 뭘 잘못하고 있는지 잘 모르겠습니다.

어떤 생각?


이 공식을 어떻게 구현합니까? 코드의이 부분을 제시 할 수 있습니까? 의사 난수 생성기 에서 문제가 될 수 있습니까?
Alex Markov

최종 질문이 진행되는 한, 이와 같은 절차는 (i) 거리가 위도 또는 경도로 잘못 변환되고 (ii) 좌표계의 메트릭 왜곡이 고려되지 않거나 잘못 설명되기 때문에 이러한 문제가 발생합니다. 지리적 좌표 시스템 대신 투영 좌표 시스템을 사용하면 일반적으로이 두 가지 문제를 모두 해결할 수 있습니다. 그렇게하면 수식의 기본 속성이 노출 될 수 있습니다. 원 속성이 아닌 위치 주변의 사각형 내에 위치가 생성됩니다 .
whuber

감사 알렉스는, 자바 코드는 유래에 게시되어 stackoverflow.com/questions/10682743/...
pindleskin

편집 된 코드를 다시 : (i) random.nextInt(1001)/1000시간의 약 0.1 %보다 큰 값을 반환합니다. 왜 사용하지 않는 random.nextDoublerandom.nextFloat? (ⅱ) 배가 x0하고 y0으로는 1E6오히려 신비; 올바른 결과를 얻는 것처럼 보이지 않습니다.
whuber

사실, nextDouble을 사용하여 방법을 편집하고 1E6을 제거했습니다. 이제 무작위로 생성 된 모든 위치는 내 위치와 동일한 좌표를 갖습니다. 도움을 주셔서 감사합니다, 내가 곧 그것을 해결하기 위하여려고하고있다 보인다
pindleskin

답변:


46

이것은 두 가지 이유로 까다 롭습니다. 첫째, 점을 사각형 대신 원으로 제한합니다. 둘째, 거리 계산에서 왜곡을 설명합니다.

많은 GIS에는 두 가지 합병증을 자동으로 투명하게 처리하는 기능이 포함되어 있습니다. 그러나, 여기서 태그는 알고리즘에 대한 GIS 독립적 설명이 바람직 할 수 있음을 제안합니다.

  1. 위치 (x0, y0) 주위 의 반경 r 의 원 안에 균일하고 무작위로 독립적으로 포인트를 생성하려면 간격 [0, 1)에서 두 개의 독립적 인 균일 랜덤 랜덤 값 uv 를 생성하는 것으로 시작하십시오 . (이것은 거의 모든 난수 생성기가 제공하는 것입니다.)

    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 미터가 있습니다.

    둘째, 단계 (1)에서 와 같이 xy 를 생성 한 후 동서 거리의 축소를 위해 x 좌표를 조정하십시오.

    x' = x / cos(y0)

    원하는 랜덤 포인트는 위치 (x '+ x0, y + y0)에 있습니다. 이것은 대략적인 절차입니다. 지구의 어느 극점으로도 확장되지 않는 작은 반경 (수백 킬로미터 미만)의 경우 일반적으로 각 중심 주위에 수만 개의 임의의 점 (x0, y0)을 생성 할 때 오류를 감지 할 수 없을 정도로 정확합니다. .


2
훌륭한 설명 whuber, 그것이 내가 알아야 할 것입니다. 이제 구현하겠습니다. 감사합니다
pindleskin

1
수식의 자바 구현을 보여주기 위해 질문을 편집했습니다.
pindleskin

1
쉼표는 천 단위 구분 기호로 사용됩니다. radiusInDegrees = radius / 111300
RMalke

2
위도, 긴 좌표는 x '= x / cos (y0 * Pi / 180)하지
않아야

2
@ whuber를 날려 버렸고 말이됩니다. 그것을 보는 또 다른 방법은 반경 20에 대해 55 개의 임의 반경이 생성되는 것을 상상하는 것입니다. 각 임의의 반경은 균일하고 정확히 0에서 20까지이므로 0, 2, 4, ..., 20이라고 가정 해 봅시다. 따라서 반지름이 5 인 점, 반지름이 2 인 5 개 등이 있습니다. 반지름이 2 인 5 개의 점 (반지름 2의 원 주위)은 반지름이있는 5 점보다 서로 가깝게 보입니다. (20)
아지즈 자 베드

11

자바 스크립트 구현 :

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

더 접근하기 쉽도록 외부 라이브러리에 대한 종속성을 제거했습니다.


제안 된 OP 편집 스택 오버 플로우 Q & A에 따라 Java Math.cos ()에서 입력은 라디안으로 표시됩니다.
MikeJRamsey56

3

수락 된 답변과 파생 상품이 저에게 효과적이지 않았습니다. 결과는 매우 부정확했습니다.

자바 스크립트에서 올바른 구현 :

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 배 (스웨덴 북쪽)의 타원으로 분포되어 있음을 발견했습니다. @ whuber의 대답에서 x 좌표 조정을 제거하면 타원이 다른 방법으로 너비보다 8 배 더 왜곡됩니다.

내 대답의 코드는 여기의 알고리즘을 기반으로했습니다 .

아래에는 스트레칭 타원의 문제를 보여주는 두 개의 jsfiddles가 있습니다.

올바른 알고리즘

왜곡 된 알고리즘


구현 한 내용이 잘못되었다고 제안한 문제에 대한 설명.
whuber

당신이 옳을 수도 있습니다. 내가 만든 jsfiddles를 살펴보고 내가 잘못한 곳을 알려주시겠습니까?
Julian Mann

나는 위의 ATOK의 자바 대답과 비교하고있는 distored의 알고리즘의 jsfiddle이 변경 한 whuberPointAtDistance(): x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180)).
Matt

1
내 교정에도 불구하고 줄리안의 요지로 훨씬 더 정확한 결과를 얻었습니다. 내 수정 사항을 whuberPointAtDistance ()에 추가하고 오류 보고서와 함께 요지에서 실행하면 세 가지 시나리오 모두에서 0.05 % 오류가 발생했습니다 (대안보다 상당히 높음).
Matt

1

파이썬에서

# 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.603988188859 포인트 사이의 거리는 0.4288975384307 포인트 간 거리는 0.503691568896 포인트 간 거리는 0.175153349209 포인트 간 거리는 0.195149463735 포인트 간 거리는 0.424094009858 포인트 간 거리는 0.286807741494 포인트 간 거리는 0.558049206307 포인트 간 거리는 0.498612171417 포인트 간 거리는 0.047344718215 포인트 간 거리는 0.484232497086

여기에 이미지 설명을 입력하십시오


0

여기에서 계산 결과를 확인할 수 있습니다 . "시작점으로부터의 거리와 방위가 주어진 목적지"섹션으로 스크롤하십시오. 이것을 구현하기 위해 맨 아래에 간단한 JavaScript 수식이 있습니다. 라디안 (북쪽에서 시계 방향으로 측정)으로 $ \ theta $를 무작위로 생성해야하지만, 이는 매우 간단합니다. 이 공식은 구형 지구 (타원형이지만)를 가정하는데, 이는 최대 0.3 %의 오차를 생성하기에 충분합니다.


0

스위프트 구현

지오 인코더에서 위도 및 경도 가져 오기 및이 기능에 전달

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

개인 무효 drawPolyline (더블 lat, 더블 ng) {

         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
이것이 OP 문제를 해결하고 코드를 간단하게 설명하는 방법에 대해 조금 확장 할 수 있습니까?
Martin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.