「X」キロメートル(またはマイル)内の都市の検索
-
03-07-2019 - |
質問
これは明確な場合とそうでない場合があります。私が基地を離れている場合、または追加情報が必要な場合はコメントを残してください。おそらく、私がPHPで欲しいもののための解決策がすでにあるでしょう。
経度または緯度の値から距離を加算または減算する関数を探しています。
理由:すべての緯度と経度を含むデータベースがあり、Xキロメートル(またはマイル)以内のすべての都市を抽出するクエリを作成したい。私のクエリは次のようになります...
Select * From Cities Where (Longitude > X1 and Longitude < X2) And (Latitude > Y1 and Latitude < Y2)
Where X1 = Longitude - (distance)
Where X2 = Longitude + (distance)
Where Y1 = Latitude - (distance)
Where Y2 = Latitude + (distance)
MySqlデータベースを使用して、PHPで作業しています。
提案も歓迎します! :)
解決
これは、必要な処理を正確に実行するMySQLクエリです。地球は完全に球形ではなく、山、丘、谷などを考慮していないため、このようなものは一般的に近似値であることに留意してください。 AcademicHomes.com とPHPおよびMySQLを使用すると、$ latitude、$ longitudeの$ radiusマイル以内のレコードを返します。
$res = mysql_query("SELECT
*
FROM
your_table
WHERE
(
(69.1 * (latitude - " . $latitude . ")) *
(69.1 * (latitude - " . $latitude . "))
) + (
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3)) *
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3))
) < " . pow($radius, 2) . "
ORDER BY
(
(69.1 * (latitude - " . $latitude . ")) *
(69.1 * (latitude - " . $latitude . "))
) + (
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3)) *
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3))
) ASC");
他のヒント
編集:どこかに、世界のすべての都市とその緯度経度のリストがある場合。そして長い。値、ルックアップを行うことができます。この場合、緯度:
正直なところ、この問題の背後にある問題は、Googleマップなどのサービスを使用してデータを取得する方がはるかに良いことです。具体的には、地球は完全な球体ではなく、2度の間の距離は、赤道に近づいたり遠ざかったりするにつれて変化します。
例については、 http://en.wikipedia.org/wiki/Geographic_coordinate_system をご覧ください。つまり、 Google Maps API をご覧ください。
含める都市の数に応じて、リストを事前計算できます。ここでは、+ 100mの不正確さがセットアップには大きすぎる内部アプリケーションに対してこれを行います。これは、location1、location2、distanceの2つのキーテーブルを持つことで機能します。その後、位置xから位置1までの距離を非常に迅速に引き戻すことができます。
また、計算はオフラインで実行できるため、システムの実行には影響しません。ユーザーはまた、より速い結果を得ます。
私は上記のコードを使用してみましたが、ポイント間の距離が20〜30マイルの範囲にある場合に回答が多すぎたため、数マイルのエラーが発生しても大丈夫です。私のマッピング仲間と話をして、代わりにこれを思いついた。コードはpythonですが、簡単に翻訳できます。ラジアンへの一定の変換を回避するために、データベースを再作成し、緯度/経度のポイントを度からラジアンに変換しました。これの良い点は、数学の大部分がほとんど一度行われることです。
ra = 3963.1906 # radius @ equator in miles, change to km if you want distance in km
rb = 3949.90275 # radius @ poles in miles, change to km if you want distance in km
ra2 = ra * ra
rb2 = rb * rb
phi = self.lat
big_ol_constant = (math.pow(ra2*math.cos(phi), 2) + pow(rb2*math.sin(phi), 2))/ (pow(ra*math.cos(phi), 2) + pow(rb*math.sin(phi), 2))
sqlWhere = "%(distance)g > sqrt((power(lat - %(lat)g,2) + power(lng-%(lng)g,2)) * %(big_ol_constant)g)" % {
'big_ol_constant': big_ol_constant, 'lat': self.lat, 'lng': self.lng, 'distance': distance}
# This is the Django portion of it, where the ORM kicks in. sqlWhere is what you would put after the WHERE part of your SQL Query.
qs = ZipData.objects.extra(where=[sqlWhere]);
距離が小さく、距離が200マイルに達すると10マイル以内にある場合、非常に正確であるようです(もちろん、それまでに「カラスが飛ぶように」と「舗装された道路」に問題があります) ;)。
これは上記のモデルZipDataです。
class ZipData(models.Model):
zipcode = ZipCodeField(null=False, blank=False, verbose_name="ZipCode", primary_key=True)
city = models.CharField(max_length=32, null=False, blank=False)
state = models.CharField(max_length=2)
lat = models.FloatField(null=False, blank=False)
lng = models.FloatField(null=False, blank=False)
追加の注意点は、 GeoNamesで郵便番号に関連する地理データを大量に取得できることです。 org を使用し、WebサービスAPIも使用できます。
多くの(悪いオプション)
-
数学式(X1-X2およびY1-Y2を扱う)をベクトルとして使用して距離を計算します。
-
すべての組み合わせで事前にルックアップテーブルを作成し、距離を保持します。
-
MySQLのGIS固有の拡張を使用することを検討してください。これは 1つの記事です。 。
lessthandot.comには、実際に3つの異なる方法があります。ブログを少しスクロールする必要がありますが、それらはあります。 http://blogs.lessthandot.com/
以下の機能は、 nerddinner の(ASP.NET MVCサンプルアプリケーション codeplexで利用可能)データベース(MSSQL)。
ALTER FUNCTION [dbo].[DistanceBetween] (@Lat1 as real,
@Long1 as real, @Lat2 as real, @Long2 as real)
RETURNS real
AS
BEGIN
DECLARE @dLat1InRad as float(53);
SET @dLat1InRad = @Lat1 * (PI()/180.0);
DECLARE @dLong1InRad as float(53);
SET @dLong1InRad = @Long1 * (PI()/180.0);
DECLARE @dLat2InRad as float(53);
SET @dLat2InRad = @Lat2 * (PI()/180.0);
DECLARE @dLong2InRad as float(53);
SET @dLong2InRad = @Long2 * (PI()/180.0);
DECLARE @dLongitude as float(53);
SET @dLongitude = @dLong2InRad - @dLong1InRad;
DECLARE @dLatitude as float(53);
SET @dLatitude = @dLat2InRad - @dLat1InRad;
/* Intermediate result a. */
DECLARE @a as float(53);
SET @a = SQUARE (SIN (@dLatitude / 2.0)) + COS (@dLat1InRad)
* COS (@dLat2InRad)
* SQUARE(SIN (@dLongitude / 2.0));
/* Intermediate result c (great circle distance in Radians). */
DECLARE @c as real;
SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a));
DECLARE @kEarthRadius as real;
/* SET kEarthRadius = 3956.0 miles */
SET @kEarthRadius = 6376.5; /* kms */
DECLARE @dDistance as real;
SET @dDistance = @kEarthRadius * @c;
return (@dDistance);
END
これが役立つと思います。
ピタゴラスの定理を使用して、2組の緯度/経度ポイントの近接度を計算できます。
2つの場所(AlphaとBeta)がある場合、それらの距離を次の方法で計算できます。
SQRT( POW(Alpha_lat - Beta_lat,2) + POW(Alpha_lon - Beta_lon,2) )
次のURLのセットアップを使用して、以下のクエリを作成しました。 (ImがcodeIgnitorを使用してデータベースを照会することに注意してください)
http:/ /howto-use-mysql-spatial-ext.blogspot.com/2007/11/using-circular-area-selection.html
function getRadius($point="POINT(-29.8368 30.9096)", $radius=2)
{
$km = 0.009;
$center = "GeomFromText('$point')";
$radius = $radius*$km;
$bbox = "CONCAT('POLYGON((',
X($center) - $radius, ' ', Y($center) - $radius, ',',
X($center) + $radius, ' ', Y($center) - $radius, ',',
X($center) + $radius, ' ', Y($center) + $radius, ',',
X($center) - $radius, ' ', Y($center) + $radius, ',',
X($center) - $radius, ' ', Y($center) - $radius, '
))')";
$query = $this->db->query("
SELECT id, AsText(latLng) AS latLng, (SQRT(POW( ABS( X(latLng) - X({$center})), 2) + POW( ABS(Y(latLng) - Y({$center})), 2 )))/0.009 AS distance
FROM crime_listing
WHERE Intersects( latLng, GeomFromText($bbox) )
AND SQRT(POW( ABS( X(latLng) - X({$center})), 2) + POW( ABS(Y(latLng) - Y({$center})), 2 )) < $radius
ORDER BY distance
");
if($query->num_rows()>0){
return($query->result());
}else{
return false;
}
}
車輪を再発明しないでください。これは空間クエリです。 MySQLを使用します緯度経度座標データを
免責事項:これはドキュメントを読むことに基づいています。私はこれを試していません。