Windows アプリ SDKと WinUI 3 には、マップの表示、ユーザーの位置の検出、ジオフェンスの設定を行う API とコントロールが用意されています。 これらの機能を使用して、ピン付きの対話型マップを表示するアプリを構築し、ユーザーの位置を追跡し、ユーザーが地理的領域に出入りしたときにアクションをトリガーします。
この記事では、各機能について説明し、1 つの作業アプリで、MapControl、Geolocatorを組み合わせたGeofenceMonitorを示します。
MapControl を使用してマップを表示する
MapControl には、Azure Mapsを利用した対話型のマップが表示されます。 ピン、レイヤーを追加し、パン、ズーム、クリックなどのユーザー操作に応答できます。
MapControl には、Azure Maps アカウントが必要です。 アカウントを作成してサービス トークンを取得するには、Azure Maps アカウントの管理に関するページを参照してください。
詳細な使用方法については、 MapControl を参照してください。
<MapControl x:Name="myMap"
MapServiceToken="YOUR_AZURE_MAPS_TOKEN"
Height="400" />
Note
UWP MapControl と Windows。Services.Maps API は非推奨となり、今後のバージョンのWindowsでは使用できない可能性があります。 WinUI 3 アプリでは、上記の新しい MapControl を使用する必要があります。 詳細については、「 非推奨の機能のリソース」を参照してください。
ユーザーの場所を検出する
Windows。Devices.Geolocation API を使用すると、デバイスの地理的位置を取得できます。 これらの API は、UWP アプリと Windows アプリ SDK (WinUI 3) アプリの両方で動作します。 次のようにすることができます。
- Geolocator.GetGeopositionAsync を使用して 1 回限りの位置を取得します。
- Geolocator.PositionChanged イベントを使用して、時間の経過に伴う位置の変化を追跡します。
- GeovisitMonitor を使用してアクセス状態の変化を監視し、バッテリー効率の高い位置認識を実現します。
詳細なガイドについては、「 ユーザーの場所を取得する」を参照してください。
ジオフェンスを設定する
ジオフェンスは地理的境界を定義します。 ユーザーが境界に出入りすると、アプリは通知を受け取ります。 ジオフェンスは、場所ベースのリマインダー、アラート、またはコンテンツ配信に役立ちます。
ジオフェンスの作成と監視の手順については、「ジオフェンス の設定」を参照してください。
場所の機能とプライバシー
すべての場所 API には、アプリのパッケージ マニフェストで宣言された Location 機能が必要です。 位置情報データにアクセスする前に、実行時に Geolocator.RequestAccessAsync を呼び出す必要もあります。
Windowsでは、[設定] > [プライバシーとセキュリティ] > [場所] を使用して、ユーザーが自分の場所にアクセスできるアプリ制御できます。 アプリは、ユーザーが場所へのアクセスを拒否または取り消すケースを処理する必要があります。
完全なコード例
次の例では、1 つの WinUI 3 ウィンドウに MapControl、 Geolocator、および GeofenceMonitor をまとめます。 Azure Maps キーが構成されていない場合、ジオロケーションとジオフェンシングが引き続き機能している間、マップは正常に低下します。
前提条件
- Windows アプリ SDK 2.2 以降
- マップ タイルを表示するために必要なAzure Maps キー。 有効なキーがない場合、MapControl はレンダリングされますが、空のマップが表示されます。
-
で宣言されている
Package.appxmanifestデバイス機能:
<DeviceCapability Name="location" />
アプリを実行する前に、Azure Maps キーを環境変数として設定します。
$env:AZURE_MAPS_KEY = "your-key-here"
MainWindow.xaml
ボタンとステータス テキストが表示された左側の 300 ピクセルのコントロール パネルと、右側に MapControl 。 Azure Maps キーが見つからない場合は、オーバーレイが表示されます。
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="MapLocationDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Map Location Demo">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar Title="Map Location Demo" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="16" RowSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Location Demo" FontSize="20" FontWeight="Bold"/>
<StackPanel Grid.Row="1" Spacing="8">
<Button x:Name="FindMeButton" Content="Find My Location"
Click="FindMeButton_Click" HorizontalAlignment="Stretch"/>
<Button x:Name="AddGeofenceButton" Content="Add Geofence Here"
Click="AddGeofenceButton_Click" HorizontalAlignment="Stretch"/>
</StackPanel>
<TextBlock x:Name="StatusText" Grid.Row="2"
Text="Click 'Find My Location' to begin." TextWrapping="Wrap"/>
<ListView x:Name="EventLog" Grid.Row="3" Header="Event Log"/>
</Grid>
<Grid Grid.Column="1">
<MapControl x:Name="MyMap" />
<StackPanel x:Name="MapKeyMissing" Visibility="Collapsed"
HorizontalAlignment="Center" VerticalAlignment="Center"
Spacing="8">
<FontIcon Glyph="" FontSize="48"
HorizontalAlignment="Center"
Foreground="{ThemeResource SystemFillColorCautionBrush}" />
<TextBlock Text="Azure Maps key not configured"
FontSize="18" FontWeight="SemiBold"
HorizontalAlignment="Center" />
<TextBlock x:Name="MapKeyHint" TextWrapping="Wrap" MaxWidth="400"
HorizontalAlignment="Center" TextAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
</Grid>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Devices.Geolocation;
using Windows.Devices.Geolocation.Geofencing;
namespace MapLocationDemo;
public sealed partial class MainWindow : Window
{
private Geolocator? _geolocator;
private BasicGeoposition _lastPosition;
private bool _mapAvailable;
public MainWindow()
{
InitializeComponent();
_mapAvailable = TryConfigureMap();
GeofenceMonitor.Current.GeofenceStateChanged += OnGeofenceStateChanged;
}
// Read the Azure Maps key from an environment variable.
// If missing, collapse the map and show an informational overlay.
private bool TryConfigureMap()
{
var key = Environment.GetEnvironmentVariable("AZURE_MAPS_KEY");
if (string.IsNullOrWhiteSpace(key))
{
MyMap.Visibility = Visibility.Collapsed;
MapKeyMissing.Visibility = Visibility.Visible;
MapKeyHint.Text = "Set the AZURE_MAPS_KEY environment variable "
+ "and restart.\nGeolocation and geofencing still work "
+ "without the map.";
Log("Azure Maps key not found — map disabled");
return false;
}
MyMap.MapServiceToken = key;
return true;
}
private async void FindMeButton_Click(object sender, RoutedEventArgs e)
{
FindMeButton.IsEnabled = false;
StatusText.Text = "Requesting location access...";
var access = await Geolocator.RequestAccessAsync();
if (access != GeolocationAccessStatus.Allowed)
{
StatusText.Text =
"Location access denied. Check Settings > Privacy > Location.";
FindMeButton.IsEnabled = true;
return;
}
_geolocator = new Geolocator { DesiredAccuracyInMeters = 100 };
try
{
var pos = await _geolocator.GetGeopositionAsync();
var lat = pos.Coordinate.Point.Position.Latitude;
var lon = pos.Coordinate.Point.Position.Longitude;
_lastPosition = new BasicGeoposition
{
Latitude = lat, Longitude = lon
};
StatusText.Text =
$"Location: {lat:F5}, {lon:F5} ({pos.Coordinate.Accuracy:F0} m)";
Log($"Position: {lat:F5}, {lon:F5}");
if (_mapAvailable)
{
var pt = new Geopoint(_lastPosition);
MyMap.Center = pt;
MyMap.ZoomLevel = 15;
var layer = new MapElementsLayer();
layer.MapElements = new List<MapElement>
{
new MapIcon { Location = pt }
};
MyMap.Layers.Clear();
MyMap.Layers.Add(layer);
}
}
catch (Exception ex) { StatusText.Text = $"Error: {ex.Message}"; }
finally { FindMeButton.IsEnabled = true; }
}
private void AddGeofenceButton_Click(object sender, RoutedEventArgs e)
{
if (_lastPosition.Latitude == 0 && _lastPosition.Longitude == 0)
{
StatusText.Text = "Get your location first.";
return;
}
var fence = new Geofence("MyGeofence",
new Geocircle(_lastPosition, 200),
MonitoredGeofenceStates.Entered | MonitoredGeofenceStates.Exited,
false, TimeSpan.FromSeconds(5));
GeofenceMonitor.Current.Geofences.Add(fence);
StatusText.Text = $"Geofence added at "
+ $"{_lastPosition.Latitude:F5}, {_lastPosition.Longitude:F5}";
Log("Geofence registered");
}
private void OnGeofenceStateChanged(
GeofenceMonitor sender, object args)
{
var reports = sender.ReadReports();
DispatcherQueue.TryEnqueue(() =>
{
foreach (var r in reports)
{
var msg = r.NewState switch
{
GeofenceState.Entered => $"Entered: {r.Geofence.Id}",
GeofenceState.Exited => $"Exited: {r.Geofence.Id}",
GeofenceState.Removed => $"Removed: {r.Geofence.Id}",
_ => null
};
if (msg != null) { StatusText.Text = msg; Log(msg); }
}
});
}
private void Log(string msg) =>
EventLog.Items.Insert(0, $"[{DateTime.Now:HH:mm:ss}] {msg}");
}
主要なパターン
-
MapControlは、Windows アプリ SDK 1.6 以降に組み込まれています。MapServiceTokenを Azure Maps キーに設定します。 -
TryConfigureMapは、起動時にAZURE_MAPS_KEY環境変数をチェックします。 変数が空の場合、マップは折りたたまれて、オーバーレイで修正方法が説明されます。クラッシュなし、空のマップはありません。 -
DeviceCapability Name="location"inPackage.appxmanifestは必須か、Geolocator.RequestAccessAsyncはDeniedを返します。 -
GeofenceMonitor.GeofenceStateChangedはバックグラウンド スレッドで発生するため、DispatcherQueue.TryEnqueueを使用して UI を更新します。
関連資料
Windows developer