6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonAdvent Calendar 2024

Day 3
Qiita100万記事感謝祭!記事投稿キャンペーン開催のお知らせ

新幹線の歴史をPython (pandas, folium, PyQtなど)で可視化しながら学ぶ

Last updated at Posted at 2025-01-19

はじめに

今まで日本で色んなところに行って、鉄道について色々興味を持ってきて色々勉強しています。最近は歴史まで覚えようとしています。

特に新幹線は本当に凄いものです。1964年から始まって既に60年も経っています。意外と歴史が長くて、あんな時代からこんなものがあったなんてあまり信じられなかったです。

最初の開業からどんどん路線と駅が増えて伸びてきて、今でもまだ続いていくようです。だからどの駅がいつからできたか気になって調べてみました。

覚えやすいようにPythonでデータを整理して地図を描きながら可視化してみました。

この記事では実装のコードと結果を載せながら簡単に解説します。

使うデータ

駅のデータを全部持っているサイトは見つからないので、色んなサイトから集めて手動で編集しました。2025年現在新幹線駅の数は120です。

駅名や緯度経度や開業時間など必要なデータをcsvファイルに纏めています。

eki.csv
駅名,読み方,前の駅,緯度,経度,,開業
東京,とうきょう,,35.681391,139.766103,東京都,1964-10-01
品川,しながわ,東京,35.62876,139.738999,東京都,1964-10-01
新横浜,しんよこはま,品川,35.506824,139.617348,神奈川県,1964-10-01
小田原,おだわら,新横浜,35.256225,139.155772,神奈川県,1964-10-01
熱海,あたみ,小田原,35.103573,139.077679,静岡県,1964-10-01
三島,みしま,熱海,35.126253,138.911133,静岡県,1964-10-01
新富士,しんふじ,三島,35.142365,138.663199,静岡県,1988-13-03
静岡,しずおか,新富士,34.971629,138.388579,静岡県,1964-10-01
掛川,かけがわ,静岡,34.769463,138.014937,静岡県,1988-13-03
浜松,はままつ,掛川,34.703866,137.734759,静岡県,1964-10-01
豊橋,とよはし,浜松,34.762734,137.382128,愛知県,1964-10-01
三河安城,みかわあんじょう,豊橋,34.96968,137.060918,愛知県,1988-13-03
名古屋,なごや,三河安城,35.170694,136.881637,愛知県,1964-10-01
岐阜羽島,ぎふはしま,名古屋,35.315705,136.685593,岐阜県,1964-10-01
米原,まいばら,岐阜羽島,35.314657,136.289992,滋賀県,1964-10-01
京都,きょうと,米原,34.985465,135.757748,京都府,1964-10-01
新大阪,しんおおさか,京都,34.734136,135.501852,大阪府,1964-10-01
新神戸,しんこうべ,新大阪,34.706417,135.195758,兵庫県,1972-03-15
西明石,にしあかし,新神戸,34.66591,134.960151,兵庫県,1972-03-15
姫路,ひめじ,西明石,34.827659,134.690769,兵庫県,1972-03-15
相生,あいおい,姫路,34.818052,134.47341,兵庫県,1972-03-15
岡山,おかやま,相生,34.666572,133.918552,岡山県,1972-03-15
新倉敷,しんくらしき,岡山,34.56509,133.678344,岡山県,1975-03-10
福山,ふくやま,新倉敷,34.489291,133.361429,広島県,1975-03-10
新尾道,しんおのみち,福山,34.429859,133.190158,広島県,1988-13-03
三原,みはら,新尾道,34.400486,133.083049,広島県,1975-03-10
東広島,ひがしひろしま,三原,34.389035,132.759762,広島県,1988-13-03
広島,ひろしま,東広島,34.397446,132.475593,広島県,1975-03-10
新岩国,しんいわくに,広島,34.164672,132.149384,山口県,1975-03-10
徳山,とくやま,新岩国,34.051555,131.8029,山口県,1975-03-10
新山口,しんやまぐち,徳山,34.093255,131.39657,山口県,1975-03-10
厚狭,あさ,新山口,34.053361,131.159965,山口県,1999-03-13
新下関,しんしものせき,厚狭,34.005651,130.948021,山口県,1975-03-10
小倉,こくら,新下関,33.886756,130.882678,福岡県,1975-03-10
博多,はかた,小倉,33.590002,130.420622,福岡県,1975-03-10
博多南,はかたみなみ,博多,33.517955,130.436997,福岡県,1990-04-01
新鳥栖,しんとす,博多南,33.370278,130.491111,佐賀県,2011-03-12
久留米,くるめ,新鳥栖,33.319778,130.501408,福岡県,2011-03-12
筑後船小屋,ちくごふなごや,久留米,33.17802333,130.4916036,福岡県,2011-03-12
新大牟田,しんおおむた,筑後船小屋,33.071123,130.488786,福岡県,2011-03-12
新玉名,しんたまな,新大牟田,32.942525,130.573506,熊本県,2011-03-12
熊本,くまもと,新玉名,32.789207,130.688499,熊本県,2011-03-12
新八代,しんやつしろ,熊本,32.517888,130.634919,熊本県,2004-03-13
新水俣,しんみなまた,新八代,32.210825,130.428893,熊本県,2004-03-13
出水,いずみ,新水俣,32.089275,130.357803,鹿児島県,2004-03-13
川内,せんだい,出水,31.813726,130.31213,鹿児島県,2004-03-13
鹿児島中央,かごしまちゅうおう,川内,31.583727,130.541789,鹿児島県,2004-03-13
武雄温泉,たけおおんせん,,33.196151,130.022322,佐賀県,2022-09-23
嬉野温泉,うれしのおんせん,武雄温泉,33.106556,129.998972,佐賀県,2022-09-23
新大村,しんおおむら,嬉野温泉,32.932778,129.956944,長崎県,2022-09-23
諫早,いさはや,新大村,32.851018,130.041596,長崎県,2022-09-23
長崎,ながさき,諫早,32.75275,129.869056,長崎県,2022-09-23
上野,うえの,東京,35.71379,139.777043,東京都,1985-03-14
大宮,おおみや,上野,35.906439,139.62405,埼玉県,1982-06-23
小山,おやま,大宮,36.312747,139.806241,栃木県,1982-06-23
宇都宮,うつのみや,小山,36.559246,139.898389,栃木県,1982-06-23
那須塩原,なすしおばら,宇都宮,36.931956,140.020694,栃木県,1982-06-23
新白河,しんしらかわ,那須塩原,37.12331,140.188969,福島県,1982-06-23
郡山,こおりやま,新白河,37.398187,140.389363,福島県,1982-06-23
福島,ふくしま,郡山,37.754123,140.45968,福島県,1982-06-23
白石蔵王,しらいしざおう,福島,37.995485,140.633035,宮城県,1982-06-23
仙台,せんだい,白石蔵王,38.260027,140.882158,宮城県,1982-06-23
古川,ふるかわ,仙台,38.571192,140.967876,宮城県,1982-06-23
くりこま高原,くりこまこうげん,古川,38.74893,141.071785,宮城県,1990-03-10
一ノ関,いちのせき,くりこま高原,38.926514,141.138177,岩手県,1982-06-23
水沢江刺,みずさわえさし,一ノ関,39.145194,141.188803,岩手県,1985-03-14
北上,きたかみ,水沢江刺,39.281196,141.121221,岩手県,1982-06-23
新花巻,しんはなまき,北上,39.405839,141.17302,岩手県,1985-03-14
盛岡,もりおか,新花巻,39.701547,141.136599,岩手県,1982-06-23
いわて沼宮内,いわてぬまくない,盛岡,39.960155,141.216955,岩手県,2002-12-01
二戸,にのへ,いわて沼宮内,40.25997,141.286211,岩手県,2002-12-01
八戸,はちのへ,二戸,40.5092,141.431499,青森県,2002-12-01
七戸十和田,しちのへとわだ,八戸,40.719917,141.153948,青森県,2010-04-12
新青森,しんあおもり,七戸十和田,40.8275,140.693472,青森県,2010-04-12
奥津軽いまべつ,おくつがるいまべつ,新青森,41.145083,140.515444,青森県,2016-03-26
木古内,きこない,奥津軽いまべつ,41.677642,140.434004,北海道,2016-03-26
新函館北斗,しんはこだてほくと,木古内,41.9054,140.646525,北海道,2016-03-26
米沢,よねざわ,福島,37.909478,140.128209,山形県,1992-07-01
高畠,たかはた,米沢,37.992314,140.15281,山形県,1992-07-01
赤湯,あかゆ,高畠,38.047081,140.148978,山形県,1992-07-01
かみのやま温泉,かみのやまおんせん,赤湯,38.152259,140.27867,山形県,1992-07-01
山形,やまがた,かみのやま温泉,38.248098,140.327253,山形県,1992-07-01
天童,てんどう,山形,38.35989,140.369303,山形県,1999-12-04
さくらんぼ東根,さくらんぼひがしね,天童,38.42806,140.380677,山形県,1999-12-04
村山,むらやま,さくらんぼ東根,38.477078,140.386399,山形県,1999-12-04
大石田,おおいしだ,村山,38.595916,140.375349,山形県,1999-12-04
新庄,しんじょう,大石田,38.76256,140.306064,山形県,1999-12-04
雫石,しずくいし,盛岡,39.689244,140.974762,岩手県,1997-03-22
田沢湖,たざわこ,雫石,39.700363,140.722139,秋田県,1997-03-22
角館,かくのだて,田沢湖,39.591716,140.570988,秋田県,1997-03-22
大曲,おおまがり,角館,39.465712,140.479628,秋田県,1997-03-22
秋田,あきた,大曲,39.716748,140.129931,秋田県,1997-03-22
熊谷,くまがや,大宮,36.139627,139.389528,埼玉県,1982-11-15
本庄早稲田,ほんじょうわせだ,熊谷,36.218575,139.179739,埼玉県,2004-03-13
高崎,たかさき,本庄早稲田,36.322239,139.012354,群馬県,1982-11-15
上毛高原,じょうもうこうげん,高崎,36.693128,138.977579,群馬県,1982-11-15
越後湯沢,えちごゆざわ,上毛高原,36.935799,138.809227,新潟県,1982-11-15
浦佐,うらさ,越後湯沢,37.167488,138.922798,新潟県,1982-11-15
長岡,ながおか,浦佐,37.447787,138.853927,新潟県,1982-11-15
燕三条,つばめさんじょう,長岡,37.648824,138.939274,新潟県,1982-11-15
新潟,にいがた,燕三条,37.912299,139.060869,新潟県,1982-11-15
ガーラ湯沢,かーらゆざわえき,越後湯沢,36.950889,138.799528,新潟県,1990-12-20
安中榛名,あんなかはるな,高崎,36.362408,138.849589,群馬県,1997-10-01
軽井沢,かるいざわ,安中榛名,36.342889,138.635096,長野県,1997-10-01
佐久平,さくだいら,軽井沢,36.277929,138.464367,長野県,1997-10-01
上田,うえだ,佐久平,36.396579,138.249298,長野県,1997-10-01
長野,ながの,上田,36.643307,138.189101,長野県,1997-10-01
飯山,いいやま,長野,36.847642,138.359335,長野県,2015-03-14
上越妙高,じょうえつみょうこう,飯山,37.081813,138.249476,新潟県,2015-03-14
糸魚川,いといがわ,上越妙高,37.043631,137.861311,新潟県,2015-03-14
黒部宇奈月温泉,くろべうなづきおんせん,糸魚川,36.8742374,137.4814155,富山県,2015-03-14
富山,とやま,黒部宇奈月温泉,36.701384,137.213091,富山県,2015-03-14
新高岡,しんたかおか,富山,36.727004,137.010459,富山県,2015-03-14
金沢,かなざわ,新高岡,36.578117,136.648166,石川県,2015-03-14
小松,こまつ,金沢,36.402545,136.452926,石川県,2024-03-16
加賀温泉,かがおんせん,小松,36.320562,136.350337,石川県,2024-03-16
芦原温泉,あわらおんせん,加賀温泉,36.214542,136.235069,福井県,2024-03-16
福井,ふくい,芦原温泉,36.062057,136.223516,福井県,2024-03-16
越前たけふ,えちぜんたけふ,福井,35.895667,136.198889,福井県,2024-03-16
敦賀,つるが,越前たけふ,35.644767,136.076488,福井県,2024-03-16

