114 lines
3.8 KiB
C#
114 lines
3.8 KiB
C#
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<string> GetParcelsNearAsync(
|
|
double latitude,
|
|
double longitude,
|
|
double radiusMeters,
|
|
CancellationToken cancellationToken = default)
|
|
=> await GetFeaturesNearAsync(
|
|
latitude,
|
|
longitude,
|
|
radiusMeters,
|
|
"CP:CadastralParcel",
|
|
100,
|
|
cancellationToken);
|
|
|
|
public async Task<string> GetSheetsNearAsync(
|
|
double latitude,
|
|
double longitude,
|
|
double radiusMeters = 10,
|
|
CancellationToken cancellationToken = default)
|
|
=> await GetFeaturesNearAsync(
|
|
latitude,
|
|
longitude,
|
|
radiusMeters,
|
|
"CP:CadastralZoning",
|
|
20,
|
|
cancellationToken);
|
|
|
|
private async Task<string> GetFeaturesNearAsync(
|
|
double latitude,
|
|
double longitude,
|
|
double radiusMeters,
|
|
string typeName,
|
|
int count,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
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<string, string>
|
|
{
|
|
["language"] = "ita",
|
|
["SERVICE"] = "WFS",
|
|
["VERSION"] = "2.0.0",
|
|
["TYPENAMES"] = typeName,
|
|
["SRSNAME"] = "urn:ogc:def:crs:EPSG::6706",
|
|
["BBOX"] = bbox,
|
|
["REQUEST"] = "GetFeature",
|
|
["COUNT"] = count.ToString(CultureInfo.InvariantCulture)
|
|
};
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|