20

2012年11月16日更新

私はこの質問をもう一度提起し、堅実で優れた解決策のための新しい恩恵を提供したいと思います。現在、解決策(shubhanshの答え)だけが効果的に機能していないようです。その理由を説明します。

まず、これは私が持っている半径と人のライブマップです。半径はにredあり、人はにありblueます。

ここに画像の説明を入力してください

ご覧のとおりtwo、このマップにはeight半径のある人がいます。基本的には、その人だけを取得していますが、Person A取得Person Bしていません。SQLが正しく取得していないため、正確にする必要があります。人の半径とマーカーの半径から正確です。

ピックアップされるのは半径の内側にあるように見えますが、半径が重なっているものではありません。互いに重なっている半径の結果をピックアップできるようにする必要があります。

shubhanshの答えよりも正確で正確なSQLを探しています。以下を読んで、正確な人を行動させてピックアップするためにクエリがどれほど正確に必要かを読んでください。

データ、人々

+-----------+-----------+--------+
| latitude  | longitude | radius |
+-----------+-----------+--------+
| 51.517395 | -0.053129 | 5.6    |
| 51.506607 | -0.116129 | 0.7    |
+-----------+-----------+--------+

radiusキロメートル単位であることに注意してください。

+-----------+-----------+-----+
| latitude  | longitude | km  |
+-----------+-----------+-----+
| 51.502117 | -0.103340 | 0.3 |
| 51.498913 | -0.120850 | 0.7 |
| 51.496078 | -0.108919 | 0.7 |
| 51.496506 | -0.095873 | 0.7 |
| 51.503399 | -0.090723 | 0.7 |
| 51.508049 | -0.100336 | 0.7 |
| 51.508797 | -0.112610 | 0.7 |
| 51.505535 | -0.125227 | 0.7 |
| 51.502331 | -0.108061 | 0.7 |
+-----------+-----------+-----+

私が使用している現在のSQL:

SELECT ppl.latitude,
       ppl.longitude,
       ppl.radius
FROM 
(
    people ppl
),
(
    SELECT latitude, longitude 
    FROM radiuses
) AS radius
WHERE (POW((ppl.longitude - radius.longitude) * 111.12 * COS(ppl.latitude), 2) + POW((ppl.longitude - radius.longitude) * 111.12, 2)) <= 4
GROUP BY ppl.id

クエリのテストに使用できるMySQLのデータ

INSERT INTO radiuses (id, latitude, longitude, km) VALUES ('1', '51.502117', '-0.103340', '0.3'), ('2', '51.498913', '-0.120850', '0.7'), ('3', '51.496078', '-0.108919', '0.7'), ('4', '51.496506', '-0.095873', '0.7'), ('5', '51.503399', '-0.090723', '0.7'), ('6', '51.508049', '-0.100336', '0.7'), ('7', '51.508797', '-0.112610', '0.7'), ('8', '51.505535', '-0.125227', '0.7'), ('9', '51.502331', '-0.108061', '0.7');

INSERT INTO people (id, latitude, longitude, radius) VALUES ('1', '51.517395', '-0.053129', '5.6'), ('2', '51.506607', '-0.116129', '0.7');

古い要約

注:すべての緯度と経度はランダムに作成されます。

私は、ユーザーが半径1kmの緯度/経度の場所の半径を配置できるマップアプレットを持っています。

現在、マップ上の任意の場所に、それぞれ半径1kmの半径を配置できる別のユーザーがいます(上記のユーザーと同じ)。

このように、ユーザーAは赤で、ユーザーBは青です。

ここに画像の説明を入力してください

基本的に、ユーザーAは、半径を次のようなテーブルに格納します。

+-----------+---------+-----------+-----------+
| radius_id | user_id | latitude  | longitude |
+-----------+---------+-----------+-----------+
|         1 |       1 | 81.802117 | -1.110035 |
|         2 |       1 | 81.798272 | -1.144196 |
|         3 |       1 | 81.726782 | -1.135919 |
+-----------+---------+-----------+-----------+

そして、ユーザーBは自分の半径を次のような別のテーブルに保存します-(注:アカウントごとに1つの座標しか保存できません):

+---------+-----------+-----------+
| user_id | latitude  | longitude |
+---------+-----------+-----------+
|       6 | 81.444126 | -1.244910 |
+---------+-----------+-----------+

半径の円が接触している場合でも、定義された半径内にあるユーザーをマップ画像でピックアップできるようにしたいと思います。マーカーのみCが単一の半径を取得できますが、取得できAませBん。

これは可能だと確信していますが、MySQLでこの種のシステムを考え出す方法がわかりません。

私はこれをGoogleDevelopersサイトで見つけました。これは近いですが、必要なパフォーマンスだけではありません。

編集:私はより良いものを見つけました、これは非常に近いですが、それでも私が探しているものではありません。テーブルに複数ある場合、緯度と経度の座標の1つの境界を使用するからです。

4

3 に答える 3

14

