LoginSignup
17
19

More than 3 years have passed since last update.

地震のデータを地図上にプロットして可視化する

Last updated at Posted at 2019-04-26

はじめに

位置情報データの可視化にめっちゃ便利なPythonのライブラリfoliumを使って、このように地震の大きさを可視化してみた。
ついでにPILを使ったgifファイルの作り方についても簡単に紹介したいと思います。

pillow_imagedraw2.gif

データを集める

気象庁の震源データをスクレイピングしました。
気象庁では震源地を60進数で記載している為、データとして取得する際には10進数による座標変換を行います。


「39度 50分 9秒」を10進数に変換する。
「度」はそのままで、「分」を60、「秒」を3,600で割りそれらを足す。
 = 139 + ( 50 ÷ 60 ) + ( 9 ÷ 60 ÷ 60 )
 = 139.691749

以下取得したデータ例

緯度  経度  深さ 震度 地名
2019/2/1 0:09:29 39.8358333333333 142.351111111111 22 1.2 岩手県沖
2019/2/1 0:26:44 24.5 122.250833333333 27 4.2 台湾付近
2019/2/1 0:27:24 37.1177777777777 141.134444444444 49 2.4 福島県沖
2019/2/1 0:28:09 34.8833333333333 135.65 12 0.8 大阪府北部
2019/2/1 0:30:52 35.2847222222222 135.302222222222 9 0.6 京都府北部

地図上にプロットする

取得したデータをfoliumを使ってプロットしていきます。
foliumの使い方についてはこちらから確認することができます。

folium.Marker(["緯度","経度"],"地図の拡大サイズ","地名")

でマーカを地図上にプロットすることが可能です。

folium.CircleMarker(["緯度","経度"],"円の大きさ","地名","円の外枠の色","円内の色")

で円を地図上にプロットすることが可能です。

今回のソースコードはこんな感じ。
地震の大きさで円を色分けしようと思ってIF条件が多くなってしまっていますが、簡単にプロット出来ると思います。
ソースコードが大変見苦しいものになってしまっているのでGW中にきれいにします。

# import libraries
import folium
import pandas as pd
import csv
from datetime import datetime, date, timedelta

#各日にちごとにマップファイルを作成する
def plot_earthquack(data_set):

    save_html_path = "./plot_html/"

    today = datetime.today()
    first_day = today.replace(day=1)
    day = datetime.strftime(first_day, '%Y-%m-%d')
    for k in range(1,500):
        print(day)

        lat1 = []
        lot1 = []
        locate1 = []
        magnitude1 = []
        day1 = []
        for j in range(len(data_set)):
            if str(data_set[j][0]) == str(day):
                lat1.append(float(data_set[j][2]))
                lot1.append(float(data_set[j][3]))
                magnitude1.append(abs(float(data_set[j][5])))
                locate1.append(str(data_set[j][6]))
                day1.append(str(data_set[j][0]))

        # Make a data frame with dots to show on the map
        data = pd.DataFrame({
        'lat':lot1,
        'lon':lat1,
        'name':locate1,
        'mag':magnitude1,
        'day':day1
        })


        #print(data)

        # Make an empty map
        m = folium.Map(location=[35.681236, 139.767125], tiles="Mapbox Bright",  zoom_start=5)

        for i in range(0,len(data)):
            if data.iloc[i]['mag'] >= 1.0 and data.iloc[i]['mag'] < 3.0:
                #folium.Marker([data.iloc[i]['lon'], data.iloc[i]['lat']], popup=data.iloc[i]['name']).add_to(m)

                folium.CircleMarker([data.iloc[i]['lon'], data.iloc[i]['lat']],
                    radius= 5 * data.iloc[i]['mag'],
                    popup=data.iloc[i]['name'],
                    color='#b7fffd',
                    fill_color='#b7fffd'
                    ).add_to(m)

            if data.iloc[i]['mag'] >= 3.0 and data.iloc[i]['mag'] < 5.0:
            #folium.Marker([data.iloc[i]['lon'], data.iloc[i]['lat']], popup=data.iloc[i]['name']).add_to(m)

                folium.CircleMarker([data.iloc[i]['lon'], data.iloc[i]['lat']],
                    radius= 5 * data.iloc[i]['mag'],
                    popup=data.iloc[i]['name'],
                    color='#004cff',
                    fill_color='#004cff'
                    ).add_to(m)

            if data.iloc[i]['mag'] >= 5.0 and data.iloc[i]['mag'] < 6.0:
                #folium.Marker([data.iloc[i]['lon'], data.iloc[i]['lat']], popup=data.iloc[i]['name']).add_to(m)

                folium.CircleMarker([data.iloc[i]['lon'], data.iloc[i]['lat']],
                    radius= 5 * data.iloc[i]['mag'],
                    popup=data.iloc[i]['name'],
                    color='#e66bff',
                    fill_color='#e66bff'
                    ).add_to(m)

            if data.iloc[i]['mag'] >= 6.0 and data.iloc[i]['mag'] < 7.0:
                #folium.Marker([data.iloc[i]['lon'], data.iloc[i]['lat']], popup=data.iloc[i]['name']).add_to(m)

                folium.CircleMarker([data.iloc[i]['lon'], data.iloc[i]['lat']],
                    radius= 5 * data.iloc[i]['mag'],
                    popup=data.iloc[i]['name'],
                    color='#f90081',
                    fill_color='#f90081'
                    ).add_to(m)

            if data.iloc[i]['mag'] >= 7.0:
                #folium.Marker([data.iloc[i]['lon'], data.iloc[i]['lat']], popup=data.iloc[i]['name']).add_to(m)

                folium.CircleMarker([data.iloc[i]['lon'], data.iloc[i]['lat']],
                    radius= 7 * data.iloc[i]['mag'],
                    popup=data.iloc[i]['name'],
                    color='#f91800',
                    fill_color='#f91800'
                    ).add_to(m)

        first_day = first_day - timedelta(days=1)
        day = datetime.strftime(first_day, '%Y-%m-%d')

        # Save it as html
        m.save(save_html_path + str(day) + '.html')

