Xcalibur.Weather.Helpers 1.0.13

dotnet add package Xcalibur.Weather.Helpers --version 1.0.13
                    
NuGet\Install-Package Xcalibur.Weather.Helpers -Version 1.0.13
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Xcalibur.Weather.Helpers" Version="1.0.13" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Xcalibur.Weather.Helpers" Version="1.0.13" />
                    
Directory.Packages.props
<PackageReference Include="Xcalibur.Weather.Helpers" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Xcalibur.Weather.Helpers --version 1.0.13
                    
#r "nuget: Xcalibur.Weather.Helpers, 1.0.13"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Xcalibur.Weather.Helpers@1.0.13
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Xcalibur.Weather.Helpers&version=1.0.13
                    
Install as a Cake Addin
#tool nuget:?package=Xcalibur.Weather.Helpers&version=1.0.13
                    
Install as a Cake Tool

Xcalibur.Weather.Helpers

Version .NET Version NuGet License

A comprehensive .NET helper library providing utility functions for weather-related operations. Includes conversion helpers for temperature, wind speed, length, and pressure, along with specialized helpers for Open-Meteo, Geocodio, IpGeolocation.io, Atmospore, SunriseSunset.io, OpenStreetMap, and combined weather alert operations (Meteoalarm, NWS, GDACS, Environment Canada, BOM, EMSC, DWD).

Created by: Joshua Arzt | Company: Xcalibur Systems, LLC.

Purpose

Xcalibur.Weather.Helpers is designed to:

  • Provide high-level helper utilities that simplify working with weather data
  • Enable easy conversion between common weather measurement units (temperature, wind speed, pressure, length)
  • Offer convenient wrapper methods around Xcalibur.Weather.Services for common use cases
  • Deliver intelligent alert consolidation to eliminate duplicate weather alerts
  • Support geographic region detection and routing for multi-provider weather services
  • Reduce boilerplate code when building weather applications
  • Provide strongly-typed, easy-to-use APIs for weather data transformation

💡 Use Cases

This library is ideal for:

  • Weather Applications: Mobile and desktop apps requiring weather data with automatic unit conversion
  • Dashboard & UI Development: Applications needing consolidated, display-ready weather alerts without duplicates
  • Smart Home Systems: IoT devices requiring weather-based automation with simplified data access
  • Agricultural Solutions: Farm management systems with easy access to weather, pollen, and alert data
  • Travel & Navigation Apps: Location-based weather with intelligent multi-provider alert aggregation
  • Health & Wellness Apps: Allergy tracking with simplified pollen data access
  • Emergency Management: Systems requiring consolidated multi-source weather alerts
  • Data Transformation: Backend services needing unit conversion and data normalization
  • Prototyping & MVPs: Rapid development with high-level helper methods
  • Web APIs: REST services exposing weather data with built-in unit conversion

🎉 What's New in v1.0.12

Consolidated Weather Alert Methods - A complete suite of consolidated alert methods for cleaner, more efficient alert handling:

  • All 7 providers now have consolidated versions: NWS, Meteoalarm, Environment Canada, BOM, GDACS, DWD, and EMSC
  • Automatic duplicate removal: Overlapping alerts of the same event type are consolidated, keeping only the highest severity
  • Clean, simple API: Returns IReadOnlyList<WeatherAlertItem> directly - no tuples, no null checks needed
  • Optimized for UI display: Perfect for weather apps that need to show unique, relevant alerts without duplicates
  • Smart consolidation: Handles escalating alerts (e.g., Winter Weather Advisory → Winter Storm Warning) intelligently

Example:

// Old way - might show duplicate alerts for the same weather event
var alerts = await WeatherAlertHelper.BuildNwsAlertsAsync(lat, lon, logger, token);

