Belirli bir mesafe ve enlem / boylam için sınırlama kutusunu nasıl hesaplayabilirim?


13

Belirli bir WGS84 enlem ve WGS84 boylam ve mesafe için bir sınırlayıcı kutu veya daire hesaplamak gerekir ama nereden başlayacağımı bilmiyorum!

Başlangıç ​​Enlem / Boylamı arasındaki mesafe 10Km veya daha az olacaktır.

Birisi bana bazı işaretçiler verebilir mi? / Bunun nasıl yapılacağıyla ilgili örnek


Her iki kutbu da içermeyen çevreler için, gis.stackexchange.com/questions/19221/… adresinde ayrıntılı bir yanıt verilir . Ancak bu, mevcut yanıtların öne sürdüğü gibi tam bir hikaye değil: hız-doğruluk-programı karmaşıklığı dengesini yapabilirsiniz. Ayrıca, lat-lon'da çalışırken sınırlayıcı kutuların belirtilmesinde "sargı" sorunu olduğunu unutmayın (zorluklar + -180 derece meridyende ortaya çıkar). Bunun çözümü için gis.stackexchange.com/questions/17788/… adresine bakın .
whuber

Gerçekten bir kutuya mı ihtiyacınız var, yoksa belirli bir noktaya yakın 4 puan yeterli mi? P noktası verildiğinde, NE, SW, SE ve NW yönlerinden p noktasına 4 nokta d mesafe bulun.
Kirk Kuykendall

@Kirk - 4 noktanın koordinatlarına sahipseniz, o zaman kutunuz var ...
martinstoeckli

@martinstoeckli doğru, sadece bir küreye yansıtılan bir kutunun nasıl göründüğünü görselleştirmek zorunda kalmadan sorunu basitleştirmeyi umuyordum. Ayrıca, kutunun kenarlarının aynı enlem / boylamda (başka bir deyişle döndürülmüş bir kutu) düşmesi gerekmediğini açıkça belirtmek için sorunun genelleştirilebileceğini unutmayın.
Kirk Kuykendall

@Kirk - Ahh, tam olarak buna ihtiyacınız varsa, o zaman elbette haklısınız. Kutunun sadece olası adayları hızlı bir şekilde bulmak için yararlı olduğunu düşünüyorum. İki noktanın belirli bir mesafe (daire) içinde olup olmadığını kontrol etmek için, daha karmaşık haversin formülü kullanılabilir.
martinstoeckli

Yanıtlar:


15

WGS ne? WGS-84? İhtiyacınız olan doğruluğa bağlı olarak, çok daha fazla bilgi sahibi olmanız gerekebilir - tahminim bu yüzden oy kullanmadınız, ancak kimse neden olduğunu söyleyen bir yorum bırakmak için uğraşmadı.

İşte iki yol:

Hatalı, ancak muhtemelen 'yeterince iyi'

Bir enlem derecesi , WGS-84 datumunu kullanarak yaklaşık 10001.965729 / 90 kilometredir (ekvatordan direğe olan mesafe, doksan dereceye bölünür) veya 111.113 kilometredir. Bu, dünyanın şekli nedeniyle bir yaklaşımdır ve kutuplara yaklaştıkça mesafeler değiştiği için (enlemi kullanmanın bir nedeni, boylam değil - sonunda bir boylamın mesafesi sıfırdır!) Dünya da mükemmel değildir küre. Her ikisi de ikinci cevabımda daha karmaşık bir projeksiyon ve veri tabanlı yaklaşım kullanmanın nedenleridir.

10001.965729km = 90 degrees
1km = 90/10001.965729 degrees = 0.0089982311916 degrees
10km = 0.089982311915998 degrees

Bu, derece / dakika / saniye değil, ondalık derece kullanıyor.

Yani sınırlayıcı kutunuz , artı ve eksi 0,08999 derece olacak. Alternatif olarak, bu sayıyı yarıçap olarak da kullanabilirsiniz ve size sınırlayıcı bir daire verir .

Bunu okuyan herhangi bir CBS kişisi titreyecektir. Bununla birlikte, dünyanın neresinde olduğunuza bağlı olarak çoğunlukla doğru olacaktır. 10 km'lik bir yarıçap için iyi olmalı.

Çok daha doğru, ancak daha fazla kod