この中にミニ新幹線や、特別な事情によって設置されたガーラ湯沢駅と博多南駅も含まれます。

変化のある日付を羅列

準備したcsvファイルをpandasで読み込んで調べてみます。まず開業の日付で駅を分けてhtmlのテーブルを作ります。

import pandas as pd

df = pd.read_csv('eki.csv')
html = '<table></tr>\n'
for ind,dfi in df.sort_values('開業',kind='mergesort').groupby('開業'):
    eki = dfi['駅名']+''
    eki = '<a href="https://ja.wikipedia.org/wiki/'+eki+'" target="_blank">'+eki+'</a>'
    a = ''.join(eki)
    html += f"  <tr><td>{ind}</td><td>{a}</td>\n"
html += '</table>'
open('eki.html','w',encoding='utf-8').write(html)

(因み並べ替えは.sort_valuesを使ってkind='mergesort'を指定したが、そうした方がいい理由に関しては前の記事「pandasのsort_valuesで勝手に不規則に並べ替える問題の原因と対策」に説明してあります)

実行したらテーブルのhtmlができて、このようなテーブルになります。wikiへのリンクも入れているので各駅の詳しくは調べられます。

1964-10-01 東京駅品川駅新横浜駅小田原駅熱海駅三島駅静岡駅浜松駅豊橋駅名古屋駅岐阜羽島駅米原駅京都駅新大阪駅
1972-03-15 新神戸駅西明石駅姫路駅相生駅岡山駅
1975-03-10 新倉敷駅福山駅三原駅広島駅新岩国駅徳山駅新山口駅新下関駅小倉駅博多駅
1982-06-23 大宮駅小山駅宇都宮駅那須塩原駅新白河駅郡山駅福島駅白石蔵王駅仙台駅古川駅一ノ関駅北上駅盛岡駅
1982-11-15 熊谷駅高崎駅上毛高原駅越後湯沢駅浦佐駅長岡駅燕三条駅新潟駅
1985-03-14 上野駅水沢江刺駅新花巻駅
1988-13-03 新富士駅掛川駅三河安城駅新尾道駅東広島駅
1990-03-10 くりこま高原駅
1990-04-01 博多南駅
1990-12-20 ガーラ湯沢駅
1992-07-01 米沢駅高畠駅赤湯駅かみのやま温泉駅山形駅
1997-03-22 雫石駅田沢湖駅角館駅大曲駅秋田駅
1997-10-01 安中榛名駅軽井沢駅佐久平駅上田駅長野駅
1999-03-13 厚狭駅
1999-12-04 天童駅さくらんぼ東根駅村山駅大石田駅新庄駅
2002-12-01 いわて沼宮内駅二戸駅八戸駅
2004-03-13 新八代駅新水俣駅出水駅川内駅鹿児島中央駅本庄早稲田駅
2010-04-12 七戸十和田駅新青森駅
2011-03-12 新鳥栖駅久留米駅筑後船小屋駅新大牟田駅新玉名駅熊本駅
2015-03-14 飯山駅上越妙高駅糸魚川駅黒部宇奈月温泉駅富山駅新高岡駅金沢駅
2016-03-26 奥津軽いまべつ駅木古内駅新函館北斗駅
2022-09-23 武雄温泉駅嬉野温泉駅新大村駅諫早駅長崎駅
2024-03-16 小松駅加賀温泉駅芦原温泉駅福井駅越前たけふ駅敦賀駅

