天気予報アプリ（ライブカメラで渋谷を見てみた）

## 天気予報＋実際の外の様子をライブカメラで見たい
このWebアプリを作るきっかけは今、自分が挑戦しているクラウドファンディングで天気に関する情報が欲しいという願望からである。また、テレビの天気予報ではよくライブカメラの映像を使っており、個人的にそのライブ映像の歩行者をみて、服装選びや傘の有無を判断する材料になるので取り入れてみた。

↓クラウドファンディングのリンクを載せておくので是非見てほしい。

https://camp-fire.jp/projects/597735/preview?token=26uwa33u

## どんなWebアプリ？
今日、明日、明後日の天気を画像付きで表示をするアプリ。
3日間の天気予報に加えて、アプリを開いた時点の渋谷スクランブルスクエアからのライブカメラ画像を表示してくれる。
完成品はこれ↓

https://thriving-tarsier-869ddc.netlify.app

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618860/84132adc-d750-6e04-19f3-c4ba3cc3e447.png)

### １. 今日、明日、明後日の天気とアイコンを表示する。
今回は天気予報APIを用いて天気の情報（日にち、天気、気温、画像）を取得をした。
コードは下に添付してあるのでそちらを確認してほしい。

https://weather.tsukumijima.net/

### ２.ライブカメラの映像を表示する。
天気予報ではよくライブカメラの映像を使っているので使いたい！！
そう思ってググっていると **Webcams.travel** というAPIを発見した。
これを使うことで世界中のライブカメラにアクセスでき、画像や動画として取得することができる。

https://rapidapi.com/webcams.travel/api/webcams-travel

#### 渋谷駅から半径1キロ以内のライブカメラの映像を取得するには

エンドポイントに
/webcams/list/nearby=**35.657764,139.701591,1**
と指定することによって渋谷駅の周辺（1km）に存在するライブカメラの映像を取得することができる。
**緯度=35.657764,経度=139.701591,範囲(km)=1**という順番で指定をしている。

```javascript:ライブカメラの画像を取得
// 緯度(lat),経度(lng),範囲(km)を設定している
url: 'https://webcamstravel.p.rapidapi.com/webcams/list/nearby=35.657764,139.701591,1,1',
```

この設定でリクエストを投げることにより、json形式でレスポンスが返されるので必要な情報（ライブカメラの映像、設置場所の情報等）を変数に格納すればよい。
今回の設定の場合（渋谷駅の半径1km以内のライブカメラ）だと2台のライブカメラが見つかり、渋谷スクランブルスクエアの画像を使用した。
↓の画像のようにGoogleマップではピンを刺した場所の緯度、経度を取得できるのでこちらをエンドポイントとして設定してあげればよい。

![監視カメラ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618860/61a3e103-9e55-6672-7dd8-9b2cb56977a6.png "今回のエンドポイント")

### 実装できなかった点（レスポンスを変数に格納できない）
ライブカメラの映像を取得する際、axios.requestでwebcams.travel APIにリクエストをしてそのレスポンスとしてjsonが返ってくる。その中に、ライブカメラの映像、設置場所の情報等があるのでそれをdata内のwebcamに格納したいのだが、その際にエラーが発生してしまった。原因は**スコープによるものでwebcamを関数内で参照できない。** ということはわかったのだが、vue.jsの理解が浅く解決することができなかった。
この問題はdata内にwebcamの初期値としてライブカメラの画像が表示されるurlを指定することでとりあえずはアプリ内に表示をさせることはできた。このurlは動的でないため、一応リアルタイムで渋谷の映像は表示ができている。
```javascript:jsonを取得する部分
axios.request(options).then(function (response) {
  webcam_data = response.data.result.webcams
  console.log(webcam_data[1].title,webcam_data[1].image.current.preview);
}).catch(function (error) {
  console.error('エラー：',error);
});
```

## 感想
今回初めてWebアプリの作成を行ったが、BootstrapやAPIを活用することで簡単にそれっぽいものが作成できることがわかった。ただ、Vue.jsとHTMLを組み合わせてひとつのアプリとして動かすのが難しかった。特に3日分の天気についての情報をhtmlに表示する際に、v-bindという要素に含まれる属性を動的に設定・変更するために必要な技術など色々と知らないことばかりだったのでフロント側の知識不足を改めて感じた。

