Belirli bir enlem / boylam konumu için sınırlayıcı kutu nasıl hesaplanır?


103

Enlem ve boylamla tanımlanan bir konum verdim. Şimdi, bu noktanın 10 kilometre yakınında bir sınırlayıcı kutu hesaplamak istiyorum.

Sınırlayıcı kutu latmin, lngmin ve latmax, lngmax olarak tanımlanmalıdır.

Panoramio API'sini kullanmak için bunlara ihtiyacım var .

Birisi bu puanları nasıl elde edeceğinin formülünü biliyor mu?

Düzenleme: Arkadaşlar, girdi olarak lat & lng alan ve latmin & lngmin ve latmax & latmin olarak bir sınırlayıcı kutu döndüren bir formül / işlev arıyorum. Mysql, php, c #, javascript iyidir, ancak sözde kod da uygun olmalıdır.

Düzenleme: Bana 2 noktanın mesafesini gösteren bir çözüm aramıyorum


Bir yerde bir coğrafi veritabanı kullanıyorsanız, kesinlikle entegre bir sınırlayıcı kutu hesaplamasına sahiptirler. Örneğin, PostGIS / GEOS'un kaynağına bile bakabilirsiniz.
Vinko Vrsalovic

Yanıtlar:


61

Dünya yüzeyini, verilen enlemde WGS84 elipsoidi tarafından verilen yarıçaplı bir küre olarak yerel olarak yaklaşık olarak tahmin etmeyi öneririm. LatMin ve latMax'ın tam olarak hesaplanmasının eliptik fonksiyonlar gerektireceğinden ve doğrulukta kayda değer bir artış sağlamayacağından şüpheleniyorum (WGS84'ün kendisi bir yaklaşımdır).

Uygulamam şu şekildedir (Python ile yazılmıştır; test etmedim):

# degrees to radians
def deg2rad(degrees):
    return math.pi*degrees/180.0
# radians to degrees
def rad2deg(radians):
    return 180.0*radians/math.pi

# Semi-axes of WGS-84 geoidal reference
WGS84_a = 6378137.0  # Major semiaxis [m]
WGS84_b = 6356752.3  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
def WGS84EarthRadius(lat):
    # http://en.wikipedia.org/wiki/Earth_radius
    An = WGS84_a*WGS84_a * math.cos(lat)
    Bn = WGS84_b*WGS84_b * math.sin(lat)
    Ad = WGS84_a * math.cos(lat)
    Bd = WGS84_b * math.sin(lat)
    return math.sqrt( (An*An + Bn*Bn)/(Ad*Ad + Bd*Bd) )

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
def boundingBox(latitudeInDegrees, longitudeInDegrees, halfSideInKm):
    lat = deg2rad(latitudeInDegrees)
    lon = deg2rad(longitudeInDegrees)
    halfSide = 1000*halfSideInKm

    # Radius of Earth at given latitude
    radius = WGS84EarthRadius(lat)
    # Radius of the parallel at given latitude
    pradius = radius*math.cos(lat)

    latMin = lat - halfSide/radius
    latMax = lat + halfSide/radius
    lonMin = lon - halfSide/pradius
    lonMax = lon + halfSide/pradius

    return (rad2deg(latMin), rad2deg(lonMin), rad2deg(latMax), rad2deg(lonMax))

DÜZENLEME: Aşağıdaki kod (dereceler, asal sayılar, saniyeler) derecelere + bir derecenin kesirlerine ve tersine (test edilmemiştir) dönüştürür:

def dps2deg(degrees, primes, seconds):
    return degrees + primes/60.0 + seconds/3600.0

def deg2dps(degrees):
    intdeg = math.floor(degrees)
    primes = (degrees - intdeg)*60.0
    intpri = math.floor(primes)
    seconds = (primes - intpri)*60.0
    intsec = round(seconds)
    return (int(intdeg), int(intpri), int(intsec))

