TL;DR: Learn how to build a real-time air quality dashboard using Blazor Spline Chart and Azure OpenAI. This tutorial covers chart rendering, AI-based forecasting, and interactive data visualization, all tailored for developers.
Welcome to our latest post in the Weekly Data Visualization blog series!
This blog explores how Syncfusion® Blazor components turn raw air quality data into insights. Using Azure OpenAI, real-time and forecasted pollution data are visualized in a Spline chart using Syncfusion® Blazor Charts. The chart enables interactive trends analysis, while seamless API integration ensures continuous data flow, making it a powerful tool for environmental management.
Let’s get started!
Why use Syncfusion® Blazor Chart?
The Syncfusion® Blazor Chart component is a powerful tool for building interactive, high-performance visualizations in Blazor apps. Ideal for dashboards, reports, and analytics, it transforms raw data into meaningful insights.
Key benefits include:
- Support for 50+ chart types like Line, Bar, Pie, Area, and Financial.
- Built-in interactivity: zooming, panning, tooltips, crosshairs, and legends.
- Full customization with themes, gradients, markers, and annotations.
- Real-time data binding for dynamic updates.
- Native Blazor support for Server and WebAssembly hosting.
Note: To start using the Syncfusion® Blazor Chart, follow our official documentation
Prerequisites
Before getting started, ensure you have access to Azure OpenAI and have deployed a model in the Azure portal. You’ll need these integration credentials:
- Azure OpenAI API key
- Model name (gpt-4o is used for this example)
- Endpoint URL
- Install and configure the Syncfusion® Blazor Chart component
Building an AI-powered air quality dashboard with Blazor components
To implement a dashboard for monitoring air quality using Azure OpenAI and Blazor components, follow the steps outlined below.
Step 1: Configure the Azure OpenAI
To integrate Azure OpenAI with your Blazor application, securely configure your credentials in the appsettings.json file. This enables safe communication between your app and Azure OpenAI services.
Add the following configuration, replacing the placeholders with your actual credentials:
{
"AzureOpenAI": {
"Endpoint": "your-endpoint-url",
"ApiKey": "your-api-key",
"DeploymentId": "model_name"
}
}
Replace your-endpoint-url
, your-api-key
, and model_name
with your actual Azure OpenAI endpoint URL, API key, and deployed model name.
Step 2: Setting up the Azure AI service
Configure the AI-powered APIs to fetch comprehensive air quality data, including historical trends and predictive analytics, through the Azure AI Service.
Define the data model:
Our journey begins with defining the AirQualityInfo
class, which is essential for capturing detailed air quality metrics:
public class AirQualityInfo {
public DateTime Date { get; set; }
public double PollutionIndex { get; set; }
public string? AirQualityStatus { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public double AIPredictionAccuracy { get; set; }
}
Next, implement the AIAirQualityService
class to integrate AI capabilities into the dashboard. This class manages API credentials, securely communicates with Azure OpenAI, and generates prompts tailored to air quality queries. It retrieves data asynchronously for better performance and includes detailed logging for monitoring and debugging, ensuring reliability and responsiveness.
Here’s a snippet of how this service might look:
public class AIAirQualityService
{
private readonly HttpClient _httpClient;
private readonly string _endpoint;
private readonly string _apiKey;
private readonly string _deployment;
private readonly ILogger<AIAirQualityService> _logger;
……
……
……
public async Task<string> GetResponseFromOpenAI(string prompt)
{
var requestBody = new
{
messages = new[]
{
new { role = "user", content = prompt }
},
max_tokens = 2000
};
var request = new HttpRequestMessage(HttpMethod.Post, $"{_endpoint}/openai/deployments/{_deployment}/chat/completions?api-version=2025-01-01-preview")
{
Content = JsonContent.Create(requestBody)
};
request.Headers.Add("api-key", _apiKey);
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string? responseJson = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(responseJson);
return doc.RootElement.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString();
}
else
{
……
}
}
private async Task<List<AirQualityInfo>> HandleResponseFromAI(string prompt)
{
string response = await GetResponseFromOpenAI(prompt);
string extractedJson = JsonExtractor.ExtractJson(response);
if (!string.IsNullOrEmpty(extractedJson))
{
var options = new System.Text.Json.JsonSerializerOptions
{
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString,
PropertyNameCaseInsensitive = true
};
return System.Text.Json.JsonSerializer.Deserialize<List<AirQualityInfo>>(extractedJson, options) ?? new List<AirQualityInfo>();
}
else
{
return new List<AirQualityInfo>();
}
}
}
The AI service uses custom prompts to generate data and predictions. Here’s how this process unfolds:
Predict historical air quality trends
The PredictAirQualityTrends
method retrieves data for the past 30 days. The AI model generates realistic datasets that reflect daily air quality variations.
public class AIAirQualityService
{
private readonly HttpClient _httpClient;
private readonly string _endpoint;
private readonly string _apiKey;
private readonly string _deployment;
private readonly ILogger<AIAirQualityService> _logger;
……
……
……
public async Task<string> GetResponseFromOpenAI(string prompt)
{
var requestBody = new
{
messages = new[]
{
new { role = "user", content = prompt }
},
max_tokens = 2000
};
var request = new HttpRequestMessage(HttpMethod.Post, $"{_endpoint}/openai/deployments/{_deployment}/chat/completions?api-version=2025-01-01-preview")
{
Content = JsonContent.Create(requestBody)
};
request.Headers.Add("api-key", _apiKey);
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string? responseJson = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(responseJson);
return doc.RootElement.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString();
}
else
{
……
}
}
private async Task<List<AirQualityInfo>> HandleResponseFromAI(string prompt)
{
string response = await GetResponseFromOpenAI(prompt);
string extractedJson = JsonExtractor.ExtractJson(response);
if (!string.IsNullOrEmpty(extractedJson))
{
var options = new System.Text.Json.JsonSerializerOptions
{
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString,
PropertyNameCaseInsensitive = true
};
return System.Text.Json.JsonSerializer.Deserialize<List<AirQualityInfo>>(extractedJson, options) ?? new List<AirQualityInfo>();
}
else
{
return new List<AirQualityInfo>();
}
}
}
Predict future trends
The PredictNextMonthForecast
method forecasts air quality for the next 30 days based on historical data, providing proactive insights.
internal async Task<List<AirQualityInfo>> PredictNextMonthForecast(List<AirQualityInfo> historicalData)
{
try
{
string today = DateTime.UtcNow.ToString("yyyy-MM-dd");
string futureDate = DateTime.UtcNow.AddDays(30).ToString("yyyy-MM-dd");
var userMessage = "You are an AI model specialized in air pollution forecasting. " +
"Based on the provided historical data, generate an accurate prediction " +
$"for air quality trends over the next 30 days ({today} to {futureDate}). " +
"Using the following historical dataset, predict the Pollution Index for the next 30 days:\n\n" +
$"{System.Text.Json.JsonSerializer.Serialize(historicalData)}\n\n" +
"Use the following structure for each entry: " +
"[ { \"Date\": \"YYYY-MM-DD\", \"PollutionIndex\": number, " +
"\"AirQualityStatus\": \"Good | Satisfactory | Moderate | Poor | Very Poor | Severe\", " +
"\"Latitude\": number, \"Longitude\": number, \"AIPredictionAccuracy\": number } ]. " +
"Output ONLY valid JSON without any additional explanations.";
return await HandleResponseFromAI(userMessage);
}
catch (Exception ex)
{
……
}
}
Step 3: Configuring the dashboard header
The Syncfusion® Blazor TextBox captures user input for a specific location, enabling dynamic retrieval of air quality data. Paired with the Syncfusion® Blazor Button, users can trigger the data-fetching process with a single click, making the interaction smooth and intuitive.
Here’s the HTML code setup using Syncfusion® components for a Blazor environment:
<div id="header-panel">
<div class="header-group">
<div class="header-img">
<img src="./images/leaf.png" style="height:60px;width:60px;" />
</div>
<div class="header-text">
<p style="font-size: 25px; margin-bottom: 5px;">AI-Powered Air Quality Command Center</p>
<p style="font-size: 15px;">Real-Time Monitoring and Smart Forecasting for a Healthier Environment</p>
</div>
</div>
<div id="input-container">
<div class="input-wrapper">
<SfTextBox @ref="countryTextBox" Placeholder="Location" CssClass="location-input" Width="200px"
Value="@SearchCountry" @onchange="TextChanged" />
<img src='./images/map.png' class='input-icon' />
</div>
<SfButton CssClass="feature-button" OnClick="ForecastButton_Click">
<img src="./images/ai.png" class="button-icon" />
</SfButton>
</div>
</div>
The following code example demonstrates how to retrieve air quality data by entering a country name and triggering a forecast using a button click.
private async Task TextChanged(Microsoft.AspNetCore.Components.ChangeEventArgs e)
{
if (!string.IsNullOrWhiteSpace(SearchCountry))
{
ShowMarker = false;
SpinnerVisibility = MapSpinnerVisibility = true;
Data = new ObservableCollection<AirQualityInfo>();
ForeCastData = new ObservableCollection<AirQualityInfo>();
MapMarkers?.Clear();
SearchCountry = e.Value?.ToString();
CurrentPollutionIndex = AIPredictionAccuracy = AvgPollution7Days = LatestAirQualityStatus = "Loading...";
await FetchAirQualityData(SearchCountry);
if (Data != null && sfDashboardLayout != null)
{
ShowMarker = true;
SpinnerVisibility = MapSpinnerVisibility = false;
await sfDashboardLayout.RefreshAsync();
await chart1.RefreshAsync();
}
}
}
private async void ForecastButton_Click()
{
if (!string.IsNullOrWhiteSpace(SearchCountry))
{
SpinnerVisibility = true;
CurrentPollutionIndex = AIPredictionAccuracy = AvgPollution7Days = LatestAirQualityStatus = "Loading...";
await PredictForecastData();
if (ForeCastData != null && sfDashboardLayout != null)
{
ShowMarker = true;
SpinnerVisibility = false;
await sfDashboardLayout.RefreshAsync();
await chart1.RefreshAsync();
}
}
}
- The TextChanged method is triggered when the user updates the location input. It initiates data retrieval, shows loading indicators, and refreshes the dashboard with updated air quality data.
- The ForecastButton_Click method fetches forecast data, displays a spinner during loading, and updates the dashboard once the data is ready.
The image below illustrates the layout implemented by the above code:
Step 4: Visualizing data with Spline Charts
Configure Syncfusion® Blazor Charts with the Spline series to present air quality data using smooth curves for better visual appeal and analysis. We bind pollution data from the AI service to the Spline series and customize the visuals to enhance clarity. Actual and forecasted data are separated in the ChartSeriesCollection, with current values shown as solid lines and forecasts as dashed lines with animations for clear distinction, as shown in the code below.
<SfChart ID="MainSplineChart" @ref="chart1" Theme="Theme.Bootstrap5" >
<SfSpinner @bind-Visible="@SpinnerVisibility" Size="50"></SfSpinner>
<ChartArea>
<ChartAreaBorder Width="0"></ChartAreaBorder>
</ChartArea>
<ChartPrimaryXAxis ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime"
IntervalType="IntervalType.Days" LabelFormat="dd MMM"
EdgeLabelPlacement="EdgeLabelPlacement.Shift" PlotOffset="5">
<ChartAxisMajorGridLines Width="0"></ChartAxisMajorGridLines>
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Title="AQI Value" PlotOffset="20">
<ChartAxisMajorGridLines Width="2" DashArray="2,2" />
<ChartAxisLineStyle Width="0"></ChartAxisLineStyle>
<ChartAxisMajorTickLines Width="0"></ChartAxisMajorTickLines>
</ChartPrimaryYAxis>
<ChartTooltipSettings Enable="true" Fill="black" Format="<b>${point.y}</b>" EnableMarker="false"></ChartTooltipSettings>
<ChartZoomSettings Enable="true" EnablePinchZooming="true" EnablePan="true"></ChartZoomSettings>
<ChartSeriesCollection>
@if (Data != null)
{
<ChartSeries DataSource="@Data" XName="Date" YName="PollutionIndex" Type="ChartSeriesType.Spline" Width="2" Fill="#008FFB"></ChartSeries>
}
@if (ForeCastData != null)
{
<ChartSeries DataSource="@ForeCastData" XName="Date" YName="PollutionIndex" Type="ChartSeriesType.Spline" Width="2.5" Fill="#2BD26E" DashArray="2,2,6,2,2,6">
<ChartSeriesAnimation Enable="true"></ChartSeriesAnimation>
</ChartSeries>
}
</ChartSeriesCollection>
</SfChart>
- In this configuration, ChartPrimaryXAxis and ChartPrimaryYAxisare configured for precise formatting and clear visibility, supporting time-series data.
- Tooltips and zoom enable detailed views and smooth chart navigation.
- Data binding shows actual and forecasted indices with solid and dashed lines for distinction.
Refer to the following spline chart image:
The data label template highlights air quality points with a pollution index below 50. It uses a custom SVG featuring a green triangle icon to indicate good air quality, as shown in the code below.
<ChartMarker>
<ChartDataLabel Visible="true" Position="Syncfusion.Blazor.Charts.LabelPosition.Middle">
<Template>
@{
var data = context as ChartDataPointInfo;
if (Convert.ToDouble(data.Y, CultureInfo.InvariantCulture) < 50)
{
<svg width="15" height="15" viewBox="0 0 15 15">
<path d="M 6.5,0 L 13,13 L 0,13 Z" fill="#196237" />
</svg>
}
}
</Template>
</ChartDataLabel>
</ChartMarker>
Refer to the following image showing the data label template in action:
Step 5: Adding Blazor Maps for location visualization
Maps are a valuable tool for providing geographical context to a selected location. By using Syncfusion® Blazor Maps to visually display the selected country, this feature highlights the country’s name and location, making it easier to identify and interpret pollution hotspots.
Now, let’s see how to configure Syncfusion Blazor Maps:
<SfMaps ID="MapsOne" @ref="MapsLayout" Height="100%" Background="#91B9FB" Theme="Theme.Bootstrap5" >
<MapsAreaSettings Background="#91B9FB" />
<MapsLayers>
<MapsLayer ShapeData='new {dataOptions ="https://cdn.syncfusion.com/maps/map-data/world-map.json"}'ShapePropertyPath='new string[] {"name"}' TValue="string">
<MapsShapeSettings Fill="#E5E5E5">
<MapsShapeBorder Color="#d6dbdf"></MapsShapeBorder>
</MapsShapeSettings>
<MapsMarkerSettings>
<MapsMarker Visible="true" DataSource="@MapMarkers" TValue="AirQualityInfo"
AnimationDuration="0" Width="30" Height="30">
<MarkerTemplate>
<div style="text-align: center;">
<img src="./images/map_pin.png" style="height: 30px; width: 30px;" alt="Marker Image" />
<div class="text" style="margin-top: 5px; font-size: 13px;">
@CountryName
</div>
</div>
</MarkerTemplate>
</MapsMarker>
</MapsMarkerSettings>
</MapsLayer>
</MapsLayers>
</SfMaps>
Now, refer to the following image showing the map configuration:
Step 6: Displaying key metrics
To provide a comprehensive understanding of air quality, the dashboard presents critical metrics in a structured and readable format using Syncfusion® Blazor Card. This layout ensures quick and easy interpretation of vital air quality insights.
- Current pollution index: Displays the real-time pollution level.
- Pollution (7 Days): Shows the average pollution level over the past week.
- Air quality status: Indicates the overall air quality condition.
- Prediction accuracy: Shows the AI model’s confidence in forecasting air quality trends.
This card-based presentation effectively highlights key data, enhancing the clarity and usability of air quality metrics.
The Blazor Charts, Blazor Maps, and Blazor Card components are placed inside the Blazor Dashboard component to ensure proper layout and alignment, as shown in the code below.
<DashboardLayoutPanel SizeX="2" SizeY="1" Row="1" Column="6" Id="first-card-panel">
<ContentTemplate>
<SfCard ID="FirstCard">
<CardHeader Title="Current Pollution Index" ImageUrl="@("./images/pollution.png")" />
<CardContent Content="@CurrentPollutionIndex" />
</SfCard>
</ContentTemplate>
</DashboardLayoutPanel>
<DashboardLayoutPanel SizeX="2" SizeY="1" Row="1" Column ="7" Id="second-card-panel">
<ContentTemplate>
<SfCard ID="SecondCard">
<CardHeader Title="Avg. Pollution (7 Days)" ImageUrl="@("./images/average.png")" />
<CardContent Content="@AvgPollution7Days" />
</SfCard>
</ContentTemplate>
</DashboardLayoutPanel>
<DashboardLayoutPanel SizeX="2" SizeY="1" Row="2" Column ="6" Id="third-card-panel">
<ContentTemplate>
<SfCard ID="ThirdCard">
<CardHeader Title="Air Quality Status (7 Days)" ImageUrl="@("./images/air_quality.png")" />
<CardContent Content="@LatestAirQualityStatus" />
</SfCard>
</ContentTemplate>
</DashboardLayoutPanel>
<DashboardLayoutPanel SizeX="2" SizeY="1" Row="2" Column ="7" Id="fourth-card-panel">
<ContentTemplate>
<SfCard ID="FourthCard">
<CardHeader Title="Prediction Accuracy" ImageUrl="@("./images/forecast.png")" />
<CardContent Content="@AIPredictionAccuracy" />
</SfCard>
</ContentTemplate>
</DashboardLayoutPanel>
Refer to the following image for a visual representation of the card layout:
Step 7: Adding the Blazor Spinner
The Syncfusion® Blazor Spinner visually indicates when the application loads or fetches data, preventing interaction with incomplete elements and ensuring a smoother, more responsive interface. Let’s add a Blazor spinner to improve the user experience during data fetches and maintain seamless UI interactions.
Here’s the code snippet for the spinner:
<SfSpinner @bind-Visible="@SpinnerVisibility" Size="50"></SfSpinner>
Refer to the following image showing the spinner in action:
To build an intuitive layout for the Air Quality Index dashboard, elements like text inputs, buttons, charts, maps, and cards are strategically arranged. This ensures a flexible, responsive design that adapts well to various screen sizes and orientations.
Now, refer to the overall image of the AI-powered Air Quality Dashboard.
Conclusion
Thanks for reading! In this blog, we have seen how to build anAI-powered air pollution monitoring dashboard. Try them out and leave your feedback in the comments section below!
Explore our Blazor Chart Example to learn more about the supported chart types and how easy it is to configure them for stunning visual effects.
The latest version of the Blazor Chart component is available for current customers from the license and downloads page. If you are not a Syncfusion® customer, try our 30-day free trial.
You can also contact us through our support forums, support portal, orfeedback portal. We are always happy to assist you!
Related Blogs
- Build AI-Powered Water Consumption Chart Using Azure OpenAI and Blazor Spline Charts
- Blazor Radar Charts: Visualizing Football Defending Stats Made Easy
- How to Build a Blazor HeatMap Chart to Visualize Global Temperature Anomalies
- How to Build an AI-Powered Blazor Chatbot That Turns Conversations into Charts
This article was originally published at Syncfusion.com.
Top comments (0)