先週公開した記事(Leaflet.js で空間フィルタリングをやってみる - Esri Leaflet Tips )で紹介した空間フィルタリングのサンプル アプリは、住所や地名を入力して周辺の施設を検索するという機能でしたが、これを Slack などのチャット上で実行できたら便利かしら?と思い立ち BOT を作ってみました。

「BOT(ボット)」とはロボットの略称で、最近では Twiter や LINE、Slack などのタイムライン上で会話ができるプログラムやアカウントのことを指す場合が多いですね。
このような BOT 向けの API やツールが公開されているため、BOT を作ってみたという開発者の記事をよく見かけるようになりました。
今回、BOT 作成にはオープンソースのチャット ロボットである Hubot という GitHub が作成した BOT プログラムを使いました。
また、最近エンジニアの間では一般的なチャットツールとなっている Slack 向けの BOT として作ってみました。
仕様
まずは BOT を使って結果を得るまでのフローを整理します。
- BOT に「探して xx駅」や「探して 東京都千代田区平河町2-7-1」のように「探して」の後に場所名や住所を伝える
- BOT は「探して」の後に記載された場所名・住所をもとに ArcGIS の住所検索サービスへのリクエストを送信
- レスポンスから結果の緯度経度を抜き出して地図の URL に付記して Slack に送信
- 地図は URL パラメーターにある緯度経度にズーム&バッファー生成
- バッファー内に含まれる無料 wifi スポットをフィルタリング
この中で Hubot として実装したのは 1 ~ 3 です。
インストールから Slack への Hubot の設定等については、いろいろ詳しい記事があるので、そちらを参照するとよいと思います。
今回は主に Slack への BOT 設定をして、Hubot を Heroku にデプロイした後の話をします。
主に Hubot のスクリプトについてです。
4, 5 は Esri Leaflet Tips にて紹介した空間フィルタリングのサンプルをベースに、バッファーの中心座標が URL パラメーターの緯度経度取得をトリガーとするように作り直しました。
サンプルを Gist に置きましたのでご覧ください。
なお、無料 wifi スポットのデータは、オープンデータとして国土交通省が公開している歩行者移動支援サービスに関するデータサイトにある「無料公衆無線LANスポット」(国土交通省観光庁) を ESRIジャパン株式会社が加工したものを使っています。
無料 wifi スポット検索

URL パラメーターへの緯度経度埋め込みを試してみたい方はこちらにアクセスしてみてください。
デフォルトは札幌駅の緯度経度です。
https://bl.ocks.org/ynunokawa/raw/8ec5c70d089a55bcabf7e3fd5dce9e39/?lat=43.06858648700046&lng=141.3508507640006
住所・地名検索用 API(Esri World Geocoding Service)
Hubot では BOT の基本フローである「特定のメッセージを受け取ったら、対応するメッセージを返信する」というプログラムを CoffeeScript で記述します。
今回の実装はメッセージを受け取った後に緯度経度を取得するための処理を加えています。
Esri の住所検索サービス(World Geocoding Service)※1 へ住所・地名を送信して、取得した緯度経度を加えたアプリ URL を返信するという流れになります。
REST API の仕様は以下に詳しいです。
ここでも簡単に説明しますが、ArcGIS の住所検索サービスには 4 つのオペレーションが存在します。
- findAddressCandidates:住所や地名に一致した緯度経度(複数候補)を返します
- geocodeAddresses:複数の住所や地名を一度にバッチ処理して緯度経度を返します
- reverseGeocode:緯度経度に一致した住所を返します
- suggest:入力テキストに対して候補となる住所や地名を返します
REST の URL は以下のように作ります。
http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/[オペレーション名]?[パラメーター]
各オペレーションに対応したパラメーターは仕様を確認してください。
今回は Slack 上で入力された住所・地名を使って緯度経度を取得できればよいので、findAddressCandidates を使います。
findAddressCandidates でよく使うパラメーターは以下の通り。
| パラメーター | 内容 |
|---|
| f | レスポンスのフォーマットを指定 (json | pjson) |
| singleLine | 緯度経度を取得する住所・地名を指定 |
| searchExtent | 検索範囲を指定 |
| location | 基点となる位置を指定、distance で指定した値の半径円内に含まれる結果を優先します |
| distance | location で指定した位置からの距離を指定 (デフォルトでは 50,000 メートル) |
| maxLocations | 取得する結果 (候補) の数を指定 |
| forStorage | 永続的に保存※2 を行うかどうかを指定 |
たとえば、永田町駅の緯度経度を取得する URL はこのようになります。
https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates?outSr=4326&forStorage=false&ou…
Hubot のスクリプト(CoffeeScript)を書く
Hubot のスクリプトとして記述する内容を整理します。
- メッセージの頭に「探して」という記述があるかどうかの判別
- 「探して」の後に記述されているキーワード (住所・地名) を参照
- キーワードをもとに住所検索リクエストの URL を生成・送信
- 結果の緯度経度をもとに地図 URL を生成
- Slack にメッセージとして地図 URL を送信
上記フローの 1 ~ 3 のスクリプトです。
Hubot のスクリプトには msg.http を使ったリクエスト URL の生成・送信が可能なので、これを利用しています。
msg.match[1] に「探して」の後に記述されたキーワードが含まれているので、それをリクエスト URL の singleLine パラメーターに指定しています。
robot.respond /探して (.*)/i, (msg) ->
keyword = msg.match[1]
msg.send keyword + '周辺の無料 wifi スポットだよ!'
request = msg.http('http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates')
.query({
f: 'json',
singleLine: keyword,
outSr: 4326,
forStorage: false,
outFields: '*',
maxLocations: 1
})
.get()残りのフロー 4 ~ 5 のスクリプトです。
コールバックにエラー処理と取得した結果は JSON.parse でパースした json を参照します。
緯度経度は json.candidates[].location に x と y パラメーターとして与えられているので、これをもとに地図 URL を作ります。
最後に msg.send で地図 URL を送信して、終わり。
request (err, res, body) ->
if err
msg.send 'みつからないよ〜'
robot.emit 'error', err, res
return
json = JSON.parse body
appUrl = 'https://bl.ocks.org/ynunokawa/raw/8ec5c70d089a55bcabf7e3fd5dce9e39/?lat=' + json.candidates[0].location.y + '&lng=' + json.candidates[0].location.x
msg.send appUrl if json.candidates.length > 0
今回作った Hubot はサンプルとして GitHub に公開しているので、よければお試しください。
なお、社内やチーム内で実運用する場合は、ArcGIS for Developers の有償プランにグレードを上げる必要があるのでご注意を。
※1 住所検索サービスは100万リクエスト/月の上限内であれば使い放題です
※2 findAddressCandidates で取得した結果の永続的な保存にはクレジットが必要です
関連リンク