// New way - automatically consolidated, unique alerts only
var consolidated = await WeatherAlertHelper.BuildNwsAlertsConsolidatedAsync(lat, lon, logger, token);
foreach (var alert in consolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

Available Consolidated Methods:

  • BuildCombinedAlertsConsolidatedAsync - All providers combined
  • BuildNwsAlertsConsolidatedAsync - US weather alerts
  • BuildMeteoalarmAlertsConsolidatedAsync - European alerts
  • BuildEnvironmentCanadaAlertsConsolidatedAsync - Canadian alerts
  • BuildBomAlertsConsolidatedAsync - Australian alerts
  • BuildGdacsAlertsConsolidatedAsync - Global disaster alerts
  • BuildDwdAlertsConsolidatedAsync - German weather warnings
  • BuildEmscAlertsConsolidatedAsync - Earthquake/seismic alerts

📋 Table of Contents

Features

Conversion Utilities

  • Temperature Conversion: Celsius ↔ Fahrenheit conversions with formatting options
  • Wind Speed Conversion: Convert between km/h, mph, ft/s, m/s, and knots
  • Length Conversion: Convert between millimeters and inches
  • Pressure Conversion: Convert between hPa, inHg, and mmHg
  • Smart Formatting: Format values with or without unit symbols

Weather Service Helpers

  • OpenMeteoHelper: Build air quality points, current forecasts, hourly forecasts, daily forecasts, and yesterday's data
  • GeocodioHelper: Test API keys, build address locations from geocoding queries
  • IpGeoHelper: Build sun/moon points and test API connectivity for astronomical data
  • AtmosporeHelper: Test API keys, retrieve pollen forecasts from the Atmospore API
  • SunriseSunsetHelper: Fetch sunrise/sunset and astronomical data from SunriseSunset.io — no API key required
  • OpenStreetMapHelper: Geocode addresses using the OpenStreetMap Nominatim API — no API key required
  • WeatherAlertHelper: Build combined weather alert information from multiple global services (Meteoalarm, NWS, GDACS, Environment Canada, BOM, EMSC, DWD)
  • WeatherRegionHelper: Determine geographic regions, check if coordinates are in Germany, determine Canadian provinces and Australian states

Installation

NuGet Package Manager

Install-Package Xcalibur.Weather.Helpers

.NET CLI

dotnet add package Xcalibur.Weather.Helpers

Package Reference

<PackageReference Include="Xcalibur.Weather.Helpers" Version="1.0.12" />

Requirements

  • .NET 10.0 or later
  • Xcalibur.Weather.Services 1.0.12 (included as dependency)
  • Microsoft.Extensions.Hosting 10.0.9 (included as dependency)

Usage

Temperature Conversion

using Xcalibur.Weather.Helpers;
using Xcalibur.Weather.Models;

// Convert Celsius to Fahrenheit
double celsius = 25.0;
double fahrenheit = celsius.CelsiusToFahrenheit(); // 77.0

// Convert Fahrenheit to Celsius
double temp = 77.0;
double celsiusValue = temp.FahrenheitToCelsius(); // 25.0

// Format temperature with unit
string formatted = celsius.FormatTemperature(TemperatureUnits.Fahrenheit, includeUnit: true);
// Output: "77°F"

Wind Speed Conversion

using Xcalibur.Weather.Helpers;
using Xcalibur.Weather.Models;

// Convert wind speed from km/h to various units
double windSpeed = 100.0; // km/h

double mph = windSpeed.ConvertWindSpeed(WindSpeedUnits.Mph);        // 62.14
double mps = windSpeed.ConvertWindSpeed(WindSpeedUnits.MSec);       // 27.78
double knots = windSpeed.ConvertWindSpeed(WindSpeedUnits.Knots);    // 53.99
double fps = windSpeed.ConvertWindSpeed(WindSpeedUnits.FtSec);      // 91.13

Length Conversion

using Xcalibur.Weather.Helpers;
using Xcalibur.Weather.Models;

// Format precipitation in different units
double? precipitation = 25.4; // millimeters

string metric = precipitation.FormatLength(DistanceUnits.Metric, includeUnit: true);
// Output: "25.40 mm"

string imperial = precipitation.FormatLength(DistanceUnits.Imperial, includeUnit: true);
// Output: "1.00 in"

Pressure Conversion

using Xcalibur.Weather.Helpers;
using Xcalibur.Weather.Models;

// Format atmospheric pressure in different units
double? pressure = 1013.25; // hectopascals

string hPa = pressure.FormatPressure(BarometerUnits.HPa, includeUnit: true);
// Output: "1013.25 hPa"

string inHg = pressure.FormatPressure(BarometerUnits.InHg, includeUnit: true);
// Output: "29.92 inHg"

string mmHg = pressure.FormatPressure(BarometerUnits.MmHg, includeUnit: true);
// Output: "760.00 mmHg"

OpenMeteo Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Build air quality data point
var airQuality = await OpenMeteoHelper.BuildAirQualityPointAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    logger: logger,
    cancellationToken: CancellationToken.None
);

