Help us understand the problem. What is going on with this article?

Rails で PostGIS を使う方法(Docker, Heroku)

More than 3 years have passed since last update.

Rails アプリケーションで、経度緯度をつかって位置情報を使い時がありました。

PostGIS は PostgreSQL の拡張です。これを使うと、その経度緯度をデータベースのカラムに入れることができて、また 2 点間の距離を計算したりすることができるようです。

この記事では、

  • ローカル開発環境を Docker でつくる
  • heroku で動くように設定する

の2つをかきます。

ローカル開発環境を Docker でつくる

やることは

  1. postgis のイメージをつかっちゃう
  2. activerecord-postgis-adapter という gem つかう
  3. database.yml の設定
  4. rake db:create を実行する
  5. model と migration をつくって動かしてみる

イメージとして、とても簡単な以下のテーブルをつくってみます。

stations テーブル

name lonlat
渋谷 経度緯度の情報
新宿 経度緯度の情報

感想: PoistGIS を知る前は、 longitude(経度)latitude(緯度) をそれぞれカラムで持つのかなぁと思っていたのですが。 PostGIS を使うと、上のように lonlat というひとまとめにしたデータを持つことができます。

Step.1 Image

ローカル開発環境は、docker-compose で用意しています。

なので、端折って書きますが。以下のようにイメージを指定すると完了です。

docker-compose.yml
version '3'
services:
  app:
  db:
-    image: postgres:10
+    image: mdillon/postgis:10
  kvs:

PostGIS のイメージは こちら にまとまっていますが、その中から今回は上を選択しました。

Step.2 Gem

Gemfile
gem 'activerecord-postgis-adapter'

こちらの gem を使います。
https://github.com/rgeo/activerecord-postgis-adapter

以降の Step.3-5 は基本的に activerecord-postgis-adapter のドキュメントに書いてある通りですので、詳細はそちらを参照ください。

Step.3 database.yml

database.yml
default: &default
-  adapter: postgresql
+  adapter: postgis

Step.4 Command

今回は、すでに PostgreSQL で諸々環境を用意したあとに、PostGIS に切り替えていますので、ドキュメントにある通り以下のコマンドを叩きました。

$ rake db:gis:setup

Step.5 Migration

簡単な migration をつくります。

create_stations.rb
class CreateStations < ActiveRecord::Migration[5.1]
  def change
    create_table :stations do |t|
      t.string :name
      t.st_point :lonlat, geographic: true
      t.timestamps
    end
  end
end
Station.rb
class Station < ApplicationRecord
end

これで、 rails console で、動作を確認してみます。

rails_console
# データ作成は、直感的です。
pry(main)> Station.create(name: "hoge", lonlat: 'POINT(30 40)')


# こんな感じで、ある場所から近いところを取れることを確認しました。
pry(main)> Station.where("ST_Distance(lonlat, 'POINT(30 50)') < 100000000").order("ST_Distance(lonlat, 'POINT(30 50)')")

=>   Station Load (0.8ms)  SELECT "stations".* FROM "stations" WHERE (ST_Distance(lonlat2, 'POINT(30 50)') < 100000000) ORDER BY ST_Distance(lonlat2, 'POINT(30 50)')
[#<Station:0x0000000004fe0840
  id: 2,
  name: "piyo",
  lonlat1: #<RGeo::Cartesian::PointImpl:0x283f714 "POINT (30.0 40.0)">,
  lonlat2: #<RGeo::Geographic::SphericalPointImpl:0x2830f84 "POINT (30.0 40.0)">,
  created_at: Mon, 15 Jan 2018 19:03:43 JST +09:00,
  updated_at: Mon, 15 Jan 2018 19:03:43 JST +09:00>,
 #<Station:0x0000000004fe0688
  id: 1,
  name: "hoge",
  lonlat1: #<RGeo::Cartesian::PointImpl:0x2882474 "POINT (-122.0 46.0)">,
  lonlat2: #<RGeo::Geographic::SphericalPointImpl:0x28a2878 "POINT (50.0 1.0)">,
  created_at: Mon, 15 Jan 2018 19:02:49 JST +09:00,
  updated_at: Mon, 15 Jan 2018 19:02:49 JST +09:00>]

以上で、ローカル環境は整ったと思います。

heroku で動かす

cf. heroku document
https://devcenter.heroku.com/articles/postgis

heroku で動かすには、(すこしはまったのですが)以下の対応をすると動きました。

  • postgres を Add-on でいれておく
  • heroku-geo-buildpack という Buildpack を入れる
  • database.yml を書き換える (<= これにハマりましたが @namitop が解決策を教えてくれました 😁

Add-on と Buildpack は普通に入ると思います。

一応 heroku-geo-buildpack には以下の順番で入っていたので、その順番にあわせてあります。

$ heroku buildpacks
=== sushi Buildpack URLs
1. https://github.com/cyberdelia/heroku-geo-buildpack.git
2. heroku/ruby

database.yml の書き換え

これに気づかなくて大変でした。

database.yml
production:
  <<: *default
  database: eikaiwa_production
+  url: <%= ENV.fetch('DATABASE_URL', '').sub(/^postgres/, "postgis") %>

該当の heroku document はこちらです。
https://devcenter.heroku.com/articles/rails-database-connection-behavior#active-record-4-1-escape-valve

以上で、heroku でも動きました。

まだ、PostGIS は出会ったばかりで、距離計算とかの仕様をこれから勉強する状況ですが、とても楽しそうです 🍏

ありがとうございました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away