4
Önerilen CPAN kitaplığının belgelerinde belirtildiği gibi, bu yalnızca halfSide <= 10km için anlamlıdır.
Federico A. Ramponi

1
Bu kutupların yakınında mı çalışıyor? Öyle görünmüyor, çünkü latMin <-pi (güney kutbu için) veya latMax> pi (kuzey kutbu için) ile bitiyor gibi görünüyor? Bir direğin yarısının yakınındayken, direkten uzak taraf için ve direğe yakın taraftaki direk için normal olarak hesaplanan tüm boylamları ve enlemleri içeren bir sınırlayıcı kutu iade etmeniz gerektiğini düşünüyorum.
Doug McClean

1
JanMatuschek.de adresinde bulunan spesifikasyondan bir PHP uygulaması: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin

2
Aşağıda bu cevabın bir C # uygulamasını ekledim.
Ε Г И І И О

2
@ FedericoA.Ramponi burada haldSideinKm nedir? anlamıyorum ... bu arjilerde neyi geçmek zorundayım, haritadaki iki nokta arasındaki yarıçapı veya ne?

53

Sınır koordinatlarını bulmayla ilgili bir makale yazdım:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

Makale formülleri açıklar ve ayrıca bir Java uygulaması sağlar. (Ayrıca Federico'nın min / maks boylam formülünün neden yanlış olduğunu da gösterir.)


4
GeoLocation sınıfınızın bir PHP bağlantı noktasını yaptım. Burada bulunabilir: pastie.org/5416584
Anthony Martin


1
Bu soruyu cevaplıyor mu? Sadece 1 başlangıç ​​noktamız varsa, bu kodda yapıldığı gibi büyük daire mesafesini hesaplayamayız, bu iki enlem, uzun konum gerektirir.
mdoran3844

C # varyantınızda kötü bir kod var, örneğin: public override string ToString()böyle genel bir yöntemi yalnızca tek bir amaç için geçersiz kılmak, yalnızca başka bir yöntem eklemek daha iyidir, ardından uygulamanın diğer bölümlerinde kullanılabilen standart yöntemi geçersiz kılmak çok kötüdür, tam gis için değil ...

İşte Jan'ın GeoLocaiton sınıfının PHP bağlantı noktasına güncellenmiş bir bağlantı: github.com/anthonymartin/GeoLocation.php
Anthony Martin

34

Burada ilgilenen herkes için Federico A. Ramponi'nin cevabını C #'a çevirdim:

public class MapPoint
{
    public double Longitude { get; set; } // In Degrees
    public double Latitude { get; set; } // In Degrees
}

public class BoundingBox
{
    public MapPoint MinPoint { get; set; }
    public MapPoint MaxPoint { get; set; }
}        

// Semi-axes of WGS-84 geoidal reference
private const double WGS84_a = 6378137.0; // Major semiaxis [m]
private const double WGS84_b = 6356752.3; // Minor semiaxis [m]

// 'halfSideInKm' is the half length of the bounding box you want in kilometers.
public static BoundingBox GetBoundingBox(MapPoint point, double halfSideInKm)
{            
    // Bounding box surrounding the point at given coordinates,
    // assuming local approximation of Earth surface as a sphere
    // of radius given by WGS84
    var lat = Deg2rad(point.Latitude);
    var lon = Deg2rad(point.Longitude);
    var halfSide = 1000 * halfSideInKm;

    // Radius of Earth at given latitude
    var radius = WGS84EarthRadius(lat);
    // Radius of the parallel at given latitude
    var pradius = radius * Math.Cos(lat);

    var latMin = lat - halfSide / radius;
    var latMax = lat + halfSide / radius;
    var lonMin = lon - halfSide / pradius;
    var lonMax = lon + halfSide / pradius;

    return new BoundingBox { 
        MinPoint = new MapPoint { Latitude = Rad2deg(latMin), Longitude = Rad2deg(lonMin) },
        MaxPoint = new MapPoint { Latitude = Rad2deg(latMax), Longitude = Rad2deg(lonMax) }
    };            
}

// degrees to radians
private static double Deg2rad(double degrees)
{
    return Math.PI * degrees / 180.0;
}

// radians to degrees
private static double Rad2deg(double radians)
{
    return 180.0 * radians / Math.PI;
}

// Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
private static double WGS84EarthRadius(double lat)
{
    // http://en.wikipedia.org/wiki/Earth_radius
    var An = WGS84_a * WGS84_a * Math.Cos(lat);
    var Bn = WGS84_b * WGS84_b * Math.Sin(lat);
    var Ad = WGS84_a * Math.Cos(lat);
    var Bd = WGS84_b * Math.Sin(lat);
    return Math.Sqrt((An*An + Bn*Bn) / (Ad*Ad + Bd*Bd));
}

1
Teşekkürler, bu benim için çalışıyor. Kodu elle test etmem gerekiyordu, bunun için bir birim testi nasıl yazacağımı bilmiyordum ama ihtiyacım olan doğruluk derecesine göre doğru sonuçlar veriyor
mdoran3844

burada haldSideinKm nedir? anlamıyorum ... bu arjilerde neyi geçmek zorundayım, haritadaki iki nokta arasındaki yarıçapı veya ne?

@GeloVolro: Bu, istediğiniz sınırlayıcı kutunun yarı uzunluğu.
Ε Г И І И О

1
Kendi MapPoint sınıfınızı yazmak zorunda değilsiniz. System.Device.Location'da Enlem ve Boylamı parametre olarak alan bir GeoCoordinate sınıfı vardır.
Lawyerson

1
Bu çok güzel çalışıyor. C # bağlantı noktasını gerçekten takdir ediyorum.
Tom Larcher

10

Bir mesafe ve bir çift koordinat verildiğinde, bir kare sınırlayıcı kutunun dört koordinatını döndüren bir JavaScript işlevi yazdım:

'use strict';

/**
 * @param {number} distance - distance (km) from the point represented by centerPoint
 * @param {array} centerPoint - two-dimensional array containing center coords [latitude, longitude]
 * @description
 *   Computes the bounding coordinates of all points on the surface of a sphere
 *   that has a great circle distance to the point represented by the centerPoint
 *   argument that is less or equal to the distance argument.
 *   Technique from: Jan Matuschek <http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates>
 * @author Alex Salisbury
*/

getBoundingBox = function (centerPoint, distance) {
  var MIN_LAT, MAX_LAT, MIN_LON, MAX_LON, R, radDist, degLat, degLon, radLat, radLon, minLat, maxLat, minLon, maxLon, deltaLon;
  if (distance < 0) {
    return 'Illegal arguments';
  }
  // helper functions (degrees<–>radians)
  Number.prototype.degToRad = function () {
    return this * (Math.PI / 180);
  };
  Number.prototype.radToDeg = function () {
    return (180 * this) / Math.PI;
  };
  // coordinate limits
  MIN_LAT = (-90).degToRad();
  MAX_LAT = (90).degToRad();
  MIN_LON = (-180).degToRad();
  MAX_LON = (180).degToRad();
  // Earth's radius (km)
  R = 6378.1;
  // angular distance in radians on a great circle
  radDist = distance / R;
  // center point coordinates (deg)
  degLat = centerPoint[0];
  degLon = centerPoint[1];
  // center point coordinates (rad)
  radLat = degLat.degToRad();
  radLon = degLon.degToRad();
  // minimum and maximum latitudes for given distance
  minLat = radLat - radDist;
  maxLat = radLat + radDist;
  // minimum and maximum longitudes for given distance
  minLon = void 0;
  maxLon = void 0;
  // define deltaLon to help determine min and max longitudes
  deltaLon = Math.asin(Math.sin(radDist) / Math.cos(radLat));
  if (minLat > MIN_LAT && maxLat < MAX_LAT) {
    minLon = radLon - deltaLon;
    maxLon = radLon + deltaLon;
    if (minLon < MIN_LON) {
      minLon = minLon + 2 * Math.PI;
    }
    if (maxLon > MAX_LON) {
      maxLon = maxLon - 2 * Math.PI;
    }
  }
  // a pole is within the given distance
  else {
    minLat = Math.max(minLat, MIN_LAT);
    maxLat = Math.min(maxLat, MAX_LAT);
    minLon = MIN_LON;
    maxLon = MAX_LON;
  }
  return [
    minLon.radToDeg(),
    minLat.radToDeg(),
    maxLon.radToDeg(),
    maxLat.radToDeg()
  ];
};

Bu kod hiç çalışmıyor. Hatta böyle bariz hatalar kadar sabitleme sonra, ortalama minLon = void 0;ve maxLon = MAX_LON;o hala işi yapmaz.
aroth

1
@aroth, test ettim ve sorun yaşamadım. centerPointArgümanın iki koordinattan oluşan bir dizi olduğunu unutmayın . Örneğin, getBoundingBox([42.2, 34.5], 50)- void 0"tanımsız" için CoffeeScript çıktısıdır ve kodların çalışma yeteneğini etkilemez.
asalisbury

bu kod çalışmıyor. degLat.degToRadbir işlev değil
user299709

Kod, üzerinde çalıştığım bir projenin içine koyup " degToRadbir işlev değil" hatası almaya başlayana kadar, Düğüm ve Chrome'da olduğu gibi çalıştı . Nedenini asla bulamadım, ancak Number.prototype.böyle bir yardımcı program işlevi için iyi bir fikir değil, bu yüzden bunları normal yerel işlevlere dönüştürdüm. Ayrıca iade edilen kutunun [LAT, LNG, LAT, LNG] yerine [LNG, LAT, LNG, LAT] olduğunu unutmamak önemlidir. Karışıklığı önlemek için bunu kullandığımda dönüş işlevini değiştirdim.
KernelDeimos

9

Çok kaba bir tahmine ihtiyacım olduğu için, bir elasticsearch sorgusundaki bazı gereksiz belgeleri filtrelemek için aşağıdaki formülü kullandım:

Min.lat = Given.Lat - (0.009 x N)
Max.lat = Given.Lat + (0.009 x N)
Min.lon = Given.lon - (0.009 x N)
Max.lon = Given.lon + (0.009 x N)

N = verilen konum için gerekli km. Durumunuz için N = 10

Doğru değil ama kullanışlı.


Doğrusu, doğru değil ama yine de kullanışlı ve uygulaması çok kolay.
MV.

6

Elipsoid bir formül arıyorsunuz.

Kodlamaya başlamak için bulduğum en iyi yer, CPAN'ın Geo :: Ellipsoid kitaplığına dayanıyor. Testlerinizi oluşturmak ve sonuçlarınızı sonuçlarıyla karşılaştırmak için size bir temel sağlar. Bunu önceki işverenimde benzer bir PHP kitaplığının temeli olarak kullandım.

Geo :: Elipsoid

locationYönteme bir göz atın . İki kez ara ve bbox'ını al

Hangi dili kullandığınızı göndermediniz. Halihazırda sizin için bir coğrafi kodlama kitaplığı mevcut olabilir.

Oh, ve eğer şimdiye kadar çözemediyseniz, Google Maps WGS84 elipsoidini kullanıyor.


5

@ Jan Philip Matuschek'in mükemmel açıklaması. (Lütfen cevabını oylayın, bunu değil; orijinal cevabı anlamak için biraz zaman ayırdığım için bunu ekliyorum)

En yakın komşuları bulmanın optimize edilmesine yönelik sınırlayıcı kutu tekniği, d mesafedeki bir P noktası için minimum ve maksimum enlem, boylam çiftlerini türetmelidir. Bunların dışında kalan tüm noktalar, noktadan kesinlikle d'den daha büyük bir uzaklıktadır. Burada dikkat edilmesi gereken bir nokta, Jan Philip Matuschek açıklamasında vurgulandığı gibi, kesişme enleminin hesaplanmasıdır. Kesişme enlemi P noktasının enleminde değil, ondan biraz uzaktır. Bu, d mesafesi için P noktası için doğru minimum ve maksimum sınır boylamının belirlenmesinde sıklıkla gözden kaçan ancak önemli bir kısımdır. Bu, aynı zamanda doğrulamada da yararlıdır.

P'nin (enlem, en yüksek boylam) ile (enlem, boylam) arasındaki haversine mesafesi, d mesafesine eşittir.

Python özeti burada https://gist.github.com/alexcpn/f95ae83a7ee0293a5225

görüntü açıklamasını buraya girin


5

Burada, enlem derecesinin kms'ye dönüştürülmesine dayanan javascript kullanan basit bir uygulama var 1 degree latitude ~ 111.2 km.

Haritanın sınırlarını belirli bir enlem ve boylamdan 10 km genişlikte hesaplıyorum.

function getBoundsFromLatLng(lat, lng, radiusInKm){
     var lat_change = radiusInKm/111.2;
     var lon_change = Math.abs(Math.cos(lat*(Math.PI/180)));
     var bounds = { 
         lat_min : lat - lat_change,
         lon_min : lng - lon_change,
         lat_max : lat + lat_change,
         lon_max : lng + lon_change
     };
     return bounds;
}

4

Tam da bunu yaptığını bulduğum bir PHP betiğini uyarladım. Bir noktanın etrafındaki kutunun köşelerini bulmak için kullanabilirsiniz (örneğin, 20 km uzaklıkta). Benim özel örneğim Google Maps API için:

http://www.richardpeacock.com/blog/2011/11/draw-box-around-coordinate-google-maps-based-miles-or-kilometers


-1 OP'nin aradığı şey şudur: bir referans noktası (enlem, boylam) ve bir mesafe verildiğinde, referans noktasından <= "mesafe" uzakta olan tüm noktalar kutunun dışında olmayacak şekilde en küçük kutuyu bulun. Kutunuzun köşeleri referans noktasından "uzaklığa" sahiptir ve bu nedenle çok küçüktür. Örnek: kuzeye doğru "mesafe" olan nokta kutunuzun çok dışındadır.
John Machin

Şans eseri, tam da ihtiyacım olan şey buydu. Bu soruyu tam olarak yanıtlamasa bile teşekkür ederim :)
Simon Steinberger

