9
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?

More than 1 year has passed since last update.

Elixir Livebook で佐賀県のGTFSからバス停を地図に落とせたらいいなー

Last updated at Posted at 2023-02-19

はじめに

「GTFSを使うことになったから調べてて。」とのこと。
何のことでしょう。GTFSって何? 路線バス情報? データですか?
ならば「@nako_sleep_9hさん主催のpiyopiyo.ex #14:Elixirでデータ分析をしてみよう!」でLivebook Explorerの使い方を勉強会したばかりのElixir初心者として、これを利用しない手はありません。

ここは業務の大義名分の下、Livebookを使ってElixirを勉強するシチュエーションに持ちこみましょう。
まあ何だかわからないけど手始めに、「Elixir Livebookで佐賀県のGTFSからバス停を地図に落とせたらいいなー」をやってみました。

実行環境

  • Mac Ventura 13.1
  • Livebook app

Livebookはデスクトップアプリを使ってます。

佐賀県のGTFSデータ

使うGTFSがまだ手に入らないとのことなので、佐賀県が公開してくださってるデータをありがたく使わせていただきます。

saga-current.zip をダウンロードして、@RyoWakabayashi さんの記事を参考にして読み込んでみます。

色々やってるうちにこんなになってしまったセッティング。

Mix.install(
  [
    {:explorer, "~> 0.5"},
    {:csv, "~> 3.0"},
    {:geo, "~> 3.4"},
    {:kino, "~> 0.8"},
    {:kino_maplibre, "~> 0.1.3"},
    {:req, "~> 0.3.0"}
  ]
)

上記記事とか色々を参考にしたエリアスとかの設定

alias Explorer.DataFrame, as: Df
alias Explorer.Series, as: Sr
alias MapLibre, as: Ml
require Explorer.DataFrame

試しにダウンロードデータ解凍してフォルダごとノートファイルのとこに設置して読み込んでみます。

Path.absname('./gtfs-sample/saga-current/calendar_dates.txt')
|> Df.from_csv!()
|> Kino.DataTable.new()

image.png

こんな調子で本命の停留所データも読み込み....できない....
とあるカラムがintegerと解釈されてる中で空文字("")が混じっていて、そこではじかれてるらしい?

ここで、「データフレーム」すらわかってなかった自分に気が付く。

データ解析がしやすい形になっているらしい?
ではCSVをまんま読み込んで文字データでデータフレームの形を作ってみてはどうだろうか。

stops_data = 
  Path.absname("./gtfs-sample/saga-current/stops.txt")
  |> File.stream!()
  |> Enum.map(& &1 |> String.replace("\uFEFF", ""))
  |> CSV.decode!()
  |> Enum.to_list()

BOM入ってるしー。
これをヘッダとデータ切り離して、ぐに〜と交わらせたらデータフレーム作れるんじゃないだろうか。

csv_header =
  stops_data
  |> hd()
  |> Enum.map(& &1 |> String.to_atom())

csv_maps =
  stops_data
  |> tl
  |> Enum.map(& List.zip([csv_header, &1]) |> Enum.into(%{}))
stops_df =
  csv_maps
  |> Df.new()

stops_df |> Kino.DataTable.new()

image.png

それらしいのが出てきた。けど、このままだと全部文字列データなので、緯度経度だけでもなんとかします。

stop_lat = stops_df[:stop_lat] |> Sr.cast(:float)
stop_lon = stops_df[:stop_lon] |> Sr.cast(:float)

stops_df =
  stops_df
  |> Df.put(:stop_lat, stop_lat)
  |> Df.put(:stop_lon, stop_lon)

stops_df |> Kino.DataTable.new

image.png

GeoJSON にして可視化してみる

どうしたら地図に落とせるのだろうか、ということでまた@RyoWakabayashiさんの記事のお世話になります。

GeoJSONにするために、さっきのデータフレームから緯度経度のタプル配列を作ってみます。

lon_list = 
  stops_df[:stop_lon]
  |> Sr.to_enum()
  |> Enum.to_list()

lat_list =
  stops_df[:stop_lat]
  |> Sr.to_enum()
  |> Enum.to_list()

lon_lat_list = 
  lon_list
  |> Enum.zip(lat_list)

image.png

ここで気が付く。自分が描きたいのはポイントで、記事にあるポリゴンデータではない。
ではGeoライブラリにポイントっぽいものを探してみます。
ここで@piacerexさんが hexdocs.pm のURLの後にライブラリ名入れると調べられると言っていたのを思い出したのでやってみます。

%Geo.Point がそれっぽい?
てゆーことは、もしかしてポリゴンデータの配列の代わりにポイントデータの配列を形作ってやればいいのだろうか。

stop_geojson =
  %Geo.GeometryCollection{
    geometries: lon_lat_list
      |> Enum.map(fn p -> %Geo.Point{coordinates: p} end)
  }

image.png

なんかそれっぽくなった気がします。
では、上の@RyoWakabayashiさんの記事を参考にしてセンター座標を作ってみます。

stop_coordinates =
  stop_geojson.geometries
  |> Enum.map(& &1.coordinates)

lon = Enum.map(stop_coordinates, & elem(&1, 0))
lat = Enum.map(stop_coordinates, & elem(&1, 1))
stop_center =
  {
    (Enum.min(lon) + Enum.max(lon)) / 2,
    (Enum.min(lat) + Enum.max(lat)) / 2
  }

これをMapLibreに入れたら地図に落としてくれるのだろうか。
スマートセルを使わないでコードで書いてみたい。
ところで落としたいのはポイントなのでそれっぽいのないだろうかと探す。

見てるとcircleというのがある。これを使えばいいのかなと思った。

と、ここでMapLibreの :style にいい感じな地図データのURL例を見つけました。

Ml.new(center: stop_center, zoom: 11, style: "https://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL")
|> Ml.add_geo_source("data", stop_geojson)
|> Ml.add_layer(
  id: "stop",
  source: "data",
  type: :circle,
  paint: [circle_color: "#aa00ff", ]
)

image.png

びっくりです。とてもらしいんですけど、マジですか?!
生半可なelixir初心者でもこんなことが出来てしまうのですか?

しかも、知らなかったのですが、このMapLibreってクォータービューになる!!
image.png
凄すぎる!!

まとめ

GTFSとお友達になるためにElixir Livebookを使うのは楽しい。
(振り返ってみると無理やりデータフレームにする必要あったのだろうか?)

9
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
9
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?