OpenWeatherMapで郵便番号が使えないときのアプローチ

OpenWeatherMap

OpenWeatherMapは、開発者に、現在の天候や予測履歴を含む各種気象データを提供する無料APIです(一部有料あり)。

使い方については、記事が沢山出ているので、参考程度にQiitaのリンクを貼っておきます。

qiita.com

郵便番号を指定してデータを取得する

zipパラメータを指定することで郵便番号指定検索ができます。

import requests
url = https://api.openweathermap.org/data/2.5/forecast
params = {
    "APPID" : API_KEY,
    "units" : "metric",
    "zip" : "100-0001,jp"
}
res = requests.get(url,params=params)

APPIDAPI keyです。取得方法については、多くの記事で紹介されているので省略します。

エンドポイント(url)にhttps://api.openweathermap.org/data/2.5/forecastを指定すると、5日間分の3時間予報が取得できます。

https://api.openweathermap.org/data/2.5/weatherを指定すると、現在の天候が取得できます。

郵便番号を指定してもデータが取得できないとき

ここからがメインです。

例えば郵便番号に 100-0001(東京都千代田区千代田)を指定すると、データが取得できます。

ただし、100-0000(東京都千代田区)を指定すると、取得できません。

{
    "cod": "404",
    "message": "city not found"
}

この問題・解決策を記載している記事が少なかったので、一つのアプローチとして、HeartRailsGeo APIを使用して緯度・経度を算出した後に、緯度経度から気象情報を取得するという方法を紹介します。 他には、そもそも郵便番号を利用しない(CityCode等の他パラメータを用いて検索)などの方法があります。
この記事は、どうしても郵便番号が使いたい人向けです。

HeartRailsGeo API

「HeartRails Geo API」 は、郵便番号/住所/緯度経度データ等の地理情報を、XMLJSON(P) 形式の API により無料でご提供させていただくサービスです。

API は商用、非商用を問わず、無料でご利用になれます。

url : http://geoapi.heartrails.com/api.html

この中の、郵便番号から郵便番号による住所検索 APIを使用します。

import requests
url = http://geoapi.heartrails.com/api/json
params = {
    "method" : "searchByPostal",
    "postal" : "<郵便番号>" (例:1000000)
}
res = requests.get(url,params=params)

郵便番号1000000を指定したときのレスポンスは次のようなものです。

x : 経度,y :緯度

{
    "response": {
        "location": [
            {
                "city": "千代田区",
                "city_kana": "ちよだく",
                "town": "(その他)",
                "town_kana": "(そのた)",
                "x": "139.754927",
                "y": "35.68576",
                "prefecture": "東京都",
                "postal": "1000000"
            }
        ]
    }
}

緯度経度を指定してOpenWeatherMapから情報を取得する

ここで取得した緯度経度を使います。

# res : 郵便番号から郵便番号による住所検索 APIのレスポンス
longitude = res["response"]["location"][0]["x"]
latitude = res["response"]["location"][0]["y"]
url='https://api.openweathermap.org/data/2.5/weather'
params={
    "lon":longitude,
    "lat":latitude,
    "units":"metric",
    "APPID":APIKEY
}
res = requests.get(url,params=params)

これで(ほぼすべての郵便番号について、正しい情報が)取得できました。

注意点

郵便番号が被る地域があります。

この記事を見ると、よくわかります。

qiita.com

郵便番号は必ず1つの町名に紐づいているわけではない
市区町村をまたいで同じ郵便番号を持つケースがある
市区町村はおろか県を飛び越えて同じ郵便番号を持ちうるケースがある

例えば、6180000を指定して、住所検索APIにリクエストを送ると次のようなレスポンスが返ってきます。

{
    "response": {
        "location": [
            {
                "city": "乙訓郡大山崎町",
                "city_kana": "おとくにぐんおおやまざきちょう",
                "town": "(その他)",
                "town_kana": "(そのた)",
                "x": "135.688862",
                "y": "34.904159",
                "prefecture": "京都府",
                "postal": "6180000"
            },
            {
                "city": "三島郡島本町",
                "city_kana": "みしまぐんしまもとちょう",
                "town": "(その他)",
                "town_kana": "(そのた)",
                "x": "135.667274",
                "y": "34.902399",
                "prefecture": "大阪府",
                "postal": "6180000"
            }
        ]
    }
}

取得できるデータは正しいですが、上記の通り、安直に

longitude = res["response"]["location"][0]["x"]
latitude = res["response"]["location"][0]["y"]

のように0番目を指定すると誤った情報を提供する可能性があります。

まとめ