各都道府県別

どの都道府県がどれくらい駅を持っているか、これも気になりますので、調べてみます。pandasを使ったらこのように1行で纏められます。

print(df.groupby('').apply(len).sort_values()[::-1].reset_index().groupby(0).apply(lambda x:''.join(x[''])).sort_index()[::-1])

結果。それぞれの数の駅を持つ都道府県。

10                                             山形県
8                                          新潟県、岩手県
6                                          福岡県、静岡県
5                                      広島県、山口県、長野県
4                          兵庫県、秋田県、福井県、宮城県、熊本県、青森県
3     佐賀県、埼玉県、富山県、鹿児島県、愛知県、東京都、栃木県、石川県、福島県、群馬県、長崎県
2                                     岡山県、神奈川県、北海道
1                                  岐阜県、滋賀県、大阪府、京都府

山形県は一番多いですね。ミニ新幹線であるため殆ど在来線の特急と同じ扱いだからです。ミニ新幹線と特別な駅を除けば岩手県と新潟県が一番で7駅となります。それにしても3駅の都道府県が多いですね。

どの県がいつから新幹線駅を持つようになったかも調べてみます。

print(df.groupby('').apply(lambda x:x['開業'].min()).sort_values(kind='mergesort').reset_index().groupby(0).apply(lambda x:''.join(x[''])))
1964-10-01    京都府、大阪府、岐阜県、愛知県、東京都、滋賀県、神奈川県、静岡県
1972-03-15                             兵庫県、岡山県
1975-03-10                         山口県、広島県、福岡県
1982-06-23                 埼玉県、宮城県、岩手県、栃木県、福島県
1982-11-15                             新潟県、群馬県
1992-07-01                                 山形県
1997-03-22                                 秋田県
1997-10-01                                 長野県
2002-12-01                                 青森県
2004-03-13                            熊本県、鹿児島県
2011-03-12                                 佐賀県
2015-03-14                             富山県、石川県
2016-03-26                                 北海道
2022-09-23                                 長崎県
2024-03-16                                 福井県

