Geographic/Map Dashboards
Grafana supports GeoMap
dashboards, which overlay location-based data on a map. As a contrived example, let’s build a dashboard that converts a stream of HL7 messages to location data.
A real-world example might be mapping disease diagnosis, emerging outbreaks, or prescription drug usage to geographic locations.
In this sample, we’ll use a custom code device to prototype the code. Typically, this would be baked into a fully compiled device for production use. We’ll perform the following steps:
Read HL7 messages from a queue.
Extract the patient address information.
Use a geo-location service to convert the address into lat/lon values.
Convert lat/lon into a geohash format.
Write the metric.
Let’s review the device code:
public partial class CustomDevice : BaseCustomDevice
{
// counter metric
private ICounter m_GeoHashCounter;
// geolocation service uri
private const string m_IpStackAddress = "https://api.positionstack.com/v1/forward?access_key=XXXXXXXXXXXXX&query={0}";
public override void Start()
{
// get a handle to the metrics provider (not required in fully compiled devices)
var provider = ServiceProvider.GetInstance<IMetricsProvider>();
if(provider == null)
throw new Exception("Metrics provider not registered.");
// create our metric. 'geohash' label is recognized by Grafana.
// you could also include a 'name' label containing the city, zip code, or other information
// to show when hovering over a map dot.
m_GeoHashCounter = provider.GetOrCreateCounter("connexion_hl7_to_geohash_counter", "Converts HL7 PID address to a GeoHash metric", MetricScope.Device, MetricKeySet.FromDevice(this), new CounterConfiguration
{
LabelNames = new [] { "geohash" }
});
}
// convert an HL7 patient address into a GeoHash string. We're using the PositionStack web service,
// but any converter could be used here.
private static async Task<string> GetGeoHash(HL7Message message)
{
// turn the PID into a queryable string
var addressField = message.PID.PatientAddress_11.First;
var address = $"{addressField.StreetAddress_01.StreetOrMailingAddress_01.Value}, {addressField.City_03.Value}, {addressField.StateOrProvince_04.Value}, USA";
var request = WebRequest.Create(string.Format(m_IpStackAddress, address));
double lat;
double lon;
using (var response = await request.GetResponseAsync().ConfigureAwait(false))
using (var reader = new StreamReader(response.GetResponseStream()))
{
// read the lat/lon out of the webservice response
var json = Newtonsoft.Json.Linq.JObject.Parse(await reader.ReadToEndAsync().ConfigureAwait(false));
lat = double.Parse(json["data"][0]["latitude"].ToString());
lon = double.Parse(json["data"][0]["longitude"].ToString());
}
// convert lat/lon to a geohash string
var hasher = new Geohasher();
return hasher.Encode(lat, lon, 12);
}
public override async Task ProcessMessageAsync(IMessageContext context, CancellationToken token)
{
var hash = await GetGeoHash(context.GetAsHL7Message()).ConfigureAwait(false);
context.WriteEvent(EventSeverity.Info, $"Geohash is {hash}");
// increment the metric with the given hash
m_GeoHashCounter.WithLabels(hash).Inc();
}
public override void OnError(IMessageContext context, Connexion.Core.ErrorEventArgs args)
{
// test code only - no error handling has been added
args.ShouldRetry = false;
Logger.Write(EventSeverity.Error, "Unable to process message: {args.Exception.Message}");
}
}
This is test code only (no null checks, etc.). Since we’re calling a (slow) web service, utilizing the batch interface would be more performant.
Newly defined metrics are disabled by default. You must do the following to enable this new metric:
Ensure the
GetOrCreateX
method has been called at least once. In this case, starting the channel will suffice.Wait at least 15 seconds for the metric registration to complete.
Navigate to the metrics configuration screen within the Connexion UI and enable the metric. Navigating away and back to the Metrics tab will refresh the listings.
To verify the metric has been published and is available to Prometheus, you can navigate to Connexion’s metric endpoint (typically https://your-cxn-server:8092/metrics
). You should see your metric listed:
Now let’s switch over to Grafana and create the dashboard. Create a new Dashboard and Panel.
Update dashboard type to GeoMap
(top right corner). Enter your metric name in the metrics browser and change the format to Table
and type to Instant
.
Scroll down on the settings panel (right side) and update Size settings (this will change the circle size based on the number of hits for that location):
Next, scroll to the bottom of the settings panel and update the threshold section:
Save and apply your changes to see the completed dashboard:
You may wish to view other options described at https://grafana.com/docs/grafana/latest/visualizations/geomap/ . For example, you can change the style to a heat map:
which gives the following output: