web-dev-qa-db-ja.com

MySQL空間拡張を使用して円内のポイントを選択する

MySQLの「ポイント」でいっぱいのflagsという列を含むcoordinatesというテーブルがあります。半径100mの緯度と経度の位置に基づいて、円内のすべてのフラグを取得するクエリを実行する必要があります。

使用の観点からは、これはユーザーの位置に基づいています。たとえば、携帯電話はユーザーの緯度と経度の位置を提供し、それをAPIのこの部分に渡します。半径100メートルのユーザーの周りに非表示の円を作成し、この円内にあるフラグを返すのは、APIの責任です。

これはAPIのこの部分です。SQLを使用してこの非表示の円を作成し、この半径内のポイントのみを選択する方法がわからないため、作成方法がわかりません。

これは可能ですか?これを行うのに役立つMySQL空間関数はありますか?

私はBuffer()関数がこれを実行できると信じていますが、それを使用する方法に関するドキュメント(例SQLなど)が見つかりません。理想的には、この関数の使用方法またはそれに最も近い方法を示す答えが必要です。これらの座標を地理空間ポイントとして保存している場合、地理空間関数を使用して、効率を最大化するために求めていることを実行する必要があります。

フラグテーブル:

  • id
  • 座標
  • 名前

行の例:

1 | [ジオメトリ-25B] |テナシーAB

フラグテーブルには、緯度、経度の位置、東および北(UTM)があります。

ユーザーの場所は標準の緯度/経度ですが、この位置をUTMに変換できるライブラリがあります

16
jskidd3

MySQLには、緯度/経度距離の計算をサポートする地理空間拡張関数はありません。MySQL 5.7以降

あなたは地球の表面に近接円を求めています。質問で、flagsテーブルの各行に緯度/経度の値があり、さらに ユニバーサルトランスバースメルカトル (UTM)のいくつかの異なる値の1つに予測値がある TMゾーン 。私のUK Ordnance Surveyマップを正しく覚えている場合、UTMはそれらのマップ上のアイテムを見つけるのに役立ちます。

UTMで2点間の距離を計算するのは簡単です同じゾーン内:デカルト距離がうまくいきます。ただし、ポイントが異なるゾーンにある場合、その計算は機能しません。

したがって、質問に記載されているアプリケーションでは、haversineまたは別の適切な式を使用して計算される Great Circle Distance を使用する必要があります。

MySQLは、地理空間拡張で拡張されており、さまざまな平面形状(ポイント、ポリライン、ポリゴンなど)を幾何プリミティブとして表現する方法をサポートしています。 MySQL 5.6は、文書化されていない距離関数st_distance(p1, p2)を実装しています。ただし、この関数はデカルト距離を返します。したがって、緯度と経度に基づく計算では完全に不適切です。温帯緯度では、緯度の線が極の近くに近づくにつれて、経度(東西)の約2倍の表面距離(南北)の範囲になります。

したがって、円形の近接式では、真の緯度と経度を使用する必要があります。

アプリケーションでは、次のようなクエリを使用して、特定のlatpoint,longpointの10マイル以内にあるすべてのflagsポイントを見つけることができます。

 SELECT id, coordinates, name, r,
        units * DEGREES( ACOS(
                   COS(RADIANS(latpoint)) 
                 * COS(RADIANS(X(coordinates))) 
                 * COS(RADIANS(longpoint) - RADIANS(Y(coordinates))) 
                 + SIN(RADIANS(latpoint)) 
                 * SIN(RADIANS(X(coordinates))))) AS distance
   FROM flags
   JOIN (
        SELECT 42.81  AS latpoint,  -70.81 AS longpoint, 
               10.0 AS r, 69.0 AS units
        ) AS p ON (1=1)
  WHERE MbrContains(GeomFromText (
        CONCAT('LINESTRING(',
              latpoint-(r/units),' ',
              longpoint-(r /(units* COS(RADIANS(latpoint)))),
              ',', 
              latpoint+(r/units) ,' ',
              longpoint+(r /(units * COS(RADIANS(latpoint)))),
              ')')),  coordinates)

20 km以内のポイントを検索する場合は、クエリのこの行を変更します

               20.0 AS r, 69.0 AS units

これに、例えば

               20.0 AS r, 111.045 AS units

rは、検索する半径です。 unitsは、地球表面の緯度1度あたりの距離単位(マイル、km、ハロンなど)です。

このクエリは、境界緯度/経度とMbrContainsを使用して、開始点から明らかに遠すぎる点を除外し、大圏距離の式を使用して残りの点の距離を生成します。 すべての説明はここにあります 。テーブルがMyISAMアクセス方式を使用していて、空間インデックスがある場合、MbrContainsはそのインデックスを利用して高速検索を実行します。

最後に、上記のクエリは長方形内のすべてのポイントを選択します。これを円内のポイントのみに絞り込み、近接度で並べ替えるには、次のようにクエリをラップします。

 SELECT id, coordinates, name
   FROM (
         /* the query above, paste it in here */
        ) AS d
  WHERE d.distance <= d.r
  ORDER BY d.distance ASC 
22
O. Jones

