緯度経度から天気予報を取得し投稿詳細画面に表示する

# １．はじめに
ポートフォリオに天気予報を追加したので、知識の定着、復習のために記事を書きます。
この記事を、ご覧いだだくことで緯度経度から天気予報を取得し表示できます。

# 2.環境
Ruby (2.6.4)
Rails (6.1.7)

# 3.前提条件
- 投稿された情報から緯度経度を取得できている。
- 実装されていない方は私が以前書いた下記記事を参考に実装してみてください。
[GoogleMapsAPIを使用した開発1（地図の表示）
](https://qiita.com/uuuDi_97722/items/6a064cf7e40d91a517c0)
- `openweathermap`のキーを取得している
- まだの方は[OpenWeatherMap公式](https://openweathermap.org/)をみて取得してください。

# 4.実装

## 1.gem`httparty`を導入する
- HTTPartyを使用するために、Gemfile に gem 'httparty' を追加してください。
- その後ターミナルで`bundle install`を実行してGemをインストールしてください。

```ruby:Gemfile
gem 'httparty'
```

## 2.緯度経度から天気予報の情報を取得する
```ruby
class PostsController < ApplicationController
  require 'httparty' # ①

  def show # ②
    @weather_data = get_weather_forecast(@post.latitude, @post.longitude)
  end

  private

  def get_weather_forecast(latitude, longitude)
    api_key = ENV['OPENWEATHERMAP_API_KEY'] # ③
    api_url = "https://api.openweathermap.org/data/2.5/forecast?lat=#{latitude}&lon=#{longitude}&appid=#{api_key}&lang=ja&units=metric" # ④
    response = HTTParty.get(api_url) # ⑤
    weather_data = JSON.parse(response.body) # ⑥
    return weather_data # ⑦
  end
end
```
①`require 'httparty'`は、HTTPartyをアプリケーション内で使用するための記述です。

②showアクションで`get_weather_forecastメソッド`(後述します)を呼び出しています。
- 引数には投稿された情報（緯度、経度）を渡しており、OpenWeatherMapAPIから天気予報データを取得します。
- 取得したデータは`@weather_data`に格納され、後ほどビューで使用します。

③`api_key = ENV['OPENWEATHERMAP_API_KEY']`でAPIキーを取得しています。
APIキーは環境変数で設定しています。

④`api_url = "https://api.openweathermap.org/data/2.5/forecast?lat=#{latitude}&lon=#{longitude}&appid=#{api_key}&lang=ja&units=metric"`でAPIリクエストのURLを構築しています。
- latとlonは引数として与えられた緯度と経度です。
- appidにはAPIキーが、langには言語（日本語）、unitsには温度の単位（摂氏）を指定しています。

⑤- `response = HTTParty.get(api_url)`でHTTPartyを使用して、構築したAPIリクエストを発行し、APIからのレスポンスを取得しています。

⑥`weather_data = JSON.parse(response.body)`でJSON.parseメソッドを使用してJSONをRubyのハッシュに変換しています。
- JSON.parseメソッドとは（簡単に）
「JSONデータ」は、単純なテキストデータであり文字列でもあります。そのためプログラムで利用しやすい形式に変換する必要があります。それを実現してくれるのが「JSON.parse()」メソッドです。

⑦`return weather_data`変換された天気予報データを呼び出し元に返します。

## 3.viewの記述
```ruby
        <% if @weather_data.present? %> # ①
        <h2>5日間の天気予報</h2>
        <% daily_forecasts = @weather_data['list'].group_by { |forecast| Time.at(forecast['dt']).strftime('%Y-%m-%d') } %> # ②
          <% daily_forecasts.each do |date, forecasts| %> # ③
              <% forecasts.each do |forecast| %> # ④
              <% if Time.at(forecast['dt']).hour == 12 %>
                <p><strong><%= Time.at(forecast['dt']).strftime('%m-%d %H:%M') %></strong></p>
                <p>気温: <%= forecast['main']['temp'].to_i %>度</p>
                <p>天気: <%= forecast['weather'][0]['description'] %></p>
                <img src="https://openweathermap.org/img/wn/<%= forecast['weather'][0]['icon'].gsub('n', 'd') %>@2x.png" alt="Weather Icon">
                <% if forecast['rain'].present? && forecast['rain']['3h'].present? %>
                <p>降水確率: <%= forecast['rain']['3h'] %>mm</p>
                <% else %>
                <p>降水確率: 0mm</p>
                <% end %>
              <% end %>
              <% end %>
          <% end %>
        <% else %> # ⑤
        <p>天気情報が取得できませんでした。</p>
        <% end %>
```
①`<% if @weather_data.present? %>`
- `@weather_data`が存在するかどうかを確認しています。
- 存在する場合は天気予報を表示し、存在しない場合はエラーメッセージを表示します。

②`daily_forecasts = @weather_data['list'].group_by { |forecast| Time.at(forecast['dt']).strftime('%Y-%m-%d') }`
- group_byメソッドを使用して、`@weather_data['list']`内の天気予報データを日付ごとにグループ化しています。
- Time.at(forecast['dt']) は、UNIXタイムスタンプをRubyのTimeオブジェクトに変換しています。
- strftime('%Y-%m-%d') は、Timeオブジェクトを年-月-日の形式に変換しています。

③`<% daily_forecasts.each do |date, forecasts| %>`
- daily_forecastsハッシュから日付とその日付に対応する予報データの配列を取り出して、それを元にループを行います。
- このループは、日付ごとの予報データを処理するためのもので、dateには日付（キー）が、forecastsにはその日付に対応する予報データの配列（値）が入ります。

④`<% forecasts.each do |forecast| %>`
- 各日付ごとの予報データの配列（forecasts）を処理するためのループです。

- `<% if Time.at(forecast['dt']).hour == 12 %>`は、その中で各予報データの時間が12時のものだけを選別する条件文です。ここでは、12時の予報データだけを表示しています。

- その後、データに対して以下の情報を表示しています
- 予報の日時`（<%= Time.at(forecast['dt']).strftime('%m-%d %H:%M') %>）`
- 気温`（<p>気温: <%= forecast['main']['temp'].to_i %>度</p>）`
- 天気の説明`（<p>天気: <%= forecast['weather'][0]['description'] %></p>）`
- 天気のアイコン`（<img src="https://openweathermap.org/img/wn/<%= forecast['weather'][0]['icon'].gsub('n', 'd') %>@2x.png" alt="Weather Icon">）`
`gsub('n', 'd')`で、取得したアイコンの文字列が'n'（夜のアイコン）の場合に'd'（昼のアイコン）に置き換えています。これにより、常時昼間のアイコンを表示させることができます。
- `<% if forecast['rain'].present? && forecast['rain']['3h'].present? %>`は、予報データに雨が含まれており、かつ3時間降水量のデータが存在する場合に、降水確率を表示しています。

⑤`@weather_data`が存在しない場合はエラーメッセージを表示します。

# 5.おわりに
以上で緯度経度から天気予報を取得できるようになります。
意外と簡単に実装できるので実装したいと思っている方はぜひチャレンジしてみてください。