// Build current weather forecast
var currentForecast = await OpenMeteoHelper.BuildCurrentForecastAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    canAssessDayNight: true,
    sunrise: new TimeOnly(6, 30),
    sunset: new TimeOnly(18, 30),
    logger: logger,
    cancellationToken: CancellationToken.None
);

// Build hourly forecast
var hourlyForecast = await OpenMeteoHelper.BuildHourlyForecastAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    canAssessDayNight: true,
    sunrise: new TimeOnly(6, 30),
    sunset: new TimeOnly(18, 30),
    logger: logger,
    cancellationToken: CancellationToken.None
);

// Build daily forecast
var dailyForecast = await OpenMeteoHelper.BuildDailyForecastAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    forecastDays: 7,
    logger: logger,
    cancellationToken: CancellationToken.None
);

// Build yesterday's hourly forecast
var yesterdayHourlyForecast = await OpenMeteoHelper.BuildYesterdayHourlyForecastAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    canAssessDayNight: true,
    sunrise: new TimeOnly(6, 30),
    sunset: new TimeOnly(18, 30),
    logger: logger,
    cancellationToken: CancellationToken.None
);

// Build yesterday's daily forecast
var yesterdayDailyForecast = await OpenMeteoHelper.BuildYesterdayDailyForecastAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    logger: logger,
    cancellationToken: CancellationToken.None
);

Geocodio Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Test Geocodio API key
bool isValid = await GeocodioHelper.TestApiKeyAsync(
    apiKey: "your-api-key",
    logger: logger
);

// Build address locations from query
var locations = await GeocodioHelper.BuildAddressLocationsAsync(
    apiKey: "your-api-key",
    query: "1600 Pennsylvania Avenue NW, Washington, DC",
    country: "US",
    logger: logger
);

IpGeolocation Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Test IpGeolocation API key
bool isValid = await IpGeoHelper.TestApiKeyAsync(
    apiKey: "your-api-key",
    logger: logger
);

// Build sun/moon astronomical data
var sunMoonData = await IpGeoHelper.BuildSunMoonPointAsync(
    ipGeoApiKey: "your-api-key",
    latitude: "40.7128",
    longitude: "-74.0060",
    logger: logger
);

Atmospore Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Test Atmospore API key
bool isValid = await AtmosporeHelper.TestApiKeyAsync(
    apiKey: "your-api-key",
    logger: logger
);

// Build pollen forecast
var pollenForecast = await AtmosporeHelper.BuildPollenForecastAsync(
    apiKey: "your-api-key",
    latitude: "39.43",
    longitude: "-77.80",
    date: "2024-05-27", // Optional, defaults to today
    forecastDays: 1,
    logger: logger
);

if (pollenForecast is not null)
{
    Console.WriteLine($"Date: {pollenForecast.ForecastDate}");
    foreach (var entry in pollenForecast.Entries)
    {
        Console.WriteLine($"{entry.DisplayName}: {entry.RiskLevel}");
    }
}

SunriseSunset Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Build sun/moon data — no API key required
var sunMoonData = await SunriseSunsetHelper.BuildSunMoonPointAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    logger: logger
);