最後に新幹線ができたのは福井県ですね。今後中央新幹線が開業できたら山梨県も加わるでしょう。

このように新幹線を持つ都道府県はどんどん増えてきましたね。

新幹線の変化を地図で見る

次は早速地図で見てみたいです。使うのはfoliumという地図を表示する為のPythonライブラリです。ただし画像として出力するためにはseleniumライブラリもインストールする必要があります。

pip install folium selenium

時代によって変わっていく新幹線の駅と路線の地図描いて見てみましょう。

import math
import pandas as pd
from PIL import Image,ImageDraw,ImageFont
import folium
import io

df = pd.read_csv('eki.csv').set_index('駅名')
df[''] = df['開業'].str[:4].astype(int)

for nen in sorted(df[''].unique()):
    lis_eki = df.index[df['']<=nen]
    latlng = df.loc[lis_eki,['緯度','経度']].values
    loc = (latlng.max(0)+latlng.min(0))/2
    zoom = 9.5-math.log2(latlng[:,1].max()-latlng[:,1].min())
    fomap = folium.Map(loc,zoom_start=zoom,width=620,height=700,zoom_control=False)
    for eki in lis_eki:
        loc = df.loc[eki,['緯度','経度']].values
        r = (2024-df.loc[eki,''])*4
        iro = '#%02x33%02x'%(r,255-r)
        folium.Circle(loc,1,color=iro).add_to(fomap)
        
        maeeki = df.loc[eki,'前の駅']
        loc2 = None
        if(eki=='上野' and nen<1991):
            continue
        if(eki=='大宮' and nen<1985):
            continue
        if(maeeki in lis_eki):
            loc2 = df.loc[maeeki,['緯度','経度']].values
        elif(pd.notna(maeeki)):
            maemaeeki = df.loc[maeeki,'前の駅']
            if(maemaeeki in lis_eki):
                loc2 = df.loc[maemaeeki,['緯度','経度']].values
        
        if(loc2 is not None):
            folium.PolyLine([loc,loc2],color='#323e0c',weight=2).add_to(fomap,index=0)
    
    img = Image.open(io.BytesIO(fomap._to_png(1))).convert('RGB')
    llmm = latlng.max(0)-latlng.min(0)
    img = img.crop([0,350*(1-llmm[0]/llmm[1]),620,350*(1+llmm[0]/llmm[1])])
    font = ImageFont.truetype('Helvetica',24)
    ImageDraw.Draw(img).text((10,10),str(nen),(r,51,255-r),font=font)
    img.save('fomap%d.jpg'%nen,quality=95)

