LoginSignup
8
11

More than 3 years have passed since last update.

GoogleMapsAPI, GeocodingAPIを使って投稿内容から緯度経度情報を取得し地図を表示させる (and 地図から投稿を検索できるようにする)

Last updated at Posted at 2020-09-20

実務未経験者です。
ポートフォリオに組み込んだGoogleMapsAPI関連について、作っておきながら面談で全く説明できなかったので、
頭を整理するために流れをまとめたものを書いてみます。

アプリ: ラーメン屋の写真や情報を友達と共有できるSNS
Ruby: 2.6.5
Rails: 5.2.0
前提: GoogleCloudPlatformでAPIキーを取得すること

投稿内容から緯度経度情報を保存

postモデルでbefore_save時 (投稿保存する時)にgeocodeメソッドを呼ぶ。
geocodeメソッドでは、バリデーションの後にGeocoding APIを使って
店名と最寄駅情報から緯度経度を取得、緯度経度カラムに保存させている。

shop_nameカラム: 店名情報
nearestカラム: 最寄駅情報 (とてもわかりにくい)
latitudeカラム: 緯度情報
longitudeカラム: 経度情報

app/models/post.rb
class Post < ApplicationRecord
  ~
  ~
  before_save :geocode
  ~
  ~

  private
    def geocode
      uri = URI.escape(
        "https://maps.googleapis.com/maps/api/geocode/json?address="
        + self.shop_name 
        + " "
        + self.nearest
        + "&key=" + Rails.application.credentials.GCP[:API_KEY] # APIキーを入れてください
      )
      res = HTTP.get(uri).to_s
      response = JSON.parse(res)
      if response["status"] == "OK"
        self.latitude = response["results"][0]["geometry"]["location"]["lat"]
        self.longitude = response["results"][0]["geometry"]["location"]["lng"]
      else
        self.latitude = 1
        self.longitude = 1
      end
    end
end

geocordingAPIを使って緯度経度情報を取得するには、下記のURIを用います。

https://maps.googleapis.com/maps/api/geocode/json?address=緯度経度情報を調べたい住所など&key=APIキー

投稿詳細画面

app/views/posts/show.html.erb
・
・
<div class="post_map">
 <div id="map"></div> <!-- cssでwidth, heightを指定しないと地図が表示されません -->
</div>
・
・
<script>
var latLng;
var marker;
var infoWindow;
function initMap() {
  latLng = {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>};
  map = new google.maps.Map(document.getElementById('map'), {
    center: latLng,
    zoom: 15,
    mapTypeControl: false,
    streetViewControl: false
  });
  marker = new google.maps.Marker({
    position: latLng,
    map: map
  });
  infoWindow = new google.maps.InfoWindow({
    content: "<a href='http://www.google.com/search?q=<%= @post.shop_name %> <%= @post.nearest %>' target='_blank' style='color: #00f;'><%= @post.shop_name %> を検索</a><br><br><a href='http://www.google.com/search?q=<%= @post.shop_name %> ラーメン&tbm=isch' target='_blank'>画像検索 by google</a>"
  });
  marker.addListener('click', function() {
    infoWindow.open(map, marker);
  });
}
initMap();
</script>
app/views/layouts/application.html.erb(レイアウトファイル)
<!DOCTYPE html>
<html>
  <head>
    <title>・・・・・・</title>
    <!-- APIキーを読み込んでいます -->
    <script src="https://maps.googleapis.com/maps/api/js?key=<%= Rails.application.credentials.GCP[:API_KEY] %>&callback=initMap" defer></script>
    ・
    ・
    ・
  </head>
  <body>
    ・
    ・
    <%= yield %></body>
</html>

スクリーンショット 2020-09-19 17.44.09.jpg

地図から投稿を検索できるようにする

スクリーンショット 2020-09-20 19.51.29.jpg

form_with と radio_button で、どのユーザーの投稿を地図に表示させるか選択する

mapsコントローラのmapアクションで、投稿内容を含む変数@postsを定義
(投稿内容は.to_jsonでjson形式に変換、respond_to doで返す)

アクション名と同じmap.js.erbがRailsによって自動的に開かれ、@postsを受け取り、
地図とマーカー (投稿内容)をセット

app/views/maps/index.html.erb

<%= form_with url: map_request_path, method: :get do |f| %>
  <%= f.radio_button :posts, "all_user", checked: true %>全てのユーザーの投稿
  <%= f.radio_button :posts, "following", disabled: current_user.nil? %>自分とフォロー中のユーザーの投稿
  <%= f.radio_button :posts, "current_user", disabled: current_user.nil? %>自分の投稿
  <%= f.submit '投稿されたお店を表示', class: "btn btn-primary" %>
  <button type="button" class="btn btn-success current_position" onclick="getLocation()">
    地図を現在地周辺に切り替える
  </button>
<% end %>

<div id="map_index"></div> <!-- cssでwidth, heightを指定しないと表示されません -->
<script>
  var map
  function initMap(){
    map = new google.maps.Map(document.getElementById('map_index'), {
    center: {lat: 37.67229496806523, lng: 137.88838989062504}, // 地図の中心を指定
    zoom: 6, // 地図のズームを指定
    mapTypeControl: false,
    streetViewControl: false
    });
  }
  function getLocation(){ // 現在地周辺に地図を移動させる
    navigator.geolocation.getCurrentPosition(
      function(position) {
        var latitude = position.coords.latitude;
        var longitude = position.coords.longitude;
        var latlng = new google.maps.LatLng(latitude, longitude);
        map.setCenter(latlng);
        map.setZoom(12);
      }
    );
  }
  initMap();
</script>
config/routes.rb
Rails.application.routes.draw do
  ~
  ~
  resources :maps, only: [:index]
  get '/map_request', to: 'maps#map', as: 'map_request'
end
app/controllers/maps_controller.rb
class MapsController < ApplicationController
  def index
  end

  def map
    if params[:posts] == "all_user"
      @posts = Post.all.to_json.html_safe
    elsif params[:posts] == "current_user"
      @posts = current_user.posts.to_json.html_safe
    elsif params[:posts] == "following"
      @posts = current_user.feed.to_json.html_safe
    end
    respond_to do |format|
      format.js { @posts }
    end
  end
end
app/views/maps/map.js.erb
var posts = <%= @posts %>;
var marker = [];
var infoWindow = [];
function initMap(){
  for (let i = 0; i < posts.length; i++) {
    marker[i] = new google.maps.Marker({
      position: {lat: parseFloat(posts[i].latitude), lng: parseFloat(posts[i].longitude)},
      map: map,
      animation: google.maps.Animation.DROP
    });
    infoWindow[i] = new google.maps.InfoWindow({
      content: posts[i].shop_name + "<br>" + "<a href='/posts/" + posts[i].id + "' target='_blank' style='color: #00f;'>このお店に関する投稿を表示</a>"
    });
    markerEvent(i);
  }
};
initMap();

function markerEvent(i) {
  marker[i].addListener('click', function() { // マーカーをクリックしたとき
    infoWindow[i].open(map, marker[i]); // 吹き出しの表示
  });
}

こんな感じ

ezgif.com-video-to-gif (1).gif

参考にさせていただきました

Rails5でGoogleMapを表示してみるまで
【Google MAP】名称から場所を検索・特定する

8
11
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
8
11