2009-11-06 169 views
33

給定一個座標(lat,long),我試圖計算一個距離座標給定距離(例如50km)的正方形邊界框。所以作爲輸入我有拉特,長和距離,作爲輸出我想要兩個座標;一個是西南(左下角),另一個是東北(右上角)。我在這裏看到了一些試圖在Python中解決這個問題的答案,但我特別尋找Java實現。我想打算在地球上只使用算法,所以我不需要適應變化的半徑。它不一定非常準確(+/- 20%很好),它只能用於計算小距離(不超過150公里)的邊界框。所以我很樂意犧牲一些有效算法的準確性。任何幫助深表感謝。計算邊界框距離Java中的緯度/經度座標一定距離

編輯:我應該更清楚了,我真的是在一個正方形之後,而不是一個圓圈。我明白,廣場中心與廣場周邊各點之間的距離並不像一個圓圈那樣是一個常數值。我想我的意思是一個正方形,如果你從中心畫一條直線到周邊四個點中的任何一個,導致垂直於周邊一側的直線,那麼這4條線具有相同的長度。

+0

這個問題只是直三角;我建議刪除java和算法標籤。 (如果這甚至是可能的。) – 2009-11-07 03:12:31

+0

哦,對不起,我現在做了。 – 2009-11-07 03:15:33

+0

需要考慮地球的球形特性。如果輸入位置是北極,你想要什麼答案? – MarkJ 2009-11-09 11:39:04

回答

51

我寫了一篇關於發現邊界座標:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

的文章解釋了公式,還提供了Java實現。 (這也說明了爲什麼鐵人三項公式爲最小/最大經度是不準確的。)

+0

謝謝你寫這篇文章;我特別欣賞SQL實現細節。 – 2010-06-25 02:17:32

+2

我很困惑爲什麼這些球形近似值被當作標準解決方案。這裏涉及的可怕數量的三角函數。此外,所有的GPS設備都會根據WGS84橢球體產生經緯度座標 - 如果您想要完全準確的話,您就不能使用球面近似...將球面近似中的這些方程適應於橢球將導致難以想象的畸變。看來,該方法必須是在3D空間中執行計算和弧長積分。 – 2014-02-13 07:51:59

+0

@StephanKlein邊界框是一個確實覆蓋距離內所有座標的框,但並非所有覆蓋的座標都等於或小於距離。邊界框就像一個圓形的矩形。它涵蓋了圓內的所有點,但在邊緣附近它也覆蓋了圓外的一些點。但只有使用一個框(一個矩形),您纔可以在SQL語句中執行簡單的'<' and '>'比較,這就是爲什麼頁面上顯示的完整SQL選擇首先測試點是否位於邊界框內(快!),然後檢查是否它真的是等於或低於所需的距離(慢!) – Mecki 2015-12-11 12:40:37

3
import com.vividsolutions.jts.geom.Envelope; 

... 
Envelope env = new Envelope(centerPoint.getCoordinate()); 
env.expandBy(distance_in_degrees); 
... 

現在env包含你的信封。這實際上並不是一個「方塊」(無論這意味着什麼在球體表面上),但它應該做的。

您應該注意,度數的距離取決於中心點的緯度。在赤道上,1度的緯度約111公里,但在紐約只有75公里左右。

真的很酷的事情是,你可以把你所有的點扔到com.vividsolutions.jts.index.strtree.STRtree然後用它來快速計算該信封內的點。

12
double R = 6371; // earth radius in km 

double radius = 50; // km 

double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 

double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 

double y1 = lat + Math.toDegrees(radius/R); 

double y2 = lat - Math.toDegrees(radius/R); 

雖然我也會推薦JTS。

+7

'JTS'代表什麼? – Gili 2014-12-03 22:19:15

+0

Java拓撲套件,它是與幾何相關的操作的一個API – prettyvoid 2018-01-25 08:42:38

1
double R = 6371; // earth radius in km 
double radius = 50; // km 
double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 
double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 
double y1 = lat + Math.toDegrees(radius/R); 
double y2 = lat - Math.toDegrees(radius/R); 