Birisine yardımcı olabileceğine sevindim!
Richard

1

Sınırlayıcı kutu problemi üzerinde, statik bir LAT, LONG noktanın SrcRad yarıçapı içindeki tüm noktaları bulmanın bir yan sorunu olarak çalışıyordum. Kullanan epeyce hesaplama var

maxLon = $lon + rad2deg($rad/$R/cos(deg2rad($lat)));
minLon = $lon - rad2deg($rad/$R/cos(deg2rad($lat)));

boylam sınırlarını hesaplamak için, ancak bunun gerekli olan tüm cevapları vermediğini buldum. Çünkü gerçekten yapmak istediğin şey

(SrcRad/RadEarth)/cos(deg2rad(lat))

Biliyorum, cevabın aynı olması gerektiğini biliyorum, ama olmadığını anladım. (SRCrad / RadEarth) yaptığımdan emin olmadığım için önce ve sonra Cos kısmına bölerek bazı konum noktalarını dışarıda bırakıyordum.

Tüm sınırlayıcı kutu noktalarınızı aldıktan sonra, enlem verilen Noktadan Noktaya Mesafeyi hesaplayan bir fonksiyonunuz varsa, sadece sabit noktadan belirli bir mesafe yarıçapı olan noktaları elde etmek kolaydır. İşte yaptığım şey. Birkaç adım daha attığını biliyorum ama bana yardımcı oldu

