0
0

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 3 years have passed since last update.

jsフレームワークstimulusでyahoo地図を操作してみた(その三)

Last updated at Posted at 2019-06-24

だいぶ間隔が空いてしまったけど、前回の続きです。

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

0. railsアプリへのstimulus適用

1. stimulusでyahoo地図を表示

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

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

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

今回は「住所から座標の取得」……つまりはジオコーディングです。
ただ取得したデータの登録までやりたいのでーー

##先に、登録先のテーブルを作っておく。

####テーブル名:「maps」
→「名前(name)」「住所(address)」「緯度(latitude)」「経度(longitude)」の情報(列)を持たせる

ターミナル

$ rails g scaffold maps name:string address:string latitude:string longitude:string

$ rails db:migrate

で、とりあえず座標登録できるようになる。
スクリーンショット 2019-06-24 11.34.28.png

……まあ現時点では、手で直接入力すれば、なんですが。

##次に、ビュー関係を整えておく。

復習がてら、この画面にもyahooの地図が表示されるようにしておこう。

app/views/maps/_form.html.erb

<div data-controller="geocode"><!-- ←追記 -->

  <%= form_with(model: map, local: true) do |form| %><!-- scaffoldで出来た記述 -->

  <!-- 中略 -->

<!-- ↓↓scaffoldで出来た記述↓↓ -->
      <div class="actions">
      <%= form.submit %>
    </div>
  <% end %>
<!-- ↑↑scaffoldで出来た記述↑↑ -->

  <!-- ↓↓以下、追記↓↓ -->
  <div id="idmeihatekitoudemoiiyo" style="width:800px; height:400px"  data-target="geocode.map" ></div>

</div>

app/javascript/controllers/geocode_controller.js

import { Controller } from "stimulus"

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

  initialize() {

       this.map = new Y.Map(this.mapTarget.id);
       this.map.drawMap(new Y.LatLng(35.66572, 139.73100), 17, Y.LayerSetId.NORMAL);

       var center = new Y.CenterMarkControl
       var control = new Y.LayerSetControl();
       this.map.addControl(center);
       this.map.addControl(control);

  }
}

今回は「data-controller名」を「geocode」にする(ので、jsのファイル名は「geocode_controller.js」になる)。
またyahooのAPIは「application.html.erb」にすでに書き込んである前提です
(ココらへんで不明点があれば、前々回の記事を参照してください)。

で、ついでに住所入力欄の隣に、座標取得用のボタンも用意しておく。

app/views/maps/_form.html.erb

  <div class="field">
    <%= form.label :address %>
    <%= form.text_field :address %>
    <input type="button" value="座標取得"><!-- ←追記 -->
  </div>

画面を確認すると、こんな感じ(地図をそのまま乗せるのはまずいっぽいので、モザイクを書けています)。
スクリーンショット 2019-06-24 12.14.50.png

この画面で実現させたいのは、
・住所を入力する
  ↓
・「座標取得」ボタンを押す
  ↓
・住所の地点にピンが立ち、その座標が「緯度」「経度」に入力される
  ↓
・ピンの場所を確認し、問題なければ登録
の流れ。

これをstimulas的(?)に考えると

①【data-action:「座標取得」ボタンのクリック】が発生した時に、
②【data-target:「住所」欄の情報を取得し】
③そこからジオコーディングで座標情報を獲得して
④【data-target:「緯度」「経度」に表示 + 「地図」にピンを立てる】

となる。

地図はすでにターゲット指定しているので、残りにもアクション、ターゲットの指定をしておこう。

app/views/maps/_form.html.erb

  <div class="field">
    <%= form.label :address %>

    <!-- ↓↓「住所」をターゲット指定↓↓ -->
    <%= form.text_field :address , :data => {:target=>"geocode.address"} %>

    <!-- ↓↓「座標獲得ボタンのクリック」をアクション指定↓↓ -->
    <input type="button" value="座標取得" data-action="click->geocode#get_lat_lng">
  </div>

  <div class="field">
    <%= form.label :latitude %>

    <!-- ↓↓「緯度」をターゲット指定↓↓ -->
    <%= form.text_field :latitude, :data => {:target=>"geocode.latitude"} %>
  </div>

  <div class="field">
    <%= form.label :longitude %>

    <!-- ↓↓「経度」をターゲット指定↓↓ -->
    <%= form.text_field :longitude , :data => {:target=>"geocode.longitude"} %>
  </div>

ーーこれでビュー側の作業は完了のはず。

##最後に、stimulus側の記述をしていく

stimulus側で行うことを大別すると、
A.【住所から座標を獲得する】
B.【獲得した座標の場所にピンを立てる】
C.【獲得した座標情報をビューに表示する】
そして
D.【「B」の前にすでにピンが立っていた場合、それを削除する】
となる(初めはDをうっかり忘れていて、何本もピンが立つ状態にしてしまった)。

ちなみにAはここ、BとCはここ(公式)ここの記述を参考にさせていただきました。

####まずは下準備

app/javascript/controllers/geocode_controller.js

import { Controller } from "stimulus"

// ピン保存用の枠……「D」で削除するために、作ったピンは保存しておく必要がある
var markers = [];

