旅行先決定アプリを作ってみた

# はじめに
私はかなり旅行が好きなんですが、有名どころの町や観光地は結構回ってしまったので、今後は普通あまり行かないようなところにも足を伸ばしていきたいなと考えていました。しかし、そうは言っても、何があるかわからないところ思い切りよく行くのも難しいです。
そこで今回、ランダムに旅の目的地を決めてくれるアプリを作ってみました。データセット作りに数週間（これが超大変だった）、画面は暇な休日に4時間くらいで開発しました。

このアプリを使ってみたいだけの人もいると思うので、成果物は先に貼っておきます。ご自由にお使いください。コメントや拡散いただけると嬉しい限りです。

**画面イメージ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/6b781dcf-fe21-4303-a2b9-55aab23a35f1.png)

**アプリ**

https://travel-maker-six.vercel.app

**プログラム**

https://github.com/MySweetEden/TravelMakerReact

# コンセプト
ズバリ、これをアプリ化したい！

https://x.com/kinokuni_fox/status/1609494453076783105

今回は、①Pythonで地理データセットを作成して、②Reactでフロントエンドを実現し、③Vercelにデプロイします。
キモとしては、どうやって日本地図を216エリアに分けて綺麗に表示させるかだと思っています。

作ってから少し時間が経ってしまい、記憶があやふやになっています。ちょっと記述が雑かもしれませんがご容赦ください。

## 環境周り
①Python周り
GeoPandas
Leaflet

②React周り
MUI
React-Dice-Complete

③Vercelデプロイ
Vercel

# 開発
## ①Pythonによる日本地図分割
アプリ化にあたって、各種地域情報を地図にオーバーレイして表示することが必須になります。サイコロ結果に対応した区域のデータを作るべく、国土交通省のサイトから行政区域のデータセットを拝借して利用していきます。

**利用データ**
国土交通省　国土数値情報ダウンロードサイト　行政区域データ
https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03-v3_1.html

今回は全国のデータが載った、こちらのデータを利用します。
N03-20230101_GML.zip
展開すると多数のファイルがありますが、shファイルを利用していきます。このファイルには各行政区域やその輪郭の情報がまとまっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/c75b8b94-7a4d-4919-aaa9-0ff86ebe0f79.png)

**分割処理**
先ほど手に入れたデータをひたすら216分割していきいます。
合計12万以上の自治体をひたすら手作業で216個に分割…………はできませんね。

この大規模データセットを以下のアルゴリズムで216地域へと分割しました。
1. 各216地域の代表的な自治体区分をまとめたテーブルを作成
（例、**利尻・礼文**、**稚内周辺**）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/2c8e904f-78cf-4068-a385-f21f3da3bb73.png)

2. 12万超の行政区域のうち、216地域のいずれかに含まれる行政区域を抽出（未分類区域多数）
（例、利尻・礼文→**利尻郡・礼文郡**、稚内周辺→**稚内市**） 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/825e84f0-277a-4b2b-91a4-4c7f2ae32114.png)

3. 未分類の行政区域を216地域のうち最も近い場所へと分類（全区域分類済み）
（例、利尻・礼文→利尻町・礼文町・**利尻富士町**、稚内周辺→稚内市・**猿払村・豊富町・…**）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/73403591-ef42-489d-8e28-981e864f4713.png)

4. データを軽量化
複雑なポリゴンデータを扱うと処理が非常に重いため、こちらのサイトでポリゴン形状を簡略化しました。オプションはちゃんと覚えていませんが、手元のメモを見る限り0.2%程度で保存しています。
https://mapshaper.org
この際、ポリゴンを簡略化することで自己交差が発生して地図表示できなくなったため、GeoDataframeの以下のメソッドを利用することでポリゴンを修正しました。
gdf.make_valid()
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/61cb17a3-59e6-4def-880f-90f60f18e177.png)

ちなみに実際に分類作業を行う際には大変だった箇所が沢山ありました。供養として例を挙げておきます。
・同名の行政区分が日本国内に複数箇所存在するため誤分類が多数発生した。
・例外的な処理を多数実装する必要があった。
・区域分類処理が非常に重かった。
・ポリゴンデータ軽量化の方法が探してもなかなか見つからなかった。（私の検索能力不足かもしれませんが）
 
## ②Reactによるフロントエンド開発
**表示コンポーネント確認**
操作性を作っていく上で以下を使うことは決めていたので、それぞれのモジュールを一旦画面表示してみました。
・地図画面：Leaflet
・サイコロ画面：React-Dice-Complete
・画面テーマ：ダークテーマ

**画面デザイン**
Miroを使って画面イメージを作りました。非常に雑です……。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/bf8c49aa-a4f3-4087-b4fc-5fb4fd566762.png)

**画面開発**
Cursor Composerに書かせたので大した作業はしていません。
サイコロを振るボタンを下に配置し直したのと、最終画面に結果コピーボタンを付け足したくらいで、予定通り作りきりました。

## ③Vercelにデプロイ
コードをGitHubにプッシュしてVercelと紐づけました。勝手にCI/CDが走る。素晴らしい。

ちなみに、こちらのアプリをPC側で作成して動作確認して公開したところ、スマホで使うとバグが出るとの報告をいただきました。どうやら、PCに比べてスマホはタッチ判定がシビアなため、１回だけタップしたつもりでも複数回タップされたと認識され、サイコロが複数回振られたことになるバグが発生していました。そのため、サイコロを１回タップするとすぐにロックをかけるなどの処理を追加することでバグを解消しました。
補足情報ですが、React-Dice-Completeはデフォルトで連続タップを受け付けてしまうという仕様で、公式サイト上のデモもサイコロを連続タップすると無限に回り続けて結果が出ません……。

https://www.adam-tyler.com/react-dice-complete/

# 成果物
成果物は以下のようになりました。（再掲）
**画面イメージ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/6b781dcf-fe21-4303-a2b9-55aab23a35f1.png)

**アプリ**

https://travel-maker-six.vercel.app

**プログラム**

https://github.com/MySweetEden/TravelMakerReact

# おわりに
思ったより見た目が良くなったので非常に満足しています。
地図データの分割で飛地が残るなど少しミスが残っているようで、いつか修正したいところです。

さて、こちらのアプリは旅行をするために作ったものなので、しっかり使っていきたいと思います。みなさん使ってみてください。もし使った方いらっしゃればコメントいただけると嬉しいです。

ちなみにこちらのアプリは社内で人づてに利用者が増えていて、24人の旅行の行き先が決まりました。（2025/5/17現在）私の行き先は「豊田・岡崎」です。半年後あたりに社内での発表会をする予定なのでそちらも非常に楽しみです。

それではみなさん、良い旅を。