-- GLOBAL Constants
gc_pi CONSTANT REAL := 3.14159265359;  -- Pi

-- Conversion Factor Constants
gc_rad_to_degs          CONSTANT NUMBER := 180/gc_pi; -- Conversion for Radians to Degrees 180/pi
gc_deg_to_rads          CONSTANT NUMBER := gc_pi/180; --Conversion of Degrees to Radians

lv_stat_lat    -- The static latitude point that I am searching from 
lv_stat_long   -- The static longitude point that I am searching from 

-- Angular radius ratio in radians
lv_ang_radius := lv_search_radius / lv_earth_radius;
lv_bb_maxlat := lv_stat_lat + (gc_rad_to_deg * lv_ang_radius);
lv_bb_minlat := lv_stat_lat - (gc_rad_to_deg * lv_ang_radius);

--Here's the tricky part, accounting for the Longitude getting smaller as we move up the latitiude scale
-- I seperated the parts of the equation to make it easier to debug and understand
-- I may not be a smart man but I know what the right answer is... :-)

lv_int_calc := gc_deg_to_rads * lv_stat_lat;
lv_int_calc := COS(lv_int_calc);
lv_int_calc := lv_ang_radius/lv_int_calc;
lv_int_calc := gc_rad_to_degs*lv_int_calc;