Bir projeksiyon kütüphanesi kullanın ve referans noktanızı vb. Belirtin. Proj4'ü öneririm; yaygın olarak kullanılır, böylece Google bununla ilgili sorular için birçok sonuç döndürür ve Delphi sarmalayıcıları vardır . Kullanmakta zorlanıyorsanız, SO'ya başka bir soru gönderin - bunun kapsamı dışında. Proj4 web sitesinde temel API'leri kullanan örnekler vardır ve bunlar C dilinde olmasına rağmen oldukça kolay çevrilebilir olmalıdır. Onların API başvurusu ardından başlamak için en iyi yerdir SSS .

Kullanmak istediğiniz veya koordinatlarınızı oluşturmak için kullanılmadığı sürece WGS-84'ü temel bir veri (dünyanın temsili) olarak kullanırdım. Yaygın olarak kullanılır ve oldukça doğrudur.

Konumunuz Google Haritalar'dan geliyorsa (örneğin), bir Mercator projeksiyonu belirtin. Başka bir projeksiyon kullanmak veya örneğin UTM koordinatlarını kullanmak isteyebilirsinizverilerinizin kaynağına bağlı olarak ve küçük bir yerel alan için yüksek doğruluk istiyorsanız, enlem ve boylam yerine. (UTM'nin birden çok bölgesi vardır, bunların tümü bu bölge içindeki bozulmayı değiştirir, bu son derece doğrudur; bunun dışında bir koordinat için bir bölge kullanırsanız, uzaklaştıkça bozulma büyük ölçüde artar. Ancak, bir bölge içinde UTM çevirileri alabileceğiniz kadar iyi olacaktır.Koordinatlar genellikle derece cinsinden değil, metre cinsinden belirtilir, bu nedenle sizin için daha yararlı olabilir, 10km'ye ihtiyacınız var 10km kolayca tek bir bölgede, merkez koordinatınıza göre uygun bölgeyi seçmeniz yeterlidir.Tek aldatıcı bit bir sınıra yaklaştığınızda: bu yaygın bir durum ve sorun değil, sadecehangisini kullandığınızı nasıl seçtiğiniz konusunda tutarlı . Proj4 ayrıca projeksiyonları çevirmenize izin verir, böylece Mercator WGS-84 enlem / boylamınızdan bir UTM bölgesine n veya örneğin iki UTM bölgesine gidebilirsiniz .)


2
Yaşadığımız yer için, bir araştırmacı bana bir keresinde zihinsel hesaplamaları için yaklaşık 1 derece yaklaşık 108 km kullandığını söyledi. Zihinsel olarak 10 km kabaca 0.1 derecedir. Bunlar kaba hassasiyetler olduğundan, 0.089982311915998 yerine 1 anlamlı basamağa (en fazla 2 veya 3) doğru davranmak en iyisidir, çünkü bu bir hassasiyet seviyesini ifade eder.
Stephen Quan

1
Enlemi dikkate alarak dereceleri daha doğru hesaplamak gerçekten zor değil. Bilgisayar hesaplamayı yaptığı için, bir yaklaşımla kazanılan hiçbir şey yoktur (örneğimdeki ilk işleve bakın).
martinstoeckli

3

Bir veritabanında sorgu yapmak istediğinizi varsayarsak, muhtemelen hızlı (yanlış) bir arama yapmak ve daha sonra sonuçta elde edilen yerler için mesafeyi tam olarak hesaplamak istersiniz. Senaryon bu mu?

