ユーザーの位置情報の取得

[Windows 10 の UWP アプリ向けに更新。 Windows 8.x の記事については、アーカイブをご覧ください ]

ユーザーの位置情報を検索し、位置の変更に対応します。 ユーザーの位置情報へのアクセスは、設定アプリのプライバシー設定で管理されています。 このトピックでは、アプリにユーザーの位置情報へのアクセス許可が与えられているかどうかを確認する方法についても説明します。

ヒント: アプリでユーザーの位置情報にアクセスする方法について詳しくは、GitHub の Windows-universal-samples リポジトリから次のサンプルをダウンロードしてください。

位置情報機能を有効にする

  1. ソリューション エクスプローラーで、package.appxmanifest をダブルクリックし、[機能] タブを選びます。
  2. [機能] ボックスの一覧で、[位置情報] チェックボックスをオンにします。 これにより、location デバイス機能がパッケージ マニフェスト ファイルに追加されます。
  <Capabilities>
    <!-- DeviceCapability elements must follow Capability elements (if present) -->
    <DeviceCapability Name="location"/>
  </Capabilities>

現在の位置情報を取得する

このセクションでは、Windows.Devices.Geolocation 名前空間の API を使ってユーザーの地理的な位置を検出する方法について説明します。

手順 1. ユーザーの位置情報へのアクセス許可を求める

アプリに Consentless Location 機能 (「注意」を参照) がない場合は、ユーザーの位置情報へアクセスする前に、RequestAccessAsync メソッドを使用してユーザーの位置情報へのアクセス許可を求める必要があります。 RequestAccessAsync メソッドは UI スレッドから呼び出す必要があり、アプリがフォアグラウンドで実行されている必要があります。 アプリがユーザーの位置情報にアクセスするには、先にユーザーがその情報へのアクセス許可をアプリに与える必要があります。*

using Windows.Devices.Geolocation;
...
var accessStatus = await Geolocator.RequestAccessAsync();

RequestAccessAsync メソッドを使って、ユーザーに位置情報へのアクセス許可を求めます。 ユーザーに対するこの要求はアプリごとに 1 回だけ行われます。 アクセス許可の付与または拒否を行った後、このメソッドはユーザーにアクセス許可を求めなくなります。 ユーザーが位置情報へのアクセス許可を求められた後にそのアクセス許可を変更できるように、位置情報の設定へのリンクを用意することをお勧めします。これについては、このトピックの後半で紹介します。

注意: Consentless Location 機能により、アプリはユーザーによる明示的な許可を必要とせずに、意図的にあいまいにされた (不正確な) 位置情報を取得できます (ただしシステム全体の位置情報スイッチは on のままである必要があります)。 アプリで Consentless Location を使用する方法について詳しくは、Geolocator クラスの AllowFallbackToConsentlessPositions メソッドをご覧ください。

手順 2. ユーザーの位置情報を取得し、位置情報のアクセス許可の変更を登録する

GetGeopositionAsync メソッドは、現在の位置情報に対して 1 回限りの読み取りを実行します。 ここでは、switch ステートメントを (前の例で示した) accessStatus と共に使って、ユーザーの位置情報へのアクセス許可が与えられている場合にのみ動作するように指定します。 ユーザーの位置情報へのアクセス許可が与えられた場合は、コードによって Geolocator オブジェクトが作成され、位置情報へのアクセス許可の変更が登録され、ユーザーの位置情報が要求されます。

switch (accessStatus)
{
    case GeolocationAccessStatus.Allowed:
        _rootPage.NotifyUser("Waiting for update...", NotifyType.StatusMessage);

        // If DesiredAccuracy or DesiredAccuracyInMeters are not set (or value is 0), DesiredAccuracy.Default is used.
        Geolocator geolocator = new Geolocator { DesiredAccuracyInMeters = _desireAccuracyInMetersValue };

        // Subscribe to the StatusChanged event to get updates of location status changes.
        _geolocator.StatusChanged += OnStatusChanged;

        // Carry out the operation.
        Geoposition pos = await geolocator.GetGeopositionAsync();

        UpdateLocationData(pos);
        _rootPage.NotifyUser("Location updated.", NotifyType.StatusMessage);
        break;

    case GeolocationAccessStatus.Denied:
        _rootPage.NotifyUser("Access to location is denied.", NotifyType.ErrorMessage);
        LocationDisabledMessage.Visibility = Visibility.Visible;
        UpdateLocationData(null);
        break;

    case GeolocationAccessStatus.Unspecified:
        _rootPage.NotifyUser("Unspecified error.", NotifyType.ErrorMessage);
        UpdateLocationData(null);
        break;
}