lv_bb_maxlong := lv_stat_long + lv_int_calc;
lv_bb_minlong := lv_stat_long - lv_int_calc;

-- Now select the values from your location datatable 
SELECT *  FROM (
SELECT cityaliasname, city, state, zipcode, latitude, longitude, 
-- The actual distance in miles
spherecos_pnttopntdist(lv_stat_lat, lv_stat_long, latitude, longitude, 'M') as miles_dist    
FROM Location_Table 
WHERE latitude between lv_bb_minlat AND lv_bb_maxlat
AND   longitude between lv_bb_minlong and lv_bb_maxlong)
WHERE miles_dist <= lv_limit_distance_miles
order by miles_dist
;

0

Panoramio web sitesine gidin ve panoramio web sitesinden Dünya Haritasını açın, ardından enlem ve boylamın gerekli olduğu belirtilen konuma gidin.

Sonra adres çubuğunda enlem ve boylamı buldunuz, örneğin bu adreste.

http://www.panoramio.com/map#lt=32.739485&ln=70.491211&z=9&k=1&a=1&tab=1&pl=all

lt = 32.739485 => enlem ln = 70.491211 => boylam

bu Panoramio JavaScript API widget'ı, bir enlem / boylam çifti etrafında bir sınırlayıcı kutu oluşturur ve ardından bu sınırlardaki tüm fotoğrafları döndürür.