雖然我也建議JTS。

這樣計算,但Google地球不接受,也不映射3D模型。

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 

package assetmap; 




public class Main { 

public double degrees; 
public double pi= 3.1416; 
public static double lon=80.304737; 
public static double lat=26.447521; 
public static double x1,x2,y1,y2; 


public static void main(String[] args) { 

double R = 6371; // earth radius in km 26.447521 

double radius = 0.300; // km 

x1 = (lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)))); 

x2 = (lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)))); 

y1 = (lat + Math.toDegrees(radius/R)); 

y2 = (lat - Math.toDegrees(radius/R)); 


System.out.println(x1+"---|"+x2+"---|"+y1+"|---|"+y2); 


} 

} 

它打印

80.30172366789824---|80.176---|26.450218964817754|---|26.444823035182242 

KML:

<?xml version="1.0" encoding="UTF-8"?> 
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom"> 
<Placemark> 
    <name>United Nations Headquarters</name> 
    <Region> 
     <LatLonAltBox> 
      <north>26.447251203518224</north> 
      <south>26.447790796481772</south> 
      <east>80.18</east> 
      <west>80.30443566678983</west> 
      <minAltitude>0</minAltitude> 
      <maxAltitude>30</maxAltitude> 
      <altitudeMode>absolute</altitudeMode> 
     </LatLonAltBox> 
     <Lod> 
      <minLodPixels>128</minLodPixels> 
      <maxLodPixels>-1</maxLodPixels> 
      <minFadeExtent>0</minFadeExtent> 
      <maxFadeExtent>0</maxFadeExtent> 
     </Lod> 
    </Region> 
    <Model id="model_1"> 
     <altitudeMode>absolute</altitudeMode> 
     <Location> 
      <longitude>80.304737</longitude> 
      <latitude>26.447521</latitude> 
      <altitude>0.406173708576</altitude> 
     </Location> 
     <Orientation> 
      <heading>0</heading> 
      <tilt>0</tilt> 
      <roll>0</roll> 
     </Orientation> 
     <Scale> 
      <x>10</x> 
      <y>10</y> 
      <z>10</z> 
     </Scale> 
     <Link> 
      <href>un.dae</href> 
     </Link> 
     <ResourceMap> 
      <Alias> 
       <targetHref>_01.jpg</targetHref> 
       <sourceHref>../images/_01.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_02.jpg</targetHref> 
       <sourceHref>../images/_02.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_04.jpg</targetHref> 
       <sourceHref>../images/_04.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_05.jpg</targetHref> 
       <sourceHref>../images/_05.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_06.jpg</targetHref> 
       <sourceHref>../images/_06.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_07.jpg</targetHref> 
       <sourceHref>../images/_07.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_08.jpg</targetHref> 
       <sourceHref>../images/_08.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_09.jpg</targetHref> 
       <sourceHref>../images/_09.jpg</sourceHref> 
      </Alias> 
     </ResourceMap> 
    </Model> 
</Placemark> 
</kml> 
1

我有一個PHP腳本和例子做到這一點。給定一個起點,它計算出一個盒子的角落到特定的距離。它是專門爲谷歌地圖,但它可以爲別的工作:

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

+0

我知道這是一個令人難以置信的舊答案,但它幫助了很多,非常感謝! – acupajoe 2015-10-23 21:59:34

+1

我很高興它幫助了某人! – Richard 2015-10-24 00:08:08

1

以前所有的答案都只是部分正確。特別是在像澳大利亞這樣的地區,它們總是包含極點,甚至在10kms時也計算出一個非常大的矩形。

特別是由Jan Philip Matuschek在http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex提供的算法,對於澳大利亞的幾乎所有點都包含一個非常大的矩形(-37,-90,-180,180)。這對數據庫中的大量用戶和距離必須針對幾乎一半的所有用戶進行計算。

我發現羅切斯特理工學院Drupal API地球算法圍繞杆以及其他地方工作得更好,並且更容易實現。

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

