キーワードで位置情報を検索して検索結果の任意の情報をDBへ保存、そして、DBへ保存した位置情報をGoogle Mapsで表示するアプリケーションを作ってみます。
キーワードから位置情報を検索するのに、Google Places API のRuby用のラッパーライブラリであるmarceldegraaf/google_places をRails4アプリケーションにインストールします。
DBへ保存した位置情報をGoogle Mapsで表示させるのに、gmaps4rails をRails4アプリケーションにインストールします。
また、作成したアプリケーションはheroku にpushしますのでRailsのデータベースドライバとしてPostgreSQLを選択します。
PostgreSQLの準備
PostgreSQLインストール
CentOS6でPostgreSQLインストール
データベースユーザ作成
PostgreSQLにpostgres
ユーザで接続します。
1
psql --username= postgres
create user
SQLを実行して、データベースユーザとしてgoogle_place_sample
をDB作成権限を付与して作成します。
1
create user google_place_sample with createdb password 'google_place_sample' ;
Railsプロジェクト作成
Railsプロジェクト作成
新しいRailsプロジェクトを作成します。
1
2
3
rails new google_place_sample --database=postgresql
cd google_place_sample
bin / spring stop
therubyracer
Gemfile
のJavascriptエンジンであるtherubyracer
の設定行をアンコメントします。
Gemfile 1
2
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
gem 'therubyracer' , platforms : :ruby
therubyracer
ライブラリをインストールします。
PostgreSQLデータベース接続設定
PostgreSQLデータベース接続情報に接続データベースユーザの情報を追記します。
config/database.yml 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
default : &default
adapter : postgresql
encoding : unicode
pool : 5
development :
<< : *default
database : google_place_sample_development
user : google_place_sample
password : google_place_sample
test :
<< : *default
database : google_place_sample_test
user : google_place_sample
password : google_place_sample
production :
<< : *default
database : google_place_sample_production
username : google_place_sample
password : <%= ENV['GOOGLE_PLACE_SAMPLE_DATABASE_PASSWORD'] %>
Bootstrap3インストール
Gemライブラリのインストール
Gemfile
に追記します。
Gemfile 1
2
3
4
5
6
7
8
# Install Twitter Bootstrap3
# https://github.com/twbs/bootstrap-sass
gem 'bootstrap-sass' , '~> 3.2.0'
# -webkit-border-radius みたいなブラウザベンダープレフィックスをよしなに管理してくれる
# Parse CSS and add vendor prefixes to rules by Can I Use
# https://twitter.com/autoprefixer
gem 'autoprefixer-rails'
Gemライブラリをインストールします。
bootstrap-sassを使用する準備
今回はSass
というプリプロセッサに対応したbootstrap3
をインストールしているのですが、CSSの//= require
行はSassでは文法として使用できない >ので注意が必要です。そして、Sass拡張子のファイルやその他のスタイルシートであっても、Bootstrapからmixinsや変数を利用できないので//= require
>行は利用できない ということです。
本家GiHubのREADME に従い、app/assets/stylesheets/application.css
は削除します。
1
rm app/assets/stylesheets/application.css
app/assets/stylesheets/application.css.scss
を新規作成します。
app/assets/stylesheets/application.css.scss 1
2
@import "bootstrap-sprockets" ;
@import "bootstrap" ;
app/assets/javascripts/application.js
にBootstrap関連のJavascriptsライブラリをrequireします。
app/assets/javascripts/application.js 1
//= require bootstrap-sprockets
app/views/layouts/application.html.erb
のhead
タグにbootstrap関連のスタイルシートをincludeする設定とメディアクエリーを使用するためのviewport設定 を追記します。
app/views/layouts/application.html.erb headタグ内の記述 1
2
< %= stylesheet_link_tag "bootstrap", media: "all", "data-turbolinks-track" => true %>
<meta content= "width=device-width, initial-scale=1" name= "viewport" >
ナビゲーションメニュー
app/views/layouts/application.html.erb 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Project name</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="/">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<form class="navbar-form navbar-left">
<input type="text" class="form-control col-lg-8" placeholder="Search">
</form>
</div><!--/.nav-collapse -->
</div><!-- /.container -->
</div><!-- /.navbar -->
Jumbotron(ジャンボトロン)
app/assets/stylesheets/theme-style.css.scss
を作成し、スタイルを追加します。
app/assets/stylesheets/theme-style.css.scss 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
body {
padding-top : 70 px ;
padding-bottom : 30 px ;
}
.theme-dropdown .dropdown-menu {
position : static ;
display : block ;
margin-bottom : 20 px ;
}
.theme-showcase > p > .btn {
margin : 5 px 0 ;
}
.theme-showcase .navbar .container {
width : auto ;
}
app/assets/stylesheets/application.css.scss
に作成したtheme-style.css.scss
を読む込むように設定します。
app/assets/stylesheets/application.css.scss
次にJumbotron(ジャンボトロン)を表示するためのコードを記述します。
app/views/layouts/application.html.erb 1
2
3
4
5
6
7
8
9
10
<div class= "container theme-showcase" role= "main" >
<!-- Main jumbotron -->
<div class= "jumbotron" >
<h1> Hello, world!</h1>
<p class= "text-warning" > bootstrap3-sample</p>
</div> <!-- /.jumbotron -->
< %= yield %>
</div> <!-- /.container -->
Railsのフラッシュメッセージ表示
コントローラ内でModelの保存や削除の成功/失敗などのメッセージを変数に格納された場合にはJumbotron(ジャンボトロン)の上部にします。
app/views/layouts/application.html.erb 1
2
3
4
5
6
7
8
9
<% if ( notice ) %>
<div class="alert alert-info alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert">
<span aria-hidden="true">×</span>
<span class="sr-only">閉じる</span>
</button>
<strong> <%= notice %> </strong>
</div>
<% end %>
google_placesライブラリでキーワードから位置情報を取得する
https://github.com/marceldegraaf/google_places をインストールすることで、東京 焼き肉
とか沖縄 しまぶた屋
などのキーワードで検索して位置情報などを取得することができるようになります。
Gemライブラリのインストール
Gemfile
に追記します。
Gemfile 1
2
3
# A Ruby wrapper around the Google Places API
# https://github.com/marceldegraaf/google_places
gem 'google_places'
Gemライブラリをインストールします。
コントローラとビューの生成
今回はscaffold
で生成しません。
rails g controller
コマンドを使用してコントローラとビューを生成します。
アクション名
役割
ビューの生成
index
検索フォームと登録済み位置情報テーブル
list
Google place API検索結果の表示
show
当該レコードの位置情報を表示
create
当該位置情報をDBへ保存
destroy
当該位置情報をDBから削除
1
bundle exec rails g controller place index list show
app/controllers/place_controller.rb
app/controllers/place_controller.rb 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class PlaceController < ApplicationController
before_action :set_place , only : [ :show , :destroy ]
def index
@places = Place . all
end
def show
end
def list
keyword = params [ :search ]
@client = GooglePlaces :: Client . new ( ENV [ 'GOOGLE_API_KEY' ] )
@places = @client . spots_by_query ( keyword )
end
def create
@place = Place . new ( place_params )
respond_to do | format |
if @place . save
format . html { redirect_to place_index_path , notice : " #{ @place . name } の位置情報を保存しました" }
else
format . html { render :index , notice : " #{ @place . name } の位置情報を保存できませんでした" }
end
end
end
def destroy
@place . destroy
respond_to do | format |
format . html { redirect_to place_index_path , notice : " #{ @place . name } の位置情報を削除しました" }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_place
@place = Place . find ( params [ :id ] )
end
# Never trust parameters from the scary internet, only allow the white list through.
def place_params
params . require ( :place ) . permit ( :name , :latitude , :longitude , :address )
end
end
app/views/place/index.html.erb
app/views/place/index.html.erb 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<h1>位置情報を検索してみよう</h1>
<div class="col-md-6">
<%= form_tag place_list_path , :role => "form" , :method => :get do %>
<div class="form-group">
<%= text_field_tag :search , params [ :search ] , { :class => "form-control" , :required => true , } %>
<%= button_tag ( { :type => "submit" , :name => nil , :class => "btn btn-default" } ) do %>
<span class="glyphicon glyphicon-search">キーワード検索</span>
<% end %>
</div>
<% end %>
</div>
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Latitude</th>
<th>Longitude</th>
<th>Address</th>
<th colspan="2"></th>
</tr>
</thead>
<tbody>
<% @places . each do | place | %>
<tr>
<td> <%= place . name %> </td>
<td> <%= place . latitude %> </td>
<td> <%= place . longitude %> </td>
<td> <%= place . address %> </td>
<td> <%= link_to ( place ), :title => "show" do %>
<span class="glyphicon glyphicon-stats"></span>
<% end %> </td>
<td> <%= link_to ( place , method : :delete , data : { confirm : " #{ place . name } の位置情報を削除します" }, :title => "delete" ) do %>
<span class="glyphicon glyphicon-trash"></span>
<% end %> </td>
</tr>
<% end %>
</tbody>
</table>
</div>
app/views/place/list.html.erb
app/views/place/list.html.erb 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<h1>希望の場所は見つかったかな?</h1>
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Latitude</th>
<th>Longitude</th>
<th>Address</th>
<th></th>
</tr>
</thead>
<tbody>
<% @places . each do | place | %>
<tr>
<td> <%= place . name %> </td>
<td> <%= place . lat %> </td>
<td> <%= place . lng %> </td>
<td> <%= place . formatted_address %> </td>
<td> <%= link_to ( '登録' , place_index_path ( :place => { :name => place . name ,
:latitude => place . lat ,
:longitude => place . lng ,
:address => place . formatted_address , } ), :method => 'post' ) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<button type="button" class="btn pull-right btn-lg btn-default">
<%= link_to 'Back' , place_index_path %>
</button>
</div>
app/views/place/show.html.erb
app/views/place/show.html.erb 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Latitude</th>
<th>Longitude</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr>
<td> <%= @place . name %> </td>
<td> <%= @place . latitude %> </td>
<td> <%= @place . longitude %> </td>
<td> <%= @place . address %> </td>
</tr>
</tbody>
</table>
<button type="button" class="btn pull-right btn-lg btn-default">
<%= link_to 'Back' , place_index_path %>
</button>
</div>
モデルの生成とDBマイグレーション
位置情報を保存するためのplace
モデルを生成します。
1
bundle exec rails g model place name:string address:string latitude:float longitude:float
DBを作成、マイグレーションを実行します。
1
2
bundle exec rake db:create
bundle exec rake db:migrate
ルーティング設定
config/routes.rb 1
2
3
4
5
6
7
8
9
10
Rails . application . routes . draw do
root 'place#index'
namespace :place do
# get 'place/list' request
get 'list'
end
resources :place , :only => [ :index , :show , :create , :destroy ]
end
ここまでのサンプルアプリケーション
google_placeライブラリを使った位置情報検索サンプルアプリケーション
gmaps4railsライブラリでDBに保存した位置情報からマップを作成する
ここまでの作業でキーワード検索でリストアップされた施設の位置情報をDBへ保存することが出来ました。
ここで保存している位置情報とは施設名
、住所
、経度
、緯度
です。
経度
と緯度
の情報があればGoogle Maps
上にマークを表示させることが出来ます。
apneadiving/Google-Maps-for-Rails (gmaps4rails) ライブラリを利用することで簡単にDBに保存された経度
、緯度
を使用してGoogle Mapsを利用できます。
Gemライブラリのインストール
Gemfile 1
2
3
4
# Enables easy Google map + overlays creation in Ruby apps
# https://github.com/apneadiving/Google-Maps-for-Rails
# http://apneadiving.github.io/
gem 'gmaps4rails'
Gemライブラリをインストールします。
gmaps4railsライブラリの使用準備
app/views/layouts/application.html.erb
のhead
タグにGoogle Maps関連のライブラリを読み込む設定を追記します。
app/views/layouts/application.html.erb headタグ内の記述 1
2
<script src= "//maps.google.com/maps/api/js?v=3.13&sensor=false&libraries=geometry" type= "text/javascript" ></script>
<script src= '//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js' type= 'text/javascript' ></script>
app/assets/javascripts/application.js
にGoogle Maps関連のJavascriptsライブラリをrequireします。
app/assets/javascripts/application.js 1
2
//= require underscore
//= require gmaps/google
underscore/underscore.js をapp/assets/javascripts/underscore.js
として作成します。
Google Mapsをレスポンシブで表示するスタイルシート
app/assets/stylesheets/gmap4rails.css.scss
app/assets/stylesheets/gmap4rails.css.scss 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.map_container {
position : relative ;
width : 100 % ;
margin-bottom : 20 px ;
padding-bottom : 56 .25 % ; /* Ratio 16:9 ( 100%/16*9 = 56.25% ) */
}
.map_container .map_canvas {
position : absolute ;
top : 0 ;
right : 0 ;
bottom : 0 ;
left : 0 ;
margin : 0 ;
padding : 0 ;
}
app/assets/stylesheets/gmap4rails.css.scss
をimportします。
app/assets/stylesheets/application.css.scss
showアクションでMapを表示する
app/controllers/place_controller.rb
app/controllers/place_controller.rb のshowアクション 1
2
3
4
5
6
7
def show
@hash = Gmaps4rails . build_markers ( @place ) do | place , marker |
marker . lat place . latitude
marker . lng place . longitude
marker . json ({ title : place . name })
end
end
app/views/place/show.html.erb
先述したapp/views/place/show.html.erb
の</table>
タグと<button type="button" class="btn pull-right btn-lg btn-default">
タグの間に以下コードを追記します。
app/views/place/show.html.erb 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="map_container">
<div id="map" class="map_canvas"></div>
</div>
<script type="text/javascript">
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers( <%= raw @hash . to_json %> );
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
handler.getMap().setZoom(12);
handler.map.centerOn(marker);
});
</script>
無事、Google Mapsが表示されました!
ここまでのサンプルアプリケーション
google_placeとgmaps4railsライブラリを使った位置情報検索サンプルアプリケーション