After we already managed to communicate with an BLE device, let’s now try something a bit easier.
As mentioned in a previous post, we already have some “requirements” for a treasure hunt. For that, we need a solution to get the users location.
Let’s see if we can manage to get the location directly from a webapplication and get around the need of writing and deploying a full Mobile App.
Get setup
As so often we start with creation of BlazorWasm template
dotnet new blazorwasm -o tryout_blazor_geolocation --hosted
For convinience and cleanliness we again create a new razor page and a link in navigation.
@@ -24,6 +24,11 @@
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</div>
+ <div class="nav-item px-3">
+ <NavLink class="nav-link" href="location">
+ <span class="oi oi-list-rich" aria-hidden="true"></span> Location
+ </NavLink>
+ </div>
</nav>
</div>
@page "/location"
@using System.Text.Json
@implements IDisposable
@inject IJSRuntime JS
<h3>Location</h3>
<button @onclick="WatchLocation">WatchLocation</button>
<div>
<p>Latitude: @position.Latitude</p>
<p>Longitude: @position.Longitude</p>
<p>Altitude: @position.Altitude</p>
<p>Heading: @position.Heading</p>
<p>Accuracy: @position.Accuracy</p>
</div>
@code{
public class Position {
public float Latitude { get; set; }
public float Longitude { get; set; }
public float Altitude { get; set; }
public float? Heading { get; set; }
public float? Accuracy { get; set; }
}
private DotNetObjectReference<Location>? objRef;
protected Position position = new Position();
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task WatchLocation() {
await JS.InvokeVoidAsync("setNetRef", objRef);
await JS.InvokeVoidAsync("UpdatePosition");
}
[JSInvokable]
public void SetPosition(string posJson)
{
Position? pos = JsonSerializer.Deserialize<Position>(posJson);
if(pos is null) return;
Console.WriteLine($"Position {pos.Latitude} {pos.Longitude} {pos.Altitude}");
position = pos;
StateHasChanged();
}
public void Dispose()
{
objRef?.Dispose();
}
}
As well as an Javascript file to contain the geolocation api calls.
@@ -20,6 +20,7 @@
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
+ <script src="location.js"></script>
</body>
</html>
let netRef;
async function setNetRef(ref) {
netRef = ref;
}
let positionWatchHandle;
async function UpdatePosition() {
if (!netRef) {
console.error("netRef is null");
return;
}
if (!navigator.geolocation) {
console.error("Has no geolocation");
return;
}
if (positionWatchHandle) {
navigator.geolocation.clearWatch(positionWatchHandle);
}
function success(position) {
console.log(position.coords);
netRef.invokeMethodAsync('SetPosition', JSON.stringify({
Latitude: position.coords.latitude,
Longitude: position.coords.longitude,
Altitude: position.coords.altitude,
Heading: position.coords.heading ?? 0,
Accuracy: position.coords.accuracy ?? -1
}));
}
positionWatchHandle = navigator.geolocation.watchPosition(success, () => {
console.error(`Don't know where you are`);
}, {
enableHighAccuracy: true,
timeout: 1000,
maximumAge: 0
});
}
And here’s how it looks on the connected tablet. (See Blazor Remote Debugging – Android)
On first call a Systemmessage asks if the page is allwed to access current location.
What is happening?
To access the location from Blazor, the JSRuntime is used to then use the Geolocation.
In the razor file, we output the values of the locally declared Position
object.
When pushing the button, first a reference to our current page instance is handed over to Javascript context, to allow calls to Blazor / Webassembly context.
Followed by invoke of UpdatePosition
. UpdatePosition
is then performing some checks, if the netRef
is already set, geolocation is actually available and if location is already watched.
The call of navigator.geolocation.watchPosition(..)
is then starting the watch timer. In the options we can set a timeout
for the update interval.
To get location data from Javascript context back to Blazor Webassembly, all information is put into a JSON Object and passed over as string.
In Blazor that string is deserialized to object of type Position
and rerender of HTML elements is triggered.
Code can be found here: https://github.com/sukapx/tryout_blazor_geolocation/tree/e407ce374aced69ad3a8cb5ec1d31326a80f85ba