LoginSignup
5
3

More than 1 year has passed since last update.

【Rails】HERE Maps APIで地図機能を作ってみる

Last updated at Posted at 2022-12-28

概要

今回、無料でMap機能が使えるかつGoogleMapのAPIよりすごい!という話を
以下の記事で見かけたので、Railsで実装してみました!
投稿機能を実装していれば実装できる機能なので、ぜひ遊んでみてください〜!

投稿機能の実装が必要です

アカウント登録とプロジェクトの作成

以下の記事にある、Developerの登録を参考に進めましょう!

上記の記事のようにプロジェクトの作成が終わったら
アプリのディレクトリ直下(Gemfileと同じ階層)に.envという名前のファイルを作成し
APIキーを.envに入れます。

.env
HERE_MAP_API_KEY=取得したAPIキー

Gemfileの変更

まず、Gemfileでgeocoderというgemを追加します。
geocoderは地名で緯度・経度を求めてくれるGemで
後々地名を入れるだけで自動でマップを表示できるようにするために使います!

dotenv-railsはRailsで環境変数(セキュリティ的に見せてはいけないもの)を入れる
.envファイルを扱えるようにするものです!

Gemfile
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にしてから行いましょう。

db/migrate/~~~_create_posts.rb
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(地名から緯度・経度を求める機能)を有効にする
カラム名の指定とバリデーションをつけておきましょう。

models/post.rb
class Post < ApplicationRecord
+  geocoded_by :address
+  after_validation :geocode
end

Controllerの変更

コントローラーでは、主にストロングパラメータの変更のみで
新たに追加したaddress(地名)のカラムを受け取れるようにします。

controllers/posts_controller.rb
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の変更

新規投稿ページの編集

新規投稿を行うページでは地名を受け取れるように変更します!

posts/new.html.erb
<%= 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(詳細)ページで表示してみようと思います!

posts/show.html.erb
<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↓で勉強してみてください〜

実装画面

以下の画像のように表示されます!
image.png

東京駅のバージョン
若干ズレてますね、、、😇
やはり、公式が用意しているgeocoderの方がいいかもしれないです🤔
image.png

表示する場所の精度を上げる方法

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) => {})

実装画面

これで実装すると、、、
image.png

おおお!東京駅のど真ん中にマーカーが刺さりました!
成功です!
こんな感じでautosuggestを利用すると、表示の精度が上がるようです✨

終わりに

本記事はいかがでしたでしょうか?
GoogleのMapsAPIよりも簡単かつ無料で地図の表示ができたのではないかな、と思います!
HEREのMaps APIはもっとカスタマイズできますし、経路の計算もできるので
ぜひ遊んでみてください〜〜🙌

参考文献

5
3
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
5
3