
SwiftUIで座標入力フォームを作る — 桁数・符号・貼り付け対策
座標変換アプリの入り口は、たいてい1個のテキストフィールドです。単純な部品に見えますが、実際に運用してみると、桁数・符号・コピペ由来の表記ゆれという3つの落とし穴が次々に出てきます。今回はGeoConverterProの座標入力フォームをSwiftUIで作る中で実際に踏んだ落とし穴と、その対策をまとめます。
何が難しいのか
座標入力欄が難しい理由は、「正しい形式で入力してもらう」ことを前提にできない点にあります。ユーザーが入力欄に持ち込む値は、少なくとも次のパターンが混在します。
| 入力パターン | 例 |
|---|---|
| 10進度(正の値のみ) | 35.681236 |
| 10進度(符号付き) | -35.681236 |
| 度分秒(記号あり) | 35°40'52.4"N |
| 度分秒(記号なし・スペース区切り) | 35 40 52.4 |
| 地図アプリからのコピペ | 35.681236, 139.767125 |
| 全角混入 | 35.681236 |
これらをすべて1つのテキストフィールドで受け止め、内部的には統一した10進度のDoubleとして扱う必要があります。度分秒(DMS)と十進度(DD)の相互変換で扱った変換式そのものは単純ですが、実装で苦労するのは変換式の前段、つまり「入力された文字列から、どの形式で書かれた値かを判定し、パースする」部分です。
桁数の扱い — 精度と表示のバランス
内部計算はDoubleで行いますが、doubleの精度でmmは守れるかで書いた通り、Double自体はmm精度を十分に保持できます。問題はむしろ表示側です。
緯度経度をそのままString(format: "%.10f")のような固定桁数で表示すると、入力していない桁まで0が並んで冗長になり、逆に桁数を切り詰めすぎるとmm単位の差が表示上わからなくなります。GeoConverterProでは次の方針にしています。
- 10進度表示は小数点以下8〜9桁(緯度経度でおよそmm〜サブmm相当)を上限に、末尾の0はトリムして表示する
- 度分秒表示の秒(〜”)は小数点以下1〜2桁までとし、それ以上は「表示上の精度」であって「入力精度」ではないことを明示する
- 入力欄と結果表示欄で桁数ポリシーを分離し、入力欄は貼り付けられた桁数をそのまま保持、結果表示欄だけ整形する

符号の扱い — N/S・E/Wと符号のどちらを正とするか
緯度の南半球・経度の西半球は、S W の記号表記と、マイナス符号の2通りで表現されます。両方を受け付けるアプリは多いですが、内部の正規化を誤ると符号が二重に反転してしまうバグを作り込みやすいポイントです。
GeoConverterProでは、パース段階で必ず「符号付きDouble」に正規化し、その後の変換パイプラインには符号表記の情報を一切持ち込まない設計にしています。表示側で改めてN/S・E/Wの記号表記に変換するかどうかを、入力形式とは独立に選べるようにすることで、「入力はDMS+記号、表示は符号付き10進度」のような組み合わせも自然に扱えます。パースと表示を同じ関数で済ませようとすると、こうした組み合わせで符号が壊れやすくなるため、意図的に責務を分離しています。
貼り付け対策 — 実装で苦労したパターン集
一番苦労したのは、地図アプリやWebサイトからのコピペで飛んでくる文字列への対応です。実際に対応が必要になったパターンを挙げます。
- 区切り文字の揺れ:カンマ・全角カンマ・スペース・タブ・改行のいずれでも緯度経度の2値として認識する
- 度分秒記号の揺れ:
°′″の正規の記号だけでなく、'"(シングル・ダブルクォート)で代用された表記も許容する - 全角数字・全角記号:全角の数字・小数点・カンマは、パース前に半角へ正規化する
- 単位付きの混入:「緯度: 35.681236」のようにラベル文字列が付いたまま貼り付けられるケースがあり、数値部分だけを正規表現で抽出する
- 1フィールドに2値貼り付け:緯度用の入力欄に「緯度, 経度」のペアが丸ごと貼り付けられた場合、カンマで分割して緯度側に緯度、経度欄に経度を自動で振り分ける
SwiftUIでの実装としては、TextFieldのonChangeでリアルタイムにバリデーションするのではなく、貼り付けイベント(UIPasteboard経由)とフォーカスが外れたタイミングの2箇所でだけ正規化処理を走らせる方式にしました。1文字入力するたびに正規表現マッチを走らせると、入力途中の不完全な文字列がバリデーションに引っかかってUXを損なうためです。
検証の考え方
入力パターンが多岐にわたるため、テストケースは実際に遭遇した貼り付けパターンをそのまま固定化しています。C#/.NET MAUI版のGeoDiveExaで採用している検証値ドリブンのテスト設計と考え方は同じで、「仕様書から逆算したテストケース」ではなく「実際に発生した入力パターンをテストケース化する」ことを優先しています。度分秒入力UIについては、.NET MAUI版のGeoDiveExaでも度分秒と十進度の入力UIをどう作るかという記事で同じ課題を扱っており、SwiftUIとMAUIそれぞれのフォーム部品の作法の違いはあっても、直面する入力バリデーションの課題そのものはほぼ共通しています。
まとめ
座標入力フォームは、変換式そのものよりも「入力された文字列をどう正しく解釈するか」に実装の手間の大半がかかります。GeoConverterProでは、桁数は入力欄と表示欄でポリシーを分離し、符号は早い段階で正規化し、貼り付け対策は実際に遭遇したパターンをテストケースとして蓄積する、という方針で対応しています。
関連記事
- 度分秒(DMS)と十進度(DD)の相互変換 — 座標の「書き方」でつまずかないために(GeoConverterPro)
- doubleの精度でmmは守れるか — 座標計算と浮動小数点(GeoConverterPro)
- 度分秒と十進度の入力UIをどう作るか — MAUIフォーム設計(GeoDiveExa)
- C#で測地計算の単体テストを書く — 検証値ドリブン開発(GeoDiveExa)
- 「緯度」は一種類じゃない — 測地緯度と地心緯度の違いを図で理解する(GeoPrism JP)
開発者より: アプリ・Kindle本・オープンソースの一覧は GitHub: amru195704 にまとめています。
お願い
本記事の情報は参考目的で掲載しており、正確性・完全性を保証するものではありません。誤記・不正確な情報がございましたら、コメント欄よりご指摘いただければ、確認のうえ修正いたします。
アプリを入手(App Store):GeoConverterPro(座標変換) | GeoPrism JP(測地系の可視化・学習)
コメントを残す