19
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RUNTEQAdvent Calendar 2021

Day 10

道を文字列に変換して保存する方法【Google Map JavaScript API, Ruby】

Last updated at Posted at 2021-12-09

概要

本記事は RUNTEQアドベントカレンダー 2021 の10日目の記事となります!

自分はデリバリーのアルバイトをしているのですが、自分の使っている近道や裏道を保存して誰かと共有できたら良いなあと思いました。

そこで、現在勉強しているRuby,Ruby on Railsを使って道を保存してGoogle Mapに表示させるお試しの機能を作ってみました。

タイトルなし.gif

記事を作成した理由

Qiitaの記事にもGoogle mapについての記事はたくさんありますが、道(一連の座標)を保存するやり方についての記事は無いような気がしたので、この記事を作成しました。

初学者ですので間違いがあるかもしれませんが、暖かい目で見て頂ければありがたいです。

この記事で伝えたいこと

  • Google Map JavaScript APIのざっくりとした使い方
  • 道を1つの文字列として保存できるgemがあること(←ココを一番伝えたい!)

前提環境

MacOS: Catalina
ruby: 3.0.0
ruby on rails: 6.1.4

使用技術

  • Google Maps Platform API(Map JavaScript API)
  • Ruby gem for Google Maps APIs

Google Maps Platform API(Map JavaScript API)

Google Maps Platform API | Google Developers
googleマップをwebサイトやスマホアプリに表示・カスタマイズするための様々なAPI等を提供しているサービスです。

料金が従量課金制なので、大量のアクセスが見込まれるサイトに使用する場合は注意が必要です。

今回はWebサービスで使いたいのでMap JavaScript APIを使います。

これを使ってマップ上にマーカーとポリラインを作成します。ポリラインとは連続した線で表された線データのことです。

Google Maps APIのためのRuby gem

Ruby gem for Google Maps APIs

READMEに使い方が丁寧に書いてあり、とても親切だと思いました。
Google Maps APIの機能を持ったメソッドが使えるようになります。

今回使うのは、一連の座標を文字列として保存できるメソッドです。
Polyline encoder/decoder

これがめちゃ便利だなと思いました。

道をどうやって保存すれば良いのかと思った時に、いくつかの座標を丸ごと保存するのかなー?
でもそうなるとカラムのデータ型は何なるのかな?json型?でも1つのカラムに複数のデータが入るのはよくないよなあ?
なんて思っていたのですが、この機能を使えば座標を文字列として保存できます。

ポリラインの座標を文字列に変換例

path = [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]]
encoded_path = GoogleMapsService::Polyline.encode(path)
=> "_p~iF~ps|U_ulLnnqC_mqNvxq`@"

文字列をポリラインの座標に変換例

encoded_path = '_p~iF~ps|U_ulLnnqC_mqNvxq`@'
path = GoogleMapsService::Polyline.decode(encoded_path)
=> [{:lat=>38.5, :lng=>-120.2}, {:lat=>40.7, :lng=>-120.95}, {:lat=>43.252, :lng=>-126.45300000000002}]

マジすげえ

機能自体はruby gemだけでなくnpmでも用意されているようです。
https://github.com/googlemaps/js-polyline-codec

実装した流れ

gemのインストール

gem 'google_maps_service'
gem 'gon' # rubyの変数をjsに渡すために必要

コード

ポリライン、マーカの作成

new.html.erb
<input type="button" id="save" value="経路を保存する">
<div id="result"></div>
<div id="map"></div>
new.js
// 必要なリンク
<script src="https://www.gstatic.com/external_hosted/jquery2.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=自分のAPIKey"></script>

<script>
var poly;
var map;
var save = document.getElementById("save");
var pathValues = [];

function initMap() {
  // マップを作成。中心はアメリカのシカゴ
  map = new google.maps.Map(document.getElementById("map"), {
    zoom: 7,
    center: { lat: 41.879, lng: -87.624 },
  });
  // ポリラインを引く
  poly = new google.maps.Polyline({
    strokeColor: "#000000",
    strokeOpacity: 0.5,
    strokeWeight: 10,
  });
  poly.setMap(map);
  map.addListener("click", addLatLng);
}

// 地図上でのクリックイベントを処理し、Polylineに新しいポイントを追加する
function addLatLng(event) {
  const path = poly.getPath();

  path.push(event.latLng);
  // ポリライン上に新しいマーカーを追加する
  new google.maps.Marker({
    position: event.latLng,
    map: map,
  });
}

  // 座標の配列をPostリクエストをAjaxで送信する
 save.addEventListener("click",function() {
    var path = poly.getPath();
    for (var i = 0; i < path.getLength(); i++) {
        pathValues.push(path.getAt(i));
    }
    $('#result').text('経路を保存しました!');
    $.ajax({
    url: '/maps',
    type: 'POST',
    dataType: 'json',
    data: {
        data: JSON.stringify(pathValues),
    },
    })
  });

$(window).load(initMap);

自分のAPIKeyを取得する方法を記載した公式リンクはこちら
https://cloud.google.com/docs/authentication/api-keys?hl=ja&visit_id=637746662897584655-1175073629&rd=1

元にした公式デモコードはこちら
https://developers.google.com/maps/documentation/javascript/examples/polyline-complex

postリクエストを受け取って保存する

一連の座標を文字列に変更し、mapsテーブルに保存しています。(テーブルの名前はpathsの方がわかりやすかったですね(^^;)

maps#create
 def create
    pathValues_json_data =  params[:data]
    # jsonデータをrubyオブジェクトに変換
    pathValues = JSON.parse(pathValues_json_data)
    polylines = []
    latlng = []

    pathValues.each do |path|
      latlng = [path["lat"],path"lng"]]
      polylines.push(latlng)
    end
    # ポリラインの座標を文字列に変換
    encoded_path = GoogleMapsService::Polyline.encode(polylines)

    path = Map.new(encorded_path: encoded_path)
    path.save
  end

マイグレーションファイル(保存先のテーブル)

class CreateMaps < ActiveRecord::Migration[6.1]
  def change
    create_table :maps do |t|
      # カラムに文字列型を指定
      t.string :encorded_path
    end
  end
end

データを取り出す

maps#index
  def index
    paths = Map.all
    pathValues = []
    maps.each do |map|
     # 文字列をポリラインの座標に変換
      path = GoogleMapsService::Polyline.decode(map.encorded_path)
      pathValues.push(path)
    end
    # rubyの変数をjsに渡すためにgonを使う
    gon.pathValues = pathValues
  end
index.js
var poly;
var map;
var pathValues = gon.pathValues

function initMap() {
  map = new google.maps.Map(document.getElementById("map"), {
    zoom: 7,
    center: { lat: 41.879, lng: -87.624 }, // Center the map on Chicago, USA.
  });

  for (var i = 0; i < pathValues.length; i++) {
      var path = pathValues[i]
      drawPolyline(path);
  }
}


function drawPolyline(path) {
  poly = new google.maps.Polyline({
    path: path,
    strokeColor: "#000000",
    strokeOpacity: 0.5,
    strokeWeight: 10,
  });
  poly.setMap(map);
}
$(window).load(initMap);

まとめ

改めてコードを見返すと、テーブルの名前はmapsではなかったですね(^^;
その他のコードももっとリファクタできたかもしれません。

今回の機能を作った収穫は座標を文字列で変換できる事を知ることができたことは良かったです。
スマホやGPS受信機を使って一連の座標を取得できればもっと良いなと思いました(^○^)

間違い等あればぜひご指摘いただければ幸いです。

19
1
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
19
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?