準備
OpenWeatherMapにアカウント登録しAPI Keyを発行する.
事前リサーチ1: 都市コードを調べる
上記画像URL欄末尾に表記される札幌市
の都市コード2128295
をメモする.
事前リサーチ2: ブラウザでJSONを表示させてみる
【URLの組み立て方】
下記URLに都市コード
とAPI Key
を設定する.
https://api.openweathermap.org/data/2.5/weather?id=都市コード&appid=API Key
下記JSONが表示された.
{"coord":{"lon":141.3469,"lat":43.0642},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"base":"stations","main":{"temp":273.71,"feels_like":269.44,"temp_min":273.71,"temp_max":273.71,"pressure":1020,"humidity":81},"visibility":10000,"wind":{"speed":4.17,"deg":158,"gust":7.56},"clouds":{"all":100},"dt":1617729138,"sys":{"type":3,"id":64679,"country":"JP","sunrise":1617739594,"sunset":1617786400},"timezone":32400,"id":2128295,"name":"Sapporo","cod":200}
Webサービスの作製
【システム環境】
OS X El Capitan 10.11以降(他のOSでも可)
Apache 2.4.28以降
PHP 5.5.38以降
本記事ではディレクトリ操作をMacに合せて記述しているため,他のOS(Linux, Windows等)の使用時は必要に応じてディレクトリ構造を置き換えて考えるものとする.
- ディレクトリとファイルの作成
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo
$ sudo touch /Library/WebServer/Documents/WeatherInfo/weather_info.php
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/common
$ sudo touch /Library/WebServer/Documents/WeatherInfo/common/header.php
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/my_module
$ sudo touch /Library/WebServer/Documents/WeatherInfo/my_module/openweathermap.php
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/config
$ sudo touch /Library/WebServer/Documents/WeatherInfo/config/opn_wm_api_key.php
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/css
$ sudo touch /Library/WebServer/Documents/WeatherInfo/css/weather_info_css.php
-
気象情報表示用ページ
weather_info.php
のソースコード
約10〜20分間隔でOpenWeatherMapから最新データが配信される.
<?php
require_once __DIR__ . '/common/header.php';
require_once __DIR__ . '/my_module/openweathermap.php';
# ページタイトル
$title = '気象情報';
# ブラウザ自動更新間隔(10分とする)
$reload_interval = 10 * 60;
# 都市名と都市コードの連想配列(新たな都市は都度追加)
$citycode1 = ['札幌' => 2128295, '東京都' => 1850144, '横浜市' => 1848354, '大阪市' => 1853909];
$citycode2 = ['名古屋市' => 1856057, '尾道市' => 1853992, '福岡市' => 1863967, '那覇市' => 1856035];
# 上記配列をマージ
$citycodes = array_merge($citycode1, $citycode2);
/*
気象情報を取得したい都市名を上記連想配列から選択し入力
今回は東京都とする
*/
$city = $citycodes['東京都'];
# 表示形式と都市コードを指定しJSONを連想配列にデコード
$response = getAssociative('weather', $city);
# 日付の書式1(表示用)
$date1 = getDatetime($response, 'Y年m月d日');
# 日付の書式2(曜日取得用)
$date2 = getDatetime($response, 'Ymd', 'weather');
# 曜日(日本語化)
$day = getWeek($date2);
# 時刻の書式
$time = getDatetime($response, 'H:i');
# 都市名(英語表記)
$city = $response['name'];
# 都市名(日本語化)
$city = getCityName($city);
# 天気情報の配列
$weather_data = $response['weather'];
# 現在の天気(英語表記)
$weather = $weather_data[0]['main'];
# 現在の天気(日本語化)
$weather = getWeatherTranslation($weather);
# 天気の詳細(英語表記)
$description = $weather_data[0]['description'];
# 天気の詳細(日本語化)
$description = getDescriptionTranslation($description);
# 天気アイコン
$icon = $weather_data[0]['icon'];
$img = 'https://openweathermap.org/img/wn/' .$icon .'@2x.png';
# 絶対零度
$absolute_zero = -273.15;
# 温度情報のメイン階層
$temp_data = $response['main'];
# 現在の気温,体感温度,最高気温,最低気温(小数点以下四捨五入かつ摂氏表示)
$temp = round($temp_data['temp'] + $absolute_zero);
$feels_like = round($temp_data['feels_like'] + $absolute_zero);
$temp_max = round($temp_data['temp_max'] + $absolute_zero);
$temp_min = round($temp_data['temp_min'] + $absolute_zero);
# 湿度
$humidity = $temp_data['humidity'];
# 気圧
$pressure = $temp_data['pressure'];
# 風の情報のメイン階層
$wind_data = $response['wind'];
# 風速(小数点第二位で四捨五入)
$speed = round($wind_data['speed'], 1);
# 風向(16方位で日本語化)
$deg = getDirection($wind_data['deg']);
?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="<?= $reload_interval; ?>">
<!--
時刻によって背景色と文字色を切り替えるCSSを設定
PHPを使用するので拡張子はcssではなくphpにする
-->
<link rel="stylesheet" href="css/weather_info_css.php">
<title><?= $title; ?></title>
</head>
<body>
<font>
<h1><?= $title; ?></h1>
<div><span><?= $date1; ?></span><span><?= '(' . $day . ')' ?></span></div>
<div><span><?= $time; ?></span> 時点の気象情報</div>
<hr style="border:0;border-top:1px none;">
<div>都市名 : <span><?= $city; ?></span></div>
<div>現在の天気: <span><?= $weather; ?></span></div>
<div>天気の詳細: <span><?= $description; ?></span></div>
<img src="<?= $img; ?>">
<div>現在の気温: <span><?= $temp . '℃'; ?></span></div>
<div>体感気温 : <span><?= $feels_like . '℃'; ?></span></div>
<div>最高気温 : <span><?= $temp_max . '℃'; ?></span></div>
<div>最低気温 : <span><?= $temp_min . '℃'; ?></span></div>
<div>湿度 : <span><?= $humidity . '%'; ?></span></div>
<div>気圧 : <span><?= $pressure . 'hPa'; ?></span></div>
<div>風速 : <span><?= $speed . 'm/s'; ?></span></div>
<div>風向 : <span><?= $deg . 'の風'; ?></span></div>
</font>
</body>
</html>
- ヘッダ設定ファイル
header.php
のソースコード
<?php
# HTMLを記述するコード内でのヘッダ設定
header('Content-type: text/html; charset=utf-8');
-
【試作版】メインモジュール
openweathermap.php
のソースコード
後述のWarning対策に対応していないので下記ソースコードは本採用しない.
<?php
require_once __DIR__ . '/../config/opn_wm_api_key.php';
/*
OpenWeatherMapのAPIを利用したJSONを取得し連想配列にデコードする関数
引数は表示形式と都市コード
*/
function getAssociative($api_type, $area_id) {
/*
API Keyを外部ファイルから呼び出し(.gitignore済)
OpenWeatherMapよりAPI Keyを取得して設定することも可能
*/
$api_key = opn_wm_api_key();
# ベースURL
$api_base = 'https://api.openweathermap.org/data/2.5/';
# ベースURLのパラメータ
$api_parm = '?id=' . $area_id . '&appid=' . $api_key;
# URLの連結
$api_url = $api_base . $api_type . $api_parm;
# URLの読み込み
$api_url = file_get_contents($api_url);
# JSONをデコード(第二引数をtrueにすることでJSONを連想配列にする)
$associative = json_decode($api_url, true);
# 戻り値はデコードされた連想配列
return $associative;
}
# 下記関数への引数(エラー制御演算子付き)
@$response = getAssociative($api_type, $area_id);
# 上記関数から取得した最新のUTCから指定した書式に変換する関数
function getDatetime($response, $format) {
# 最新のUNIX時刻(UTC)
$utc = $response['dt'];
# DateTimeZoneオブジェクトのインスタンスを生成しJSTを設定
$jst = new DateTimeZone('Asia/Tokyo');
/*
DateTimeオブジェクトのインスタンスを生成
UNIX時刻を日時に変換
*/
$datetime = new DateTime();
# UTCをJSTに変換
$datetime->setTimestamp($utc)->setTimeZone($jst);
# 書式設定し年月日を取得
$date = $datetime->format($format);
return $date;
}
# 日本語表記の曜日を取得する関数
function getWeek($date) {
# $date2をUNIX時刻に変換
$day = strtotime($date);
# 曜日の配列番号を取得
$day = date('w', $day);
# 曜日の配列
$week = ['日', '月', '火', '水', '木', '金', '土'];
# 連想配列の長さマイナス1
$length = count($week) - 1;
# 曜日の日本語表示の分岐
for ($i = 0; $i <= $length; $i++):
if ($day == $i):
return $week[$i];
endif;
endfor;
}
# 都市名を日本語化する関数(新たな都市は都度追加)
function getCityName($city) {
# 都市名の連想配列
$list1 = ['Sapporo' => '札幌市', 'Tokyo' => '東京都', 'Yokohama' => '横浜市', 'Osaka' => '大阪市'];
$list2 = ['Nagoya' => '名古屋市', 'Onomichi' => '尾道市', 'Fukuoka' => '福岡市', 'Naha' => '那覇市'];
# 上記配列をマージ
$lists = array_merge($list1, $list2);
# 連想配列の長さマイナス1
$length = count($lists) - 1;
# 連想配列のキー
$keys = array_keys($lists);
# 連想配列の値
$values = array_values($lists);
# 都市名の日本語表示の分岐
for ($i = 0; $i <= $length; $i++):
if($city == $keys[$i]):
return $values[$i];
endif;
endfor;
}
# 現在の天気を翻訳する関数
function getWeatherTranslation($weather) {
# 翻訳用天気名の連想配列(新たな英語表記の天気名が表示された場合は都度追加)
$list = ['Clear' => '晴れ', 'Clouds' => 'くもり', 'Rain' => '雨', 'Snow' => '雪', 'Mist' => '霧'];
# 連想配列の長さマイナス1
$length = count($list) - 1;
# 連想配列のキー
$keys = array_keys($list);
# 連想配列の値
$values = array_values($list);
# 天気名の日本語化
for ($i = 0; $i <= $length; $i++):
if ($weather == $keys[$i]):
return $values[$i];
endif;
endfor;
}
# 天気詳細を翻訳する関数
function getDescriptionTranslation($description) {
switch ($description):
case 'overcast clouds':
return 'どんよりした雲<br class="nosp">(雲85~100%)';
break;
case 'broken clouds':
return '千切れ雲<br class="nosp">(雲51~84%)';
break;
case 'scattered clouds':
return '散らばった雲<br class="nosp">(雲25~50%)';
break;
case 'few clouds':
return '少ない雲<br class="nosp">(雲11~25%)';
break;
case 'light rain':
return '小雨';
break;
case 'moderate rain':
return '雨';
break;
case 'heavy intensity rain':
return '大雨';
break;
case 'very heavy rain':
return '激しい大雨';
break;
case 'clear sky':
return '快晴';
break;
case 'shower rain':
return 'にわか雨';
break;
case 'light intensity shower rain':
return '小雨のにわか雨';
break;
case 'heavy intensity shower rain':
return '大雨のにわか雨';
break;
case 'thunderstorm':
return '雷雨';
break;
case 'snow':
return '雪';
break;
case 'light snow':
return '小雪';
break;
case 'light shower snow':
return '弱いにわか雪';
break;
case 'mist':
return '靄';
break;
case 'tornado':
return '強風';
break;
default:
return $description;
endswitch;
}
# 16方位により風向を取得する関数
function getDirection($deg) {
if ($deg >= 0 && $deg <= 10):
return '北';
elseif ($deg >= 11 && $deg <= 29):
return '北北東';
elseif ($deg >= 30 && $deg <= 60):
return '北東';
elseif ($deg >= 61 && $deg <= 79):
return '東北東';
elseif ($deg >= 80 && $deg <= 100):
return '東';
elseif ($deg >= 101 && $deg <= 119):
return '東南東';
elseif ($deg >= 120 && $deg <= 150):
return '南東';
elseif ($deg >= 151 && $deg <= 169):
return '南南東';
elseif ($deg >= 170 && $deg <= 190):
return '南';
elseif ($deg >= 191 && $deg <= 209):
return '南南西';
elseif ($deg >= 210 && $deg <= 240):
return '南西';
elseif ($deg >= 241 && $deg <= 259):
return '西南西';
elseif ($deg >= 260 && $deg <= 280):
return '西';
elseif ($deg >= 281 && $deg <= 299):
return '西北西';
elseif ($deg >= 300 && $deg <= 330):
return '北西';
elseif ($deg >= 331 && $deg <= 349):
return '北北西';
elseif ($deg >= 350 && $deg <= 360):
return '北';
endif;
}
-
API Key記述ファイル
opn_wm_api_key.php
のソースコード(下記戻り値にAPI Key
を設定)
GitHub等でのバージョン管理を考慮して外部ファイルとした.(.gitignoreに記述を推奨)
<?php
# OpenWeatherMapのAPI Keyを返す関数
function opn_wm_api_key() {
$api_key = 'API Key';
return $api_key;
}
-
画面装飾ファイル
weather_info_css.php
のソースコード
PHPでCSSを制御しシステム時刻により背景色・文字色を切り替える.
参照: PHPでCSSファイルを制御する
<?php
# タイムゾーンの設定
require_once __DIR__ . '/../common/timezone.php';
# CSSを記述するコード内でのヘッダ設定
header('Content-Type: text/css; charset=utf-8');
# 現在のシステム時刻(ゼロなしの時)
$now = date('G');
# システム時刻5:00〜16:59までとそれ以外の時刻で背景色と文字色を切り替える
$bgcol = $now >= 5 && $now < 17 ? '#87ceeb' : '#000033';
$ftcol = $now >= 5 && $now < 17 ? '#000000' : '#ffffff';
?>
body {
background-color: <?= $bgcol; ?>;
color: <?= $ftcol; ?>;
}
動作確認
Apacheの起動
$ sudo apachectl start
ブラウザ上にlocalhost/Weather/weather_info.php
を入力して動作確認を行う.
システム時刻により背景色・文字色を切り替わることが確認された.
Warning画面(ネットワーク系統の不具合発生時)
前述した【試作版】メインモジュールopenweathermap.php
のままだとLAN/WAN系,その他偶発的なネットワーク系統の不具合が発生すると以下の画面が表示される.
【Warning画面の再現方法】
使用マシンのネットワーク接続(Wi-Fi等)を切
にする.
ブラウザ上にlocalhost/Weather/weather_info.php
を入力する.
日付が1970年01月01日
と表示され,各種パラメータに異常値が表示される.
Warning対策
【下記curlコマンドに対するレスポンスの検証】
Wi-Fiを入
にした状態で下記curlコマンドを実行してみる.
グローバルIPアドレスが返ってくる.
$ curl -s ifconfig.me | grep '^[21]'
XXX.XXX.XXX.XXX
Wi-Fiを切
にした状態で下記curlコマンドを実行してみる.
グローバルIPアドレスが返ってこない.
$ curl -s ifconfig.me | grep '^[21]'
上記検証よりネットワーク系統に不具合が発生するとグローバルIPアドレスが返ってこないことが判明した.グローバルIPアドレスが返ってこない場合は専用画面を表示させ,5秒ごとに再接続を試みるようにすることでWarning対策を行う.
前述した【試作版】メインモジュールopenweathermap.php
に下記コードを追記する.
- 専用画面を表示させ,5秒ごとに再接続を試みるようにする関数をソースコードの始まりに追記
<?php
# ネットワークエラー画面を表示する関数(5秒間隔でリロード再接続を試みる)
function networkError($msg, $sec = 5) {
?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="<?= $sec; ?>">
<title><?= $msg; ?></title>
</head>
<body>
<div><?= $msg; ?></div>
<div><?= $sec; ?>秒後に再接続します</div>
</body>
</html>
<?php
exit();
}
?>
- getAssociative関数内の始まりに下記コードを追記
# グローバルIPアドレスを調べるコマンド
$command = 'curl -s ifconfig.me | grep \'^[21]\'';
# 上記コマンドからレスポンス値を取得(グローバルIPアドレス)
exec($command, $response);
# グローバルIPアドレスが返ってこないの場合のエラーメッセージ
$msg = 'Network Error..';
# グローバルIPアドレスが返ってこないの場合はエラー画面を表示する
!isset($response[0]) ? networkError($msg) : null;
-
【決定版】Warning対策済メインモジュール
openweathermap.php
デメリットとしてifconfig.me
に一旦アクセスするためレスポンスの取得が若干遅くなる.
<?php
require_once __DIR__ . '/../config/opn_wm_api_key.php';
# ネットワークエラー画面を表示する関数(5秒間隔でリロード再接続を試みる)
function networkError($msg, $sec = 5) {
?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="<?= $sec; ?>">
<title><?= $msg; ?></title>
</head>
<body>
<div><?= $msg; ?></div>
<div><?= $sec; ?>秒後に再接続します</div>
</body>
</html>
<?php
exit();
}
/*
OpenWeatherMapのAPIを利用したJSONを取得し連想配列にデコードする関数
引数は表示形式と都市コード
*/
function getAssociative($api_type, $area_id) {
# グローバルIPアドレスを調べるコマンド
$command = 'curl -s ifconfig.me | grep \'^[21]\'';
# 上記コマンドからレスポンス値を取得(グローバルIPアドレス)
exec($command, $response);
# グローバルIPアドレスが返ってこないの場合のエラーメッセージ
$msg = 'Network Error..';
# グローバルIPアドレスが返ってこないの場合はエラー画面を表示する
!isset($response[0]) ? networkError($msg) : null;
/*
API Keyを外部ファイルから呼び出し(.gitignore済)
OpenWeatherMapよりAPI Keyを取得して設定することも可能
*/
$api_key = opn_wm_api_key();
# ベースURL
$api_base = 'https://api.openweathermap.org/data/2.5/';
# ベースURLのパラメータ
$api_parm = '?id=' . $area_id . '&appid=' . $api_key;
# URLの連結
$api_url = $api_base . $api_type . $api_parm;
# URLの読み込み
$api_url = file_get_contents($api_url);
# JSONをデコード(第二引数をtrueにすることでJSONを連想配列にする)
$associative = json_decode($api_url, true);
# 戻り値はデコードされた連想配列
return $associative;
}
# 下記関数への引数(エラー制御演算子付き)
@$response = getAssociative($api_type, $area_id);
# 上記関数から取得した最新のUTCから指定した書式に変換する関数
function getDatetime($response, $format) {
# 最新のUNIX時刻(UTC)
$utc = $response['dt'];
# DateTimeZoneオブジェクトのインスタンスを生成しJSTを設定
$jst = new DateTimeZone('Asia/Tokyo');
/*
DateTimeオブジェクトのインスタンスを生成
UNIX時刻を日時に変換
*/
$datetime = new DateTime();
# UTCをJSTに変換
$datetime->setTimestamp($utc)->setTimeZone($jst);
# 書式設定し年月日を取得
$date = $datetime->format($format);
return $date;
}
# 日本語表記の曜日を取得する関数
function getWeek($date) {
# $date2をUNIX時刻に変換
$day = strtotime($date);
# 曜日の配列番号を取得
$day = date('w', $day);
# 曜日の配列
$week = ['日', '月', '火', '水', '木', '金', '土'];
# 連想配列の長さマイナス1
$length = count($week) - 1;
# 曜日の日本語表示の分岐
for ($i = 0; $i <= $length; $i++):
if ($day == $i):
return $week[$i];
endif;
endfor;
}
# 都市名を日本語化する関数(新たな都市は都度追加)
function getCityName($city) {
# 都市名の連想配列
$list1 = ['Sapporo' => '札幌市', 'Tokyo' => '東京都', 'Yokohama' => '横浜市', 'Osaka' => '大阪市'];
$list2 = ['Nagoya' => '名古屋市', 'Onomichi' => '尾道市', 'Fukuoka' => '福岡市', 'Naha' => '那覇市'];
# 上記配列をマージ
$lists = array_merge($list1, $list2);
# 連想配列の長さマイナス1
$length = count($lists) - 1;
# 連想配列のキー
$keys = array_keys($lists);
# 連想配列の値
$values = array_values($lists);
# 都市名の日本語表示の分岐
for ($i = 0; $i <= $length; $i++):
if($city == $keys[$i]):
return $values[$i];
endif;
endfor;
}
# 現在の天気を翻訳する関数
function getWeatherTranslation($weather) {
# 翻訳用天気名の連想配列(新たな英語表記の天気名が表示された場合は都度追加)
$list = ['Clear' => '晴れ', 'Clouds' => 'くもり', 'Rain' => '雨', 'Snow' => '雪', 'Mist' => '霧'];
# 連想配列の長さマイナス1
$length = count($list) - 1;
# 連想配列のキー
$keys = array_keys($list);
# 連想配列の値
$values = array_values($list);
# 天気名の日本語化
for ($i = 0; $i <= $length; $i++):
if ($weather == $keys[$i]):
return $values[$i];
endif;
endfor;
}
# 天気詳細を翻訳する関数
function getDescriptionTranslation($description) {
switch ($description):
case 'overcast clouds':
return 'どんよりした雲<br class="nosp">(雲85~100%)';
break;
case 'broken clouds':
return '千切れ雲<br class="nosp">(雲51~84%)';
break;
case 'scattered clouds':
return '散らばった雲<br class="nosp">(雲25~50%)';
break;
case 'few clouds':
return '少ない雲<br class="nosp">(雲11~25%)';
break;
case 'light rain':
return '小雨';
break;
case 'moderate rain':
return '雨';
break;
case 'heavy intensity rain':
return '大雨';
break;
case 'very heavy rain':
return '激しい大雨';
break;
case 'clear sky':
return '快晴';
break;
case 'shower rain':
return 'にわか雨';
break;
case 'light intensity shower rain':
return '小雨のにわか雨';
break;
case 'heavy intensity shower rain':
return '大雨のにわか雨';
break;
case 'thunderstorm':
return '雷雨';
break;
case 'snow':
return '雪';
break;
case 'light snow':
return '小雪';
break;
case 'light shower snow':
return '弱いにわか雪';
break;
case 'mist':
return '靄';
break;
case 'tornado':
return '強風';
break;
default:
return $description;
endswitch;
}
# 16方位により風向を取得する関数
function getDirection($deg) {
if ($deg >= 0 && $deg <= 10):
return '北';
elseif ($deg >= 11 && $deg <= 29):
return '北北東';
elseif ($deg >= 30 && $deg <= 60):
return '北東';
elseif ($deg >= 61 && $deg <= 79):
return '東北東';
elseif ($deg >= 80 && $deg <= 100):
return '東';
elseif ($deg >= 101 && $deg <= 119):
return '東南東';
elseif ($deg >= 120 && $deg <= 150):
return '南東';
elseif ($deg >= 151 && $deg <= 169):
return '南南東';
elseif ($deg >= 170 && $deg <= 190):
return '南';
elseif ($deg >= 191 && $deg <= 209):
return '南南西';
elseif ($deg >= 210 && $deg <= 240):
return '南西';
elseif ($deg >= 241 && $deg <= 259):
return '西南西';
elseif ($deg >= 260 && $deg <= 280):
return '西';
elseif ($deg >= 281 && $deg <= 299):
return '西北西';
elseif ($deg >= 300 && $deg <= 330):
return '北西';
elseif ($deg >= 331 && $deg <= 349):
return '北北西';
elseif ($deg >= 350 && $deg <= 360):
return '北';
endif;
}
今回は簡易版としてAPIを使いOpenWeatherMapから気象情報が取得できるWebサービスをPHPで作成する方法について記述したが,次回は応用編としてOpenWeatherMapから取得した気象情報をDBに記録する方法に関して記述予定である.