個人制作で登山用GPSアプリを作った話
概要
2年前の話になりますが、個人制作で登山用GPSアプリを開発しました。
今はクローズしているのですが、開発時に工夫した点などの共有をします。
何をするアプリか
登山時にGPSを使い、事前にダウンロードした地図上で位置を表示することで、山中で圏外にいても自分の現在位置を知ることができるアプリです。
また、どのような時間と経路で登山したのか、GPSの移動履歴を記録できます。
古くからこのようなアプリは他にもあるのですが、「サーバーなしで、国土地理院地図のみを使うことで、無料かつ無制限に地図データをダウンロードできる」という点を既存製品との大きな差別ポイントとして開発しました。
作ったもの
開発時の話
Flutter採用
最初はReactNativeで開発していましたが、1ヶ月ほど作ってからデザインの壁にぶちあたりました。
個人制作ですしデザインがネックになることは事前から想像できていたのですが、勢いで作ったもののやはりデザインをなんとかしたいという想いが日に日に強くなってきました。
Flutterは当初未経験だったのですが、美しいデザインのコンポーネントが多数あり、開発1ヶ月のタイミングでReactNativeを捨ててFlutterに移行しました。(まだデザインは微妙かもしれませんが、個人的には大満足です👍)
この判断は本当に大正解で、スムーズな開発環境、デザインが美しいコンポーネント群と、それらを簡単に組み込めるフレームワーク設計のおかげで、Flutter上での開発はとても楽しいものになりました。
その後、アプリの開発自体は1ヶ月程度でほぼ完成し、データ作成に2ヶ月ほどかかりました。
工夫1. 国土地理院地図を利用
国土地理院の出している地図で、無料で誰でも使える地図があります。
当時は既存の登山アプリで地図の無料ダウンロード数制限が行われるなどの仕様変更があり、その際に登山仲間と話している中で地図を無料で使いたいニーズが一定あると思ったため、国土地理院の地図を使うことで、無料で使えるようにしようと考えたのが発端でした。
国土地理院の地図を使うにあたっては、国土地理院に細かく用途などを事前に申請する必要がありますが、審査も無事に通り、使わせていただくことにできました。
国土地理院の地図は精度も高く、ラスタ画像だけでなくXMLやベクター地図など様々な形式のデータが用意されています。
また、2年前のflutterの地図ライブラリだとラスタ画像しか扱えなかったのですが、ベクター地図をなんとかflutter上で表示しようと色々調べて断念したのを覚えています。
今ならflutter上でベクター地図を表示するライブラリを作ってくださった方がいるようなので、シームレスに拡大して細部まで美しいベクター地図による地図アプリが可能だと思います。(本アプリはラスタ画像で実装してます)
工夫2. 画面遷移せず日本中をピンチとスクロールで閲覧できる地図
GoogleMapなどでは当たり前の機能ですが、既存の登山アプリではエリアごとに地図のスクロール可能範囲が限られていました。
登山の行き先を探す作業はとてもワクワクする楽しい作業なのですが、当時のエリアマップごとに区切られた地図では「広範囲の地図画面に移動して別のエリアマップを開いて...」と画面遷移しながら探す必要がありました。
これが体験を阻害していると感じて、本アプリでの地図参照画面は、日本中をスクロール可能かつ、ピンチ操作で縮尺変更が可能な1画面のみとしました。
また、地図だけみれても行き先探しは楽しくならないので、日本中の地点データ(poi)と登山道データもローカルFlutterアプリ内に持たせることで、地図の画像データのみを通信でダウンロードしながら、そこに何があるのか?どんな道があるのか?といった情報も一緒に表示できるようにしました。
また、各種データをクライアントアプリに組み込んで、ネット利用は地理院地図のみとすることで、サーバー運用を不要にしています。
データはすべてオンメモリーのSQLite(数十mb)でもち、マップスクロール時に中心点からpoiと経路を範囲検索することで、遅延や引っ掛かりなくスムーズに表示できてました。
クライアントアプリ内にSQLiteを同梱して起動時にオンメモリDBとして起動することで、SQLアクセスの使い勝手をそのままにパフォーマンスを確保できます。
ダウンロードは任意の地点を中心に無制限でダウンロードできるようになっています。
工夫3. 登山中の標高データを細かく表示
私自身は登山をするとき、しょっちゅう登山アプリを開きます。
さっきの地点から標高差でどれくらい上下があったのかを調べて、ペース管理に役立てるので、標高データと移動距離・時間のデータは細かく表示してほしい派でした。
本アプリでは、登山中、登山後に見れるタイムライン表示については、地点間の移動で概算で標高差がいくらあったのか?(地点間の標高差でなく、移動中のGPSデータの標高上下の積算)を表示しました。
また、地図内の縮尺はドラッグ可能にしていました。
縮尺はその性質上、地図内を自由にドラッグできた方が使いやすいだろうという考えがあったためでした。
また、縮尺ドラッグ機能と合わせて、縮尺左上の白◯を合わせた地点の標高を知ることができる機能もつけていました。
通常、登山アプリでは地点データの標高のみが記載されていて、それ以外の標高は等高線から読み解くのが一般的です。
しかしせっかくアプリで楽しむなら紙の地図でできない機能を入れたく、このような機能を入れていました。
今思えばなんで他の地点と同じデザインの白丸にしたのか...。
任意の地点の標高を取得する方法
国土地理院から、以下のような標高地図が公開されています。
こちらの地図の各ピクセルのrgbの各画素に、その地点の標高データが入っているため、この画像を地図データと一緒にダウンロードしておくことで、圏外にいても任意の地点の標高を取得可能です。
RGBから標高を求める計算式は以下のようになります。
double getAltitudeByColor(Color color) {
final pow2_8 = math.pow(2, 8);
final pow2_16 = math.pow(2, 16);
final pow2_23 = math.pow(2, 23);
final pow2_24 = math.pow(2, 24);
if (color.red != 128 || color.green != 0 || color.blue != 0) {
num d = color.red * pow2_16 + color.green * pow2_8 + color.blue;
num h = (d < pow2_23) ? d : d - pow2_24;
if (h == -1 * pow2_23) {
return 0.0;
}
return h * 0.01;
}
return 0.0;
}
工夫4. 他者の登山記録をインポートして、地図上に表示
ほとんどの登山者は登山前に他の人の経路やレポートを参照していると思います。
このときに参考にしたレポートを、自分の登山中にも一緒に表示できたら便利だよな。というのがこの機能になります。
複数の登山レポートを取り込んで、色分けして地図内に表示できるようになっています。
(下図では赤とオレンジのラインのみがアプリ内蔵の登山道データであり、紫、黄、薄緑などのラインは、すべて他の登山者のルートです。)
こちらの機能は「他登山サイトのURLを検索窓に添付するだけでインポート可能」というふうにしたかったのですが規約の問題で難しかったため、ユーザーが各自で取得したGPXをインポートするという方法にしました。
工夫5. データ
データ作成はもっとも時間がかかった工程でした。地図にまつわる地点データ(バス停・登山口・山頂・山小屋・施設など)や経路データは多くが各種権利によって守られておりあるけど使えないといったものがほとんどです。
この点については弁護士事務所に相談しながら使えるものを集めたり各種データをもっている日本中の方々にメールを送って許可をいただいたりもしながら、それらのデータを時間をかけてプログラムや手動で補正して制作しアプリ開発の倍の時間を要しました。
ネット上のGPSデータについては欠損や飛びが多いですし、休憩地点や山頂では一本線でないデータも多数あったり、また人気のないルートは獣道の可能性もあり安全性を考えると多数の人が通っている箇所しか使えないなど非常に厄介でした。
このように多数のGPSデータがあるときに、これらを一本の道に補正し直す必要があり、今なら機械学習で対応するのでしょうけど、当時はRuby on Railsで全10工程ほどの補正パイプラインを構築して、工程ごとの中間データも保存して確認しながら工程の処理を最適化し、手動での補正にも結構時間をかけました。
生データ(大量)
補正後データ(1本道)
しかしながら、冬山ルートの区別や経路の標準移動時間などはどうしようもなく開発時点では切り捨てました。
データ作成ツールではjavascriptのLeafletライブラリを用いてデータを表示・調整していました。Flutter同様、geoデータを扱うのは楽しいものです😀
工夫6. 横持ち可能・タブレット対応
地図は基本上が北なので、縦持ち限定だと山容によっては不便な場合があると考え、横で見たければ横で見れるようにしています。また広々と地図を見れるようにタブレットにも対応しました。
タブレットの横持ち(以下)だとめちゃ広範囲を表示できます。
工夫(?)7. ロゴ
デザインに壁を感じてFlutterに移行しましたと書きましたが、ロゴについても同じくデザインに壁がありました。
最終的に以下のようなロゴを自作したのですが、当時はコンセプトからいろいろとごにょごにょしっかりと考えたつもりだったのですが、改めて客観的にみてみると攻め過ぎで異様な仕上がりだったと感じています。
まぁ個人制作ですしこれくらいでもいいんですが、開発途中に客観的な視点を持てればもっと良い仕上がりになったような気がします。
開発途中に客観的な視点を持ちづらいのが個人制作の一番難しいところだと感じます。
※ アプリ名はHiker(フォントはライセンスを購入しました。)
おわりに
最後まで読んでくださりありがとうございます。
初めて地図アプリを実装してみて、地図まわりの技術やフレームワークに先駆者の知見が多数積み重なっているのを感じました。地図アプリの開発はめちゃくちゃ面白い。そしてFlutterも初めてでしたが、当時から(今も)最高だと思ってます。
当時はエンジニアとしてのキャリアを見直していた時期であり、技術力の向上のためには役立ちましたが、今振り返ると挑む領域を間違っていたように思います。個人開発にはまた随時取り組みたい(最近ならAIを使って)と思っているので、何かしら取り組みましたらまたzennにしたいと思います。
よろしければ、ハート・フォロー・シェアをいただけますと喜びます :)
失礼いたします。
Discussion
作成期間はどれくらいですか?
平日5時間作業ペースで、アプリ開発が1ヶ月、データ作成が2ヶ月くらいでした。
こんばんは!
こちらの登山用GPSアプリは全て一人で開発されたということでしょうか。
すごすぎます!
一度使ってみたいなと思ったのですが、今は使えないのでしょうか。