できた地図を見ながら簡単に解説しましょう。

1964年

fomap1964.jpg

新幹線が最初に開業したのは1964年で、東京駅から新大阪駅までの東海道新幹線です。当初はまだ14駅しかなかったです。

1972年

fomap1972.jpg

1972年になったら新大阪駅から岡山駅までの山陽新幹線ができました。5駅追加されて19駅になりました。

1975年

fomap1975.jpg

1975年、山陽新幹線は博多駅まで延長しました。10駅も増えて29駅になりました。

1982年

fomap1982.jpg

1982年になると、まず大宮駅から盛岡駅までの東北新幹線ができて、そして同年新潟駅までの上越新幹線もできました。駅の数は50になりました。

ただしあの時大宮駅が始点であり、東京から東北新幹線に乗ることはできなかったらしいです。

1985年

fomap1985.jpg

1985年に、東北新幹線は上野駅まで延長したが、東京駅まではまだもう少し。この地図のスケールではわかりにくいのですが、上野駅と東京駅の新幹線はまだなかったです。

その他に東北新幹線では水沢江刺駅と新花巻駅が挿入されました。駅の数は53となりました。

1988年

fomap1988.jpg

1988年に、東海道・山陽新幹線の中では新富士駅、掛川駅、三河安城駅、新尾道駅、東広島駅、この5駅が挿入されました。これで駅の総数は58となりました。