手順 3. 位置情報へのアクセス許可の変更を処理する

Geolocator オブジェクトは StatusChanged イベントをトリガーして、ユーザーの位置情報設定が変化したことを示します。 このイベントは、引数の Status プロパティ (PositionStatus 型) を使って、対応する状態を渡します。 このメソッドは UI スレッドから呼び出されず、Dispatcher オブジェクトが UI の変更を呼び出します。

using Windows.UI.Core;
...
async private void OnStatusChanged(Geolocator sender, StatusChangedEventArgs e)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Show the location setting message only if status is disabled.
        LocationDisabledMessage.Visibility = Visibility.Collapsed;

        switch (e.Status)
        {
            case PositionStatus.Ready:
                // Location platform is providing valid data.
                ScenarioOutput_Status.Text = "Ready";
                _rootPage.NotifyUser("Location platform is ready.", NotifyType.StatusMessage);
                break;

            case PositionStatus.Initializing:
                // Location platform is attempting to acquire a fix.
                ScenarioOutput_Status.Text = "Initializing";
                _rootPage.NotifyUser("Location platform is attempting to obtain a position.", NotifyType.StatusMessage);
                break;

            case PositionStatus.NoData:
                // Location platform could not obtain location data.
                ScenarioOutput_Status.Text = "No data";
                _rootPage.NotifyUser("Not able to determine the location.", NotifyType.ErrorMessage);
                break;

            case PositionStatus.Disabled:
                // The permission to access location data is denied by the user or other policies.
                ScenarioOutput_Status.Text = "Disabled";
                _rootPage.NotifyUser("Access to location is denied.", NotifyType.ErrorMessage);

                // Show message to the user to go to location settings.
                LocationDisabledMessage.Visibility = Visibility.Visible;

                // Clear any cached location data.
                UpdateLocationData(null);
                break;

            case PositionStatus.NotInitialized:
                // The location platform is not initialized. This indicates that the application
                // has not made a request for location data.
                ScenarioOutput_Status.Text = "Not initialized";
                _rootPage.NotifyUser("No request for location is made yet.", NotifyType.StatusMessage);
                break;

            case PositionStatus.NotAvailable:
                // The location platform is not available on this version of the OS.
                ScenarioOutput_Status.Text = "Not available";
                _rootPage.NotifyUser("Location is not available on this version of the OS.", NotifyType.ErrorMessage);
                break;

            default:
                ScenarioOutput_Status.Text = "Unknown";
                _rootPage.NotifyUser(string.Empty, NotifyType.StatusMessage);
                break;
        }
    });
}

位置情報の更新への対応

このセクションでは、PositionChanged イベントを使って、特定の期間におけるユーザーの位置の更新情報を受け取る方法について説明します。 ユーザーはいつでも位置情報へのアクセス許可を取り消すことができるため、前のセクションで説明したように、RequestAccessAsync を呼び出して StatusChanged イベントを使うことが重要になります。

このセクションでは、既に位置情報機能を有効にし、フォアグラウンド アプリの UI スレッドから RequestAccessAsync を呼び出していることを前提としています。

手順 1. レポート間隔を定義し、位置情報の更新を登録する

この例では、switch ステートメントを (前の例で示した) accessStatus と共に使って、ユーザーの位置情報へのアクセス許可が与えられている場合にのみ動作するように指定します。 ユーザーの位置情報へのアクセス許可が与えられた場合は、コードによって Geolocator オブジェクトが作成され、追跡の種類の指定と位置情報の更新の登録が行われます。