export default class extends Controller {
  static targets = [ "map", "address","latitude","longitude"  ]
                             //↑住所,緯度,経度を追加

  initialize() {

// 以下、略

追加されたターゲット、および「作成したピンの保存場所(後で削除するかもしれないので)」を用意しておく。

####次いで、A.【住所から座標を獲得する】……の前に、
この【住所から座標を獲得する】処理は、非同期で行われる。
で、stimulusでは「this.【ターゲット名】Target」でビュー要素の取得や設定を行うのだが、非同期処理中だとこの記述では要素を取得出来ない

なので非同期処理が始まる前に、使用する要素を別の変数に格納しておく(ここで一日詰まって、いろいろ試してみて見つけた「動かせる方法」がこのやり方。もっといい方法が分かる方入れば、ご教授よろしくおねがいします)。

app/javascript/controllers/geocode_controller.js

  get_lat_lng() {

    //ターゲットを変数に移す
    var address = this.addressTarget.value;
    var latitude = this.latitudeTarget
    var longitude = this.longitudeTarget;

    var map_box = this.map

    // 続く

####あらためて、A.【住所から座標を獲得する】

app/javascript/controllers/geocode_controller.js

    var map_box = this.map

    // 続き
    var request = { query : address };

    var geocoder = new Y.GeoCoder();
    geocoder.execute( request , function( ydf ) {//←ジオコーディング処理

        //成功すれば↓の式が正となり、「ydf」に座標情報が獲得される
        if ( ydf.features.length > 0 ) {
        /////↓↓ので、ここ(非同期処理中)でB,C,Dの処理を行う↓↓////////////////////

        /////↑↑ので、ここ(非同期処理中)でB,C,Dの処理を行う↑↑////////////////////

        }else{
          //【住所から座標を獲得できなかった場合の処理……今回は抜けるだけでいいや】
          return;
        }
      
      } );

####続けて、座標情報を取得した場合の処理
#####・まずはD.【「B」の前にすでにピンが立っていた場合、それを削除する】

app/javascript/controllers/geocode_controller.js

//略

/////↓↓ここ(非同期処理中)でB,C,Dの処理を行う↓↓////////////////////
//Dの処理
if(markers.length > 0){
  for (var i = 0; i < markers.length; i++) {

    map_box.removeFeature(markers[i]);
  }
  markers = [];	//参照を開放
}
/////↑↑、ここ(非同期処理中)でB,C,Dの処理を行う↑↑////////////////////

//略

「markers」に格納しているピンを(存在すれば)削除する。
ピンが複数あっても対応できるように「for」で回しているけど、今回の場合なら
map_box.removeFeature(markers[0]);
でも問題ないはず。

#####・続けてB.【獲得した座標の場所にピンを立てる】

app/javascript/controllers/geocode_controller.js

//略
/////↓↓ここ(非同期処理中)でB,C,Dの処理を行う↓↓////////////////////
//Dの処理          
var current_location = new Y.LatLng(ydf.features[0]["latlng"]["Lat"],ydf.features[0]["latlng"]["Lon"])
var marker = new Y.Marker(current_location);
map_box.addFeature(marker);

// // 作成したマーカーを保存(削除できるように)
markers.push(marker);
          
// ピンの場所に移動
map_box.panTo(current_location, true);

/////↑↑、ここ(非同期処理中)でB,C,Dの処理を行う↑↑////////////////////
//略

作成したピンを、「markers」に保存しておくことを忘れないように(でないと「D」の意味がなくなる)。

#####・最後にC.【獲得した座標情報をビューに表示する】

app/javascript/controllers/geocode_controller.js

//略
/////↓↓ここ(非同期処理中)でB,C,Dの処理を行う↓↓////////////////////
//Cの処理          
latitude.value =ydf.features[0]["latlng"]["Lat"];
longitude.value =ydf.features[0]["latlng"]["Lon"];

/////↑↑、ここ(非同期処理中)でB,C,Dの処理を行う↑↑////////////////////
//略

これで座標を取得できる住所であれば、その座標の表示とピン作成ができるように……
スクリーンショット 2019-06-24 14.08.09.png

なりました。

ちなみに住所からではなく地図を直接クリックして、【クリックした地点の座標を取得】することもできる(これについてはまた別の記事にまとめる予定)。
これで結構簡単に座標登録できるようになりそうだ……と、思ったけど、、、

##でもこれって、登録しちゃっていいの!!??
ここまで作ったあとでふと、APIのFAQに目をやってみたら……
>API経由で取得したデータを保存したり、二次利用することはできません。
……あれ、俺、規約違反してる?

##大丈夫でした!!!
Yahoo! JAPANカスタマーセンターに確認したところ、問題ないそうです
(詳細については、また別の記事を書く予定)。
ご対応いただいたYahoo! JAPANカスタマーサービスの担当者様、どうもありがとうございました。

###ということで、さっそく自作アプリに使ってみました。
ルート作成ツール【Maっぷら】、新規登録画面
……「座標→住所」「住所→座標」の両方を実装できたので、使い勝手もだいぶ良くなったと思います。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?