Live location ingestion and mapping

Live location ingestion and mapping

Using Azure IOT Hub & Service Bus

·

3 min read

In the first article in this series (Building map based apps - The Camino), the data being sent to Azure was a jpeg image that happened to have geospatial information attached. For that use case it worked well, but it was not designed for high-volume or low-latency scenarios.

So let's change it a bit and make a real-time tracking/mapping solution. The basic architecture is shown here.

The only real difference between this approach and the photo capture solution is replacing the blob storage account with an IOT Hub and Service Bus.

The mobile client is using this package Maui-GeolocatorPlugin (James Montemagno/Travis Yeik) to subscribe to location changes on the device.

When a location change is detected, a few lines of code send the metadata to Azure IOT Hub :

public async Task SendIOTMessage2(DateTimeOffset dt,double longitude,double latitude, double altitude)
{
   DeviceClient client = DeviceClient.CreateFromConnectionString(ConnString, TransportType.Http1);

   String dataBuffer = string.Format("{{\"Longitude\":\"{0}\",\"Latitude\":\"{1}\",\"Altitude\":\"{2}\",\"Timestamp\":\"{3}\"}}", longitude,latitude,altitude,dt);

   using var msg = new Message(Encoding.UTF8.GetBytes(dataBuffer));
   try {
      await client.SendEventAsync(msg).ConfigureAwait(false);
   }
   catch (Exception ex) {
      txtLongitude.Text = "Error in IOT";
      //deal with this.
   }
}

In this case as I'm using an IOT Hub, so each device needs to be enrolled and will have a connection string similar to :

"HostName=myiothubname.azure-devices.net; DeviceId=device1;SharedAccessKey=XXX34jh5g3j4hg5j3h4g5j3hg43jhgXXX="

An IOT Hub provides two way messaging and a host of other features, but a simple Event Hub could be used if these are not needed or device enrollment is overkill.

The IOT Hub is using simple message routing to drop each update into a Service Bus queue :

From there, an Azure Function is triggered that adds a record to the SQL table :

#r "Newtonsoft.Json"
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Microsoft.Data.SqlClient;

public static void Run(string myQueueItem, ILogger log)
{

    dynamic json = JsonConvert.DeserializeObject(myQueueItem);
    string altitude = json["Altitude"];
    string longitude = json["Longitude"];
    string latitude = json["Latitude"];
    DateTime ts = json["Timestamp"];
    string format = "yyyy-MM-dd HH:mm:ss";

    //Important - add data validation and error handling

    SqlConnection sqlConnection1 = new SqlConnection(Connstring);
    SqlCommand cmd = new SqlCommand();
    log.LogInformation(ts.ToString(format));
    cmd.CommandText = "insert into locations (latitude,longitude,altitude,ts) VALUES (" + latitude + "," + longitude + "," + altitude + ",'" + ts.ToString(format) + "')";

    cmd.Connection = sqlConnection1;

    sqlConnection1.Open();
    cmd.ExecuteNonQuery();
    sqlConnection1.Close();
    return;
}

The rest of the solution is essentially unchanged from Building map based apps - The Camino.

A second function listens on a HTTP endpoint and can return a date range or the last X data points. The results are returned as JSON, ready to be mapped :

The client side HTML & JS is largely unchanged:

 function GetLocationsTrack() {
     function reqListener() {
        var myObj = JSON.parse(this.responseText);
           DrawTrack(myObj);
           SetView(myObj);
        }

      var oReq = new XMLHttpRequest();
      oReq.addEventListener("load", reqListener);
      url = "https://myfunctionurl.azurewebsites.net/api/GetLocations?code=ReplaceWithYourCode==&count=450";
      oReq.open("GET", url);
      oReq.send();
}

The net result of this is a relatively precise location track looking something like this:

Typically less than 1 second for a message to flow through the pipeline and be visible on a refreshed map.

As always, please note: General disclaimer and notes on coding style, structure and security.