Aşağıdaki işlev (PHP'de, üzgünüm) enlem ve boylam farklarını kabaca hesaplayacaktır. Bu farklılıklar arama noktanızın enlemine bağlıdır. Veritabanında hızlı bir arama yapmak için bunları (küçük bir toleransla) kullanın. Kutu basitçe enlem + -deltaLatitude ve boylam + -deltaLongitude ile hesaplanabilir.

deltaLatitude[rad] = distance[m] / earthRadius[m]
deltaLongitude[rad] = distance[m] / (cos(latitude[rad]) * $earthRadius[m])

/**
 * Calculates the deltas in latitude and longitude to use, for a db search
 * around a location in the database.
 * @param float $distance Radius to use for the search [m]
 * @param float $latitude Latitude of the location, we need the angle deltas for [deg decimal]
 * @param float $deltaLatitude Calculated delta in latitude [deg]
 * @param float $deltaLongitude Calculated delta in longitude [deg]
 * @param float $earthRadius Mean earth radius in [m]
 */
public static function angleFromSphericDistance($distance, $latitude,
  &$deltaLatitude, &$deltaLongitude, $earthRadius = 6371000)
{
  $lat = deg2rad($latitude);

  $radiusOnLatitude = cos($lat) * $earthRadius;
  $deltaLatitude = $distance / $earthRadius;
  $deltaLongitude = $distance / $radiusOnLatitude;

  $deltaLatitude = rad2deg($deltaLatitude);
  $deltaLongitude = rad2deg($deltaLongitude);
}

Haversine formülü ile küredeki mesafeleri hesaplayabilirsiniz. Bulunan yerlerin her biri için "kesin" mesafeyi elde etmek için kullanın. Bu şekilde, iki yerin belirli bir yarıçap içinde (kutu yerine daire) olup olmadığını test edebilirsiniz.

/**
 * Calculates the great-circle distance between two points, with
 * the Haversine formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
public static function haversineGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $latDelta = $latTo - $latFrom;
  $lonDelta = $lonTo - $lonFrom;

  $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
    cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
  return $angle * $earthRadius;
}

3

Bir lat / lon sınırlayıcı dairenin içinde veya dışında olup olmadığını test etmek için referans lat / lon ile test etmek istediğiniz lat / lon noktası arasındaki mesafeyi hesaplamanız gerekir. Mesafeniz 10km veya daha az olduğu için, basitlik nedeniyle Haversine yerine mesafeyi elde etmek için Eşitlikli yaklaşımı kullanmayı deneyeceğim. Mesafeyi km olarak almak için:

x = (lonRef - lon) * cos ( latRef )
y = latRef - lat
distance = EarthRadius * sqrt( x*x + y*y )

Önemli not: bu formülde lat / lon derece değil radyan cinsindendir. EarthRadius'un tipik değeri 637 km'dir ve bu mesafe km cinsinden geri döner. Mesafenizin dairenin içinde veya dışında olması basit bir testtir. Eğer sınırlayıcı bir daire çalışırsa, bununla giderdim.

Sınırlayıcı bir dikdörtgen için, dikdörtgenin ekvatora paralel olarak tanımlanmasını istediğinizi varsayarım. Daha sonra sınırlama kutusunun köşelerini menzil / yatak hesaplamaları (rulmanlar 45 derece, 135 derece, 225 derece ve 315 derece olan) kullanarak hesaplayacağım. Oradan, kutupların etrafında olmadığınızı ve çokgen testinde bir nokta kullandığınızı varsayarım.


2

Aşağıda SQL-Server 2012'de sınırlama kutusu oluşturmak için kullandığım T-SQL kodu var. Benim durumumda Lat, Long için ondalık değerler alıyorum. STDistanceSonuçları gerçekten belirli bir mesafe içinde olduğunu doğrulamak için SQL işlevini kullanmadan önce hızlı bir şekilde satır sayısını sınırlamak için bunu kullanın . Coğrafya işlevleri SQL Server'da çok maliyetlidir, bu nedenle sınırlayıcı kutu oluşturarak büyük ölçüde yürütülmesi gereken sayısını azaltabiliyorum.

DECLARE @Lat DECIMAL(20, 13) = 35.7862
   ,@Long DECIMAL(20, 13) = -80.3095
   ,@Radius DECIMAL(7, 2) = 5
   ,@Distance DECIMAL(10, 2)
   ,@Earth_Radius INT = 6371000;

SET @Distance = @Radius * 1609.344;

DECLARE @NorthLat DECIMAL(20, 13) = @Lat + DEGREES(@distance / @Earth_Radius)
   ,@SouthLat DECIMAL(20, 13) = @Lat - DEGREES(@distance / @Earth_Radius)
   ,@EastLong DECIMAL(20, 13) = @Long + DEGREES(@distance / @Earth_Radius / COS(RADIANS(@Lat)))
   ,@WestLong DECIMAL(20, 13) = @Long - DEGREES(@distance / @Earth_Radius / COS(RADIANS(@Lat)));

SELECT *
    FROM CustomerPosition AS cp
    WHERE (
            cp.Lat >= @SouthLat
            AND cp.Lat <= @NorthLat )
        AND (
              cp.Long >= @WestLong
              AND cp.Long <= @EastLong )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.