กำลังสร้างสถานที่สุ่มในบริเวณใกล้เคียง


30

ฉันกำลังพยายามสร้างตำแหน่งสุ่มที่อยู่ใกล้กับตำแหน่งของฉัน สิ่งที่ฉันต้องการคือการสร้างคู่ละติจูด / ลองจิจูดแบบสุ่มภายในวงกลม 200 เมตรรอบ ๆ สถานที่ของฉัน

นี่คือสูตรที่ฉันคิดขึ้นมา (ด้วยความช่วยเหลือของผู้คนที่ StackOverFlow): (สุ่มตัวเลขระหว่าง -1 ถึง 1) * รัศมี + (ลองจิจูดเก่า) = ลองจิจูดใหม่ภายในรัศมีลองจิจูดเก่า

(ตัวเลขสุ่มระหว่าง -1 ถึง 1) * รัศมี + (ละติจูดเก่า) = ละติจูดใหม่ภายในรัศมีของละติจูดเก่า

สิ่งที่เป็นสิ่งที่แปลกเกิดขึ้นกับการใช้งานของฉันเพราะสถานที่สุ่มทั้งหมดอยู่ใกล้กับศูนย์ตำแหน่งของฉันมากเกินไปดูเหมือนว่าสูตรจะไม่ครอบคลุมรัศมีทั้งหมด

ความคิดเกี่ยวกับสิ่งที่อาจผิดกับสูตรของฉัน?

แก้ไขเพื่อแสดงการใช้จาวาปัจจุบัน:

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

ฉันไม่แน่ใจว่าสิ่งที่ฉันทำผิดเพราะตำแหน่งใหม่ถูกสร้างขึ้นกลางทะเล

ความคิดใด ๆ


คุณใช้สูตรนี้อย่างไร คุณสามารถแสดงส่วนของรหัสนี้ได้หรือไม่? อาจเป็นปัญหาของคุณในเครื่องกำเนิดหมายเลข Pseudorandom ?
Alex Markov

เท่าที่คำถามสุดท้ายเกิดขึ้นกระบวนการเช่นนี้พบปัญหาดังกล่าวเนื่องจาก (i) ระยะทางถูกแปลงเป็นองศาละติจูดและลองจิจูดไม่ถูกต้องและ (ii) การบิดเบือนระบบเมตริกของระบบพิกัดไม่ถูกนำมาใช้ การใช้ระบบพิกัดที่คาดการณ์ไว้แทนที่จะเป็นระบบพิกัดทางภูมิศาสตร์มักจะแก้ไขปัญหาทั้งสองนี้ได้ การทำเช่นนั้นจะเป็นการเปิดเผยคุณสมบัติพื้นฐานของสูตรของคุณซึ่งอาจเป็นที่ต้องการหรืออาจไม่เป็นที่ต้องการ: มันจะสร้างสถานที่ภายในสี่เหลี่ยมรอบ ๆ สถานที่ไม่ใช่ภายในวงกลม
whuber

ขอบคุณอเล็กซ์โค้ดจาวาถูกโพสต์ที่ stackoverflow: stackoverflow.com/questions/10682743/…
pindleskin

รหัสที่แก้ไขอีกครั้ง: (i) random.nextInt(1001)/1000จะส่งคืนค่าที่มากกว่า 1 ประมาณ 0.1% ของเวลา ทำไมคุณไม่ใช้random.nextDoubleหรือrandom.nextFloat? (ii) การคูณx0และy0โดย1E6ค่อนข้างลึกลับ ดูเหมือนว่าจะให้ผลลัพธ์ที่ถูกต้อง
whuber

จริงฉันแก้ไขวิธีการโดยใช้ nextDouble และกำจัด 1E6 ตอนนี้ตำแหน่งสุ่มที่สร้างทั้งหมดมีพิกัดเดียวกันกับที่ตั้งของฉัน ขอบคุณสำหรับความช่วยเหลือดูเหมือนว่าฉันจะแก้ปัญหาได้ทุกเวลาเร็ว ๆ นี้
pindleskin

คำตอบ:


46

สิ่งนี้เป็นเรื่องยุ่งยากด้วยเหตุผลสองประการคือประการแรก จำกัด จุดให้เป็นวงกลมแทนที่จะเป็นสี่เหลี่ยมจัตุรัส ประการที่สองการบัญชีสำหรับการบิดเบือนในการคำนวณระยะทาง

