Device Geolocation in MAUI
A common question we get from mobile developers is how to display a user’s device location on the map. Whether it’s for vehicle tracking or enhancing user experience in e-commerce apps, plotting a user’s current location is essential in many applications.
In this post, we’ll dive into the .NET MAUI Geolocation class and explore how it can dynamically update and display a user’s real-time location on a map. We’ll also discuss the new .NET 8.0 Dispatching construct and how it can be used to asynchronously update the UI thread.
Tip
To see the geolocation sample shown in this article, just clone the MAUI HowDoI samples, run the solution, select the 'Navigate the Map' sample and click the 'bullseye' button in the lower right corner to show the current device location.
Sample Overview
In the MAUI ‘Navigate the Map’ sample, there are several separate functions that are being demonstrated. Map rotation, zooming and scrolling labels are all being shown in the sample. But for the purposes of this article, we will focus on the ‘bullseye’ icon in the lower right corner of the map.
When this bullseye icon is pressed, the sample uses the MAUI Geolocation api to query the device’s current location, add an animated blue marker, and center the map on that location. It’s worth noting that on mobile devices, the operating system will first get the user’s confirmation to share their current location with your application.
Code Walkthrough
The first step in the sample is to create the animated blue dot marker. In this example, the GPSMarker class is defined in the GpsMarker.xaml shown below. Note that the marker inherits from the ThinkGeo MAUI Marker class and uses the icon_gps_point animated gif to create a marker that’s 20x20 pixels:
Next, we need to create an async function that calls the MAUI Geolocation api to return the current device location. This method will be called initially when the user clicks the bullseye icon and subsequently when the _gpsTimer expires every 20 seconds.
In the example below, the Geolocation.GetLocationAsync() function is called with a 15 second timeout. Any exceptions during the location acquisition will be caught and displayed to the user. It’s also worth noting that the Geolocation returns a point in EPSG:4326 (WGS84 ) and since our basemap is in EPSG:3857(Spherical Mercator), we apply a ProjectionConverter to the point before we return.
private async Task<PointShape> GetGpsPointAsync()
{
try
{
// Use the MAUI Geolocation api to query the device's current location.
var location = await Geolocation.GetLocationAsync(new GeolocationRequest
{
DesiredAccuracy = GeolocationAccuracy.Medium,
Timeout = TimeSpan.FromSeconds(15)
});
if (location == null)
{
throw new Exception("Unable to retrieve GPS Point");
}
var newCenter = ProjectionConverter.Convert(4326, 3857, location.Longitude, location.Latitude);
return new PointShape(newCenter.X, newCenter.Y);
}
catch (Exception e)
{
// Show error message to user and fade after 5 seconds.
WarningLabel.Text = e.Message;
WarningLabel.IsVisible = true;
await Task.Delay(5 * 1000);
WarningLabel.IsVisible = false;
return null;
}
}
Finally, we just need to create a timer that will be used to invoke the GetGpsPointAsync method and wire everything together. When the timer expires, the _gps_TimerElapsed function is invoked and the new .NET 8.0 Dispatcher construct is used to invoke the GetGpsPointAsync() function from our worker thread and update the map on the UI thread.
private readonly System.Timers.Timer _gpsTimer = new();
...
// Update GPS position every 20s.
_gpsTimer.Interval = 20000;
_gpsTimer.Elapsed += _gpsTimer_Elapsed;
...
// start/stop timer on button click event.
_gpsTimer.Start();
...
private async void _gpsTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!GpsEnabled)
{
_gpsTimer.Stop();
return;
}
// Use the MAUI dispatcher to call the GetGpsPointAsync function.
var gps = await Dispatcher.DispatchAsync(GetGpsPointAsync);
if (gps == null)
return;
// Update the gpsMarker's position and refresh the map view.
_gpsMarker.Position = gps;
await Dispatcher.DispatchAsync(async () => await MapView.Overlays["simpleMarkerOverlay"].RefreshAsync());
}
Summary
We hope today’s post has been helpful. If you have any questions or if you have a topic you would like to learn more about, please email sales@thinkgeo.com or schedule a meeting to talk in person.
About ThinkGeo
We are a GIS software company founded in 2004 and located in Frisco, TX. Our clients are in more than 40 industries including agriculture, energy, transportation, government, engineering, IT, and defense. We pride ourselves on our excellent service and transparency. ThinkGeo offers a variety of products and services to meet almost any GIS application need. We can even help you develop your next project - anywhere from a few hours of consulting to outsourcing an entire project. To learn more, email us at sales@thinkgeo.com, or call us direct at 1-214-449-0330.