GRASS GISで日照時間を計算してみる！！

# GRASS GISで日照時間を計算してみる！！

## はじめに

この記事は、FOSS4G Advent Calendar 2020 の　12/24の記事です。　・・・えええ、
すでに26日で、遅れておりますが（汗

事の始まりは、[QGIS 初心者質問グループ](https://groups.google.com/g/qgisshitumon01) の [任意の月日,時刻の日射分布図](https://groups.google.com/g/qgisshitumon01/c/1jA4IxBky2k/m/d0ECqXHQBAAJ)という投稿でした。こうした機能の多くは、QGISそのものよりも、[GRASS GIS](https://grass.osgeo.org/)や[SAGA](http://www.saga-gis.org/en/index.html)の機能を利用することが多いです。
今回は、[GRASS GIS](https://grass.osgeo.org/)を使って、任意の日時の日照時間、および、それを累積した1日での累積日照時間の算出方法について、説明します。

・・・すみません、QGISでスマートにやる方法がわからなかttので、GRASSでやりたいと思います（汗

なお、手順については、こちらの[word ファイル](https://www.dropbox.com/s/mcx73zxnm1y8fkk/Sunlight_by_GRASS.docx?dl=0)にもまとめてあります。併せてごらんください。

## 処理の流れ
GISにより、何かの結果を得たいと考える場合、多くの場合は単一の機能して実装されておらず、複数の機能を組み合わせる必要があります。今回の場合は、ある地域について、一日のうちに何時間太陽が当たっているかを計算することとしました。[GRASS GIS](https://grass.osgeo.org/)には多彩な機能があるものの、さすがにこの値を直接出してくれる機能はありませんでした。
関連する機能としては、[r.summask](https://grass.osgeo.org/grass78/manuals/r.sunmask.html)があります。これは、

>太陽の位置と高度のラスターマップから影の領域を計算します。
正確な太陽の位置(A)を指定するか、太陽の位置(B)をr.sunmask自身で計算する日付/時刻を指定します。[みんなの自動翻訳](https://mt-auto-minhon-mlt.ucri.jgn-x.jp/)で翻訳

という機能を持つものになります。
この機能を使って分析する場合の流れを、実施の手順と逆（バックキャスト、という奴）に説明すると、

* ある日の日照時間を計算する ← 総時間から、r.sunmaskで計算された日陰の時間を引くことで、総日照時間とする ← r.maskである時間の影か否かを計算し、それを積算することで総日陰時間データを作成する

という手順を取ることにしました。

今回は、サンプル地域として以下の範囲について処理を行いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/c36e6384-9d55-0706-7236-d2ee968af9d5.png)
なお、DEMのデータは、国土地理院の基盤地図情報からダウンロードしたものを使っていますが、5mや10mの解像度だと処理に時間がかかるため、100mに解像度を落として行っています。解像度を落とす方法は、[r.sun.incidoutの処理高速化は可能でしょうか？](https://groups.google.com/g/qgisshitumon01/c/0AGthDnT5vA/m/BQ73rP8-AQAJ)を参考にして下さい。

## 動作環境
動作確認をした環境は、

* Windows 10 Pro (64bit)
* [GRASS GIS 7.8 (OSGeo4W 64bit版)](https://trac.osgeo.org/osgeo4w/wiki/OSGeo4W_jp)

です。OSGeo4Wのコマンドラインを使うので、QGIS付属のGRASSでは動きませんでした [^1]

## データ処理

### GRASSへのデータのインポート

さて、実際の処理を行う前に、まず初めにデータを[GRASS GIS](https://grass.osgeo.org/)のデータベースにインポートする必要があります。これは、[GRASS GIS](https://grass.osgeo.org/)がGeoTiffやSpatiaLiteといったファイルを直接扱うのではなく、独自のデータベースにデータを格納し、処理するものだからです。GRASSを使おうとして初めに躓くのがここなのですが、ここを乗り越えれば、全行程の半分は突破したものと思って下さい！
重要な概念は、以下の2枚のスライドの通りになります。（[FOSS4G Tokyo 2011 GRASS GISハンズオンセッション](https://www2.slideshare.net/wata909/foss4g-2011-tokyo-grass)よりこぴぺ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/355053ad-1e9a-1d3e-258e-6408280fb808.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/cff56e8a-331e-39b9-fd52-732f06ed7fa7.png)
具体的な手順については、[word ファイル](https://www.dropbox.com/s/mcx73zxnm1y8fkk/Sunlight_by_GRASS.docx?dl=0)にステップbyステップで書いてありますので、そちらをご覧下さい。

### r.sunmaskの実行
つぎに、実際にr.sunmaskを実行してみます。さてここで、Mapsetの変更を行います。これまでの手順でDEMをインポートしましたが、その際のMapsetは、PERMANENTマップセットになっています。ですがこの先、複数のラスタデータ作成する予定です。そのため、これをPERMANENTマップセットとは別のマップセットに作成することにします。ここでは、sunny_shade と言うマップセットを作成し、そこで作業することにします。

実行には、Layer Manager ウィンドから、

* 設定→GRASS作業環境→マップセット作成　[g.mapsets]

を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/242b5f8f-2296-4b9c-9278-8da957039209.png)
表示されたg.mapsetウィンドの「Name of mpaset」に「sunny_shade」と入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/0a559d6a-9aac-2af3-f746-5f624a4800ec.png)
次に「作成」タブをクリックし、「マップセットがない場合はマップセットを作成」にチェックを入れます。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/6c2d945d-171e-d06c-defd-f71b1e410d72.png)

その上で実行をクリックすると、下の画面のようにマップセットの作成が完了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/5fe97fb0-bfaf-a021-0ec0-d6868c9d1e83.png)
作成が完了したら、閉じるをクリックしてください。これで、これから作業するデータは、「sunny_shade」に作成されます。

次に、r.summaskで日向、日陰を計算します。実行には、Layer Manager ウィンドから、

* ラスター→太陽照度と影→陰影図　[r.sunmask]

をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/8f9b9a19-1929-4f28-0e4e-cfd78bfdf200.png)
以下の画面が表示されるので、まず、計算に用いる標高マップを選択します。ここでは、PERMANENTマップセットに含まれるmerge_50を選択します[^2]。

次に、テーマタブをクリックします。ここで、計算する日時を指定します。例えば、日本における2020年5月1日午前6時を指定する場合、以下のようになります。この際、一度、Minutesの所に「0」を入力して下さい（何か数字を入力しないと、上手くいかないようです）。また、一番最後のタイムゾーンに「9」を入力するのを忘れないでください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/a813530c-9578-b34e-96b1-f298cca1f4fb.png)
最後に、オプションタブを選択し、出力する地図を選択します。ここでは、「SS_20200501_06」としました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/6f2d902e-15da-eae5-2d23-668790fa4c87.png)
計算が上手くいけば、以下のように表示されると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/1c63d1a7-4286-1813-4ada-451af5bef2b0.png)
このうち、黄色いところが影になっているところです。また、Layer Managerのチェックボックスを外すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/bb976e1f-5157-c15e-86d1-335d1d634e09.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/e3c31348-8b53-c1de-9c86-565a22329af2.png)
のように表示されます。
次に、12時のマップを作成してみます。テーマの時間を12、出力ファイル名をSS_20200501_12とします。上手く動くと、以下のようになります（SS_20200501_06はチェックを外して表示しません）。
一見すると、何もないように見えますが、これは日陰がないという事なので、問題ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/fb3923d7-d23d-ccaa-fa4d-a5e54cf2aae4.png)
18時について計算すると以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/aa364b6b-419e-eca9-923e-4ab8b8bf76ed.png)
改めて3枚の地図を並べると
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/1c63d1a7-4286-1813-4ada-451af5bef2b0.png"><img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/fb3923d7-d23d-ccaa-fa4d-a5e54cf2aae4.png"><img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/aa364b6b-419e-eca9-923e-4ab8b8bf76ed.png">
となり、太陽の位置に合わせて影の領域が変化していることが分かります。