OpenStreetMap Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Geocode an address — no API key required
var locations = await OpenStreetMapHelper.BuildAddressLocationsAsync(
    query: "1600 Pennsylvania Avenue NW, Washington, DC",
    country: "US",
    logger: logger
);

Weather Alert Helper

using Xcalibur.Weather.Helpers.Services;
using Microsoft.Extensions.Logging;

// Build combined weather alerts from multiple sources
// (Meteoalarm, NWS, GDACS, Environment Canada, BOM, EMSC, DWD)
// Intelligently selects services based on geographic location
var alerts = await WeatherAlertHelper.BuildCombinedAlertsAsync(
    latitude: "52.52",
    longitude: "13.41",
    logger: logger,
    token: CancellationToken.None,
    provinceCode: null,  // Optional: for Canada (e.g., "ON", "BC")
    stateCode: null      // Optional: for Australia (e.g., "NSW", "VIC")
);

if (alerts is not null && alerts.Alerts.Any())
{
    Console.WriteLine($"Active Alerts: {alerts.TotalAlerts}");
    foreach (var alert in alerts.Alerts)
    {
        Console.WriteLine($"[{alert.Severity}] {alert.Event}");
        Console.WriteLine($"  Source: {alert.Source}");
        Console.WriteLine($"  Effective: {alert.Effective}");
        Console.WriteLine($"  Expires: {alert.Expires}");
    }
}

// BUILD WITH AUTOMATIC CONSOLIDATION (Recommended for UI display)
// Automatically consolidates overlapping alerts (e.g., Winter Weather Advisory + Winter Storm Warning)
// Keeps only the highest severity alert from each overlapping group
var consolidatedAlerts = await WeatherAlertHelper.BuildCombinedAlertsConsolidatedAsync(
    latitude: "39.4300996",
    longitude: "-77.804161",
    logger: logger,
    token: CancellationToken.None
);

// Simple and clean - just iterate the results
foreach (var alert in consolidatedAlerts)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
    Console.WriteLine($"  Source: {alert.Source}");
    Console.WriteLine($"  Effective: {alert.Effective}");
    Console.WriteLine($"  Expires: {alert.Expires}");
}

// MANUAL CONSOLIDATION (if you need access to the full CombinedWeatherAlertInformation object)
var fullAlerts = await WeatherAlertHelper.BuildCombinedAlertsAsync(
    "39.4300996", "-77.804161", logger, CancellationToken.None);

if (fullAlerts is not null)
{
    Console.WriteLine($"Total Alerts: {fullAlerts.TotalAlerts}");
    Console.WriteLine($"Data Sources: {string.Join(", ", fullAlerts.DataSources)}");

    // Manually consolidate if needed
    var consolidated = WeatherAlertHelper.ConsolidateAlerts(fullAlerts.Alerts, logger);
    foreach (var alert in consolidated)
    {
        Console.WriteLine($"[{alert.Severity}] {alert.Event}");
    }
}

// Build alerts from specific services
var meteoalarmAlerts = await WeatherAlertHelper.BuildMeteoalarmAlertsAsync(
    latitude: "52.52",
    longitude: "13.41",
    logger: logger,
    token: CancellationToken.None
);

var nwsAlerts = await WeatherAlertHelper.BuildNwsAlertsAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    logger: logger,
    token: CancellationToken.None
);

// NWS with automatic consolidation (for US locations)
var nwsConsolidated = await WeatherAlertHelper.BuildNwsAlertsConsolidatedAsync(
    latitude: "39.4300996",
    longitude: "-77.804161",
    logger: logger,
    token: CancellationToken.None
);