1990年

fomap1990.jpg

1990年に、東北新幹線で宮城県のくりこま高原駅が挿入されました。同年に博多南線という特別な路線として福岡県の博多南駅も開業しました。似たような事情で、新潟県のスキー場にあるガーラ湯沢駅も冬限定で開業しました。駅の数は61になりました。

又、1991年に上野駅と東京駅の間の路線もできて、東京駅から東北新幹線に乗ることができるようになりました。

1992年

fomap1992.jpg

1992年、ミニ新幹線である山形新幹線は開業しました。当初は山形駅までだけで5駅あります。駅の総数は66に。

1997年

fomap1997.jpg

1997年に、ミニ新幹線である秋田新幹線は秋田駅まで5駅開業しました。そして同年高崎駅から長野駅までの北陸新幹線5駅も開業しました。これによって76駅になりました。

因みに北陸新幹線と名乗ったが、当初は長野駅までしかなく、実際に北陸地方まで延長するのはその18年後。結構時間かかりましたね。

1999年

fomap1999.jpg

1999年に、山陽新幹線に山口県山陽小野田市の厚狭駅が挿入それました。同年山形新幹線も新庄駅まで延長して、5駅増えてきました。駅の数は82になりました。

2002年

fomap2002.jpg

2002年、東北新幹線は青森県の八戸駅まで延長しました。これで3駅増えて85駅になりました。

2004年

fomap2004.jpg

2004年に、新八代駅から鹿児島中央駅までの九州新幹線5駅が開業しました。同じ日に上越新幹線の本庄早稲田駅が挿入されました。駅の数は91になりました。

ただしあの時の九州新幹線は山陽新幹線と繋がっていなくて孤島になったので、博多駅まで延長するまで7年の間は在来線に乗り換えるしかなくて不便そうです。

2010年

fomap2010.jpg

2010年、東北新幹線が新青森駅まで延長し、2駅増えて全部93駅に。これで東北新幹線は完成です。

2011年

fomap2011.jpg