Arka plan rengini örnek ve kodla değiştirebileceğiniz başka bir Panoramio JavaScript API widget'ı da burada .

Beste havasında gösterilmez, yayınlandıktan sonra gösterilir.

<div dir="ltr" style="text-align: center;" trbidi="on">
<script src="https://ssl.panoramio.com/wapi/wapi.js?v=1&amp;hl=en"></script>
<div id="wapiblock" style="float: right; margin: 10px 15px"></div>
<script type="text/javascript">
var myRequest = {
  'tag': 'kahna',
  'rect': {'sw': {'lat': -30, 'lng': 10.5}, 'ne': {'lat': 50.5, 'lng': 30}}
};
  var myOptions = {
  'width': 300,
  'height': 200
};
var wapiblock = document.getElementById('wapiblock');
var photo_widget = new panoramio.PhotoWidget('wapiblock', myRequest, myOptions);
photo_widget.setPosition(0);
</script>
</div>

0

Burada, ilgilenen varsa Federico A. Ramponi'nin cevabını PHP'ye dönüştürdüm:

<?php
# deg2rad and rad2deg are already within PHP

# Semi-axes of WGS-84 geoidal reference
$WGS84_a = 6378137.0;  # Major semiaxis [m]
$WGS84_b = 6356752.3;  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
function WGS84EarthRadius($lat)
{
    global $WGS84_a, $WGS84_b;

    $an = $WGS84_a * $WGS84_a * cos($lat);
    $bn = $WGS84_b * $WGS84_b * sin($lat);
    $ad = $WGS84_a * cos($lat);
    $bd = $WGS84_b * sin($lat);

    return sqrt(($an*$an + $bn*$bn)/($ad*$ad + $bd*$bd));
}

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
function boundingBox($latitudeInDegrees, $longitudeInDegrees, $halfSideInKm)
{
    $lat = deg2rad($latitudeInDegrees);
    $lon = deg2rad($longitudeInDegrees);
    $halfSide = 1000 * $halfSideInKm;

    # Radius of Earth at given latitude
    $radius = WGS84EarthRadius($lat);
    # Radius of the parallel at given latitude
    $pradius = $radius*cos($lat);

    $latMin = $lat - $halfSide / $radius;
    $latMax = $lat + $halfSide / $radius;
    $lonMin = $lon - $halfSide / $pradius;
    $lonMax = $lon + $halfSide / $pradius;

    return array(rad2deg($latMin), rad2deg($lonMin), rad2deg($latMax), rad2deg($lonMax));
}
?>

