Navigation And Tracking in MAUI
We often receive questions from customers about building a Vehicle Navigation or Asset Tracking application. In today’s blog post, we’ll walk through our MAUI Vehicle Navigation sample, covering key topics such as:
A comprehensive overview of the sample project, data handling, and map styling.
A step-by-step walkthrough of the sample code.
Adding extra functionality with assets, geofences, and custom alerts.
While the following example is built using ThinkGeo Mobile for MAUI, much of the code can also be reused to develop a similar tracking app in WPF or WinForms.
Tip
To run the vehicle navigation sample in Visual Studio, just pull the MAUI HowDoI Samples from here, run the solution and then click on the ‘Vehicle Navigation’ sample in the menu.
Overview of the Sample, Data and Styling
The Vehicle Navigation sample demonstrates a simple way to show a vehicle’s location centered on the map while the background pans smoothly. The sample defaults to a road basemap, but there’s also an ‘Aerial Background’ checkbox in the lower left corner of the sample. If you want to see satellite imagery on your basemap, just click this checkbox. Both the roads and aerial basemaps are powered by the ThinkGeo Cloud Mapping Services.
Learn More
To learn more about the ThinkGeo Cloud service used for the basemap, you can visit the cloud's Online Samples. Also check out past blog posts on the Cloud's Javascript API and REST APIs.
For simplicity in the sample, a vehicle-route.csv file with latitude and longitude values is used to represent the path or route of the vehicle. In the real world, this .csv could be replaced with live route data from a routing provider like ThinkGeo Cloud.
The sample then loops through the sample locations updates the vehicle position and recenters the map with an offset to ensure the vehicle is in the bottom center of the screen. If the new point is not inline with the previous point, the sample rotates the map to simulate a vehicle turn.
A blue triangle image is used to simulate the vehicle’s current location. As the vehicle moves, you’ll also notice that there’s a yellow line ahead of the vehicle, and a green line behind the vehicle. The yellow just shows a line of all positions in the vehicle-route.csv sample dataset and the green line shows where the vehicle has traveled in the past. As you’ll see below, the styling is completely customizable and you may choose other styles for visualizing the vehicle’s movement.
Sample Code Walkthrough
The first step in building any map is to initialize the map, dataset and styles - and the vehicle tracking sample is no different. In the code below, MapView_OnSizeChanged event handler is the entry-point where we do those initializations.
Step 1 - Initialize the background map (streets and aerial) using the ThinkGeoRasterOverlay and add the overlay to the map:
// Add ThiknGeo Cloud Maps as a background overlay
var backgroundOverlay = new ThinkGeoRasterOverlay
{
ClientId = SampleKeys.ClientId,
ClientSecret = SampleKeys.ClientSecret,
MapType = ThinkGeoCloudRasterMapsMapType.Light_V2_X2,
TileCache = new FileRasterTileCache(FileSystem.Current.CacheDirectory, "ThinkGeoRasterCache")
};
MapView.Overlays.Add("Background Maps", backgroundOverlay);
Step 2 - Load the sample route data and create an InMemoryFeatureLayer for the route which will be styled as a yellow line
Note: the InitGpsData() function is not shown in the code, but it simply reads the sample .csv file and loads them into a Collection<Vertex>.
//Initialize our GPS Points and create an InMemoryFeatureLayer and Style for the Route (styled in yellow)
_gpsPoints = await InitGpsData();
var routeLayer = GetRouteLayerFromGpsPoints(_gpsPoints);
private static InMemoryFeatureLayer GetRouteLayerFromGpsPoints(Collection gpsPoints)
{
var lineShape = new LineShape();
foreach (var vertex in gpsPoints)
{
lineShape.Vertices.Add(vertex);
}
// create the layers for the routes.
var routeLayer = new InMemoryFeatureLayer();
routeLayer.InternalFeatures.Add(new Feature(lineShape));
routeLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = LineStyle.CreateSimpleLineStyle(GeoColors.Yellow, 6, true);
routeLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
return routeLayer;
}
Step 3 - Create a LayerNonRotationGraphicsViewOverlay, add the route layers created in step 2, and add the overlay to the map.
Note: In future releases the ‘NonRotation’ layer used in this sample will be obsoleted and replaced by the LayerGraphicsViewOverlay, but will function the same.
var layerOverlay = new LayerNonRotationGraphicsViewOverlay();
layerOverlay.UpdateDataWhileTransforming = true;
layerOverlay.Layers.Add(routeLayer);
layerOverlay.Layers.Add(_visitedRoutesLayer);
MapView.Overlays.Add(layerOverlay);
Step 4 - Create a SimpleMarkerOverlay to show the blue vehicle_location.png which depicts the current vehicle location. Later we’ll see how to keep this vehicle marker centered at the bottom of the screen while the map rotates.
// Create a marker overlay to show where the vehicle is
var markerOverlay = new SimpleMarkerOverlay();
// Create the marker of the vehicle
_vehicleMarker = new ImageMarker
{
Position = new PointShape(_gpsPoints[0]),
ImagePath = "vehicle_location.png",
WidthRequest = 24,
HeightRequest = 24
};
markerOverlay.Children.Add(_vehicleMarker);
MapView.Overlays.Add(markerOverlay);
Step 5 - As the vehicle position changes, implement smooth panning and map rotation while keeping the blue vehicle icon centered at the bottom of the map and facing up. To accomplish this, the sample implements a ZoomToGpsPointAsync() method that the sample calls for each point in the _gpsPoints collection we explored in Step 2 above.
In the ZoomToGpsPointAsync() method shown below, The MapUtil’s OffsetPointWithScreenOffset function to generate a center point that can be used to zoom into that will result in the blue vehicle remaining in the same position while the rest of the map moves and rotates.
Also note that the AnimationSettings class is used to ensure a smooth panning experience for the user.
Learn More
To learn more about MAUI Animations and the AnimationSettings class, check out this post.
private async Task ZoomToGpsPointAsync(int gpsPointIndex, CancellationToken cancellationToken)
{
if (gpsPointIndex >= _gpsPoints.Count)
return;
var currentLocation = _gpsPoints[gpsPointIndex];
var angle = GetRotationAngle(gpsPointIndex, _gpsPoints);
var animationSettings = new AnimationSettings
{
Type = MapAnimationType.DrawWithAnimation,
Length = 1000,
Easing = Easing.Linear
};
var centerPoint = new PointShape(currentLocation);
// Recenter the map to display the GPS location towards the bottom for improved visibility.
centerPoint = MapUtil.OffsetPointWithScreenOffset(centerPoint, 0, 200, angle, MapView.MapScale, MapView.MapUnit);
await MapView.ZoomToExtentAsync(centerPoint, MapView.MapScale, angle, animationSettings, OverlaysRenderSequenceType.Default, cancellationToken);
UpdateVisitedRoutes(_gpsPoints[gpsPointIndex]);
_vehicleMarker.Position = new PointShape(currentLocation.X, currentLocation.Y);
}
Adding additional assets, geofences, and alerts.
While the Vehicle Tracking sample above does a great job showing a vehicle navigating a predefined route, it could be easily modified to track multiple assets and add geofences to alert you when vehicles enter or exit user-defined areas on a map.
Modifying the sample to behave like an asset tracking application such as Life360 would be fairly simple. Instead of a single .csv file for your data feed, you would have multiple data feeds typically from a web service. And after you plot each asset’s location and speed, you would just want to skip the step about recentering the map on the blue vehicle triangle.
In an asset tracking application, you may also want to add geofences to alert you when an asset enters or leaves a certain area.
To draw a geofence, check out the MapView’s TrackOverlay and EditOverlay properties and how they are used in the ‘Draw, Edit and Delete Shapes’ sample. Once you have geofences drawn in your application, it’s a simple matter of performing a spatial query using the GetFeaturesContaining method in the QueryTools class. For a good reference of using the QueryTools class, you can view the ‘Find Containing Features’ sample.
Summary
We hope today’s post on the MAUI Vehicle Navigation sample has been helpful. Start your evaluation today and stay tuned for future posts where we dive into other real-world examples that can be used in your own projects.
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.