OpenWeatherMapで郵便番号を指定してリクエストをする方法と、できないときのアプローチとしてHeartRailsGeo APIを使用して緯度・経度を算出した後に、緯度経度から気象情報を取得する方法を紹介しました。

しかし、日本の郵便番号の設定上、誤った情報を提供する可能性があることに注意する必要があります。

郵便番号が被っていない地域に対してサービスを提供するような場合、今回のアプローチは有効ですが、サービス提供先の地域の郵便番号が複数の地域に及ぶ場合は、今回の方法は使えません。

誕生日を祝ってくれるSlackBotを作った

とあるワークスペースで誕生日大公開キャンペーンがあったのでSlackBotに祝わせます。

システム概要

GoogleFormに誕生日と名前 + αを入力してもらうと、SlackBotが期日に「○○君、おめでとう!」と投稿します。主にGoogleAppScriptを使っています。

f:id:sho0126hiro:20190707231828j:plain

Step1.Google Formで入力部を作成

GoogleFormを作って、簡単な入力フォームを作ります。

f:id:sho0126hiro:20190707231934p:plain

入力する日付は( format x/y )と固定にしました。(処理が簡単なので)

Step2.フォームの入力を指定のシートに入力

本来自動でスプレッドシートの作成 + 入力をしてくれます。特定のスプレッドシートの特定箇所に入力させたい場合は次のような処理が必要です。

注:今回の場合は設計時にGoogleFormの使用は考えていなかったので、ユーザ入力部(GoogleForm)はSlackBotへの投稿プログラムの作成後に取り掛かりました。

フォームの右上の︙ボタンをクリック → スクリプトエディタをクリック

次のようにコードを入力します。

function onSubmitForm(e) {
  FormApp.getActiveForm();
  var res = e.response.getItemResponses();
  var name = res[0].getResponse(); // 質問の1番目の入力結果 => 名前
  var date = res[1].getResponse(); // 質問の2番目の入力結果 => 誕生日
  var opt  = res[2].getResponse(); // 質問の3番目の入力結果 => 追加メッセージ
  // input to specific sheet
  var ss_id = "<ss_id>";
  var sh_name = "<sheet_name>";
  var sh = SpreadsheetApp.openById(ss_id).getSheetByName(sh_name);
  var last_row = sh.getLastRow(); // シートの最終行を取得
  // sh.getRange(row,column).setValue(value);
  sh.getRange(last_row+1,1).setValue(name);
  sh.getRange(last_row+1,2).setValue(date);
  sh.getRange(last_row+1,3).setValue(opt);
}

ss_id はシートのURLに含まれています。sheet_nameはシート名です。

https://docs.google.com/spreadsheets/d/<ここ>/edit

Step.3 フォーム入力時に関数実行トリガの作成

スクリプトエディタ => 編集 => 現在のプロジェクトのトリガーを選択

G suit Developer Hubに飛ばされるので、右下のトリガーを追加からトリガーを追加します。

実行する関数 onSubmitForm
デプロイ HEAD
イベントのソース フォームから
イベントの種類 フォーム送信時

これで、フォームを入力すると、指定の行と列に入力項目が入ります。

Step.4 日付の確認とSlackへの送信

シートの形式

シートの形式は次のようになっています

name date option
一郎 1/1 追加メッセージ
二郎 7/13 追加メッセージ

日付の確認とSlackへの送信

シートは毎日実行されます。今日の日付がシートに含まれているかを確認し、Slackに送信します。

誕生日と名前が記入されるシートから、ツール => スクリプトエディタを選択

次のようなコードを書きます。

function main() {
  var sheet = SpreadsheetApp.getActiveSheet();
  // 今日の日付を取得 (format : MM/dd)
  var now = Utilities.formatDate(new Date(), 'JST' , "MM/dd");
  var index = 2;
  while(sheet.getRange('B'+ index).getValue() != ''){
    // シートから日付を取得 (format : MM/dd)
    sheet.getRange('B'+index ).getValue()
    var birthday = Utilities.formatDate(value, 'JST', 'MM/dd');
    if(now == birthday){
      var name = sheet.getRange('A'+index).getValue();
      var opt = sheet.getRange('C'+index).getValue();
      // slackへ送信
      postToSlack(now,name,opt);
    }
    index+=1;
  }
}

B列(2行目から)に誕生日が含まれています。

誕生日の入力がなくなるまでwhileループで日付を確認しています。

function postToSlack(date,name,opt){
  const SLACK_TOKEN = '<slack_token>';
  // message
  var msg = "今日は"+name+"の誕生日!おめでとう!"+opt;
  // url
  var url = "https://slack.com/api/chat.postMessage?token="+SLACK_TOKEN+
            "&channel=<channel_name>"+
            "&text="+msg +
            "&username=<user_name>BOT"+
            "&pretty=1";
  // post
  var res = UrlFetchApp.fetch(url);
}