### 複数の日陰マップを一括して作る

さて、これである時間の日陰の地図を作ることができました。次にこの地図を毎分ごとに作成する必要があります。2020年5月1日の高知の日の出は5:17、日の入りは18:49（[国立天文台 > 暦計算室 > 各地のこよみ > 高知(高知県)](https://eco.mtk.nao.ac.jp/koyomi/dni/2020/s4005.html)参照）となります。そうすると、5時から19時まで、毎分ごとのデータを作ろうとすると、13時間×60分＝780回も上記の入力をする必要があります。それは、なかなかキツいです。そこでこの計算を自動的に何度も行ってもらうことが必要になります。この際に利用できるのが、黒い画面、コマンドウインドです。

そこに行く前に、まず、r.sunmuskウィンドにあるコピーボタンをクリックし、Windowsであれば、メモ帳等にペーストしてみて下さい。すると、以下の画面のような文字列が貼り付けられると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/581a7b67-a99f-d685-83e9-cc57d6ce5d84.png)
テキストにすると、以下の通りです。

```bash
r.sunmask elevation=merge_50@PERMANENT output=SS_202005010601 year=2020 month=5 day=1 hour=06 minute=01 timezone=9
```

これが、GRASSがr.sunmuskを実行する際に、実際に行っている命令になります。この命令をそのままコマンドウィンドにペースとしてリターンを押すと、以下の通り実行が可能です（赤枠をご覧下さい）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/3e83eb21-3cda-4933-1856-812f0ff65a90.png)