// Clean and simple - just use the results directly
foreach (var alert in nwsConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

var gdacsAlerts = await WeatherAlertHelper.BuildGdacsAlertsAsync(
    latitude: "35.6762",
    longitude: "139.6503",
    logger: logger,
    token: CancellationToken.None
);

var canadaAlerts = await WeatherAlertHelper.BuildEnvironmentCanadaAlertsAsync(
    latitude: "43.65",
    longitude: "-79.38",
    provinceCode: "ON",
    logger: logger,
    token: CancellationToken.None
);

// Environment Canada with automatic consolidation (for Canadian locations)
var canadaConsolidated = await WeatherAlertHelper.BuildEnvironmentCanadaAlertsConsolidatedAsync(
    latitude: "43.65",
    longitude: "-79.38",
    provinceCode: "ON",
    logger: logger,
    token: CancellationToken.None
);

foreach (var alert in canadaConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

var bomAlerts = await WeatherAlertHelper.BuildBomAlertsAsync(
    latitude: "-33.87",
    longitude: "151.21",
    stateCode: "NSW",
    logger: logger,
    token: CancellationToken.None
);

// BOM with automatic consolidation (for Australian locations)
var bomConsolidated = await WeatherAlertHelper.BuildBomAlertsConsolidatedAsync(
    latitude: "-33.87",
    longitude: "151.21",
    stateCode: "NSW",
    logger: logger,
    token: CancellationToken.None
);

foreach (var alert in bomConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

var meteoalarmAlerts = await WeatherAlertHelper.BuildMeteoalarmAlertsAsync(
    latitude: "52.52",
    longitude: "13.41",
    logger: logger,
    token: CancellationToken.None
);

// Meteoalarm with automatic consolidation (for European locations)
var meteoalarmConsolidated = await WeatherAlertHelper.BuildMeteoalarmAlertsConsolidatedAsync(
    latitude: "52.52",
    longitude: "13.41",
    logger: logger,
    token: CancellationToken.None
);

foreach (var alert in meteoalarmConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

var gdacsAlerts = await WeatherAlertHelper.BuildGdacsAlertsAsync(
    latitude: "35.6762",
    longitude: "139.6503",
    logger: logger,
    token: CancellationToken.None
);

// GDACS with automatic consolidation (for global disaster alerts)
var gdacsConsolidated = await WeatherAlertHelper.BuildGdacsAlertsConsolidatedAsync(
    latitude: "35.6762",
    longitude: "139.6503",
    logger: logger,
    token: CancellationToken.None
);

foreach (var alert in gdacsConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

var dwdAlerts = await WeatherAlertHelper.BuildDwdAlertsAsync(
    latitude: "52.52",
    longitude: "13.41",
    logger: logger,
    token: CancellationToken.None
);

// DWD with automatic consolidation (for German locations)
var dwdConsolidated = await WeatherAlertHelper.BuildDwdAlertsConsolidatedAsync(
    latitude: "52.52",
    longitude: "13.41",
    logger: logger,
    token: CancellationToken.None
);

foreach (var alert in dwdConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

var emscAlerts = await WeatherAlertHelper.BuildEmscAlertsAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    radiusKm: 500,
    logger: logger,
    token: CancellationToken.None
);

// EMSC with automatic consolidation (for earthquake/seismic alerts)
var emscConsolidated = await WeatherAlertHelper.BuildEmscAlertsConsolidatedAsync(
    latitude: "40.7128",
    longitude: "-74.0060",
    radiusKm: 500,
    logger: logger,
    token: CancellationToken.None
);

foreach (var alert in emscConsolidated)
{
    Console.WriteLine($"[{alert.Severity}] {alert.Event}");
}

Weather Region Helper

using Xcalibur.Weather.Helpers.Services;

// Determine geographic region from coordinates
var region = WeatherRegionHelper.DetermineRegion(
    latitude: 52.52,
    longitude: 13.41
);

Console.WriteLine($"Region: {region}"); // Output: Europe

// Check if coordinates are in Germany
bool isGermany = WeatherRegionHelper.IsInGermany(
    latitude: 52.52,
    longitude: 13.41
);

// Determine Canadian province from coordinates
var province = WeatherRegionHelper.DetermineCanadianProvince(
    latitude: 43.65,
    longitude: -79.38
);

Console.WriteLine($"Province: {province}"); // Output: ON

// Determine Australian state from coordinates
var state = WeatherRegionHelper.DetermineAustralianState(
    latitude: -33.87,
    longitude: 151.21
);

Console.WriteLine($"State: {state}"); // Output: NSW

API Overview

ConversionHelper

Method Description
CelsiusToFahrenheit(double) Converts temperature from Celsius to Fahrenheit
CelsiusToFahrenheit(double?, double) Converts nullable Celsius to Fahrenheit; returns defaultValue when null
FahrenheitToCelsius(double) Converts temperature from Fahrenheit to Celsius
FahrenheitToCelsius(double?, double) Converts nullable Fahrenheit to Celsius; returns defaultValue when null
ConvertWindSpeed(double, WindSpeedUnits?) Converts wind speed from km/h to specified unit
ConvertWindSpeed(double?, WindSpeedUnits?) Converts nullable wind speed; returns 0 when null
FormatTemperature(double, TemperatureUnits?, bool) Formats temperature with optional unit symbol
FormatTemperature(double?, TemperatureUnits?, bool) Formats nullable temperature; returns empty string when null
FormatLength(double?, DistanceUnits, bool) Formats length/precipitation with optional unit symbol
FormatPressure(double?, BarometerUnits, bool) Formats pressure with optional unit symbol

OpenMeteoHelper

Method Description
BuildAirQualityPointAsync(string, string, ILogger, CancellationToken) Retrieves and builds air quality data for coordinates
BuildCurrentForecastAsync(...) Retrieves and builds current weather forecast point
BuildHourlyForecastAsync(...) Retrieves and builds hourly forecast points
BuildDailyForecastAsync(string, string, int, ILogger, CancellationToken) Retrieves and builds daily forecast points
BuildYesterdayHourlyForecastAsync(...) Retrieves and builds yesterday's hourly forecast
BuildYesterdayDailyForecastAsync(string, string, ILogger, CancellationToken) Retrieves and builds yesterday's daily forecast

GeocodioHelper

Method Description
TestApiKeyAsync(string, ILogger) Tests the validity of a Geocodio API key
BuildAddressLocationsAsync(...) Geocodes an address query and builds location models

IpGeoHelper

Method Description
TestApiKeyAsync(string, ILogger) Tests the validity of an IpGeolocation API key
BuildSunMoonPointAsync(string, string, string, ILogger) Retrieves and builds sun/moon astronomical data

AtmosporeHelper

Method Description
TestApiKeyAsync(string, ILogger) Tests the validity of an Atmospore API key
BuildPollenForecastAsync(string, string, string, string?, int, ILogger?) Retrieves and maps Atmospore pollen forecast data to a PollenInformation model

SunriseSunsetHelper

Method Description
BuildSunMoonPointAsync(string, string, ILogger?) Fetches sunrise/sunset data from SunriseSunset.io and maps it to a SunMoonPoint — no API key required

OpenStreetMapHelper

Method Description
BuildAddressLocationsAsync(string, string, ILogger?) Geocodes an address query via OpenStreetMap Nominatim and returns location models — no API key required

WeatherAlertHelper

Method Description
BuildCombinedAlertsAsync(string, string, ILogger, CancellationToken, string?, string?) Aggregates weather alerts from multiple global sources (Meteoalarm, NWS, GDACS, Environment Canada, BOM, EMSC, DWD) into a unified CombinedWeatherAlertInformation model. Intelligently selects services based on geographic location. Optional provinceCode for Canada and stateCode for Australia.
BuildCombinedAlertsConsolidatedAsync(...) Recommended for UI display. Returns only the consolidated alerts list, removing overlapping duplicates and keeping the highest severity alert from each group. Returns an empty list if no alerts exist. Clean, simple API.
ConsolidateAlerts(IEnumerable<WeatherAlertItem>, ILogger?) Consolidates a collection of alerts by removing overlapping duplicates and keeping the highest severity alert from each group. Use this for manual consolidation when you need access to the full CombinedWeatherAlertInformation object.
BuildMeteoalarmAlertsAsync(string, string, ILogger, CancellationToken) Retrieves alerts from Meteoalarm (Europe)
BuildMeteoalarmAlertsConsolidatedAsync(string, string, ILogger, CancellationToken) Recommended for Europe. Retrieves alerts from Meteoalarm with automatic consolidation. Returns a list of unique, consolidated alerts.
BuildNwsAlertsAsync(string, string, ILogger, CancellationToken) Retrieves alerts from the US National Weather Service
BuildNwsAlertsConsolidatedAsync(string, string, ILogger, CancellationToken) Recommended for US. Retrieves alerts from NWS with automatic consolidation. Returns a list of unique, consolidated alerts.
BuildGdacsAlertsAsync(string, string, ILogger, CancellationToken) Retrieves global disaster alerts from GDACS
BuildGdacsAlertsConsolidatedAsync(string, string, ILogger, CancellationToken) Recommended for global disasters. Retrieves alerts from GDACS with automatic consolidation. Returns a list of unique, consolidated alerts.
BuildEnvironmentCanadaAlertsAsync(string, string, string, ILogger, CancellationToken) Retrieves alerts from Environment Canada (requires province code)
BuildEnvironmentCanadaAlertsConsolidatedAsync(string, string, string, ILogger, CancellationToken) Recommended for Canada. Retrieves alerts from Environment Canada with automatic consolidation. Returns a list of unique, consolidated alerts.
BuildBomAlertsAsync(string, string, string, ILogger, CancellationToken) Retrieves alerts from the Australian Bureau of Meteorology (requires state code)
BuildBomAlertsConsolidatedAsync(string, string, string, ILogger, CancellationToken) Recommended for Australia. Retrieves alerts from BOM with automatic consolidation. Returns a list of unique, consolidated alerts.
BuildDwdAlertsAsync(string, string, ILogger, CancellationToken) Retrieves alerts from the German weather service (DWD)
BuildDwdAlertsConsolidatedAsync(string, string, ILogger, CancellationToken) Recommended for Germany. Retrieves alerts from DWD with automatic consolidation. Returns a list of unique, consolidated alerts.
BuildEmscAlertsAsync(string, string, int, ILogger, CancellationToken) Retrieves earthquake/seismic alerts from EMSC within specified radius (in km)
BuildEmscAlertsConsolidatedAsync(string, string, int, ILogger, CancellationToken) Recommended for earthquakes. Retrieves alerts from EMSC with automatic consolidation. Returns a list of unique, consolidated alerts.

WeatherRegionHelper

Method Description
DetermineRegion(double, double) Determines the geographic region (US, Canada, Europe, Australia, Other) based on coordinates
IsInGermany(double, double) Checks if coordinates fall within German geographic bounds
DetermineCanadianProvince(double, double) Returns the two-letter Canadian province code for the given coordinates
DetermineAustralianState(double, double) Returns the Australian state code for the given coordinates

Testing

The library ships with a comprehensive xUnit test suite covering all helpers and conversion utilities.

Test Coverage

Area Tests Coverage
ConversionHelper Temperature, wind speed, length, and pressure conversions and formatting — including nullable overloads, null-unit guards, near-zero normalisation, and invalid-unit exceptions Full public API
OpenMeteoHelper Air quality, current, hourly, daily, and yesterday forecasts — including absent/empty response blocks and day/night assessment Full public API
GeocodioHelper Address location mapping (single and multiple results), null/empty/invalid-JSON responses, API key validation Full public API
IpGeoHelper Sun/moon point mapping, null/whitespace key guards, deserialization, and HTTP error responses Full public API
SunriseSunsetHelper Sun/moon point mapping, successful deserialization, HTTP error and invalid-JSON responses Full public API
OpenStreetMapHelper Address location mapping, town fallback, empty/null/invalid-JSON/HTTP error responses Full public API
AtmosporeHelper Pollen forecast deserialization, API key validation, null/whitespace guards, HTTP error and invalid-JSON responses Full public API
WeatherAlertHelper Combined alert aggregation, individual source helpers (Meteoalarm, NWS, GDACS, Environment Canada, BOM, EMSC, DWD), cancellation behavior Full public API
WeatherRegionHelper Region determination (US, Canada, Europe, Australia), Germany bounds check, Canadian province detection, Australian state detection Full public API

Running the Tests

dotnet test

Or via the .NET CLI targeting the test project directly:

dotnet test Xcalibur.Weather.Helpers.Tests/Xcalibur.Weather.Helpers.Tests.csproj

Best Practices

Null Handling

All conversion methods include overloads that handle nullable values:

double? temperature = null;
double result = temperature.CelsiusToFahrenheit(defaultValue: 0); // Returns 0

Logging

All service helpers accept an ILogger parameter for diagnostics and troubleshooting:

using Microsoft.Extensions.Logging;

ILogger logger = loggerFactory.CreateLogger<YourClass>();
var forecast = await OpenMeteoHelper.BuildCurrentForecast(
    latitude, longitude, true, sunrise, sunset, logger
);

HttpClient Usage

Service helpers manage HttpClient usage internally, so callers can use the helper APIs directly without constructing provider service instances.

Dependencies

This library depends on:

Changelog

Version 1.0.12 (Latest)

  • 🔗 Updated: Dependency on Xcalibur.Weather.Services v1.0.12
  • 📚 Documentation: Enhanced README with Purpose and Use Cases sections
  • 🏗️ Improved: Project structure and formatting consistency
  • 🔧 Maintenance: Minor improvements and dependency updates

Version 1.0.10

  • New: Added consolidated alert methods for all 7 weather alert providers
    • BuildCombinedAlertsConsolidatedAsync - Multi-provider consolidated alerts
    • BuildNwsAlertsConsolidatedAsync - US National Weather Service
    • BuildMeteoalarmAlertsConsolidatedAsync - European weather alerts
    • BuildEnvironmentCanadaAlertsConsolidatedAsync - Canadian weather alerts
    • BuildBomAlertsConsolidatedAsync - Australian Bureau of Meteorology
    • BuildGdacsAlertsConsolidatedAsync - Global disaster alerts
    • BuildDwdAlertsConsolidatedAsync - German weather warnings
    • BuildEmscAlertsConsolidatedAsync - Earthquake/seismic alerts
  • 🔄 Improved: Automatic consolidation of overlapping alerts by event type and severity
  • 🎯 Optimized: Clean API design returning IReadOnlyList<WeatherAlertItem> for easier consumption
  • 📚 Documentation: Comprehensive examples for all consolidated methods
  • Testing: 23 new tests covering all consolidated alert scenarios

Version 1.0.9

  • 📝 Updated README with version information
  • 🔧 Minor improvements and bug fixes

Version 1.0.8

  • 🌍 Comprehensive weather alert support (Meteoalarm, NWS, GDACS, Environment Canada, BOM, EMSC, DWD)
  • 🛠️ Helper utilities for Open-Meteo, Geocodio, IpGeolocation, Atmospore, SunriseSunset, OpenStreetMap
  • 🔄 Conversion helpers for temperature, wind speed, length, and pressure
  • 📍 Geographic region detection and routing

License

This project is licensed under the Apache License 2.0. See the LICENSE-2.0.txt file for details.

Copyright © 2006 - 2026, Xcalibur Systems, LLC - All Rights Reserved

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests to improve the library.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Part of the Xcalibur Weather ecosystem for comprehensive weather data integration.

Author

Joshua Arzt
Xcalibur Systems, LLC

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.13 86 7/7/2026
1.0.12 96 7/6/2026
1.0.11 128 7/3/2026
1.0.10 95 7/2/2026
1.0.9 110 7/2/2026
1.0.8 97 7/2/2026
1.0.7 136 6/18/2026
1.0.6 144 6/17/2026
1.0.5 106 5/19/2026
1.0.4 109 5/2/2026
1.0.3 99 5/1/2026
1.0.2 114 4/30/2026
1.0.1 104 4/29/2026
1.0.0 179 3/6/2026

Weather code value retired for multi-language compatibility.