テレグラムボットとPostGIS

サイト上のTelegramメッセンジャーのボットの実装については、すでにかなり多くの投稿がありました。 しかし、私の意見では、まだ対処されていないトピックが1つあります。 これは、ボット内のジオロケーションを操作する実装です。 この投稿では、 Aroundus_botボットでの私自身の経験に基づいて、ユーザーが送信したジオロケーション情報をボットがどのように処理できるかの例を示します。







実際、Telegramメッセンジャーは、ジオロケーションを操作する非常に便利な機会を提供します。 ユーザーは現在の位置を送信したり、マップ上の他のポイント(モバイルデバイス、添付ファイルの場所)を送信したり、 foursquareボットを使用して任意の場所(レストラン、公園、広場など)のアドレスと場所を送信したりできます。







この情報を使用してさらに何ができますか? さまざまなオプションが可能です。 これは、クエストの実装(ボットはユーザーがフォローする必要がある次のポイントを送信するか、ユーザーの位置に基づいて情報を提供する)と、近くの何か(トイレ、バー、レストラン)を検索することができます。 たとえば、私が実装したボットを使用すると、マップ上のユーザーが指定した場所に関連付けられたストーリーを送信し、その後、ユーザーが特定の地点から特定の近所に送信したすべてのストーリーを読むことができます。







場所は、緯度と経度を持つ地図上のポイントの形で提供されます。 直感的に、この情報をどう処理するか-座標データをデータベースに保存するだけで、ユーザーが特定の近隣からストーリーをリクエストすると、この近隣に該当するすべてのストーリーが検索されます。



問題



ただし、緯度と経度は、地表上のオブジェクトの位置を決定するポイントの地理座標です。 そしてご存じのように、地球は楕円体の形をしています。 また、これらの座標にたとえば100メートルを単純に追加する方法はなく、これがストーリーを検索するエリアの端であると言うことはできません。 球座標で点の近傍を計算する必要があります。



車輪を再発明し、計算曲線を実現するのは間違っていると思うので、これで何ができるかを長い間考えていました。 少なくとも2つの地理的なポイント間の距離をメートル単位で計算できる既製のツールを使用したいと思います。



Postgis



私の検索で、現在のサイトで公開されている投稿に出会いました。 そして彼は、ボットに必要な情報を保存するために私が使用するDBMSであるPostgreSQLを完全に正しい選択にしたことに気付きました。 簡単に言うと、このDBMSに提供できるPostGIS拡張機能により、通常のSQLクエリで特別な関数を使用して地理座標を効率的に操作できます。 PostGISの詳細については、 こちらをご覧ください



したがって、ポイントの近傍からのすべてのストーリーの選択(または特定のポイントに関連する最も近いものの検索)は、SQLクエリの実装に限定されます。これは、GiSTインデックスを使用して座標を操作するため、非常に迅速に実行されます。 ただし、拡張機能を効果的に使用するには、位置データを特別なジオメトリタイプの列に格納する必要があることに注意してください。その後、インデックスをハングさせる必要があります。 私の実装では、使いやすいように、緯​​度と経度のデータを二重形式で別々に保存し、列をジオメトリ形式で保存します。



特定の近隣(たとえば半径100メートル以内)で何かを検索するSQLクエリの例は、次のようになります。



ST_Distance(ST_Transform(coordinates, 26986), ST_Transform(ST_SetSRID(ST_MakePoint(x, y), 4326), 26986)) < 100
      
      





ここで:



ST_DistanceST_TransformST_SetSRIDST_MakePoint-地理座標を操作するための特別な機能、

座標 -タイプジオメトリの列

xy-ポイントの緯度と経度。その近くに関心がある。



MySQL DBMSのこの拡張機能の実装についても知っていると言わなければなりませんが、情報を検索する過程で、PostgreSQLの拡張機能の実装のほうがうまく機能し、エラーが少なくなるという強い意見がありました。



おわりに



地理座標で動作する必要があるTelegramのボットを実装する必要がある場合、PostgreSQL DBMSを使用して情報を保存することをお勧めします。 実行する必要があるのは、ユーザーから受け取った緯度と経度のデータをジオメトリタイプの列に保存することだけです。その後、SQLクエリを操作してこの情報を効果的に操作できます。



All Articles