概要
今回、無料でMap機能が使えるかつGoogleMapのAPIよりすごい!という話を
以下の記事で見かけたので、Railsで実装してみました!
投稿機能を実装していれば実装できる機能なので、ぜひ遊んでみてください〜!
投稿機能の実装が必要です
アカウント登録とプロジェクトの作成
以下の記事にある、Developerの登録
を参考に進めましょう!
上記の記事のようにプロジェクトの作成が終わったら
アプリのディレクトリ直下(Gemfileと同じ階層)に.env
という名前のファイルを作成し
APIキーを.env
に入れます。
HERE_MAP_API_KEY=取得したAPIキー
Gemfileの変更
まず、Gemfileでgeocoder
というgemを追加します。
geocoder
は地名で緯度・経度を求めてくれるGemで
後々地名を入れるだけで自動でマップを表示できるようにするために使います!
dotenv-rails
はRailsで環境変数(セキュリティ的に見せてはいけないもの)を入れる
.env
ファイルを扱えるようにするものです!
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.0.4'
# ~~~長いので省略~~~
gem 'geocoder'
gem 'dotenv-rails'
編集後、以下のコマンドを実行しましょう!
bundle install
gemのgeocoderと相性が悪くいため、公式ガイドにある方のgeocoderの方がいいかもしれません
Migrationファイルの変更
続いて、住所、緯度、経度のカラムを追加します!
マイグレーションファイルの編集はrails db:rollback
で
該当のマイグレーションファイルをDownにしてから行いましょう。
class CreatePosts < ActiveRecord::Migration[6.1]
def change
create_table :posts do |t|
t.string :body
+ t.string :address
+ t.float :latitude
+ t.float :longitude
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
編集後、以下のコマンドでDBに反映させましょう!
rails db:migrate
Modelの変更
Postモデルにて、geocode(地名から緯度・経度を求める機能)を有効にする
カラム名の指定とバリデーションをつけておきましょう。
class Post < ApplicationRecord
+ geocoded_by :address
+ after_validation :geocode
end
Controllerの変更
コントローラーでは、主にストロングパラメータの変更のみで
新たに追加したaddress
(地名)のカラムを受け取れるようにします。
class PostsController < ApplicationController
# 変更がないため省略~~~
private
def set_q
@q = Post.ransack(params[:q])
end
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = Post.find(params[:id])
end
# Only allow a list of trusted parameters through.
def post_params
+ params.require(:post).permit(:body, :address)
end
end
Viewの変更
新規投稿ページの編集
新規投稿を行うページでは地名を受け取れるように変更します!
<%= form_with(model: post) do |form| %>
<div class="field">
<%= form.label :body, "文章" %>
<%= form.text_field :body %>
</div>
+ <div class="field">
+ <%= form.label :address, "場所" %>
+ <%= form.text_field :address %>
+ </div>
<div class="actions">
<%= form.submit "投稿する" %>
</div>
<% end %>
Mapを表示するページの編集
今回は、Show(詳細)ページで表示してみようと思います!
<p>
<strong>Body:</strong>
<%= @post.body %>
</p>
<% if current_user.id == @post.user_id %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<% end %>
<%= link_to 'Back', posts_path %>
<div style="width: 50vw; height: 50vh" id="map"></div>
<script>
var platform = new H.service.Platform({
'apikey': '<%= ENV['HERE_MAP_API_KEY'] %>'
});
// configure an OMV service to use the `core` enpoint
var omvService = platform.getOMVService({path: 'v2/vectortiles/core/mc'});
var baseUrl = 'https://js.api.here.com/v3/3.1/styles/omv/oslo/japan/';
// create a Japan specific style
var style = new H.map.Style(`${baseUrl}normal.day.yaml`, baseUrl);
// instantiate provider and layer for the basemap
var omvProvider = new H.service.omv.Provider(omvService, style);
var omvlayer = new H.map.layer.TileLayer(omvProvider);
var position = {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>}
// instantiate (and display) a map:
var map = new H.Map(
document.getElementById('map'),
omvlayer,
{
zoom: 17,
center: position
});
map.addObject(new H.map.Marker(position));
// make map interactive
window.addEventListener('resize', () => map.getViewPort().resize());
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
</script>
解説
初期化
var platform = new H.service.Platform({
'apikey': '<%= ENV['HERE_MAP_API_KEY'] %>'
});
// configure an OMV service to use the `core` enpoint
var omvService = platform.getOMVService({path: 'v2/vectortiles/core/mc'});
var baseUrl = 'https://js.api.here.com/v3/3.1/styles/omv/oslo/japan/';
// create a Japan specific style
var style = new H.map.Style(`${baseUrl}normal.day.yaml`, baseUrl);
// instantiate provider and layer for the basemap
var omvProvider = new H.service.omv.Provider(omvService, style);
var omvlayer = new H.map.layer.TileLayer(omvProvider);
ここではAPIを利用する際に必要な初期化などを行なっています。
'apikey': '<%= ENV['HERE_MAP_API_KEY'] %>'
は
.env
ファイルに記述したAPIキーを取得します。
その他はテンプレートのようなものなので、あまり気にしなくて良いです!
マップの作成部分
var position = {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>}
// instantiate (and display) a map:
var map = new H.Map(
document.getElementById('map'),
omvlayer,
{
zoom: 17,
center: position
});
map.addObject(new H.map.Marker(position));
var position = {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>}
では、Geocoderで計算された緯度・経度を入れています。
JavaScriptのコードにRails(DB)の情報を入れたいときは、このような記述をします。
{ zoom: 17, center: position }
はマップを表示する際のオプションです。
zoom
ではどの縮尺で表示するか、center
はマップの中心位置の指定です。
map.addObject(new H.map.Marker(position));
では
マップにマーカーを表示する際に使います。
マーカーがいらない方は消していただいても構いません〜!
実行部分
window.addEventListener('resize', () => map.getViewPort().resize());
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
このコードは、今まで作成した設定などを実行する部分です。
addEventListner
が気になる方はMDN↓で勉強してみてください〜
実装画面
東京駅のバージョン
若干ズレてますね、、、😇
やはり、公式が用意しているgeocoderの方がいいかもしれないです🤔
表示する場所の精度を上げる方法
HERE Maps APIにはAutoSuggest
という、スペルミスなどに対応したgeocode機能があります。
そちらを利用します!!
コード
var platform = new H.service.Platform({
'apikey': '<%= ENV['HERE_MAP_API_KEY'] %>'
});
// configure an OMV service to use the `core` enpoint
var omvService = platform.getOMVService({path: 'v2/vectortiles/core/mc'});
var baseUrl = 'https://js.api.here.com/v3/3.1/styles/omv/oslo/japan/';
// create a Japan specific style
var style = new H.map.Style(`${baseUrl}normal.day.yaml`, baseUrl);
// instantiate provider and layer for the basemap
var omvProvider = new H.service.omv.Provider(omvService, style);
var omvlayer = new H.map.layer.TileLayer(omvProvider);
var position = {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>}
// Get an instance of the geocoding service:
var service = platform.getSearchService();
// Call the "autosuggest" method with the search parameters,
// the callback and an error callback function (called if a
// communication error occurs):
service.autosuggest({
// Search query
q: '<%= @post.address %>',
// Center of the search context
at: '<%= @post.latitude %>,<%= @post.longitude %>'
}, (result) => {
let center = result.items[0].position
// instantiate (and display) a map:
var map = new H.Map(
document.getElementById('map'),
omvlayer,
{
zoom: 17,
center: center ?? position
});
map.addObject(new H.map.Marker(center))
// make map interactive
window.addEventListener('resize', () => map.getViewPort().resize());
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
}, (error) => {
console.error(error)
});
解説
引数
autosuggest
は、引数に地名と緯度・経度を入れることで
最も近い候補を5つ提供してくれます。
service.autosuggest({
// Search query
q: '<%= @post.address %>',
// Center of the search context
at: '<%= @post.latitude %>,<%= @post.longitude %>'
},
動作
動作としては、成功した時に1つ目の関数を
失敗した時に2つ目の関数を実行します。
service.autosuggest({
// Search query
q: '<%= @post.address %>',
// Center of the search context
at: '<%= @post.latitude %>,<%= @post.longitude %>'
},
// 成功時に動作
(res) => {},
// 失敗時に動作
(err) => {})
実装画面
おおお!東京駅のど真ん中にマーカーが刺さりました!
成功です!
こんな感じでautosuggestを利用すると、表示の精度が上がるようです✨
終わりに
本記事はいかがでしたでしょうか?
GoogleのMapsAPIよりも簡単かつ無料で地図の表示ができたのではないかな、と思います!
HEREのMaps APIはもっとカスタマイズできますし、経路の計算もできるので
ぜひ遊んでみてください〜〜🙌
参考文献