ですので、上記のコマンドの```hour=12 minute=0```が自動的に変わり、さらに出力するマップ名```SS_202005010601```の時・分を示す```0601```の数字が自動的に変わるようにプログラムを書けばいいことになります。今回はLinuxのshellscriptの機能を用いて、この機能を持つスクリプトを作成しました。[^3]

```bash:sum_mask_hou_min.sh
# 5時から9時までの計算
for hou in {5..9}
 do
  # 0分から9分までの計算
  for min in {0..9}
   do
    r.sunmask elevation=merge_50@PERMANENT output=SS_202005010${hou}0${min} year=2020 month=5 day=1 hour=${hou} minute=${min} timezone=9
   done
  # 10分から59分までの計算
  for min in {10..59}
   do
    r.sunmask elevation=merge_50@PERMANENT output=SS_202005010${hou}${min} year=2020 month=5 day=1 hour=${hou} minute=${min} timezone=9
   done
done

# 10時から18時までの計算
for hou in {10..18}
 do
  # 0分から9分までの計算
  for min in {0..9}
   do
    r.sunmask elevation=merge_50@PERMANENT output=SS_20200501${hou}0${min} year=2020 month=5 day=1 hour=${hou} minute=${min} timezone=9
   done
  # 10分から59分までの計算
  for min in {10..59}
   do
    r.sunmask elevation=merge_50@PERMANENT output=SS_20200501${hou}${min} year=2020 month=5 day=1 hour=${hou} minute=${min} timezone=9
   done
  done
```
実際のファイルは、こちらです[(sun_mask_hou_min.shへのリンク)](https://www.dropbox.com/s/haqqyl8o6zmg094/sun_mask_hou_min.sh?dl=0)
この内容を、```sum_mask_hou_min.sh```として、```c:\GRASS```フォルダに保存して下さい。このコマンドを実行するには、

```winbatch
C:\c:
C:\cd c:\grass
C:\g.region res=100
C:\sh sum_mask_hou_min.sh
```
と入力します（入力する際は、```c:\```は除いて下さい）。成功すると、以下のように毎分ごとの日陰マップが生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/41e59bf8-b528-623b-9df8-b3b591080957.png)

### 日照時間マップの作成
残りは、わずかです！　ここまでに作成した分ごとの日陰地図から、1日の間の日照時間マップを作成します。考え方としては、毎分日陰マップを作成した5時から18時、つまり780分のうち、何分間、ひなたになっているかを計算することにます。こうした、地図間の計算を行う場合、[GRASS GIS](https://grass.osgeo.org/)では、```r.mapcalc```というコマンドを使います（[r.mapcalcのマニュアル](https://grass.osgeo.org/grass78/manuals/r.mapcalc.html)）具体的な計算の内容は、

* 全時間分のデータを持つ地図を作る → 毎分後のと日陰地図を引く

という流れです。つまり、日陰地図には、日陰の所に1という数字が入っています。これを全時間（780分）から引くことで、残りの時間が、その日の日照時間という事になります。この計算についても、日陰地図を作ったときと同様に、スクリプトを作成して行いました。以下、スクリプトの内容です。

```bash:sunny_min.sh
# 日陰時間を計算する元ファイルを作成
r.mapcalc expression="shade_min=0"

# 総日陰時間を計算
# 5時から9時までの計算
for hou in {5..9}
  do
   for min in {0..9}
    do
     # null値を0で置き換え
     r.null map=SS_202005010${hou}0${min}@sunny_shade null=0

     # tmp_mapに日陰時間を加える
     r.mapcalc expression="tmp_map=shade_min+SS_202005010${hou}0${min}@sunny_shade"

     # 古い日陰時間マップを削除
     g.remove -f type=raster name=shade_min

     # tmp_mapを新しい日陰時間マップに変更
     g.rename raster=tmp_map,shade_min
    done
   for min in {10..59}
    do
     r.null map=SS_202005010${hou}${min}@sunny_shade null=0
     r.mapcalc expression="tmp_map=shade_min+SS_202005010${hou}${min}@sunny_shade"
     g.remove -f type=raster name=shade_min
     g.rename raster=tmp_map,shade_min
    done
   done

# 10時から18時までの計算
for hou in {10..18}
 do
   for min in {0..9}
    do
     r.null map=SS_20200501${hou}0${min}@sunny_shade null=0
     r.mapcalc expression="tmp_map=shade_min+SS_20200501${hou}0${min}@sunny_shade"
     g.remove -f type=raster name=shade_min
     g.rename raster=tmp_map,shade_min
    done
   for min in {10..59}
    do
     r.null map=SS_20200501${hou}${min}@sunny_shade null=0
     r.mapcalc expression="tmp_map=shade_min+SS_20200501${hou}${min}@sunny_shade"
     g.remove -f type=raster name=shade_min
     g.rename raster=tmp_map,shade_min
    done
 done

# 総時間から総日陰時間を引いて、日向時間マップを作成
r.mapcalc expression="sunny_min=780-shade_min"
```
この内容を、```sunny_min.sh```として、```c:\GRASS```フォルダに保存して下さい。
実際のファイルは、こちらです[(sunny_min.shへのリンク)](https://www.dropbox.com/s/e4eevmu5xv2o6fq/sunny_min.sh?dl=0)

このコマンドを実行するには、コマンド画面に

```winbatch
C:\sh sum_mask_hou_min.sh
```
と入力すればOKです。計算した結果は、以下の通りとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/a2d08181-5b9f-99f6-8299-69eb1290a5f3.png)

この画像をQGIS等で見たい場合には、GeoTiffとしてエクスポートするといでしょうか。Layer Manage ウィンドから、

* ファイル→ラスターマップのエクスポート→一般的なアクスポートフォーマット　[r.out.gdal]

をクリックして下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/ed8926f9-a002-e103-598a-00f4233f86e0.png)

ここで、

* Name of raster map → sunnmy_min
* Name for output raster file → C:\GRASS\sunny_min.tif
* Raster data format to write → GTiff

とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/e7760b25-ade4-2ac0-44df-d5deadab5a6e.png)
これで実行を押すと、GRASSで作られたデータがGeoTiffファイルとして出力されます。実際のファイルはこちらです（[sunny_min.tifへのリンク](https://www.dropbox.com/s/4f5985izccwkpm4/sunny_min.tif?dl=0)）

上記のファイルをQGISに読み込み、彩色等を少し修正すると、以下のように表示できます。レイヤを選択し手情報を表示すると、日照時間が分で表示されることが確認できます（右上）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39797/3e26fb1f-feb5-7220-9bcc-ba570f110901.png)

以上で、手順の説明は終わりです。

## おわりに

さて、今回の記事はこれで終わりですが実はやろうとして出来なかったことがあります。

* 生成した日陰マップを連続してエクスポートし、アニメーションGIF化する
    * エクスポート自体は出来るのですが、何故かカラーテーブルの設定がおかしく、あんまり美しくなくなってしまう
* 生成したマップではなく、GRASSのマップウィンドに表示されたデータをエクスポートする
    * これは、エクスポートできなかった。Windowsだからダメなのかしら？

このへんは、誰かがっつりやってくれるとありがたいですw

また、今回は自分が使い慣れている環境と言うことで、GRASS GIS + Shell Scriptでの処理について説明しました。FOSS4Gのいいところは、色々な選択肢がある事です。QGISでダメならGRASS GISで。GRASSでもダメならPostGISで等など、目的の応じてツールの使い分けることで、様々なことが実現できると思います。なので、これをお読みになって頂いたかにも、出来れば様々なFOSS4Gにさわってみて、新しい使い方を発見して頂ければと思います。

$\huge{それではみなさん、よいお年を！}$ [^4]


[^1]: 私がよく知らんだけかも...
[^2]: すみません、100mにリサンプルする前です
[^3]: 桁数をそろえるために、かなり美しくない実装になっています。WindwosのOSGeo4Wでは、私ではここまでが限界でした。誰か、美しい実装を・・・。
[^4]: アドベンドカレンダーだったのではないか・・・・？