0

Phyton uygulaması için @Fedrico A.'ya teşekkürler, bunu bir Objective C kategori sınıfına taşıdım. Burada:

#import "LocationService+Bounds.h"

//Semi-axes of WGS-84 geoidal reference
const double WGS84_a = 6378137.0; //Major semiaxis [m]
const double WGS84_b = 6356752.3; //Minor semiaxis [m]

@implementation LocationService (Bounds)

struct BoundsLocation {
    double maxLatitude;
    double minLatitude;
    double maxLongitude;
    double minLongitude;
};

+ (struct BoundsLocation)locationBoundsWithLatitude:(double)aLatitude longitude:(double)aLongitude maxDistanceKm:(NSInteger)aMaxKmDistance {
    return [self boundingBoxWithLatitude:aLatitude longitude:aLongitude halfDistanceKm:aMaxKmDistance/2];
}

#pragma mark - Algorithm 

+ (struct BoundsLocation)boundingBoxWithLatitude:(double)aLatitude longitude:(double)aLongitude halfDistanceKm:(double)aDistanceKm {
    double radianLatitude = [self degreesToRadians:aLatitude];
    double radianLongitude = [self degreesToRadians:aLongitude];
    double halfDistanceMeters = aDistanceKm*1000;


    double earthRadius = [self earthRadiusAtLatitude:radianLatitude];
    double parallelRadius = earthRadius*cosl(radianLatitude);

    double radianMinLatitude = radianLatitude - halfDistanceMeters/earthRadius;
    double radianMaxLatitude = radianLatitude + halfDistanceMeters/earthRadius;
    double radianMinLongitude = radianLongitude - halfDistanceMeters/parallelRadius;
    double radianMaxLongitude = radianLongitude + halfDistanceMeters/parallelRadius;

    struct BoundsLocation bounds;
    bounds.minLatitude = [self radiansToDegrees:radianMinLatitude];
    bounds.maxLatitude = [self radiansToDegrees:radianMaxLatitude];
    bounds.minLongitude = [self radiansToDegrees:radianMinLongitude];
    bounds.maxLongitude = [self radiansToDegrees:radianMaxLongitude];

    return bounds;
}

+ (double)earthRadiusAtLatitude:(double)aRadianLatitude {
    double An = WGS84_a * WGS84_a * cosl(aRadianLatitude);
    double Bn = WGS84_b * WGS84_b * sinl(aRadianLatitude);
    double Ad = WGS84_a * cosl(aRadianLatitude);
    double Bd = WGS84_b * sinl(aRadianLatitude);
    return sqrtl( ((An * An) + (Bn * Bn))/((Ad * Ad) + (Bd * Bd)) );
}

+ (double)degreesToRadians:(double)aDegrees {
    return M_PI*aDegrees/180.0;
}

