TL;DR: Learn how to forecast water consumption using Azure OpenAI and visualize it with Syncfusion® Blazor Spline Chart. This blog walks you through building an AI-powered dashboard featuring a dynamic water consumption chart that predicts future usage trends in New York City with interactive, real-time visualizations
Welcome to our Weekly Data Visualization blog series!
Water is our most vital resource, but it’s not unlimited. With growing populations and changing environmental conditions, managing water consumption efficiently has become a critical global challenge. Traditional water usage monitoring relies on historical data; however, accurate forecasting of future demand could revolutionize resource management. This foresight would be a game-changer for water authorities, enabling them to optimize supply, reduce waste, and proactively manage resources.
This is where modern technology steps in. By harnessing the capabilities of Artificial Intelligence alongside rich, interactive data visualization, historical data can be transformed into a powerful predictive tool.
This blog post outlines the process of building an intelligent water consumption forecasting application. A spline chart from Syncfusion® Blazor Chart is used to deliver a visually appealing and intuitive representation of the data. The intelligent aspect is powered by an AI model that analyzes historical trends to predict future water demand, showcasing how complex datasets can be transformed into actionable insights.
Before getting started, ensure you have access to Azure OpenAI and have set up a deployment in the Azure portal. You will need the following credentials for the integration:
When it comes to visualizing data in a Blazor application, you need a component that is not only powerful but also seamlessly integrates with the framework. Syncfusion® suite of Blazor components, especially its Charts library, stands out as an exceptional choice for several reasons:
By choosing Syncfusion®, you’re not just getting a chart; you’re equipping your application with a robust, feature-rich, and developer-friendly tool that turns complex data into beautiful and actionable insights.
Note: To get started with the Syncfusion® Blazor Chart, follow our getting started documentation.
Let’s get started!
The initial step involves collecting water consumption data for New York City spanning from 1979 to 2024, sourced from Data.Gov. Following this, a WaterConsumption class is defined to represent annual water usage and population figures, accommodating both historical records and predicted data points.
public class WaterConsumption
{
public DateTime Year { get; set; }
public double Population { get; set; }
public double WaterConsumptionInGallons { get; set; }
}
The next step involves organizing the collected data into a list containing annual water consumption values, measured in million gallons per day, calculated based on population figures. This structured list serves as the data source for the chart visualization.
public List<WaterConsumption> WaterConsumptionData = new List<WaterConsumption>
{
new WaterConsumption {
Year = new DateTime(2010, 01, 01),
Population = 8175133,
WaterConsumptionInGallons = 1039
},
//….
new WaterConsumption {
Year = new DateTime(2024, 01, 01),
Population = 8478000,
WaterConsumptionInGallons = 1002
}
};
Let’s configure the Syncfusion® Blazor Chart to effectively visualize water consumption patterns over time. The Spline chart type is ideal for this scenario as it creates smooth curves between data points, providing a more natural representation of continuous data like water consumption trends.
The implementation configures the chart with two primary axes:
For the chart series, the ChartSeries is configured with:
<SfChart>
<ChartPrimaryXAxis Interval="1" IntervalType="IntervalType.Years" ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime">
</ChartPrimaryXAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@WaterConsumptionData" Width="3" Fill="#3674B5" XName="Year" YName="WaterConsumptionInGallons" Type="Syncfusion.Blazor.Charts.ChartSeriesType.Spline" >
</ChartSeries>
</ChartSeriesCollection>
</SfChart>
Refer to the following image,
Once the Blazor Spline Chart is configured, customization can be applied to enhance its visual appeal and informational clarity. The chart is enriched with the following elements:
<SfChart Title="Water Consumption in New York City" Theme="Theme.Bootstrap5">
<SfSpinner @bind-Visible="@SpinnerVisibility"></SfSpinner>
<ChartPrimaryXAxis Interval="1" IntervalType="IntervalType.Years" Title="Year"
ValueType="Syncfusion.Blazor.Charts.ValueType.DateTime">
</ChartPrimaryXAxis>
<ChartPrimaryYAxis Title="Water Consumption(Million gallons per day)">
<ChartAxisTitleStyle TextAlignment="Syncfusion.Blazor.Charts.Alignment.Center" />
</ChartPrimaryYAxis>
<ChartSeriesCollection>
<ChartSeries DataSource="@WaterConsumptionData" Width="3" Fill="#3674B5" XName="Year" YName="WaterConsumptionInGallons" Type="Syncfusion.Blazor.Charts.ChartSeriesType.Spline" >
<ChartMarker Visible="true" Height="5" Width="5">
<ChartDataLabel Visible="true">
</ChartDataLabel>
</ChartMarker>
</ChartSeries>
</ChartSeriesCollection>
<ChartTooltipSettings Enable="true" Format="${point.x} : <b>${point.y} MGD</b>"></ChartTooltipSettings>
<ChartZoomSettings EnableMouseWheelZooming="true" EnablePinchZooming="true" EnableSelectionZooming="true"></ChartZoomSettings>
</SfChart>
Refer to the following image.
With the configuration and customization of the Blazor Spline Chart complete, the next step involves implementing logic to forecast future water consumption trends using historical data, powered by Azure OpenAI.
The next step involves preparing the user interface to display the water consumption forecast alongside historical data. To accomplish this, the following enhancements are introduced:
An AI-powered button is created using the Syncfusion® Blazor Button component to generate the forecast. Upon interaction, the button triggers a call to Azure OpenAI to retrieve predictive data and dynamically plots the new trendline on the chart.
For a seamless user experience, the button is styled with custom animations and a visually appealing design to initiate the forecasting process.
<SfButton @> Refer to the following image,
To enhance the user experience, the Syncfusion® Blazor Spinner component is incorporated. It provides clear visual feedback, indicating that the AI model is processing the request and the chart is being updated with newly forecasted data.
<SfSpinner @bind-Visible="@SpinnerVisibility"></SfSpinner>
@code{
private bool SpinnerVisibility { get; set; } = false;
}
Refer to the following image,
To begin with, when integrating Azure OpenAI, service credentials must be configured. This involves adding the unique endpoint and access key to the appsettings.json file, ensuring a secure and standardized approach to managing configuration settings.
Insert the following JSON structure into the appsettings.json file, substituting the placeholder values with your specific Azure OpenAI 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.
To establish a connection with Azure OpenAI and retrieve AI-generated responses based on user input, a dedicated service class named AISampleService is created. This service accepts user queries, internally constructs a detailed prompt, and sends it to the Azure OpenAI API.
Upon receiving the response, the service processes the data and returns it for dynamic chart updates with relevant forecast information.
using System.Text.Json;
public class AISampleService
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly string? _endpoint;
private readonly string? _apiKey;
private readonly string? _deploymentId;
public AISampleService(HttpClient httpClient, IConfiguration configuration)
{
_httpClient = httpClient;
_configuration = configuration;
_endpoint = _configuration["AzureOpenAI:Endpoint"];
_apiKey = _configuration["AzureOpenAI:ApiKey"];
_deploymentId = _configuration["AzureOpenAI:DeploymentId"];
}
public async Task<string> GetAIResponse(string prompt)
{
var requestBody = new
{
messages = new[]
{
new { role = "user", content = prompt }
},
max_tokens = 2000
};
var request = new HttpRequestMessage(HttpMethod.Post, $"{_endpoint}/openai/deployments/{_deploymentId}/chat/completions?api-version=2023-07-01-preview")
{
Content = JsonContent.Create(requestBody)
};
request.Headers.Add("api-key", _apiKey);
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var responseJson = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(responseJson);
return doc.RootElement.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString() ?? string.Empty;
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
return $"Error: {response.StatusCode} - {errorContent}";
}
}
}
To generate realistic water consumption forecasts, a precise prompt is crafted for Azure OpenAI. The prompt requests data for the next five years in a specific JSON format, incorporates the historical dataset for context, and instructs the AI to maintain realistic fluctuations rather than linear patterns.
var lastYear = WaterConsumptionData.Max(d => d.Year).Year;
string dataJson = JsonConvert.SerializeObject(WaterConsumptionData);
string prompt = $"Generate {value} data for the next 5 years after {lastYear} " +
$"in JSON array format using this structure: " +
$"[{{\"Year\": \"2025-01-01\", \"Population\": 8500000, \"WaterConsumptionInGallons\": 1000}}]. " +
$"Base it on: {dataJson} and the data should avoid uniform increase or decrease " +
$"and should realistically reflect past trends and fluctuations."; The AnimateButton method creates a smooth user experience when requesting AI-generated forecasts. It prevents multiple simultaneous requests by using an isLoading flag that guards against parallel executions.
When activated, it immediately displays a spinner component and applies a pulsing animation to the button itself, providing users with clear visual feedback that the system is processing their request.
After a brief delay to ensure UI updates are visible, it calls the GetForecastData method to fetch the AI-generated predictions. Once forecasting is complete, it returns all UI elements to their normal state by removing animations and hiding the spinner.
private async Task AnimateButton()
{
if (isLoading)
{
return;
}
isLoading = true;
SpinnerVisibility = true;
animationClass = "pulse-animation";
await InvokeAsync(StateHasChanged);
await Task.Delay(2000);
await GetForecastData("water consumption forecast ");
isLoading = false;
animationClass = "";
await InvokeAsync(StateHasChanged);
SpinnerVisibility = false;
}
The GetForecastData method handles generating and integrating AI forecast data. It serializes existing data into JSON to provide context to the AI model, then constructs a detailed prompt requesting forecast data in a specific format.
After receiving the response, it deserializes the JSON back into WaterConsumption objects and animates the addition of each new data point with a delay.
The combination of these methods creates an engaging user experience with clear visual indicators of AI processing and seamless integration of predictions into the existing chart visualization.
public async Task GetForecastData(string value)
{
var lastYear = WaterConsumptionData.Max(d => d.Year).Year;
string dataJson = JsonConvert.SerializeObject(WaterConsumptionData);
string prompt = $@"Generate {value} data for the next 5 years after {lastYear} in JSON array format using this structure:
[{{""Year"": ""2025-01-01"", ""Population"": 8500000, ""WaterConsumptionInGallons"": 1000}}].
Base it on: {dataJson} and the data should avoid uniform increase or decrease and should realistically reflect past trends and fluctuations.";
string result = await OpenAIService.GetAIResponse(prompt);
var jsonStart = result.IndexOf('[');
var jsonEnd = result.LastIndexOf(']');
if (jsonStart >= 0 && jsonEnd > jsonStart)
{
var jsonSubstring = result.Substring(jsonStart, jsonEnd - jsonStart + 1);
try
{
var forecastData = JsonConvert.DeserializeObject<List<WaterConsumption>>(jsonSubstring);
if (forecastData != null)
{
foreach (var item in forecastData)
{
WaterConsumptionData.Add(item);
StateHasChanged();
await Task.Delay(500);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error deserializing forecast data: {ex.Message}");
}
}
} To differentiate the historical data from the AI-predicted data, striplines are used in charts. The conditional rendering code below displays blue background strips in the chart that visually highlight the AI-generated forecast data.
When forecast data exists, semi-transparent blue strip lines appear behind the data points, starting from the first forecast date and extending across all predicted points.
@if (HasForecastData) {
<ChartStriplines>
@foreach(var strip in forecastStriplines) {
<ChartStripline StartFromAxis="false" Start="@strip.Start" Size="@strip.Size" ZIndex="ZIndex.Behind" Opacity="0.2" Color="blue"/>
}
</ChartStriplines>
}
When forecast data is successfully generated, the GetForecastData method sets a flag indicating forecasts and creates a stripline entry that marks the starting point and span of the AI-generated data.
This allows users to visually distinguish between historical water consumption data and the AI-predicted future trends in the chart.
public async Task GetForecastData(string value)
{
…
…
if (forecastData != null)
{
HasForecastData = true;
var stripline = new StriplineInfo
{
Start = forecastData[0].Year,
size = forecastData.Count
};
forecastStriplines.Add(stripline);
…
…
}
} Refer to the following image,
For further details, check out the GitHub demo.
Thanks for reading! In this blog, we’ve learned how to forecast water consumption in New York City by collaborating with Azure OpenAI and Syncfusion® Blazor Spline Chart. Give it a try and leave your feedback in the comment section below.
If you’re already a Syncfusion® user, you can download the latest version of Essential Studio® from the license and downloads page. New to Syncfusion® Start your journey with a 30-day free trial and explore over 1,900 UI components, including powerful charting tools for Blazor.
If you need assistance, please do not hesitate to contact us via our support forum, support portal, or feedback portal. We are always eager to help you!