GIS หลายแห่งมีความสามารถที่จะจัดการภาวะแทรกซ้อนทั้งสองแบบอัตโนมัติและโปร่งใส อย่างไรก็ตามแท็กที่นี่แนะนำว่าคำอธิบายอัลกอริทึมที่เป็นอิสระจาก 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. เมื่อใช้พิกัดทางภูมิศาสตร์ (lat, lon)ดังนั้น x0 (ลองจิจูด) และ y0 (ละติจูด) จะเป็นองศาแต่rน่าจะอยู่ในหน่วยเมตร (หรือฟุตหรือไมล์หรือการวัดเชิงเส้นอื่น ๆ ) ก่อนอื่นแปลงรัศมีrเป็นองศาราวกับว่าคุณอยู่ใกล้เส้นศูนย์สูตร ที่นี่มีระดับประมาณ 111,300 เมตร

    ประการที่สองหลังจากสร้างxและyตามขั้นตอน (1) ให้ปรับพิกัด x สำหรับการหดตัวของระยะทางตะวันออก - ตะวันตก:

    x' = x / cos(y0)

    จุดสุ่มที่ต้องการอยู่ที่ตำแหน่ง (x '+ x0, y + y0) นี่เป็นขั้นตอนโดยประมาณ สำหรับรัศมีขนาดเล็ก (น้อยกว่าไม่กี่ร้อยกิโลเมตร) ที่ไม่ขยายไปทั่วทั้งขั้วโลกมันมักจะแม่นยำดังนั้นคุณไม่สามารถตรวจพบข้อผิดพลาดใด ๆ แม้ว่าจะสร้างจุดสุ่มนับหมื่นรอบแต่ละศูนย์ (x0, y0) .


2
คำอธิบายที่ยอดเยี่ยมมากนั่นคือสิ่งที่ฉันจำเป็นต้องรู้ ตอนนี้ฉันจะใช้มัน ขอบคุณ
pindleskin

1
ฉันแก้ไขคำถามเพื่อแสดงการใช้งานจาวาของสูตร
pindleskin

1
"มีระดับประมาณ 111,300 เมตรในระดับหนึ่ง" เพียงเพื่อให้ทราบว่าใช้เครื่องหมายจุลภาคเป็นตัวคั่นหลักพัน radiusInDegrees = radius / 111300
RMalke

2
สำหรับ lat พิกัดที่ยาวคุณไม่ควรทำ 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.
Aziz Javed

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

ฉันลบการอ้างอิงในไลบรารีภายนอกเพื่อให้เข้าถึงได้มากขึ้น


แก้ไขเสนอของ OP ตามนี้ StackOverflow Q & A ใน 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 coord ออกจากคำตอบ @ whuber วงรีจะบิดเบี้ยวด้วยวิธีอื่นสูงกว่าความกว้าง 8 เท่า

รหัสในคำตอบของฉันนั้นขึ้นอยู่กับอัลกอริทึมจาก ที่นี่

ด้านล่างคุณจะเห็น jsfiddles สองตัวที่แสดงปัญหาด้วยการยืดวงรี

อัลกอริทึมที่ถูกต้อง

อัลกอริทึมที่ผิดเพี้ยน


คำอธิบายของคุณเกี่ยวกับปัญหาที่คุณมีแนะนำการใช้งานไม่ถูกต้อง
whuber

คุณอาจจะพูดถูก คุณสนใจที่จะดู jsfiddles ที่ฉันทำและบอกฉันว่าฉันไปไหนผิด
Julian Mann

ผมเทียบกับ ATOK ของ Java คำตอบข้างต้นและทำการเปลี่ยนแปลงนี้เพื่อ jsfiddle ของคุณของขั้นตอนวิธี distored ใน:whuberPointAtDistance() x1 = (w * Math.cos(t)) / Math.cos(y0 * (Math.PI / 180))
Matt

1
แม้จะมีการแก้ไขของฉันฉันมีผลลัพธ์ที่แม่นยำมากขึ้นด้วยส่วนสำคัญของจูเลียน การเพิ่มการแก้ไขของฉันไปยัง whuberPointAtDistance () และเรียกใช้พวกเขาในส่วนสำคัญด้วยรายงานข้อผิดพลาดมันแสดงข้อผิดพลาด 0.05% ในทั้งสามสถานการณ์ (สูงกว่าทางเลือก)
แมตต์

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.4080,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,986,8,8,987,987,961,987,961,987,987,961,987,645,987,987,961,987,961,490 ระหว่าง 4 จุดระหว่าง 0.459,080,880,988,988,961,490,490,490,490,490,490,490,490,490,445,445,445,445,432,445,4452 ระยะทางระหว่างจุดคือ 0.503691568896 ระยะห่างระหว่างจุดคือ 0.175153349209 ระยะทางระหว่างจุดคือ 0.19514946373735 ระยะห่างระหว่างจุดคือ 0.42,864,4,4,4,5,4,4,5,6,9,986,986,804 ระยะทางระหว่างจุดคือ 0.558649176,647 ระยะทางระหว่างจุดคือ 0.4986121714,174 ระยะทางระหว่างจุดคือ 0.4986121714,174

ป้อนคำอธิบายรูปภาพที่นี่


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

ในตัวอย่างของฉันด้านบนฉันได้ละติจูดและลองจิจูดจาก geoencoding ชื่อของประเทศตามแต่ละครั้งชื่อประเทศจะให้ละติจูดและลองจิจูดเหมือนกันซึ่งอยู่ตรงกลางของประเทศดังนั้นฉันจึงต้องการการสุ่ม


-1

โมฆะส่วนตัว drawPolyline (คู่ 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
คุณช่วยขยายความเข้าใจในวิธีการแก้ปัญหา OPs และอธิบายรหัสของคุณได้อย่างชัดเจน?
มาร์ติน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.