C#で測地計算の単体テストを書く — 検証値ドリブン開発


C#で測地計算の単体テストを書く — 検証値ドリブン開発

C#で測地計算の単体テストを書く — 検証値ドリブン開発

GeoDiveExa(GDE)のC#版座標変換エンジンでは、国土地理院が公開している公式ツールの出力値をそのままテストケースとして取り込み、xUnitで結果を検証するという開発法を採っています。「動いているように見える」が一番危ないのが座標計算というジャンルで、どのように検証値を集め、どういうテストを書いているかをまとめます。


「動いているように見える」が一番危ない

座標変換のコードは、実行してみればとりあえず何かしらの数値が返ってきます。桁数がそれらしく、地図上にプロットしても大きく外れた位置に出なければ、一見「動いている」ように見えてしまいます。

ところが座標変換は、係数の符号が1箇所違うだけで数十cm〜数mずれた値が返ってきても、パッと見では気づけないことがあります。特に複数の補正(地震時補正・セミダイナミック補正・ジオイド補正など)を組み合わせる処理では、ある補正だけが微妙に効いていない、といった不具合が「動いているように見える」まま埋もれやすいと感じています。この危うさが、検証値をテストコードに組み込む一番の動機です。


検証値の集め方 — 地理院Webツール・公表値

テストケースの土台にしているのは、次のような公的な検証値です。

  • 国土地理院が公開している座標変換ツール(TKY2JGD・PatchJGD・SemiDynaEXE・POS2JGDなど)の出力値
  • 国土地理院が公表している基準点の座標成果値

これらのツールに特定の座標を入力して得られた出力値を「正解」として記録し、同じ入力をC#版のテストコードに与えて出力を比較する、という形でテストケースを作成しています。GCVP側では、この検証を地震補正テスト・重複地震テスト・境界域テスト・47都道府県庁などのテストポイント群に対して網羅的に行っており(全テストポイントを国土地理院の変換サイトと比較する)、C#版のテストでもこの同じ検証値の一部を流用しています。


テストの書き方 — 許容誤差の設計

座標計算のテストで悩ましいのは、浮動小数点の丸め誤差をどこまで許容するかです。完全な一致(==)でアサートすると、環境やライブラリの違いによるごくわずかな誤差でテストが落ちてしまいます。そこで、次のような方針で許容誤差を設計しています。

  • 水平位置(緯度・経度)は、ミリメートル相当の桁で許容誤差を設定する
  • 標高(楕円体高・正標高)についても同様に、実務上問題にならない範囲でミリメートル級の許容誤差を設定する
  • 単純な Assert.Equal ではなく、許容誤差付きの比較(デルタ指定のアサート)を使う

許容誤差を緩めすぎると本来の不具合を見逃し、厳しすぎると環境依存の誤差でテストが不安定になります。「実務で意味のあるずれかどうか」を基準に、許容誤差の桁をテストケースごとに検討しています。


回帰テストとしての価値 — パラメータ更新時に効く

このテスト群がもっとも効果を発揮するのは、地殻変動補正パラメータの年次更新やジオイドモデルの切り替えなど、内部データを更新する場面です。

パラメータファイルを差し替えたあとに検証値ベースのテストを実行すれば、更新によって既存の変換結果が意図せず変わっていないかを機械的に確認できます。手作業で数点をピックアップして目視確認するだけでは見逃しがちな、更新の副作用を早期に検出できるのが、検証値ドリブンなテストを継続的に持っておく価値だと感じています。


検証値そのものも更新が必要という悩み

ただし、この仕組みには見落としがちな運用コストがあります。パラメータファイルを更新した瞬間、「正解」として記録していた検証値そのものが古くなってしまうという点です。

セミダイナミック補正(jgd2024c)や定常時地殻変動補正(jgd2024d)のパラメータは、国土地理院から年に複数回公開されています。実際、2026年に入ってからも2026年4月版のpos2jgdパラメータ更新2026年5月版の更新2026年度版セミダイナミック補正パラメータの公開と、短い間隔で更新が続いています。パラメータが更新されるたびに、同じ入力座標を国土地理院の公式ツールに入れ直しても、返ってくる出力値自体が新しいパラメータを反映して変わるため、テストコードに記録している「正解」の数値も、その都度取り直す必要があります。

これを怠るとどうなるか。アプリ側のパラメータファイルは最新版に差し替えたのに、テストコードの検証値は旧パラメータ時代のまま、という状態になります。すると、アプリは正しく新パラメータに基づいて計算しているにもかかわらず、テストは「値が変わった」という理由で落ち続けます。つまり、新パラメータへの追従が正しくできているときほど、古い検証値を残しているとテストが失敗するという逆説的な状況が起こり得ます。

  • 更新のたびに、国土地理院の各Webツール(SemiDynaEXE・POS2JGDなど)へ同じテストポイント群(地震補正テスト・重複地震テスト・境界域テスト・47都道府県庁など)を再入力し、出力を取り直す
  • テストポイントの数と補正の種類(水平位置・楕円体高・標高など)の組み合わせぶんだけ、突き合わせの手間が発生する
  • 現状はこの検証値の取り直し作業を自動化できておらず、手作業でWebツールに入力し直しているのが実情

パラメータ更新時には「アプリの計算結果を検証する」だけでなく「検証値そのものを最新化する」という、いわば二重の更新作業が発生しているのが正直なところです。将来的には、国土地理院のWebツールへの入力を自動化するスクリプトなどで、この手作業を減らせないか検討したいと考えています。


Swift版と同じ検証値を使う

C#版とSwift版(GeoConverterPro・GeoPrism JPが使うGeoCoreJP)は、座標変換エンジンをSwiftとC#で二重実装するで紹介したとおり、アルゴリズム仕様を共通の正としながら言語ごとに実装されています。この二重実装の結果一致を保証するうえでも、同じ検証値をSwift版・C#版の両方のテストケースとして使うことが土台になっています。

同じ入力に対して、国土地理院の公式値との差、そしてSwift版とC#版どうしの差、という2段階の突き合わせができるのは、検証値を一元管理しているからこそです。テストコードのアサート対象は言語ごとに異なりますが、テストケースの「入力と正解値」自体は共通の検証値セットから作られています。


まとめ

  • 座標計算は「動いているように見える」だけでは不具合に気づきにくいため、国土地理院の公式ツール出力値・公表値をそのままテストケースとして取り込んでいる。
  • 完全一致ではなく、実務上意味のあるずれかどうかを基準にミリメートル級の許容誤差でアサートしている。
  • このテスト群は、パラメータ年次更新やジオイドモデル切り替え時の回帰テストとして特に効果を発揮する。
  • 一方でパラメータが更新されるたびに検証値そのものも古くなるため、正解値の取り直しという二重の更新作業が発生し、現状は自動化できず手作業に頼っている。
  • C#版とSwift版で同じ検証値セットを使うことで、Swift版とC#版の結果一致の保証にもつながっている。

関連記事


開発者より: アプリ・Kindle本・オープンソースの一覧は GitHub: amru195704 にまとめています。


お願い
本記事の情報は参考目的で掲載しており、正確性・完全性を保証するものではありません。誤記・不正確な情報がございましたら、コメント欄よりご指摘いただければ、確認のうえ修正いたします。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

Back to top