テックアカデミーというプログラミングスクールで自作のWEBアプリを作ることが課題となっています。
僕は地図や店舗情報を使ったサイトを作ろうと思っていまして、Google Map APIを調べていました。けど課金が高いので講師の方にアドバイスをもらい、YahooのAPIを使ってはどうかという事で、こちらをリサーチし始めたのです。
ここでは、Yahoo地図APIの使い方を整理しています。
僕が使いそうなAPIは次の2つです。
・Yahoo! JavaScriptマップAPI
・Yahoo!ローカルサーチAPI
上の方はJavascriptを使って、ブラウザ画面に地図を表示させる機能です。
下の方はURLでGETして検索結果を受け取れるAPIです。
今日はこの2つを使って自分のWEBアプリに組み込みが出来るか調査をしようと思いました。
Yahooのデベロッパーに登録する
APIを使うためにYahooデベロッパーに登録する必要があるということで登録しました。
商用利用も一定の範囲までは無料ということを確認できました。
ここで「Client ID」と「シークレット」の2つの番号を貰いました。
Yahoo! JavaScriptマップAPI
サンプルコードを試してみた
サンプルコードがあるので簡単に試すことができました。
<あなたのアプリケーションID>のところはYahooデベロッパーに登録したときのClient IDを記述すればよいです。
<!DOCTYPE html> <html> <body> <div id="map" style="width:400px; height:300px"></div> <script type="text/javascript" charset="utf-8" src="https://map.yahooapis.jp/js/V1/jsapi?appid=【アプリケーションID】"></script> <script type="text/javascript"> window.onload = function(){ var ymap = new Y.Map("map"); ymap.drawMap(new Y.LatLng(35.66572, 139.73100), 17, Y.LayerSetId.NORMAL); } </script> </body> </html>
2地点をマーカー表示できた
ちょっと躓いたけどマップ上に2つの地点を表示させる事ができました。
まずはymap.drawMapで、マップを表示してから、じゃないとマーカー表示ができませんでした。
リファレンス探すのも一苦労だったので、URLを残しておく。
コードはこのようになりました。
Y.Markerを2つつくって、配列に入れてaddFeaturesメソッドでマーカーを表示することができた。
<!DOCTYPE html> <html> <body> <div id="map" style="width:400px; height:300px"></div> <script type="text/javascript" charset="utf-8" src="https://map.yahooapis.jp/js/V1/jsapi?appid=【アプリケーションID】"></script> <script type="text/javascript"> window.onload = function(){ var ymap = new Y.Map("map"); ymap.drawMap(new Y.LatLng(35.729032,139.719566), 17, Y.LayerSetId.NORMAL); //配列で2点マーカー表示 var marker1 = new Y.Marker(new Y.LatLng(35.729032,139.719566)); var marker2 = new Y.Marker(new Y.LatLng(35.728012,139.714063)); var markers = []; markers.push(marker1); markers.push(marker2); //markers.push(marker1,marker2); //これでもいい ymap.addFeatures(markers); } </script> </body> </html>
Yahoo!ローカルサーチAPI
このAPIを使ってYahooから店舗情報を入手してみようと思います。
URLをブラウザに貼り付けてみる
サンプルに従って以下のURLをブラウザに貼り付けてみます。
https://map.yahooapis.jp/search/local/V1/localSearch?appid=<あなたのアプリケーションID>&query=%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%B3
ブラウザに表示されたのはXML形式のものでした。
私はRubyでデータをいじりたいのでJSON形式でデータを受け取りたいと思っているので以下のようにしました。
https://map.yahooapis.jp/search/local/V1/localSearch?output=json&appid=<あなたのアプリケーションID>&query=%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%B3
output=jsonで指定することで、JSON形式のデータを受け取る事ができました。
しかし受け取ったデータを見るとわかりますが、日本語がUTF16で表示されて日本語で読めません。
ここからRubyを使って受け取ったデータの利用方法を考えていこうと思います。
日本語を含むURLをUTF8へエンコード
APIにURLを投げる時に、例えば以下のようなURLを考えます。
yquery = 'https://map.yahooapis.jp/search/local/V1/localSearch?output=json&appid=<あなたのアプリケーションID>&query=デニーズ 新宿'
日本語で「デニーズ 新宿」と入っていますが、これだとデータを渡せないのでエンコードが必要です。
Rubyではエンコードのメソッドが4つある事がわかりました。
yquery_enc = URI.encode yquery yquery_esc = URI.escape yquery yquery_erb = ERB::Util.url_encode yquery yquery_cgi = CGI.escape yquery
現在上2つは非推奨となっているようで、下2つを使うようなのですが下2つだと私の期待するエンコードにはなりません。(/や&や?までエンコードされてしまい、Yahoo側が期待するURL形式になってくれない)
ここではURI.encodeを使って進めていきます。
エンコードするとこのようなURLになりました。
https://map.yahooapis.jp/search/local/V1/localSearch?output=json&appid=<アプリケーションID>&query=%E3%83%87%E3%83%8B%E3%83%BC%E3%82%BA%E3%80%80%E6%96%B0%E5%AE%BF
これでAPIに投げるURLを作ることができました。
Yahoo!ローカルサーチAPIへURLを投げてJSON形式を受け取る
require 'net/http' require 'uri' require 'json' yquery = 'https://map.yahooapis.jp/search/local/V1/localSearch?output=json&appid=<アプリケーションID>&sort=match&&query=デニーズ 新宿' yquery_enc = URI.encode yquery uri = URI.parse(yquery_enc) json = Net::HTTP.get(uri) hairetu = JSON.parse(json) #配列形式に変換 puts hairetu
これで出来ました。JSON形式で受け取った日本語はUTF16で表示されて読めませんでしたが、JSON.parseメソッドによって、配列形式に変換し、かつ日本語化もやってくれました。
レスポンスコードを解析して欲しいデータを取り出す
さて、上記の通りhairetuに入れる事ができました。
試しに以下のRubyコードを書いてみた所、思うようなデータを取る事ができました。
@shop1name = hairetu['Feature'][0]['Name'] @shop2name = hairetu['Feature'][1]['Name'] puts @shop1name puts @shop2name
1店舗目→hairetu[‘Feature’][0]
2店舗目→hairetu[‘Feature’][1]
で取れることがわかりましたね。
取得した店舗情報を地図に表示させる
本当はループで店舗数分を地図に表示させたいのですが、まずは上記の2店舗だけ地図に表示させてみようと思います。
地図に表示させるには緯度・軽度が必要とのことで、以下のコードを書きました。かなりゴチャゴチャしてる。もっとスマートな書き方もあったはずだ‥
@shop1coor = hairetu['Feature'][0]['Geometry']['Coordinates'] #1店舗目の緯度,経度 @shop2coor = hairetu['Feature'][1]['Geometry']['Coordinates'] #2店舗目の緯度,経度 @keido1 = @shop1coor.split(",")[0].to_f #緯度,経度を分割して経度(浮動少数型)にする @ido1 = @shop1coor.split(",")[1].to_f #緯度,経度を分割して緯度(浮動少数型)にする @keido2 = @shop2coor.split(",")[0].to_f #こっちは2店舗目 @ido2 = @shop2coor.split(",")[1].to_f
これをVIEW側に渡して、VIEWの方でJavascriptで地図表示、といけばよいのですが。
地図表示はこのページの上の方でやったサンプルを参考にしました。
<div id="map" style="width:400px; height:300px"></div> <script type="text/javascript" charset="utf-8" src="https://map.yahooapis.jp/js/V1/jsapi?appid=<アプリケーションID>"></script> <script type="text/javascript"> window.onload = function(){ var ymap = new Y.Map("map"); ymap.drawMap(new Y.LatLng(<%= @ido1 %>,<%= @keido1 %>), 17, Y.LayerSetId.NORMAL); var marker1 = new Y.Marker(new Y.LatLng(<%= @ido1 %>, <%= @keido1 %>)); var marker2 = new Y.Marker(new Y.LatLng(<%= @ido2 %>, <%= @keido2 %>)); var markers = []; markers.push(marker1); markers.push(marker2); ymap.addFeatures(markers); } </script>
これで表示ができました。
残念なのは縮尺か設定がうまくなくて、地図上に2地点がマーカーされていないのです。
地図に店舗名を表示させる
Yahoo地図APIのラベルというメソッドを使って文字を表示する事ができるようです。
Ruby側では以下のようにJSON形式で受け取ったレスポンスから店舗名を変数に入れてます。
@shop1name = hairetu['Feature'][0]['Name'] @shop2name = hairetu['Feature'][1]['Name']
なのでVIEW側でこの変数を使います。
var label1 = new Y.Label(new Y.LatLng(<%= @ido1 %>, <%= @keido1 %>), "<%= @shop1name %>"); var label2 = new Y.Label(new Y.LatLng(<%= @ido2 %>, <%= @keido2 %>), "<%= @shop2name %>"); var labels = []; labels.push(label1); labels.push(label2); ymap.addFeatures(labels);
これで2地点に店舗名を表示することができました。
地図の縮尺変更
ymap.drawMap(new Y.LatLng(<%= @ido1 %>,<%= @keido1 %>), 15, Y.LayerSetId.NORMAL);
ここの数字を変えることで地図の縮尺ができた。10にすると日本全体が出ちゃう。15~17がいいかもしれない。
地図の縮尺変更ボタン
これで地図を縮尺拡大するために[+][-]ボタンが出た。
var control = new Y.ZoomControl(); ymap.addControl(control);
自分的な今後の課題
なんとか調べながらここまで調査が出来ました。後の課題を整理しておきます。
Yahooから受け取るレスポンスの店舗数の制限。MAX100件取れるようだが、そんなに店舗情報を入手して全部地図上に表示できない。件数は10件とか20件とかに制限して検索ワードにマッチする順にsort=hybridかsort=match指定する。
地図の縮尺をどうするかも課題。店舗10件あるけど縮尺が大きすぎて全部で3件しか地図上に出せない、なんてこともよくある。かといって全店舗表示させようとすれば地図の縮尺を縮小しなければいけない。。
例えば「札幌 ラーメン」で検索して、東京にある札幌ラーメン店と、札幌市のラーメン店の2地点を地図に表示させようと思ったら、中間地点の宮城県の地図が出ちゃう。地図を縮小すれば日本全土が出ちゃう。こんな極端なケースはないだろうけど。
地図の中心をどこにするかも課題。全ての店舗の中心地を地図の中心にしたらよいのか。→これだと地図縮尺が詳細になると地図上にマーカが1件も表示されない、なんてこともある。なのでソートして1番上の店舗を中心にすれば確実に1件は地図上にプロットできる。
この当たりが直近の課題です。
あ、あと店舗をループで回すのも実装しなきゃ。