使用earth_latitude_rangeearth_longitude_range從上述算法用於計算邊界矩形

下面是實現是爪哇

/** 
* Get bouding rectangle using Drupal Earth Algorithm 
* @see https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54 
* @param lat 
* @param lng 
* @param distance 
* @return 
*/ 
default BoundingRectangle getBoundingRectangleDrupalEarthAlgo(double lat, double lng, int distance) { 
    lng = Math.toRadians(lng); 
    lat = Math.toRadians(lat); 
    double radius = earth_radius(lat); 
    List<Double> retLats = earth_latitude_range(lat, radius, distance); 
    List<Double> retLngs = earth_longitude_range(lat, lng, radius, distance); 
    return new BoundingRectangle(retLats.get(0), retLats.get(1), retLngs.get(0), retLngs.get(1)); 
} 


/** 
* Calculate latitude range based on earths radius at a given point 
* @param latitude 
* @param longitude 
* @param distance 
* @return 
*/ 
default List<Double> earth_latitude_range(double lat, double radius, double distance) { 
     // Estimate the min and max latitudes within distance of a given location. 

     double angle = distance/radius; 
     double minlat = lat - angle; 
     double maxlat = lat + angle; 
     double rightangle = Math.PI/2; 
     // Wrapped around the south pole. 
     if (minlat < -rightangle) { 
     double overshoot = -minlat - rightangle; 
     minlat = -rightangle + overshoot; 
     if (minlat > maxlat) { 
      maxlat = minlat; 
     } 
     minlat = -rightangle; 
     } 
     // Wrapped around the north pole. 
     if (maxlat > rightangle) { 
     double overshoot = maxlat - rightangle; 
     maxlat = rightangle - overshoot; 
     if (maxlat < minlat) { 
      minlat = maxlat; 
     } 
     maxlat = rightangle; 
     } 
     List<Double> ret = new ArrayList<>(); 
     ret.add((minlat)); 
     ret.add((maxlat)); 
     return ret; 
    } 

/** 
* Calculate longitude range based on earths radius at a given point 
* @param lat 
* @param lng 
* @param earth_radius 
* @param distance 
* @return 
*/ 
default List<Double> earth_longitude_range(double lat, double lng, double earth_radius, int distance) { 
     // Estimate the min and max longitudes within distance of a given location. 
     double radius = earth_radius * Math.cos(lat); 

     double angle; 
     if (radius > 0) { 
     angle = Math.abs(distance/radius); 
     angle = Math.min(angle, Math.PI); 
     } 
     else { 
     angle = Math.PI; 
     } 
     double minlong = lng - angle; 
     double maxlong = lng + angle; 
     if (minlong < -Math.PI) { 
     minlong = minlong + Math.PI * 2; 
     } 
     if (maxlong > Math.PI) { 
     maxlong = maxlong - Math.PI * 2; 
     } 

     List<Double> ret = new ArrayList<>(); 
     ret.add((minlong)); 
     ret.add((maxlong)); 
     return ret; 
    } 

/** 
* Calculate earth radius at given latitude 
* @param latitude 
* @return 
*/ 
default Double earth_radius(double latitude) { 
     // Estimate the Earth's radius at a given latitude. 
     // Default to an approximate average radius for the United States. 
     double lat = Math.toRadians(latitude); 

     double x = Math.cos(lat)/6378137.0; 
     double y = Math.sin(lat)/(6378137.0 * (1 - (1/298.257223563))); 

     //Make sure earth's radius is in km , not meters 
     return (1/(Math.sqrt(x * x + y * y)))/1000; 
    } 

並使用由谷歌記錄的距離計算公式映射計算距離

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

爲了通過公里而非英里搜索,與6371. 對於(緯度,經度)=(37,-122)中,用緯度列和lng一個標記表替換3959,所述式爲:

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; 
+0

觀察earth_radius代碼。確保地球半徑以km爲單位,而不是米:return(1 /(Math.sqrt(x * x + y * y)))/ 1000; – 2017-08-30 02:47:56

相關問題