[〜#〜]更新[〜#〜]

ST_Distance_Sphere()を使用して、緯度/経度を使用して距離を計算します

http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html#function_st-distance-sphere

11
Mihado

これは、テーブルの座標が「ポイント」というラベルの付いた列にPOINT()データ型として格納されていることを前提としています。関数X(point)およびY(point)は、それぞれポイント値から緯度と経度の値を抽出します。

SET @lat = the latitude of the point
SET @lon = the longitude of the point
SET @rad = radius in Kilometers to search from the point
SET @table = name of your table

SELECT
    X(point),Y(point),*, (
      6373 * acos (
      cos ( radians( @lat ) )
      * cos( radians( X(point) ) )
      * cos( radians( Y(point) ) - radians( @lon ) )
      + sin ( radians( @lat ) )
      * sin( radians( X(point) ) )
    )
) AS distance
FROM @table
HAVING distance < @rad

マイル単位で実行する場合は、定数6373を3959に置き換えます

クエリ構文を減らしたい人のために、これは、Haversineの式に基づいて距離関数を実装するためのユーザー定義のMySQL関数の一般的な実装です。

CREATE FUNCTION HAVERSINE ( coord1 POINT, coord2 POINT )
RETURNS DOUBLE
DETERMINISTIC
BEGIN
    DECLARE dist DOUBLE;
    SET rlat1 = radians( X( coord1 ) );
    SET rlat2 = radians( X( coord2 ) );
    SET rlon1 = radians( Y( coord1 ) );
    SET rlon2 = radians( Y( coord2 ) );
    SET dist  = ACOS( COS( rlat1 ) * COS( rlon1 ) * COS( rlat2 ) * COS( rlon2 ) + COS( rlat1 ) * SIN( rlon1 ) * COS( rlat2 ) * SIN( rlon2 ) + SIN( rlat1 ) * SIN( rlat2 ) ) * 6372.8;
    RETURN dist;
END
10

MySQL 5.6未満では、バッファはあまり役に立ちません。これは、バッファがポリゴンであり、MySQL 5.6未満のポリゴン操作が「最小境界長方形」(MBR)として実装されているため、ほとんど役に立たないためです。

MySQL 5.6以降、 完全な非MBR st_*操作が実装されました 。しかし、円の場合の最善の解決策は、 非文書化 関数st_distanceを使用することです。

select *
from waypoints
where st_distance(point(@center_lon, @center_lat), coordinates) <= radius;

文書化されていないため、見つけるのは困難でした:-)しかし、それは このブログ で言及されており、その著者も 言及されたバグレポート に記入しています。ただし、注意点があります(ブログを引用):

悪いニュースは:

1)すべての関数は依然として平面システム座標のみを使用します。異なるSRIDはサポートされていません。

2)空間インデックス(RTREE)はMyISAMテーブルでのみサポートされます。 InnoDBテーブルの関数を使用できますが、空間キーは使用しません。

ポイント1)は、距離の単位が座標の単位(WGS84の場合は度)と同じになることを意味します。メートル単位の距離が必要な場合は、メートルに対応する単位を持つ投影座標系(UTMなど)を使用する必要があります。

そのため、これらの警告に対応したくない場合、またはMySQL <5.6の場合は、独自のカスタム距離関数を作成する必要があります。

5
TMS

私のバージョンが役に立てば幸い

SELECT 
    *
FROM 
    `locator`
WHERE
    SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`

詳細はこちら http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql

1
dexxtr

から: https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql

SELECT
    id, (
      6371 * acos (
      cos ( radians(78.3232) )
      * cos( radians( lat ) )
      * cos( radians( lng ) - radians(65.3234) )
      + sin ( radians(78.3232) )
      * sin( radians( lat ) )
    )
) AS distance
FROM markers
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;

(すべての定数を置き換えることを忘れないでください。この例はキロメートル用です)

1

完全を期すために、MySQL 5.7.6以降。同じ結果を得る ST_Distance_Sphere 関数を使用できます。

SET @pt1 = ST_GeomFromText('POINT(12.3456 34.5678)');

SELECT * from 
(SELECT * ,(ST_Distance_Sphere(@pt1, location, 6373)) AS distance FROM mydb.Event ORDER BY distance) x WHERE x.distance <= 30;

この場合、地球のおおよその半径をキロメートル単位(6373)とポイント(@ pt1)で示します。このコードは、そのポイント(長い12.3456、緯度34.5678)と、データベースに含まれる30 km以下のすべてのポイントとの間の距離(キロメートル単位)を計算します。

0
AlterX

以下を使用できます。

SELECT name, lat, lng   
FROM vw_mytable 
WHERE ST_Contains(ST_Buffer(
          ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1  

式:上記のステートメント内の0.00001 * 1000は、直径1000メートルの円を与えます。ここのビューに適用されています。名前列はポイントのラベルにすぎません。mypointは私のポイント列の名前です。緯度は内部で計算されましたST_X(mytable.mypoint)で表示し、ST_Y(mytable.mypoint)でlngを表示すると、latとlngのリテラル値が表示されます。それはあなたに円に属するすべての座標を与えます。

0
newway