Geolocator オブジェクトは、位置の変化 (距離に基づく追跡) または時間の変化 (期間に基づく追跡) に基づいて PositionChanged イベントをトリガーできます。

  • 距離に基づく追跡の場合は、MovementThreshold プロパティを設定します。
  • 期間に基づく追跡の場合は、ReportInterval プロパティを設定します。

どちらのプロパティも設定されていない場合は、位置が 1 秒ごとが返されます (ReportInterval = 1000 と同じです)。 ここでは、2 秒 (ReportInterval = 2000) のレポート間隔を使います。

using Windows.Devices.Geolocation;
...
var accessStatus = await Geolocator.RequestAccessAsync();

switch (accessStatus)
{
    case GeolocationAccessStatus.Allowed:
        // Create Geolocator and define perodic-based tracking (2 second interval).
        _geolocator = new Geolocator { ReportInterval = 2000 };

        // Subscribe to the PositionChanged event to get location updates.
        _geolocator.PositionChanged += OnPositionChanged;

        // Subscribe to StatusChanged event to get updates of location status changes.
        _geolocator.StatusChanged += OnStatusChanged;

        _rootPage.NotifyUser("Waiting for update...", NotifyType.StatusMessage);
        LocationDisabledMessage.Visibility = Visibility.Collapsed;
        StartTrackingButton.IsEnabled = false;
        StopTrackingButton.IsEnabled = true;
        break;

    case GeolocationAccessStatus.Denied:
        _rootPage.NotifyUser("Access to location is denied.", NotifyType.ErrorMessage);
        LocationDisabledMessage.Visibility = Visibility.Visible;
        break;

    case GeolocationAccessStatus.Unspecified:
        _rootPage.NotifyUser("Unspecificed error!", NotifyType.ErrorMessage);
        LocationDisabledMessage.Visibility = Visibility.Collapsed;
        break;
}

手順 2. 位置情報の更新を処理する

Geolocator オブジェクトは PositionChanged イベントをトリガーし、構成方法に応じて、ユーザーの位置が変化したことまたは時間が経過したことを示します。 このイベントは、引数の Position プロパティ (Geoposition) を使って、対応する位置を渡します。 この例では、メソッドは UI スレッドから呼び出されず、Dispatcher オブジェクトが UI の変更を呼び出します。

using Windows.UI.Core;
...
async private void OnPositionChanged(Geolocator sender, PositionChangedEventArgs e)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        _rootPage.NotifyUser("Location updated.", NotifyType.StatusMessage);
        UpdateLocationData(e.Position);
    });
}

位置情報のプライバシー設定を変更する

位置情報のプライバシー設定でアプリにユーザーの位置情報へのアクセス許可を与えていない場合は、設定アプリの [位置情報のプライバシー設定] へのリンクを用意することをお勧めします。 この例では、ハイパーリンク コントロールを使って、ms-settings:privacy-location という URI に移動します。

<!--Set Visibility to Visible when access to location is denied -->  
<TextBlock x:Name="LocationDisabledMessage" FontStyle="Italic"
                 Visibility="Collapsed" Margin="0,15,0,0" TextWrapping="Wrap" >
          <Run Text="This app is not able to access Location. Go to " />
              <Hyperlink NavigateUri="ms-settings:privacy-location">
                  <Run Text="Settings" />
              </Hyperlink>
          <Run Text=" to check the location privacy settings."/>
</TextBlock>

また、アプリで LaunchUriAsync メソッドを呼び出し、コードで設定アプリを起動することもできます。 詳しくは、「Windows 設定アプリの起動」をご覧ください。

using Windows.System;
...
bool result = await Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-location"));

アプリのトラブルシューティングを行う

アプリがユーザーの位置情報にアクセスする前に、デバイスで [位置情報] を有効にする必要があります。 設定アプリで、次の位置情報に関するプライバシー設定がオンになっていることを確認します。

  • [このデバイスの位置情報]オン になっている (Windows 10 Mobile には適用されません)
  • 位置情報サービス設定の [位置情報]オン になっている
  • [位置情報を使うことができるアプリを選ぶ] で、アプリが オン になっている