WebSurfer's Home

Filter by APML

GeoCoordinateWatcher で緯度経度を取得

by WebSurfer 24. May 2025 19:13

.NET Framework 4.8/4.8.1 の GeoCoordinateWatcher クラスを使っての緯度経度データ取得について調べたことを備忘録として書いておきます。

Microsoft のドキュメント GeoCoordinateWatcher class の Remarks に書いてありますが、GeoCoordinateWatcher は位置プロバイダから座標データを取得して位置情報を提供するそうです。

位置プロバイダには GPS, Wi-Fi triangulation, cell phone tower triangulation などがあるそうです。cell phone tower triangulation は関係なさそうに思いましたが、ノート PC にはモバイルルーターを使わず直接携帯データネットワーク経由でインターネットにアクセスできるものがあって、そのような PC に搭載されているのかもしれません。(未確認&想像です)

(Microsoft 位置情報サービスは IP アドレスも位置情報を得るための手段の一つとして使うそうですが GeoCoordinateWatcher クラスが使うかどうかは不明です)

複数の位置プロバイダが搭載されている場合は優先付けがされますが、その優先順序は状況に応じて変わり、例えば GPS の電波が届かない場所に移動した場合は Wi-Fi triangulation プロバイダが使われるそうです。

それらのプロバイダのいずれも使えない場合、Windows OS の「既定の場所」から情報を取得できます。Windows 10 の場合[設定]⇒[プライバシー]⇒[位置情報]と進み、下の画像の[規定値に設定]ボタンをクリックして表示される画面で設定します。

既定の場所

ちなみに自分の Windows 10 ノート PC には Wi-Fi しか搭載されていませんので、GeoCoordinateWatcher クラスが使う位置プロバイダは Wi-Fi triangulation のみということになります。

なので、自分が検証できるのは Wi-Fi triangulation プロバイダを使った場合と、Wi-Fi をオフにして「既定の場所」の情報を使った場合だけですが、実際に GeoCoordinateWatcher クラスを使ったコンソールアプリを作って試してみました。アプリのコードはこの記事の下の方に載せておきます。

まず「既定の場所」を設定します。下の画像は[規定値に設定]ボタンをクリックして表示される画面で既定の位置を設定したところです。(Wi-Fi と違う結果になるのがすぐ分かるよう今回の検証では皇居に設定しています)

既定の位置

Wi-Fi をオンにした状態でアプリを走らせたコンソール出力は以下の通りとなりました。(緯度経度は自宅の位置になるので小数点以下を xxxx で隠しています)

Permission: Unknown, Status: NoData
Permission: Granted, Status: Ready
Lat: 35.xxxxxxxxxxxxx, Long: 139.xxxxxxxxxxx

アプリを走らせるたびに計測を行うようで結果は毎回微妙に異なりますが、Google Map に緯度経度を入力して表示されたポイントを見ると計測ごとの差は 10m もありませんでした。

自宅(PC が置いてある場所)の周りに適切な Wi-Fi サービス提供者のアクセスポイントが複数存在するということが精度に影響するのでしょうが、自宅の環境ですとかなり正確に Google Map 上で自宅の位置が特定できてしまいました。悪用されるとかなりヤバそうです。(汗)

ちなみに triangulation は三角測量という意味です。自宅(PC が置いてある場所)の近くに存在する複数の Wi-Fi サービス提供者のアクセスポイントを、PC に搭載された Wi-Fi triangulation プロバイダがスキャンしてその位置を知るためのデータ (MAC アドレスという記事を見ましたが真偽不明) と電波強度を取得し、電波強度からアクセスポイントとの距離を計算し、三角測量により PC の位置を特定するというプロセスだそうです。

Wi-Fi triangulation

そのプロセスの過程でアクセスポイントの位置情報(緯度経度とか)が必要になります。もし仮に、スキャンして得たデータがアクセスポイントの MAC アドレスだとすると、MAC アドレスとアクセスポイントの緯度経度がデータベースに登録してあって、プロバイダはアクセスポイントの緯度経度を得るためにそのデータベースに照会すると言った操作が必要になるはずです。しかし、そのデータベースというのは何で、どこにあって、どのように位置情報を得ているのかは調べても分かりませんでした。

また、自分が試した限りですが、ネットに接続されているか否かは関係なく Wi-Fi がオンになっていれば正確な緯度経度データを取得できました。その時には外部のデータベースにはアクセスできないはずで、どのようにアクセスポイントの位置情報を得たのかは謎です。Windows の位置情報サービスとプライバシーの [場所の履歴] によると過去に取得した位置情報が一定時間 (Windows 10 では 24 時間) ローカルストレージに保存されるそうなので、そこから取得しているのかもしれません。(未検証、未確認です)

次に、Wi-Fi をオフにしてアプリを走らせるとコンソール出力は以下の通りとなります。下の Lat, Long の値を Google Map に入力して表示される場所を見ると「既定の場所」に設定した通り皇居となってました。

Permission: Unknown, Status: NoData
Permission: Granted, Status: Ready
Lat: 35.6839239255279, Long: 139.750550645143

以下に自分が検証に使ったサンプルコードを載せておきます。

using System;
using System.Device.Location;

namespace GeoCoordinateWatcherSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            GetLocationProperty2();
        }

        static void GetLocationProperty2()
        {
            GeoCoordinateWatcher geoWatcher = new GeoCoordinateWatcher();
            Console.WriteLine($"Permission: {geoWatcher.Permission}, " +
                              $"Status: {geoWatcher.Status}");

            geoWatcher.Start();

            // geoWatcher.Position を取得する時点で Status がまだ NoData
            // の場合は位置情報は取得できないので Ready になるまで待つ。
            // geoWatcher.Permission != GeoPositionPermission.Denied も
            // while の条件に入れないと、位置情報の取得が有効になってな
            // い場合は無限ループに陥るので注意
            while ((geoWatcher.Status != GeoPositionStatus.Ready) &&
                (geoWatcher.Permission != GeoPositionPermission.Denied))
            {
                System.Threading.Thread.Sleep(100);
            }

            if (geoWatcher.Permission == GeoPositionPermission.Denied)
            {
                Console.WriteLine($"Permission: {geoWatcher.Permission}, " +
                                  $"Status: {geoWatcher.Status}");
            }
            else
            {
                Console.WriteLine($"Permission: {geoWatcher.Permission}, " +
                                  $"Status: {geoWatcher.Status}");
                GeoCoordinate coord = geoWatcher.Position.Location;

                if (coord.IsUnknown != true)
                {
                    Console.WriteLine("Lat: {0}, Long: {1}",
                        coord.Latitude,
                        coord.Longitude);
                }
                else
                {
                    Console.WriteLine("Unknown latitude and longitude.");
                }
            }
        }
    }
}

Microsoft のドキュメント GeoCoordinateWatcher Class のサンプルコードはそのままでは動かないので注意してください。(環境によっては動くこともあるようですが)

動かない理由は、GeoCoordinateWatcher.TryStart メソッドでデータの取得が始まり即 true が返されますが、その時点では GeoCoordinateWatcher.Status はまだ NoData だからです。緯度経度データを取得するには TryStart メソッド実行後 Status が Ready になるまで待つ必要があります。

Tags: , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  June 2025  >>
MoTuWeThFrSaSu
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

View posts in large calendar