MySQLに超・雑に大量のPOINTデータ(位置データ)を作成する方法

これは、RDBMS-GIS(MySQL,PostgreSQLなど) Advent Calendar 2020 の11日目のエントリだったはずのものです。実際には3日ほど遅れてしまいました。

 MySQLで、空間情報を扱う動作の確認をしたいときに、ちょっとばかり多めのデータが欲しくなることがあります。
実際の世の中のデータを取り込む形で実現しても良いのですが、ある一定の範囲内に存在する点の分量を自由に増減できない点は不便です。

 ということで、とってもザツに大量データを作成する機会があったので、本日記ではその方法を披露。

とりあえずテーブルを作る

 とりあえず ID めいたものと、POINT型のカラムを持つテーブルを作成します。
今回は、点(POINT)しか入れるつもりがないので、GEOMETRY型ではなく POINT 型にしてみました。
この後、空間インデックスを張るので、SRID の指定が必須です。とりあえずよく見る WGS84 にて。

CREATE TABLE sp1 (
  id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  sp POINT SRID 4326 NOT NULL
);

空間インデックスを張る

 用途に依るのだけど、とりあえず今回の後続の目的では高速に検索することを試したかったので(というか、普通、DBにデータをつっこんだら高速に検索したいものです)、POINT型のカラムに空間インデックスを張っておきます。

CREATE SPATIAL INDEX idx_sp1_sp ON sp1(sp);

データを生成するPythonスクリプトを作成

 以下のファイルを作成しておきます。今回は雑な作業なので z という名前のファイルとして作成しました。
作成方法も、 $ cat > z としてからペタっと貼り付けて ctrl-D するという徹底したザツさで。

lat_st = 35.5
lat_en = 35.9
lat_ivl= 0.0007
lng_st = 139.6
lng_en = 140
lng_ivl= 0.0008
ins_str = "INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(%s %s)', 4326));"

lat=lat_st
while lat<lat_en:
  lat=lat+lat_ivl
  lng=lng_st
  while lng<lng_en:
    lng=lng+lng_ivl
    print (ins_str % (lat, lng))

 少し解説すると、北緯 lat_stからlat_enの範囲(上記では 35.5~35.9)、東経lng_stからlng_enの範囲(同 139.6~140)に、 緯度は lat_ivlおき(同 0.0007)、経度は lng_ivlおき(同 0.0008) の点を生成します。 正確に言うと、生成するというか、そういう点データをINSERTするSQL文を作成します。
 指定範囲に、格子状に点を敷き詰めるイメージですね。バラつきが要求される場合には使えませんが、とりあえず指定範囲にいっぱい点があれば良い、という程度のものには非常に便利です。

実行

 MySQL の test データベースにテーブルが作成されている場合の例。

$ python z | mysql -uroot -p test

または

$ python3 z | mysql -uroot -p test


 結果例(抜粋):以下のようなINSERT文が作成され、上記コマンド例では そのまま mysql に喰わされます。

:
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73519999999968)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73599999999968)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73679999999968)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73759999999967)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73839999999967)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73919999999967)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.73999999999967)', 4326));
INSERT INTO sp1 (sp) VALUES (ST_GeomFromText('POINT(35.63580000000037 139.74079999999967)', 4326));
:


 もうちょっとちゃんとやるなら、BEGIN/COMMIT で括るとか、マルチINSERT化するとかで高速化の余地があります。


 次回の日記では、ここで生成したデータを使って、遊びます。