Obsidianのデイリーノートに今日の天気を埋め込みたい（その2）

### この記事でできること

以下のような天気情報をObsidianのdaily noteなどに埋め込めるようになります。

```
Tokyoの天気 | 🌅05:54 / 🌇17:47
00:00 | 曇り　　　　 | 10°C |   0% |  0.0mm | 1023hPa | ↖️ 16km/h |  27%
03:00 | 所により曇り | 12°C |   0% |  0.0mm | 1023hPa | ↖️  2km/h |  26%
06:00 | 晴れ時々曇り | 15°C |   0% |  0.0mm | 1021hPa | ↘️ 15km/h |  31%
09:00 | 快晴　　　　 | 15°C |   0% |  0.0mm | 1022hPa | ↙️  8km/h |  44%
12:00 | 快晴　　　　 | 11°C |   0% |  0.0mm | 1025hPa | ↖️ 22km/h |  33%
15:00 | 晴れ　　　　 |  9°C |   0% |  0.0mm | 1025hPa | ↖️ 23km/h |  41%
18:00 | 所により曇り |  8°C |   0% |  0.0mm | 1025hPa | ↖️ 22km/h |  44%
21:00 | 曇り　　　　 |  7°C |   0% |  0.0mm | 1026hPa | ↖️ 19km/h |  38%
```

### 背景