2011年になると、漸く博多駅から新八代駅駅までの九州新幹線が開業しました。これで孤島が消えて新幹線は全部繋がることになりました。増えた駅の数は6駅で、全部99駅になりました。

尚、九州新幹線は博多南駅を通るが、博多南駅は九州新幹線に含まれていない、別の路線扱いです。

2015年

fomap2015.jpg

2015年、北陸新幹線が金沢駅まで延長して、やっと本当の意味の北陸新幹線になりました。7駅が開業して、106駅になりました。

2016年

fomap2016.jpg

2016年に、新函館北斗駅までの北海道新幹線3駅が開業して、これで109駅に。やっと北海道まで新幹線で行けることになりました。

2022年

fomap2022.jpg

2022年、武雄温泉駅から長崎駅までの西九州新幹線5駅は開業しました。これで114駅に。

ただしこの路線は他の新幹線の路線と繋がっていないので、また孤島になりました。しかも佐賀県内の問題により路線を繋ぐ計画すらまだ目処が立っていない状態になっているらしいので、この状態は長く続く恐れがありますね。

2024年

fomap2024.jpg

最後に2024年、北陸新幹線が敦賀駅まで延長しました。6駅増えて120駅になりました。

この路線は今後新大阪駅まで延長してループにする予定ですが、これもいつなのかまだ目処が立っていない状態です。

それに元々2027年に完成する予定の中央新幹線は延期されてしまったので、これから暫く新幹線に変化ない状態が続くでしょう。

GUIで表示する

最後に、新幹線の歴史を簡単に調べられるようにPyQtを使ってGUIを作ってみます。

PyQtとfoliumによる地図のGUIの基本はこの記事に纏めてあるので参考に。

実装のコードです。

import math,sys,re
import pandas as pd
import folium
from PyQt6.QtWidgets import QApplication,QWidget,QHBoxLayout,QVBoxLayout,QLabel,QSlider,QTableWidget,QTableWidgetItem
from PyQt6.QtGui import QColor
from PyQt6.QtCore import Qt
from PyQt6.QtWebEngineWidgets import QWebEngineView

