jsフレームワークstimulusでyahoo地図を操作してみた(その四、最終回)


やったこと、やる予定のこと

0. railsアプリへのstimulus適用

1. stimulusでyahoo地図を表示

2. 表示した地図上で、指定座標に移動

3. 住所から座標を取得し、それをDBに登録(前回)

4. 「3」で登録した情報を選択し、yahoo地図上でその場所に移動(←今ここ)

だいぶ間が空いてしまいましたが、最終回。
一覧画面にて、
【「3」で登録した座標情報を選択→その地図の場所に移動】
をできるようにします。

まずは、一覧画面にて地図を表示

今回は、作成するjsファイルの名称は「index_map_controller.js」にしました
(この場合、html側の名称は「index-map」にする必要あり……詳しくはこちらを参照)。

app/views/maps/index.html.erb
<!-- 「data-controller」開始 -->
<div data-controller="index-map">

  <!-- 中略 -->

  </table><!-- ←scaffoldで自動作成される「table」の終了 -->

  <!-- これで地図を表示 -->
  <div id="map" style="width:800px; height:400px"  data-target="index-map.map" ></div>

</div>
<!-- 「data-controller」終了 -->

前回・前々回と同様の、地図表示。
ただし今回は、
 a.地図の中心点表示なし
 b.縮尺変更用のスライドバー表示
 c.マウスのスクロールホイールでも地図縮尺変更可能
としています。

app/javascript/controllers/index_map_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "map"]

  initialize() {

       this.map = new Y.Map(this.mapTarget.id,{configure : {

     // ↓【c.マウスのスクロールホイールでも地図縮尺変更可能】
         scrollWheelZoom : true

       }});

       this.map.drawMap(new Y.LatLng(0, 0), 17, Y.LayerSetId.NORMAL);

    // ↓【a.地図の中心点表示なし_1】
       // var center = new Y.CenterMarkControl

       var control = new Y.LayerSetControl();

    // ↓【a.地図の中心点表示なし_2】
       // this.map.addControl(center);

       this.map.addControl(control);

    // ↓【b.縮尺変更用のスライドバー表示】
       var sliderzoom = new Y.SliderZoomControlVertical();
       this.map.addControl(sliderzoom);

  }
}

このへんの細かな地図表示方法については、こちら
……とYOLP公式頁を覗いてみたら、「API提供終了のお知らせ」って、マジか!!!

…………………………………………まあ、気を取り直して。

次に、一覧画面のレコードごとに「押したらその場所に移動するボタン」を設置します

app/views/maps/index.html.erb

  <tbody>
    <% @maps.each do |map| %>
      <tr>
        <!-- 中略 -->
        <td><%= link_to 'Destroy', map, method: :delete, data: { confirm: 'Are you sure?' } %></td>

        <!-- ↓scaffoldで自動作成されている「レコード削除」の後ろに、追記 -->
        <!--  →「@maps」に格納されているレコード数だけ、ボタンも作られる。 -->
        <td><input type="button" value="移動" data-action="click->index-map#move" data-lat = <%= map.latitude %>  data-lon = <%= map.longitude %> ></td>

      </tr>
    <% end %>
  </tbody>

ボタンの「data-action」設定
  →クリックしたらjs側で「move」メソッドを走らせる

また↑の「move」メソッド内で、そのレコードの位置情報(経度、緯度)を使いたいので、
それも「data-〇〇」形式でボタンに持たせてやります(この場合は、「data-lat」と「data-lon」)。

最後に、js側で地図を移動する動き=「move」メソッドを記述する。

これは、かんたん
……html側にて「data-〇〇=」で設定した値は、js側では「el(アクション対象を引数として取得).target.dataset.〇〇」で取得できるので、

app/javascript/controllers/index_map_controller.js
  move(el){
    var current_location = new Y.LatLng(el.target.dataset.lat,el.target.dataset.lon);
    this.map.panTo(current_location, true);
  }

取得した緯度、経度を「current_location」に放り込み、それをmapの「panTo」に与えてやればいいわけです(この場合は「current_location」なんて作らずに、一行で記述しても良かったかも)。

これで、

「移動」ボタンを押すとその場所に地図上を移動できるようになります。

おまけで、ピンを立ててみる。

移動できるのはいいけど、そのレコードの位置情報がどこを指しているのかが若干分かりにくい
──ということで、登録されているレコードの場所にははじめ(画面ロード時)から📍が立っていることにしてみます。

画面ロード時に📍を立てるには、【js側のinitializeメソッド内】で【全レコードの位置情報】を取得する必要があります。

そこでhtml側では「移動」ボタンに、

app/views/maps/index.html.erb
<td><input type="button" value="移動" data-action="click->index-map#move" data-lat = <%= map.latitude %>  data-lon = <%= map.longitude %> <%# これ追記→ %>data-target="index-map.pin" <%# ←これ追記 %> > ></td>

と、【お前は『pin』というdata-targetだ!】という記述を追加してやります。

ただ、この「移動」ボタンはレコード数だけあるので、【『pin』というdata-target】もレコード数だけ存在します。

……さっきも出した

 ↑の場合、
「大洗」「銚子」「焼津」「境港」それぞれのボタンが【『pin』というdata-target】となっています。

この複数ある【target】は、js側では【targets】として纏めて取得することができます。

↓は画面ロード時、【js側のinitializeメソッド】の途中でストップさせたコンソール画像
  登録されているレコードは、上に貼った画像と同じ「大洗」「銚子」「焼津」「境港」です。

this.pinTargets → 4つのボタン全てをターゲットとして取得
this.pinTargets[0] →そのうち1つ目、「大洗」のボタンをターゲットとして取得

「座標情報を持ったターゲット」の取得目処がついたので、
あとは取得したターゲット分だけ、地図に📍を立ててあげます。

app/javascript/controllers/index_map_controller.js
  initialize(){

    //地図を表示する記述……中略

    var map_box = this.map;

    this.pinTargets.forEach(function(pin){
      var current_location = new Y.LatLng(pin.dataset.lat,pin.dataset.lon);
      var marker = new Y.Marker(current_location);
      map_box.addFeature(marker);
    });
  }

これで
・地図の「登録された場所」全てにピンが立ち、
・レコードを選択すれば「その登録地点に地図が移動」
するようになります。

サンプルコードはscaffoldで作った一覧なので見栄えがいまいちですが、レイアウトを整えて画像登録と組み合わせれば

  ● 東海道五十三次

  ● シン・ゴジラ、進撃経路(パスワードは「sin」と入力してみてください)

なんてものも作れるようになります。

なんですが…………………………………………
メインで使っていたAPI、Yahoo! JavaScriptマップAPIが2020年10月末で提供終了予定。
代替サービスは紹介されてるけど──無料プランだとアクセス数その他、色々制約がありそうです orz。