前記事（[Obsidianのデイリーノートに今日の天気を埋め込みたい - Qiita](https://qiita.com/alumite14/items/fe05a85c2ef4f4ea608b)）の続きです。
[前記事](https://qiita.com/alumite14/items/fe05a85c2ef4f4ea608b)ではwttr.inというサービスを使ってObsidian上に天気を表示させていましたが、私の住む地域の天気を取得しようとするとエラーが頻発したので（特に朝早くの起動時）、代替案としてOpenWeatherMapというサービスを使って天気表示させました。
この記事はその方法についてまとめたものです。

### OpenWeatherMapとは？

- [Current weather and forecast - OpenWeatherMap](https://openweathermap.org/)

- 天気情報を提供するweb apiサービスです
- wttr.inとは違い会員登録が必要です
- 無料枠があります（今回使うのはこれ）
- wttr.inよりもいつでも安定して情報を取得できます
- wttr.inよりも細かい地域選択が可能です（後述）

会員登録やapi keyの取得方法については[無料天気予報APIのOpenWeatherMapを使ってみる - Qiita](https://qiita.com/nownabe/items/aeac1ce0977be963a740)を参考にしてください（api keyは次の画像の場所で作成・確認可能です）。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665424/b225cc2d-7352-4ff5-ae87-6c11d686ada7.png)

以下では会員登録＆api keyを取得した前提で話します。
そうでないなら他記事を見て作っておいてください（Templaterの使い方についてはこちら：[ObsidianのTemplaterの使い方と設定 | eiji.page](https://eiji.page/blog/obsidian-templater-howto/)）。

### OpenWeatherMapからの情報取得

次の構文で取得可能です。
都市名指定：
`http://api.openweathermap.org/data/2.5/forecast?q={city_name}&appid={api_key}`
都市ID指定：
`http://api.openweathermap.org/data/2.5/forecast?id={city_id}&appid={api_key}`
郵便番号指定：
`https://api.openweathermap.org/data/2.5/forecast?zip={zip_num},JP&appid={api_key}`

郵便番号指定のほうが特定地域の都市指定が楽なのでおすすめです。
都市名・都市IDについては登録されていないと使えないです（多分）。
登録の有無の確認はこちら：[Index of /sample/](https://bulk.openweathermap.org/sample/)
その他の詳細はドキュメントを読んでください：[Current weather data - OpenWeatherMap](https://openweathermap.org/current)

後の流れは[前記事](https://qiita.com/alumite14/items/fe05a85c2ef4f4ea608b)と同じで、取得したjsonを整形するjsファイルを作成しTemplaterに登録することで利用可能となります。一例として私が作ったものを載せておきます。お好きに改変してください。

```javascript:getWeather3.js
const formatTime = (dateTimeStr) => {
  const date = new Date(dateTimeStr);
  return date.toLocaleTimeString('ja-JP', { hour: '2-digit', minute: '2-digit', hour12: false });
};

const padJapanese = (str, length) => {
  const padding = '　'.repeat(Math.max(0, length - str.length));
  return str + padding;
};

const getJapaneseDescription = (description) => {
  const descMap = {
    'clear sky': '快晴',
    'few clouds': '晴れ',
    'scattered clouds': '晴れ時々曇り',
    'broken clouds': '所により曇り',
    'overcast clouds': '曇り',
    'light rain': '小雨',
    'shower rain': '霧雨',
    'moderate rain': '雨',
    'thunderstorm': '雷雨',
    'heavy rain': '大雨',
    'snow': '雪',
    'light snow': '小雪',
    'mist': '霧',
    'thunderstorm with light rain': '弱い雨を伴う雷雨',
    'thunderstorm with rain': '雨を伴う雷雨',
    'thunderstorm with heavy rain': '強い雨を伴う雷雨',
    'light thunderstorm': '弱い雷雨',
    'heavy thunderstorm': '強い雷雨',
    'ragged thunderstorm': '断続的な雷雨',
    'thunderstorm with light drizzle': '弱い霧雨を伴う雷雨',
    'thunderstorm with drizzle': '霧雨を伴う雷雨',
    'thunderstorm with heavy drizzle': '強い霧雨を伴う雷雨',
    'light intensity drizzle': '弱い霧雨',
    'drizzle': '霧雨',
    'heavy intensity drizzle': '強い霧雨',
    'light intensity drizzle rain': '弱い霧雨混じりの雨',
    'drizzle rain': '霧雨混じりの雨',
    'heavy intensity drizzle rain': '強い霧雨混じりの雨',
    'shower rain and drizzle': '霧雨を伴うにわか雨',
    'heavy shower rain and drizzle': '強い霧雨を伴うにわか雨',
    'shower drizzle': 'にわか霧雨',
    'rain': '雨',
    'very heavy rain': '非常に強い雨',
    'extreme rain': '猛烈な雨',
    'freezing rain': '凍雨',
    'light intensity shower rain': '弱いにわか雨',
    'heavy intensity shower rain': '強いにわか雨',
    'ragged shower rain': '断続的なにわか雨',
    'heavy snow': '大雪',
    'sleet': 'みぞれ',
    'light shower sleet': '弱いにわかみぞれ',
    'shower sleet': 'にわかみぞれ',
    'light rain and snow': '弱い雨雪',
    'rain and snow': '雨雪',
    'light shower snow': '弱いにわか雪',
    'shower snow': 'にわか雪',
    'heavy shower snow': '強いにわか雪',
    'smoke': '煙',
    'haze': 'もや',
    'sand/dust whirls': '砂塵旋風',
    'fog': '濃霧',
    'sand': '砂',
    'dust': '粉塵',
    'volcanic ash': '火山灰',
    'squalls': 'スコール',
    'tornado': '竜巻'
  };
  return descMap[description] || description;
};


const convertTimestamp = (timestamp) => {
  const date = new Date(timestamp * 1000);
  const hours = date.getHours().toString().padStart(2, '0');
  const minutes = date.getMinutes().toString().padStart(2, '0');
  return `${hours}:${minutes}`;
}


async function formatWeatherData(weatherData) {
  const city = weatherData.city.name
  const { list } = weatherData;

  const sunrise = convertTimestamp(weatherData.city.sunrise); // 日の出時刻
  const sunset = convertTimestamp(weatherData.city.sunset);  // 日の入時刻


  const formattedData = list.slice(0, 7).map(period => {
    const {
      dt_txt,
      weather,
      main: { temp, pressure, humidity },
      pop,
      rain,
      wind: { speed, deg }
    } = period;

    const time = formatTime(dt_txt);
    const iconCode = weather[0].icon
    const iconImageUrl = `![icon|16](https://openweathermap.org/img/wn/${iconCode}@2x.png)`; // 画像URLを生成
    const description = getJapaneseDescription(weather[0].description);
    const paddedDesc = padJapanese(description, 6);
    const temperature = `${Math.round(temp - 273.15)}°C`;
    const rainChance = `${Math.round(pop * 100)}`;
    const precipitation = rain ? `${rain['3h'].toFixed(1)}` : '0.0';
    const windSpeed = `${Math.round(speed * 3.6)}`;
    //let compassSector = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"];
    let compassSector = ["⬆️", "↗️", "↗️", "↗️", "➡️", "↘️", "↘️", "↘️", "⬇️", "↙️", "↙️", "↙️", "⬅️", "↖️", "↖️", "↖️", "⬆️"];
    const windDirection = compassSector[(deg / 22.5).toFixed(0)];
    
    return `${time} | ${iconImageUrl} ${paddedDesc} | ${temperature.padStart(4)} | ${rainChance.padStart(3)}% | ${precipitation.padStart(4)}mm | ${pressure}hPa | ${windDirection} ${windSpeed.padStart(2)}km/h | ${humidity.toString().padStart(3)}%`;
  });

  return `${city}の天気 | 🌅${sunrise} / 🌇${sunset}\n${formattedData.join('\n')}`;
}

async function weathernow() {
  const cityName = // 取得したい都市名を入力
  const api_key = // 取得した自分のapiを入力
//  const response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?id=${cityID}&appid=${api_key}`);
//  const response = await fetch(https://api.openweathermap.org/data/2.5/forecast?zip={zipCode},JP&appid=${api_key});
  const response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?q=${cityName}&appid=${api_key}`);
  const weatherData = await response.json();
  return formatWeatherData(weatherData);
}

module.exports = weathernow;

```

## まとめ

素敵なObsidianライフを🖖。