座標(緯度、経度)が与えられた場合、座標から特定の距離(50 kmなど)離れた正方形の境界ボックスを計算しようとしています。したがって、入力として緯度、経度、距離があり、出力として2つの座標が必要です。 1つは南西(左下)コーナーで、もう1つは北東(右上)コーナーです。 Pythonでこの質問に対処しようとするいくつかの回答をここで見ましたが、特にJavaの実装を探しています。
明確にするために、私は地球上でのみアルゴリズムを使用するつもりであるため、可変半径に対応する必要はありません。
非常に正確である必要はなく(+/- 20%で問題ありません)、短い距離(150 km以内)での境界ボックスの計算にのみ使用されます。したがって、効率的なアルゴリズムのために精度を犠牲にして満足しています。どんな助けも大歓迎です。
編集:私は明確にすべきだった、私は本当に正方形ではなく、円の後です。正方形の中心と正方形の周囲に沿ったさまざまな点との間の距離は、円の場合のように一定の値ではないことを理解しています。私が言っていることは、中心から周囲の4つのポイントのいずれか1つに線を引き、その周囲の側面に垂直な線を描く場合、それらの4本の線は同じ長さであると思います。
境界座標の検索に関する記事を書きました。
http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates
この記事では、式について説明し、Java実装も提供しています。また、最小/最大経度のIronManの式が不正確である理由も示しています。)
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もお勧めしますが。
import com.vividsolutions.jts.geom.Envelope;
...
Envelope env = new Envelope(centerPoint.getCoordinate());
env.expandBy(distance_in_degrees);
...
Envにエンベロープが含まれるようになりました。これは実際には「正方形」ではありません(球の表面上での意味は何でも)が、そうすべきです。
度単位の距離は、中心点の緯度に依存することに注意してください。赤道では、1度の緯度は約111kmですが、ニューヨークでは約75kmしかありません。
本当にクールなことは、すべてのポイントをcom.vividsolutions.jts.index.strtree.STRtree
その後、それを使用して、そのエンベロープ内のポイントをすばやく計算します。
PHPスクリプトとこれを行う例があります。開始点が与えられると、特定の距離までの周囲のボックスの角を計算します。Googleマップ専用ですが、他の何かのために働く:
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 Earthは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.30775033210176---|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.30503833321018</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>
以前の回答はすべて部分的に正しいだけです。特にオーストラリアのような地域では、常にポールが含まれており、10kmでも非常に大きな長方形を計算します。
特に、Jan Philip Matuschekによる http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex のアルゴリズムには、オーストラリアのほぼすべてのポイントに対して(-37、-90、-180、180)の非常に大きな長方形が含まれていました。 。これは、データベース内の大規模なユーザーに影響を与え、ほぼ半分の国のすべてのユーザーについて距離を計算する必要があります。
ロチェスター工科大学のDrupal API Earth Algorithmは、ポールの周りだけでなく他の場所でもうまく機能し、実装がはるかに簡単であることがわかりました。
https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
使用する earth_latitude_range
およびearth_longitude_range
境界矩形を計算するための上記のアルゴリズムから
実装はJavaです
/**
* 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;
}
Googleマップで文書化された距離計算式を使用して距離を計算します
マイルではなくキロメートルで検索するには、3959を6371に置き換えます。(Lat、Lng)=(37、-122)および列latおよびlngを持つマーカーテーブルの場合、式は:
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;
GeoNames citieJSON API で使用するバウンディングボックス座標を生成して、gpsの10進座標から近くの大都市を取得するために使用した簡単なソリューションを次に示します。
これはJava GitHubリポジトリのメソッド: FusionTableModifyJava
小数のGPSロケーションがあり、そのロケーションに「近い」最大の都市/州を見つける必要がありました。私は、そのバウンディングボックスで最大の都市を取得するために、citiesJSON GeoNames Webサービスに渡す比較的正確なバウンディングボックスが必要でした。場所と、関心のある「半径」(km)を渡すと、cityJSONに渡すために必要な北、南、東、西の10進座標が返されます。
(私はこれらのリソースが私の研究を行うのに役立ちました:
それは非常に正確ではありませんが、私がそれを使用していたのに十分正確です:
// Compute bounding Box coordinates for use with Geonames API.
class BoundingBox
{
public double north, south, east, west;
public BoundingBox(String location, float km)
{
//System.out.println(location + " : "+ km);
String[] parts = location.replaceAll("\\s","").split(","); //remove spaces and split on ,
double lat = Double.parseDouble(parts[0]);
double lng = Double.parseDouble(parts[1]);
double adjust = .008983112; // 1km in degrees at equator.
//adjust = 0.008983152770714983; // 1km in degrees at equator.
//System.out.println("deg: "+(1.0/40075.017)*360.0);
north = lat + ( km * adjust);
south = lat - ( km * adjust);
double lngRatio = 1/Math.cos(Math.toRadians(lat)); //ratio for lng size
//System.out.println("lngRatio: "+lngRatio);
east = lng + (km * adjust) * lngRatio;
west = lng - (km * adjust) * lngRatio;
}
}