class Qmado(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('新幹線の歴史を覗くGUI')
        self.setStyleSheet('font-family: Kaiti SC; font-size: 16px;')
        
        self.df = pd.read_csv('eki.csv').set_index('駅名')
        self.df[''] = self.df['開業'].str[:4].astype(int)
        self.lis_eki = list(self.df.index)
        
        vbl = QVBoxLayout()
        self.setLayout(vbl)
        
        hbl1 = QHBoxLayout()
        vbl.addLayout(hbl1)
        
        self.nenlabel = QLabel()
        hbl1.addWidget(self.nenlabel)
        self.nenlabel.setStyleSheet('font-size: 20px;')
        
        self.nenslider = QSlider()
        hbl1.addWidget(self.nenslider)
        self.nenslider.setOrientation(Qt.Orientation.Horizontal)
        self.nenslider.setRange(1964,2024)
        self.nenslider.valueChanged.connect(self.nen_kawattara)
        
        hbl2 = QHBoxLayout()
        vbl.addLayout(hbl2)
        
        self.fowg = QWebEngineView(self)
        hbl2.addWidget(self.fowg)
        self.fowg.setFixedSize(620,620)
        
        self.qtable = QTableWidget()
        hbl2.addWidget(self.qtable)
        self.qtable.setFixedSize(360,620)
        self.qtable.setColumnCount(3)
        self.qtable.verticalHeader().setFixedWidth(30)
        for i,w in enumerate([140,80,90]):
            self.qtable.setColumnWidth(i,w)
        self.qtable.cellClicked.connect(self.cell_click)
        
        self.nenslider.setValue(1964)
        self.nen_kawattara(1964)

    def nen_kawattara(self,x):
        self.nen = x
        self.nenlabel.setText(str(x)+'')
        lis_eki = list(self.df.index[self.df['']<=self.nen])
        if(self.lis_eki!=lis_eki):
            self.lis_eki = lis_eki
            self.table_koushin()
        
    def table_koushin(self):
        df = self.df.loc[self.lis_eki]
        self.qtable.clear()
        self.qtable.setHorizontalHeaderLabels(['駅名','都道府県','開業日付'])
        self.qtable.setRowCount(len(df))
        for i,(eki,sr) in enumerate(df.iterrows()):
            qitem = QTableWidgetItem(eki)
            self.qtable.setItem(i,0,qitem)
            qitem.setToolTip(sr['読み方'])
            qitem.setBackground(QColor(30,50,50))
            qitem.setForeground(QColor(255,255,255))
            
            qitem = QTableWidgetItem(sr[''])
            self.qtable.setItem(i,1,qitem)
            qitem.setBackground(QColor(50,50,20))
            qitem.setForeground(QColor(255,255,255))
            
            qitem = QTableWidgetItem(sr['開業'])
            self.qtable.setItem(i,2,qitem)
            r = (2024-df.loc[eki,''])*4
            qitem.setBackground(QColor(r,51,255-r))
            qitem.setForeground(QColor(255,255,255))
        
        self.chizu_koushin()
        
    def chizu_koushin(self):
        ar_latlng = self.df.loc[self.lis_eki,['緯度','経度']].values
        chuushin = (ar_latlng.max(0)+ar_latlng.min(0))/2
        zoom = 9.5-math.log2(ar_latlng[:,1].max()-ar_latlng[:,1].min())
        fomap = folium.Map(chuushin,zoom_start=zoom)
        
        for eki in self.lis_eki:
            latlng = self.df.loc[eki,['緯度','経度']].values
            r = (2024-self.df.loc[eki,''])*4
            iro = '#%02x33%02x'%(r,255-r)
            yomi = self.df.loc[eki,'読み方']
            ido = self.df.loc[eki,'緯度']
            keido = self.df.loc[eki,'経度']
            popup = folium.Popup(
                '<h4>%s駅</h4><h5>%s</h5>%.4f,%.4f'%(eki,yomi,ido,keido),
                auto_close=False
            )
            folium.CircleMarker(
                latlng,
                radius=4,
                weight=0,
                fill_color=iro,
                fill_opacity=1,
                popup=popup
            ).add_to(fomap)
            
            maeeki = self.df.loc[eki,'前の駅']
            latlng2 = None
            if((eki=='上野' and self.nen<1991) or (eki=='大宮' and self.nen<1985)):
                continue
            if(maeeki in self.lis_eki):
                latlng2 = self.df.loc[maeeki,['緯度','経度']].values
            elif(pd.notna(maeeki)):
                maemaeeki = self.df.loc[maeeki,'前の駅']
                if(maemaeeki in self.lis_eki):
                    latlng2 = self.df.loc[maemaeeki,['緯度','経度']].values
            
            if(latlng2 is not None):
                folium.PolyLine([latlng,latlng2],color='#323e0c',weight=2).add_to(fomap,index=0)
        
        html = fomap.get_root().render()
        self.lis_mkobj = re.findall(r'var (circle_marker_\S+)',html)
        self.fowg.setHtml(html)
    
    def cell_click(self,i):
        mkobj = self.lis_mkobj[i]
        self.fowg.page().runJavaScript(f'{mkobj}.openPopup(); null')


qAp = QApplication(sys.argv)
qmado = Qmado()
qmado.show()
qAp.exec()

実行したらこのようなGUIが出てきます。最初は1964年に開業した駅しか表示されません。

截屏2025-01-19-16.43.12.jpg

そして上のスライダーを調整することによってその年まで開業した新幹線駅が表示されます。色は開業の年によって違って、古い駅は赤で、新しい駅は青にしています。

截屏2025-01-19-16.46.59.jpg

ズームして細部を見ることもできますし、点をクリックしたら駅名と詳細のポップアップが表示されます。又、右のテーブルの中の行をクリックしたらその駅のポップアップも立ち上がります。

截屏2025-01-19-16.45.26.jpg

終わりに

このように、新幹線の歴史の勉強のついでにPythonの運用の練習にもなります。一石二鳥ですね。

6
5
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
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?