これを解決するには、次のような円の方程式を理解する必要があります。任意の点(x、y)が中心(x1、y1)および半径r単位の円内に入るには

(x-x1)^2 + (y - y1)^2 <= r^2

where a^b = a to the power b

ここで、あなたの場合、ユーザーB(緯度、経度)は円の中心であり、ユーザーA(緯度、経度)は点(x、y)であり、半径=2kmです。

しかし、基本的な問題は緯度から経度への変化であるため、ここに解決策があります。1度=111.12kmです。したがって、方程式の両側で単位を同じに保つために、それをKmsに変換します

したがって、最終的な方程式は次のようになります。

((x-x1)*111.12)^2 + ((y-y1)*111.12)^2 = 4      (=2^2) 

同じSQLステートメントは次のようになります

SELECT A.user_id, A.radius_id, A.latitude, A.logitude
FROM UserA AS A, 
     (SELECT user_id, latitude, longitude 
       FROM UserB 
       WHERE user_id = 8) AS B
WHERE (POW((A.latitude-B.latitude)*111.12, 2) + POW((A.longitude - B.longitude)*111.12, 2)) <= 4
/* **Edit** Here I have used (A.longitude - B.longitude)*111.12, for more accurate results one can replace it with (A.longitude - B.longitude)*111.12*cos(A.latitude)) or (A.longitude - B.longitude)*111.12*cos(B.latitude)) 

And, as i have suggested in the comments that first filter some records based on approximation, so whether one uses A.latitude or B.latitude it will not make much difference */

これがお役に立てば幸いです...

于 2012-07-20T21:54:18.550 に答える
7

あなたの問題の中心にあるのは、「2つの円が重なっているかどうかをどうやって知るか」という質問です。その答えは、「中心間の距離が半径の合計よりも小さい場合」です。したがって、探しているのは、2点間の距離を決定する方法です。

もう1つの答えは、緯度と経度をデカルト平面を構成するかのように扱うことです。それらはそうではありません(赤道から極に近づくにつれて経度はゼロに近づく傾向があります)。さて、概算として、ソリューションに必要な精度によっては、ソリューションに問題なく機能する場合があります。一方、これを非常に正確にする必要がある場合は、Haversine式が必要です。MySQLでそれを実装する方法の素晴らしい説明がここにあります:

http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

そのプレゼンテーションのスライド7から、次の式が得られます。

3956*2*ASIN(SQRT(POWER(SIN((orig.lat-dest.lat)*pi()/180/2),2)+
    COS(orig.lat*pi()/180)*COS(dest.lat*pi()/180)*
    POWER(SIN((orig.lon-dest.lon)*pi()/180/2),2)))

最初の数値は、マイル単位の地球の平均半径であることに注意してください。キロメートルの場合は6371に変更します。

この計算された距離をどのように使用するかは、処理しているポイントの数、地理的な分散、パフォーマンス要件、データが静的であるか継続的に更新されているかなど、投稿に含まれていない詳細によって異なります。 。

特に大量のデータがある場合や継続的に更新されている場合(携帯電話のGPSデータに基づくユーザーの位置など)、パフォーマンスが問題になるため、これらのことについて言及します。

パフォーマンスの問題を解決する1つの方法は、円の代わりに正方形を使用し、1度=111.12kmの近似値を使用することです。そうすれば、明らかに互いに遠く離れているポイントを自動的に選別できます。次に、関心のある領域内にある少数のポイントについてのみ、Haversine式を計算するだけです。

これがあなたを正しい方向に向けるのに役立つことを願っています。

于 2012-07-20T23:06:02.890 に答える
6

ジオメトリの重要なポイントは、中心間の距離が半径の合計よりも小さい場合、2つの円が重なることです。比較を行っているので、平方根演算を回避できるため、距離の2乗を使用できます。オリジナルでは、各半径は1に固定されており、2つの半径の合計は2であり、合計の2乗は4です。

元の質問と新しい質問には大きな違いがあります。最初に固定半径の円があり、2番目にさまざまな半径の円があります。4比較式の定数[...distance^2...] <= 4は、元の固定半径のアーティファクトであるため、置き換える必要があります。これを実装するには、kmフィールドをクエリに追加します。また、確認する必要があるppl.radiusように、WHEREフィルターで使用していなかったため、その値を変更してもクエリ結果が変更されなかったことは驚くことではありません。

SELECT ppl.latitude, ppl.longitude, ppl.radius
FROM 
  ( people ppl ),
  ( SELECT latitude, longitude, km FROM radiuses ) AS B
WHERE [...distance^2...] <= POW( ppl.radius + B.km, 2)

この質問は、実際に呼ばれるべきプロパティを持っているときに、エンティティ(つまり、人ではない)を「半径」と呼んでいるため、理解するのに必要な時間よりもはるかに長い時間がかかったと言えます。 2つの異なるエンティティのradius'。したがって、他のエンティティにわかりやすい名前を付けます。

于 2012-11-17T16:09:08.620 に答える