Slackへの送信はGoogle App Script標準のUrlFetchApp.fetchメソッドを使います。

SlackAPIのchat.postMessageにリクエストを送信しています。

Step.5 日付確認プログラムを定期実行させる

Step.4で作ったプログラムにトリガを付けます。

スクリプトエディタ => 編集 => 現在のプロジェクトのトリガーを選択

G suit Developer Hubに飛ばされるので、右下のトリガーを追加からトリガーを追加します。

実行する関数を選択 main
イベントのソースを選択 時間主導型
時間ベースのトリガーのタイプを選択 日付ベースのタイマー
時刻を選択 <〇時~〇時>(お好み)

Google側が重くならないように「〇時〇分ぴったり」設定できないみたい。(するなら時間ベース・分ベースのタイマーにしよう)

8時ピッタリにとか、0時ピッタリに設定したい場合は、Step2で作ったプログラムの日付フォーマットをMM/dd/hhまで設定して比較しましょう。

トリガについて

指定したプロジェクトは全てG Suite Developer Hubでまとめて見れます。

どんなプロジェクトでいつトリガ設定していたっけ…?みたいな時、一括で見れるから便利そう。

f:id:sho0126hiro:20190707232120p:plain

動作

googleFormに誕生日を入力すると、SlackBotが祝ってくれます。太郎君おめでとう!

f:id:sho0126hiro:20190707232440j:plain

お願いするとWebカメラで写真を撮って送信してくれるSlackBotを作った

はじめに

Pythonで Raspberry Pi3 B+ に Webカメラ ( logicool C270) ) を接続して、Slackのchannelで特定の発言があった時に、写真とって送ってくれるSlackBotを作りました。

最初はLINEで実装しましたが、「Slackでもやって!」といわれたので実装しました。

LINEBotと違うのは、常時SlackBotを動かしておき、特定の発言をトリガとして「写真を撮って保存 → 保存した写真をSlack channel に送信」というプログラムを実行しているところです。

開発期間は半日ぐらい(?)LINEBotよりちょっと難しかったです。

続きを読む

Webカメラで定期的に写真を撮って定期的に送ってくるLINEBotを作った

はじめに

Pythonで Raspberry Pi3 B+ に Webカメラ ( logicool C270) ) を接続して、定期的に写真を撮って送ってくれるLINEBotを作りました。

上記の機材があって、友達に「技術の無駄遣いしよ、なんか面白そうだからつくって」と言われたので実装しました。

「写真を撮って保存 → 保存した写真をライングループに送信」という流れをプログラムで実装し、それをRaspberry Pi で定期実行させています。

開発期間は半日ぐらい、LINEBotはかなり簡単でした。

続きを読む

雑談対話(自然対話)APIの比較

忘備録です。雑談対話APIを探す機会があったので、雑談対話APIの種類と、実際に試したしたもの、それぞれの特徴などを書いておきます。

Pythondocomo自然対話APIとTalkAPIをたたきました

docomo自然対話API

詳細はこちら

雑談対話APIを使うには、メールでの登録と、アプリケーションの登録申請を出す必要があります。登録申請は比較的早く通ります。

雑談対話API一部の機能を制限されています。

続きを読む

本のしおりをIoTにしてみた (SHIWORI)

開発概要

組み込みマイスター2018-2019で制作した作品です。
開発期間は半年弱、高専の4年生4名で開発しました。
(内2名アプリ開発、2名サーバーサイド兼ハードウェア開発)

目次

続きを読む

文化祭のHPとLINE@のマップ機能を実装した話 (4/4)

はじめに

学校の文化祭で技術系の開発をする団体ができたので、活動の一環としてHPとLINE@の製作に携わりました。
このページでは主にCreateJSを使ったHPの具体的な実装方法などをまとめています。 文化祭の話については、4つのページに分けています。

1.実装した機能(HP/LINE@)全体の概要

sho0126hiro.hatenablog.com

2.LINE@のmap機能について

sho0126hiro.hatenablog.com

3.HPのmapページについて(概要とCreateJSやHTML,JSの基本事項について)

sho0126hiro.hatenablog.com

4.HPのmapページについて(実装方法、実装時の注意点などについて)(今回)

ここでは、CreateJSを用いたHPのMAP機能実装や構成についての詳細と

苦労したことや、開発上気を付けたほうが良いこと、兼忘備録や引継ぎ資料みたいなものを書いています。

目次

続きを読む