天気をオフラインで予測できるスマホアプリを作る


# はじめに

カラッと晴れた日、気持ち良いですね。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136288/eae065b8-06b8-8c76-b398-0ba26f8692e1.png" width=50%>
<font color="Gray">写真は奈良県・大台ヶ原山で撮影</font>

こんな日は、バイクでのお出かけが捗ります。
しかし、私はこの時はまだ、知るよしもなかったのです(山中で圏外の為)... この後大雨に打たれることに・・・


こんな**辛い思いをしたくない**ということで、オフラインで動作する晴雨計アプリを作ります。
オフラインなので、災害時やイベントでの回線混雑時など、ネットワークに接続しづらいあらゆる状況でも使用できるかもしれません。

本記事では、新しい技術を活用するといった内容ではありませんが、**スマホ内蔵のセンサを使用することにより一風変わったアプリが作れる**ということが伝わればと思っています。

また、本記事は[DeNA20新卒アドベントカレンダー](https://qiita.com/advent-calendar/2019/dena-20-shinsostu)として執筆させていただいています。このような執筆のきっかけをいただけて感謝です。

※高精度な天気予測を目指すものではありません。また、私は気象の専門家でもありませんのであしからず。


----

# 天気予測の方針

#### スマートフォン内蔵の気圧センサの利用
スマートフォンには加速度・ジャイロ・地磁気、照度、近接、気圧、GPSなど多種多様なセンサが内蔵され、オフライン環境下でも、身の回りの情報を取得することができます。
これらセンサのうち、気圧センサは天気予測に使えるかもしれません。**天候と気圧は密接な関係がある**と言われているためです。
上昇気流により雲が発生している間は、地上では空気が薄くなります。そのため、一般的には、気圧が下がると雨が降ると言われています。
実際に気圧計が17世紀に発明されて以降、気圧は天気予報のバロメータとして漁師や農家に重宝されてきたそうです[^1]。
現在でも、登山用の一部の時計に気圧ベースの天気予測機能を搭載した製品が販売されています。

[^1]: Weather Doctor's Weather People and History: The Invention of the Barometer, http://www.islandnet.com/~see/weather/history/barometerhistory1.htm

#### 高度による海面気圧への補正
気圧のみを使用する晴雨予測の一つの弱点として、高度の変化に比例して気圧も変わってしまうことが挙げられます。特にスマートフォンは持ち歩いて使うため尚更影響を受けます。ですが、幸いなことに、スマートフォン搭載のGPSにより、高度情報を取得できます。下記の式[^2]により気圧センサの値を海面気圧に補正して、高度による気圧変化の影響を低減します。

$$ \normalsize P_0=P\left(1-{\large\frac{0.0065h}{T+0.0065h+273.15}}\right)^{-5.257}\ ・・・ (1) $$ 
h: 現在地の標高
P: 現在地の気圧
T: 気温 (今回は15度に設定)

[^2]: 海面更正気圧の計算, http://www.es.ris.ac.jp/~nakagawa/met_cal/sea_press.html

#### 海面気圧を基にした天気予測

天気の予測には機械学習を使いたいところですが、~~修論で時間がなかなか取れないため、~~今回は決めうちで気圧と天気を紐付けます。
下図は、気象庁公開[^3]の奈良における2018年まるまる1年間の気象データを元に作成した、降水量毎の平均海面気圧です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136288/233c3aa3-6f79-e686-4a11-d09fef1964f2.png)

基本的に、降水量が増えるに従って、平均気圧が低くなっているのがわかります。
雨が降り始めるのは、降水量0mmと0.5mmの間であると考えられるので、それぞれの平均気圧である1016hPaと1011.8hPaの中間である1013.9hPaを基準としてこれより低ければ雨、高ければ晴といった具合に設定することにします。

なお、気象庁の公開している過去の気象データは[こちら](http://www.data.jma.go.jp/obd/stats/etrn/index.php)から表示・ダウンロードできます。

[^3]: 気象庁|過去の気象データ・ダウンロード, http://www.data.jma.go.jp/gmd/risk/obsdl/index.php


# アプリの作成

## 要件
* オフラインで動作すること
    * 気象APIは使用せず、端末内のセンサの値のみ使用
* スマートフォン(iOS)上で動作すること
    * Swift言語で実装

## 実装

#### 気圧の取得
```swift
    import CoreMotion   // 気圧取得(CMAltimeter)に必要

    let altimeter = CMAltimeter()
    public private(set) var pressure: Double?
    // 気圧取得開始のリクエスト
    func requestPressure() {
        if (CMAltimeter.isRelativeAltitudeAvailable()) {
            altimeter.startRelativeAltitudeUpdates(to: .main, withHandler:
                {
                    data, error in
                    if error == nil {
                        self.pressure = Double(truncating: data!.pressure) * 10 // hPaで取得
                    }
            })
        }
    }
```

#### 高度の取得
```swift
    import CoreLocation // 高度取得(CLLocationManager)に必要

    let locationManager = CLLocationManager()
    public private(set) var altitude: Double?

    // 位置情報取得権限のリクエスト
    func requestAuthorization() {
        locationManager.requestAlwaysAuthorization()
    }
    
    // 位置情報(高度情報)取得のリクエスト
    func requestLocation() {
        if (CLLocationManager.locationServicesEnabled()) {
            locationManager.delegate = self
            locationManager.startUpdatingLocation()
        }
    }
    
    // 位置情報(高度情報)が更新されるたびに呼ばれる
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        altitude = locations.first!.altitude
    }
```

位置情報取得権限を得るにはInfo.plistに次の内容の追記が必要です。
```Privacy - Location When In Use Usage Description```

#### 海面気圧への補正処理
式 (1)を使い計算

```swift

    func calcSeaLevelPressure(altitude: Double, pressure: Double) -> Double {
        let temperature = 15.0
        let num1 = 1.0 - ( 0.0065 * altitude) / (temperature + 0.0065 * altitude + 273.15)
        let num2 = pow(num1, -5.257)
        let seaLevelPressure = pressure * num2
        return seaLevelPressure
    }
```


## 開発したアプリ

#### アプリ概要
次の様なUIのアプリが完成しました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136288/88023022-4983-7eb0-026d-080e1b17033a.png)

それぞれの機能は次の通り。
① 気圧に応じて、該当する天気アイコンが出現します。天気アイコン下のバーは気圧を表します。
② 気圧(海面補正済)を表示します。
③ 過去の気圧変化をベースに天候の変化を予測します。今回は気圧が上昇していれば雨→晴、下降していれば晴→雨と表示しているだけです。
④ 最大で過去12時間の気圧の変化をグラフで表示します。

GitHubは[こちら](https://github.com/rirex5/OffWeather)。

#### 使用結果
二日しか試していませんが、晴れと曇りは割と当たっているような?
まだ、雨の日に試すことができていませんので、追って結果を掲載できればと思います。

#### 今後について
まずはリリースする。
次に、急激な気圧下降時に通知してくれる機能などを追加したい。
最終的には気圧を入力として、数時間後が晴・曇り・雨かを出力とする機械学習モデルを取り入れたりもしてみたい。

# おわりに
スマホだけでの天気予測は実用的な精度が出ないかもしれませんが、取り組みとして、面白いなと感じて頂けたのであれば幸いです。
また、スマホは身近なセンサの塊でもあり、色々と遊ぶことができますので、皆さんも是非試してもらえると！

さてQiita投稿を終えたところで、修論を書きに大学に戻ります。
早速このアプリで天気が晴れであることを確認してから向かうこととします。(嫌な予感しかしない)

最後までお読みくださりありがとうございました！