LoginSignup
14
14

More than 5 years have passed since last update.

[Rails]google mapのAPIを使って地図を表示させる方法

Last updated at Posted at 2018-11-28

この記事の目的

自分の投稿したワード(住所など)をフォームに入れて、google mapで検索できるようにする。(今回は例としてカフェの名前を検索できるようにする)

環境

ruby 2.5.3
rails 5.2.1

(筆者はbundlerを使ってrailsはローカルでインストールして使用が、今回はグローバルでインストールした時のコマンドの打ち方で書く)

googleAPIを取得

https://console.developers.google.com/?hl=ja
このホームページに登録してAPIを取得する(調べれば出るので今回はカット)

アプリ作成

rails new cafe_map

モデルは以下のようにする。
(id,created_at,update_atカラムは省略)

・topicモデル(contentカラムはなくても問題ない)

カラム名  型
cafe_name string
content string

・mapモデル

カラム名  型
topic_id integer
latitude float
longitude float
address string

latitudeは経度。longitudeは緯度という意味。
モデルの作り方はそれぞれ 

rails g model topic cafe_mame:string content:string
rails g scaffold map address:string latitude:float longitude:float

mapモデルはscaffoldを使って作成する。

modelのアソシエーション

topic.rb
has_many :maps
model.rb
belongs_to :topic

geocoderをインストール

Gemfileに以下を追加。

gem 'geocoder'

geocoderは住所から緯度と経度を割り出して取得してくれる。
bundle installを忘れずに。

geocoderを使えるようにするため以下を追加。

map.rb
geocoded_by :address
after_validation :geocode, if: :address_changed?

これで住所(address)からlatitudeとlongitudeを作成できる。

ルート、コントローラー、ビュー

流れとして、
topic/newでtopicを作成 => topic/:idでそのtopicを表示 => topic/topic_id/maps/newでtopicに紐づいたmapを作成 => /topics/:topic_id/maps/:idでmapを表示
という風に作る。

・ルート

resources :topics do
    resources :maps
  end

ネストして設定。これでtopicのidは、mapのtopic_idに紐付く。(簡単に言えば、topic.id == map.topic_id)

・コントローラー

topics_controller.rb
def index
    @topics = Topic.all
  end

  def new
    @topic = Topic.new
  end

  def show
    @topic = Topic.find(params[:id])
  end

  def create
    @topic = current_user.topics.new(topic_params)
    if @topic.save
      redirect_to root_path
    else
      render 'topic/new'
    end
  end

  private
    def topic_params
      params.require(:topic).permit(:cafe_name,:content)
    end

topicは投稿、一覧の基本的な部分のみ作成。
次はmap。

maps_controller.rb
  before_action :set_map, only: [:show, :edit, :update, :destroy]
  before_action :set_topic, only: [:index, :new, :edit, :create, :destroy]
  # GET /topics/:topic_id/maps
  # GET /maps.json
  def index
    @maps = @topic.maps.all
  end

  # GET /topics/:topic_id/maps/:id
  # GET /maps/1.json
  def show
  end

  # GET /topics/:topic_id/maps/new
  def new
    @map = Map.new
  end

  # GET /topics/:topic_id/maps/:id/edit
  def edit
  end

  # POST /topics/:topic_id/maps
  # POST /maps.json
  def create
    @map = @topic.maps.new(map_params)
    respond_to do |format|
      if @map.save
        format.html { redirect_to  topic_map_path(topic_id: @map.topic_id,id: @map.id), notice: 'Map was successfully created.' } #:topic_idと:idを2つの引数でそれぞれ指定
        format.json { render :show, status: :created, location: topic_map_path(topic_id: @map.topic_id,id: @map.id) }
      else
        format.html { render :new }
        format.json { render json: @map.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /topics/:topic_id/maps/:id
  # PATCH/PUT /maps/1.json
  def update
    respond_to do |format|
      if @map.update(map_params)
        format.html { redirect_to topic_map_path(topic_id: @map.topic_id,id: @map.id), notice: 'Map was successfully updated.' }
        format.json { render :show, status: :ok, location: topic_map_path(topic_id: @map.topic_id,id: @map.id) }
      else
        format.html { render :edit }
        format.json { render json: @map.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /topics/:topic_id/maps/:id
  # DELETE /maps/1.json
  def destroy
    @map.destroy
    respond_to do |format|
      format.html { redirect_to topic_maps_path(topic_id: @topic.id), notice: 'Map was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_map
      @map = Map.find(params[:id])
    end

    def set_topic
      @topic = Topic.find_by(id: params[:topic_id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def map_params
      params.require(:map).permit(:address,:latitude,:longitude,:topic_id)
    end

topicに紐づいたmapを作成、表示、編集、削除などできるように設定。

最後にビュー。
scaffoldでほとんど生成されているが多少変更する。

maps.show.html.rb

<script type="text/javascript">
    function initMap() {

        var test = {lat: <%= @map.latitude %>, lng: <%= @map.longitude %>};
        var map = new google.maps.Map(document.getElementById('map'), {
          center: test,
          zoom: 15
        });

        var transitLayer = new google.maps.TransitLayer();
        transitLayer.setMap(map);

        var contentString = '住所:<%= @map.address %>';
        var infowindow = new google.maps.InfoWindow({
            content: contentString
        });

        var marker = new google.maps.Marker({
            position:test,
            map: map,
            title: contentString
        });

        marker.addListener('click', function() {
            infowindow.open(map, marker);
        });
    }
</script>
<script async defer
        src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=[google map apiのキー]&callback=initMap">
</script>

<p id="notice"><%= notice %></p>

<p>
  <strong>Address:</strong>
  <%= @map.address %>
</p>

<p>
  <strong>Latitude:</strong>
  <%= @map.latitude %>
</p>

<p>
  <strong>Longitude:</strong>
  <%= @map.longitude %>
</p>

<div id="map"></div>

<%= link_to 'Edit', edit_topic_map_path(topic_id: @map.topic_id,id: @map.id) %> |
<%= link_to 'Index', topic_maps_path %>

[google map apiのキー ]のところに取得してapiのキーを書く。google mapが表示されるのは、最後の方の
<div id="map"></div>

で表示されている。cssで
#map{
height: 400px;
width: 400px;
}

などと設定しないと地図が表示されないので注意。(%を使って高さや幅を指定すると、親ブロックが高さや横を指定してないと表示されないので、最初はpxで設定するのがおすすめ。)

他のmapsのビューで、link_toの設定でエラーが出るが、rake routes で確認して直せば良いので省略。

最後にformを変更する。

mapのnewアクションとeditアクションの時のフォームは、renderで表示している。この時form_forは二つの引数(@topic@map)を持ってきてcreateとupdateで分けるようにした。(form_forは引数が既存かどうかでアクションを振り分ける)

_form.html.erb
<%= form_for [@topic,@map] do |form| %>
  <% if @map.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@map.errors.count, "error") %> prohibited this map from being saved:</h2>

      <ul>
      <% @map.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <% if @map.address == nil %>
      <%= form.label :address %>
      <%= form.text_field :address,:value => @topic.cafe_name %>
    <% else %>
      <%= form.label :address %>
      <%= form.text_field :address,:value => @map.address %>
    <% end %>
  </div>


  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

text_fieldにvalueを上記のように設定することで、新規の時はカフェの名前を入力されたままの状態にする。

これで完成。

参考記事

こちらの記事を参考に作りました。

また何か間違いや質問があればぜひお願いします。

14
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
14