Reputation: 75
I have created a .NET MAUI desktop application that reads "hitrorical data" form an SD card that is in a dummy LoRa network scanner. As displayed in the image below, the map shows the different positions of the scanner relative to the "dummy" LoRa transmitter during scanning and in different scans. Instead, I would like to create a heatmap layer based on the signal strength (RSSI) value.
I have been looking online for a way to do it, but to no vail. The closest thing I stumbled upon is the following link: Analyze hotspots. However, I am having a hard time to tweak it to fit my needs, to a point that I am doubting it as the correct solution.
Would anybody please give me a tip to how to approach this issue? Please note that I am quite new to C# and .NET MAUI.
I am looking to generate a map that looks like:
Image source: https://learn.microsoft.com/en-us/bingmaps/v8-web-control/map-control-concepts/heat-map-module-examples/basic-heat-map-example
Update!!
So I have read the article recommended by Nixta, and made the following code based on the article. However, when I click on the "UpdateRendererButton_Clicked" Button I don't get any haet map generated, instead the original map points disappear.
HeatMapRenderer.cs:
using Newtonsoft.Json;
namespace HeatMapRendererJson
public partial class HeatMapRenderer
{
public string ToJson()
{
return JsonConvert.SerializeObject(this);
}
public void AddColorStop(double ratio, Color color)
{
if (ratio > 1.0 || ratio < 0.0) { throw new Exception("Argument 'ratio' must be a value between 0 and 1."); };
ColorStop stop = new ColorStop(ratio, color);
ColorStops.Add(stop);
}
public void ClearColorStops()
{
ColorStops.Clear();
}
[JsonProperty("type")]
public string Type { get; set; } = "heatmap";
[JsonProperty("blurRadius")]
public long BlurRadius { get; set; }
[JsonProperty("colorStops")]
public List<ColorStop> ColorStops { get; set; } = new List<ColorStop>();
[JsonProperty("field")]
public string Field { get; set; }
[JsonProperty("maxPixelIntensity")]
public double MaxPixelIntensity { get; set; }
[JsonProperty("minPixelIntensity")]
public double MinPixelIntensity { get; set; }
}
public partial class ColorStop
{
[JsonProperty("ratio")]
public double Ratio { get; set; }
[JsonProperty("color")]
public int[] Color { get; set; }
public ColorStop(double ratio, Color color)
{
Ratio = ratio;
Color = new int[] { (int)color.Red, (int)color.Green, (int)color.Blue, (int)color.Alpha };
}
}
}
MainWindow.xaml.cs:
public partial class MainWindow : ContentPage
{
// URL to a sample layer with earthquake points.
private string _earthquakesUrl ="https://sampleserver6.arcgisonline.com/arcgis/rest/services/Earthquakes_Since1970/FeatureServer/0";
// Store a reference to the quakes layer and its default renderer.
private FeatureLayer _quakesLayer;
private Renderer _defaultRenderer;
private List<Field> numericFields = new List<Field>();
public MainWindow()
{
InitializeComponent();
Init();
Esri.ArcGISRuntime.ArcGISRuntimeEnvironment.ApiKey = "myKey";
}
private async void Init()
{
// Create the OpenStreetMap basemap.
Basemap osmBasemap = new Basemap(BasemapStyle.OSMStandard);
// Create the map with the OpenStreetMap basemap.
Map map = new Map(osmBasemap);
// Create the earthquake layer, load it, and get its default renderer.
_quakesLayer = new FeatureLayer(new Uri(_earthquakesUrl));
await _quakesLayer.LoadAsync();
_defaultRenderer = _quakesLayer.Renderer;
// Add the quakes layer to the map, add the map to the map view.
map.OperationalLayers.Add(_quakesLayer);
MyMapView.Map = map;
int x =1;
foreach (var fld in _quakesLayer.FeatureTable.Fields)
{
if (fld.FieldType.HasFlag(FieldType.Float32 | FieldType.Float64 | FieldType.Int16 | FieldType.Int32))
{
numericFields.Add(fld);
}
Debug.WriteLine(x + "-fields: " + fld);
x++;
}
Debug.WriteLine("numericFields: " + numericFields[11]);
private void UpdateRendererButton_Clicked(object sender, EventArgs e)
{
// Parse some of the user inputs.
double.TryParse(MinIntensityTextBox.Text, out double minIntensity);
double.TryParse(MaxIntensityTextBox.Text, out double maxIntensity);
//int.TryParse(BlurRadiusComboBox.SelectedItem.ToString(), out int blurRadius);
// Create a new HeatMapRenderer with info provided by the user.
HeatMapRenderer heatMapRendererInfo = new HeatMapRenderer
{
BlurRadius = 6,
MaxPixelIntensity = 1000.0,
MinPixelIntensity = 0.0
};
heatMapRendererInfo.Field = numericFields[11].Name;
heatMapRendererInfo.AddColorStop(0.0, Colors.Transparent);
heatMapRendererInfo.AddColorStop(0.10, new Color(1, 0, 0, (float)0.6)); // Red color at position 0
//heatMapRendererInfo.AddColorStop(0.5, new Color(1, 1, 0, (float)0.6)); // Yellow color at position 0.5
heatMapRendererInfo.AddColorStop(1.0, new Color(0, 1, 0, (float)0.6)); // Green color at position 1
// Get the JSON representation of the renderer class.
string heatMapJson = heatMapRendererInfo.ToJson();
// Use the static Renderer.FromJson method to create a new renderer from the JSON string.
var heatMapRenderer = Renderer.FromJson(heatMapJson);
// Apply the renderer to a point layer in the map.
_quakesLayer.Renderer = heatMapRenderer;
}
private void ResetRenderer_Clicked(object sender, EventArgs e)
{
// Reapply the default renderer for the layer.
_quakesLayer.Renderer = _defaultRenderer;
}
}
and finally the MainWindow.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Scanner_MAUI.Pages.MainWindow"
Title="MainWindow"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Row="7" Grid.ColumnSpan="2"
Text="Update renderer"
Width="120" Height="25"
Clicked="UpdateRendererButton_Clicked"/>
<Button Grid.Row="8" Grid.ColumnSpan="2"
Text="Reset"
Width="120" Height="25"
Clicked="ResetRenderer_Clicked"/>
</Grid>
<esri:MapView x:Name="MyMapView"
Grid.Column="1"/>
</Grid>
</ContentPage>
Upvotes: 1
Views: 353
Reputation: 464
We don’t have a full API around the heat map renderer, as you noticed, but you can construct appropriate JSON (as the Map Viewer does) and construct it from that. Take a look at this article which walks through it.
Upvotes: 0