def get_data():
    data = []
    with open('./csv/today.csv',  newline='') as f:
        dataReader = csv.reader(f)
        for row in dataReader:
            data.append(row)
    return data

def main():
    data = get_data()
    plot_earthquack(data)


if __name__ == '__main__':
    main()

以下のように作成したMapオブジェクトはhtml形式として保存することができます。

# Save it as html
m.save(save_html_path + str(day) + '.html')

保存したhtml形式のマップを開くと以下のようにプロット出来ていることがわかると思います。
無題.png

プロットした各日にちのマップをgifファイルを作る

pngファイルを作成する。

各日にちごとにプロットした地震マップをgifにしたいと思います。
seleniumのwebdriverを使って、html形式のマップのスクリーンショットを撮ることによってpngファイルを作成しました。

driver.save_screenshot("保存先ファイル名")

を利用することによってwebdriverで開くサイトやhtmlのファイルをスクリーンショットを撮ることが出来ます。
今回のソースは以下のようになっています。

import os
import time
from selenium import webdriver
from datetime import datetime, date, timedelta


read_html_path = "./plot_html/"
save_img_path = "./png/"

today = datetime.today()
first_day = today.replace(day=1)
first_day = first_day - timedelta(days=1)
day = datetime.strftime(first_day, '%Y-%m-%d')

driver_path = "./chromedriver.exe"
delay=5

for i in range(1,500):
    fn = str(day)+".html"
    tmpurl='file://{path}/plot_html/{mapfile}'.format(path=os.getcwd(),mapfile=fn)
    print(tmpurl)
    driver = webdriver.Chrome(driver_path)
    driver.get(tmpurl)
    #Give the map tiles some time to load
    time.sleep(delay)

    save = save_img_path + str(day) + ".png"
    driver.save_screenshot(save)
    driver.quit()

    first_day = first_day - timedelta(days=1)
    day = datetime.strftime(first_day, '%Y-%m-%d')

PILを使って、先ほど生産したpngファイル達からgifファイルを生成します。

ImageFont.truetype("Arial Bold.ttf", "文字サイズ")

を利用することによって、gifファイルの中に文字などを入れることが出来ます。
.tffファイルはweb上から入手することが可能で、好きなフォントを使うことが出来ます。

以下今回のソースプログラム

from PIL import Image, ImageDraw, ImageFont
from datetime import datetime, date, timedelta

read_img_path = "./png/"
im = []

today = datetime.today()
first_day = today.replace(day=1)
first_day = first_day - timedelta(days=1)
day = datetime.strftime(first_day, '%Y-%m-%d')

for i in range(1,300):
    png = read_img_path + str(day)+".png"
    print(png)
    img = Image.open(png)
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype("Arial Bold.ttf", 100)
    draw.text((40, 40),str(day),font=font,fill=(0,0,0))
    im.append(img)
    first_day = first_day - timedelta(days=1)
    day = datetime.strftime(first_day, '%Y-%m-%d')

im[0].save('pillow_imagedraw.gif',
               save_all=True, append_images=im[1:], optimize=False, duration=400, loop=0)

まとめ

foliumを使った地図上のプロットとPILを使ったgifファイルの作り方について簡単に紹介しました。
私自身、業務ではデータやPythonについて一切触れていないので、ご指摘や知見についてなにかあれば教えてください。

17
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
19