laravel
PostGIS

PostGISをLaravelで操作する

はじめに

  • LaravelでPostGISを利用する際に調査した拡張ライブラリとその利用方法を備忘録として残しておきます。

ライブラリ

  • 色々と調べてみたところ、以下の2つのライブラリがよさそうで実際に利用してみました。
  • 結論からいうとlaravel-geoを利用することにしました。

laravel-postgis

  • laravel-postgis
  • starの数も多く使いやすそうで、はじめはこちらを利用しようとしていました。
    • ただ、githubのREADMEにもありますが現状だと図形の演算機能が利用できないようです
    • Geometry関連の検索がしたいというのがPostGISの利用の理由なのでこちらのライブラリの利用は見送りました。

利用方法

  • ほぼgithubのREADMEのとおりですが、以下のように利用します。

  • インストール

composer require phaza/laravel-postgis
  • マイグレーション
    • デフォルトだとGEOGRAPHY型でSRIDが4326(WGS84)で定義されます。
    • 以下のようにして独自に定義も出来ます。
      • $table->point('location', 'GEOMETRY', 27700);
use Illuminate\Database\Migrations\Migration;
use Phaza\LaravelPostgis\Schema\Blueprint;

class CreateGeoTable extends Migration {

public function up()
    {
        Schema::create('geometry_sample', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('name');
            $table->point('geom1');  //Geometry型(point)
            $table->polygon('geom2'); //Geometry型(polygon)
            $table->timestamps();
        });
    }
  • モデル定義
class GeometrySample extends Model
{
    use PostgisTrait;
    protected $postgisFields = [
        'geom1',
        'geom2'
    ];

    protected $postgisTypes = [
        'geom1' => [
            'geomtype' => 'geography',
            'srid' => 4326
        ],
        'geom2' => [
            'geomtype' => 'geography',
            'srid' => 4326
        ]
    ]
}
  • 利用
    • 空間検索などのPostGISの関数は未対応ですが、、Geometry型のデータをいい感じであつかえるようにしてくれます。
    • PointやLineStringなどのGeometryクラスが内部で定義されているのでこれを利用します。
use Phaza\LaravelPostgis\Geometries\Point;
use Phaza\LaravelPostgis\Geometries\Polygon;

$geometryData = new GeometrySample();
$geometryData->name = 'hogehoge';
$geometryData->geom1 = new Point(139.55723, 35.52675);
$polygon = new Polygon(
    [
        new Point(139.55723, 35.52675),
        new Point(139.57574, 35.52675),
        new Point(139.55723, 35.50841)
    ]
);
$geometryData->geom2 = $polygon;
$geometryData->save();

laravel-geo

  • laravel-geo
  • こちらは以下のような図形演算が利用可能です。
    • intersection
    • difference
    • contains
    • intersects
    • touches
    • overlaps
    • centroid
    • distance
    • equals
  • PostGISだけでなくMySQLのGeometry型にも対応しています。

利用方法

  • インストール
composer require elevenlab/laravel-geo
  • マイグレーション
    • laravel-postgisと同じ感じで定義します。
    • Geometryデータは、GEOGRAPHY型でsridは4326(WGS84)固定で定義されるようです。(独自の定義は出来ないみたい)
class CreateGeoTable extends Migration
{

    public function up()
    {
        Schema::create('geometry_sample', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('name');
            $table->point('geom1');  //Geometry型(point)
            $table->polygon('geom2'); //Geometry型(polygon)
            $table->timestamps();
        });
    }
  • モデル定義
    • GeoModelをextendsして定義します。
use ElevenLab\GeoLaravel\Eloquent\Model as GeoModel;

class GeometrySample extends GeoModel
{
    protected $table = "geometry_sample";

    protected $geometries = [
        "points" => ['geom1'],
        "polygons" =>   ['geom2']
    ];
}
  • 利用
    • Geometryクラスはlaravel-postgisとは違いphp-ogcというライブラリを内部で利用しています。
      • Polygonオブジェクトの作り方が若干違いますが、利用方法としてはほぼ同じです。
use ElevenLab\PHPOGC\DataTypes\Point;
use ElevenLab\PHPOGC\DataTypes\Polygon;

$geometryData = new GeometrySample();
$geometryData->name = 'hogehoge';
$geometryData->geom1 = new Point(139.55723, 35.52675);
$lineString = new LineString(
    [
        new Point(139.55723, 35.52675),
        new Point(139.57574, 35.52675),
        new Point(139.55723, 35.50841)
    ]
);
$polygon = new Polygon([$lineString]);


$geometryData->geom2 = $polygon;
$geometryData->save();

  • 図形演算のクエリもEloquentのQueryBuilderを使って利用出来ます。
    • たの利用可能な演算はこちらに記載されています。
GeometrySample::select('name')->whereContains('geom2', new Point(139.55723, 35.52675));

まとめ

  • 緯度経度などのデータを扱う場合、ここにあげたライブラリ使わずに独自で実装するにしても結局は内部でGeometryクラスを独自実装することになると思うので、それだけでもこれらのライブラリの利用価値はあるかと思います。
  • PostGISの全ての関数に対応しているわけでは無いですが、図形演算処理ができればそこそこの要件には足りるかと思います。
    • 駅から**m以内にあるコンビニを検索とか
  • ただし、どうしても要件を満たさない場合はDB::rawとかで直接SQL書くことになると思います。。