using System.Globalization; using System.Xml.Linq; namespace LMEMappeCatastaliApp.Services; public sealed class OfficialCadastreService { private const string WfsEndpoint = "https://wfs.cartografia.agenziaentrate.gov.it/inspire/wfs/owfs01.php"; private readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(30) }; public OfficialCadastreService() { _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2"); _httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/gml+xml"); } public async Task GetParcelsNearAsync( double latitude, double longitude, double radiusMeters, CancellationToken cancellationToken = default) { var meters = Math.Clamp(radiusMeters, 2, 30); var deltaLatitude = meters / 111_320d; var longitudeScale = Math.Cos(latitude * Math.PI / 180d); var deltaLongitude = meters / (111_320d * Math.Max(0.1, longitudeScale)); var bbox = string.Join(",", [ Format(latitude - deltaLatitude), Format(longitude - deltaLongitude), Format(latitude + deltaLatitude), Format(longitude + deltaLongitude) ]); var query = new Dictionary { ["language"] = "ita", ["SERVICE"] = "WFS", ["VERSION"] = "2.0.0", ["TYPENAMES"] = "CP:CadastralParcel", ["SRSNAME"] = "urn:ogc:def:crs:EPSG::6706", ["BBOX"] = bbox, ["REQUEST"] = "GetFeature", ["COUNT"] = "100" }; var url = WfsEndpoint + "?" + string.Join("&", query.Select(item => $"{Uri.EscapeDataString(item.Key)}={Uri.EscapeDataString(item.Value)}")); using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseContentRead, cancellationToken); if (!response.IsSuccessStatusCode) { throw new HttpRequestException($"Il servizio WFS AdE ha risposto {((int)response.StatusCode)}."); } var xml = await response.Content.ReadAsStringAsync(cancellationToken); ThrowIfExceptionReport(xml); return xml; } private static string Format(double value) => value.ToString("R", CultureInfo.InvariantCulture); private static void ThrowIfExceptionReport(string xml) { try { var document = XDocument.Parse(xml); var exceptionText = document.Descendants() .FirstOrDefault(element => element.Name.LocalName == "ExceptionText") ?.Value .Trim(); if (!string.IsNullOrWhiteSpace(exceptionText)) { throw new InvalidOperationException($"Servizio WFS AdE: {exceptionText}"); } } catch (System.Xml.XmlException error) { throw new InvalidDataException("Il servizio WFS AdE ha restituito una risposta non valida.", error); } } }