+ (double)radiansToDegrees:(double)aRadians {
    return 180.0*aRadians/M_PI;
}



@end

Test ettim ve iyi çalışıyor gibi görünüyor. Struct BoundsLocation bir sınıfla değiştirilmelidir, sadece burada paylaşmak için kullandım.


0

Yukarıdaki yanıtların tümü yalnızca kısmen doğrudur . Özellikle Avustralya gibi bir bölgede, her zaman kutup içerirler ve 10 km için bile çok büyük bir dikdörtgen hesaplarlar.

Özellikle http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex adresinde Jan Philip Matuschek tarafından hazırlanan algoritma , Avustralya'daki hemen hemen her nokta için (-37, -90, -180, 180) 'den çok büyük bir dikdörtgen içeriyordu. Bu, veri tabanında büyük bir kullanıcıya ulaşır ve ülkenin neredeyse yarısındaki tüm kullanıcılar için mesafe hesaplanmalıdır.

Rochester Institute of Technology tarafından hazırlanan Drupal API Earth Algorithm'in , başka yerlerde olduğu kadar kutup çevresinde daha iyi çalıştığını ve uygulanmasının çok daha kolay olduğunu buldum .

https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54

Sınırlayıcı dikdörtgeni hesaplamak için yukarıdaki algoritmadan earth_latitude_rangeve kullanınearth_longitude_range

Ve kullanmak google tarafından belgelenmiş mesafe hesaplama formülü haritaları hesapla mesafeye

https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php

Mil yerine kilometreye göre arama yapmak için 3959'u 6371 ile değiştirin. (Enlem, Boy ) = (37, -122) ve enlem ve boy sütunlarını içeren bir İşaretçiler tablosu için formül şu şekildedir:

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

Ayrıntılı cevabımı https://stackoverflow.com/a/45950426/5076414 adresinde okuyun.


0

İşte Federico Ramponi'nin Go'daki cevabı. Not: hata kontrolü yok :(

import (
    "math"
)

// Semi-axes of WGS-84 geoidal reference
const (
    // Major semiaxis (meters)
    WGS84A = 6378137.0
    // Minor semiaxis (meters)
    WGS84B = 6356752.3
)

// BoundingBox represents the geo-polygon that encompasses the given point and radius
type BoundingBox struct {
    LatMin float64
    LatMax float64
    LonMin float64
    LonMax float64
}

// Convert a degree value to radians
func deg2Rad(deg float64) float64 {
    return math.Pi * deg / 180.0
}

// Convert a radian value to degrees
func rad2Deg(rad float64) float64 {
    return 180.0 * rad / math.Pi
}

// Get the Earth's radius in meters at a given latitude based on the WGS84 ellipsoid
func getWgs84EarthRadius(lat float64) float64 {
    an := WGS84A * WGS84A * math.Cos(lat)
    bn := WGS84B * WGS84B * math.Sin(lat)

    ad := WGS84A * math.Cos(lat)
    bd := WGS84B * math.Sin(lat)

    return math.Sqrt((an*an + bn*bn) / (ad*ad + bd*bd))
}

// GetBoundingBox returns a BoundingBox encompassing the given lat/long point and radius
func GetBoundingBox(latDeg float64, longDeg float64, radiusKm float64) BoundingBox {
    lat := deg2Rad(latDeg)
    lon := deg2Rad(longDeg)
    halfSide := 1000 * radiusKm

    // Radius of Earth at given latitude
    radius := getWgs84EarthRadius(lat)

    pradius := radius * math.Cos(lat)

    latMin := lat - halfSide/radius
    latMax := lat + halfSide/radius
    lonMin := lon - halfSide/pradius
    lonMax := lon + halfSide/pradius

    return BoundingBox{
        LatMin: rad2Deg(latMin),
        LatMax: rad2Deg(latMax),
        LonMin: rad2Deg(lonMin),
        LonMax: rad2Deg(lonMax),
    }
}
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.