https://codelikes.com/vue-v-bind/


## ソースコード

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="abqEozZ" data-user="kosuke05" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kosuke05/pen/abqEozZ">
  WebCam</a> by kosuke05 (<a href="https://codepen.io/kosuke05">@kosuke05</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<details><summary><b>HTML</b></summary><div>

```html:HTML
<!-- 全体をVue.js有効にする -->
<div class="container text-center p-3 mb-2 bg-dark text-white" id="app">
  
  <!-- タイトル行 -->
  <div class="row my-3">
    <div class="col-sm-6 mx-auto"><h1>Weather APP</h1></div>
  </div>
<!--今日を含め3日間の天気を表示 -->
  <div class="col-sm-6 mx-auto" id="app01" >
  場所：{{ prace }}<br><br>
  <dl v-for="obj01 in object">
    {{ obj01.date }}：{{ obj01.weather }}<br><br>
    <img v-bind:src=obj01.image>
  </div>
<!--WebCamの映像を表示  -->
  <div id="app02" >
  <dl v-for="obj02 in webcam">
    場所：{{ obj02.prace }}<br>
    <img v-bind:src=obj02.image>
  </div>
</div>
```

</div></details>

<details><summary><b>Vue</b></summary><div>

```javascript:Vue
const app = new Vue({
  el: '#app', // Vueが管理する一番外側のDOM要素
  data: {
    // Vue内部で使いたい変数は全てこの中に定義する
    prace: '',
    // 天気予報のためのオブジェクトを定義
    object:[{
      date:'',
      weather: '',
      min_temperature:'',
      max_temperature:'',
      image:''
    }],
    // ライブカメラのオブジェクトを定義
    webcam :[{
      prace:'Shibuya: Shibuya Scramble Square',
      image:'https://images-webcams.windy.com/79/1626679779/current/preview/1626679779.jpg'
    }],
  },
  methods: {
    // 関数はここ
    getInfected: async function() {
      let response;
      try {
        // 天気のデータを取得
        response = await axios.get('https://weather.tsukumijima.net/api/forecast/city/130010');
        // 場所のデータを取得する
        this.prace = response.data.title;
        w_data = response.data.forecasts;
        this.object.shift();
        // プロパティーとして日にち、天気、気温、画像を追加する
        for(let i=0;i<3;i++){
          this.object.push({date: w_data[i].date,weather : w_data[i].telop,min_temperature:w_data[i].temperature.min.celsius,max_temperature:w_data[i].temperature.max.celsius,image:w_data[i].image.url});
        }     
      } catch (error) {
        console.error(error);
      }
    },
    getWebCam: async function(){
      const options = {
        method: 'GET',
        // 緯度(lat),経度(lng),範囲(km)を設定している
        url: 'https://webcamstravel.p.rapidapi.com/webcams/list/nearby=35.6561943,139.6994324,1',
        params: {lang: 'en', show: 'webcams:image,location'},
        headers: {
          'X-RapidAPI-Host': 'webcamstravel.p.rapidapi.com',
          'X-RapidAPI-Key': 'ccfe588e01msh1f24264e1abd3c3p15beb0jsn6b60edfd33b1'
        }
      };
      axios.request(options).then(function (response) {
        webcam_data = response.data.result.webcams
        console.log(webcam_data[1].title,webcam_data[1].image.current.preview);
      }).catch(function (error) {
        console.error('エラー：',error);
      });
    }
  },
  // データが扱えるようになったら以下が実行される
  created: async function() {
    this.getInfected();
    this.getWebCam();
  },
  // DOMを構築し、画面に表示完了したら以下が実行される
  mounted: async function() {
    // 今回は使わない・ロード後にDOMの構造が変更されるような場合などに使う
  },
});
```

</div></details>

### 参考サイト

https://blog.api.rakuten.net/ja/api-tutorial-webcams-travel-jp/