Rilascia strumenti avanzati e archivio 0.3.0

This commit is contained in:
livio committed 2026-09-24 16:15:23 +02:00
1 parent b19afbce91
commit 620a4bec26
8 files changed
+1351 -33

No files matched your search

+152
View File
@@ -35,6 +35,8 @@ public sealed class MapExportService
{
"kml" => "kml",
"kmz" => "kmz",
"gpx" => "gpx",
"pdf" => "pdf",
_ => "geojson"
};
var baseName = NormalizeFileName(requestedName);
@@ -49,6 +51,18 @@ public sealed class MapExportService
return new(path, "application/geo+json", featureCount, Path.GetFileName(path));
}
if (format == "gpx")
{
await File.WriteAllTextAsync(path, BuildGpx(features, baseName), new UTF8Encoding(false), cancellationToken);
return new(path, "application/gpx+xml", featureCount, Path.GetFileName(path));
}
if (format == "pdf")
{
await File.WriteAllBytesAsync(path, BuildPdfReport(features, baseName), cancellationToken);
return new(path, "application/pdf", featureCount, Path.GetFileName(path));
}
var kml = BuildKml(features, baseName);
if (format == "kml")
{
@@ -67,6 +81,108 @@ public sealed class MapExportService
return new(path, "application/vnd.google-earth.kmz", featureCount, Path.GetFileName(path));
}
private static string BuildGpx(JsonElement features, string name)
{
var root = new XElement("gpx",
new XAttribute("version", "1.1"),
new XAttribute("creator", "LME Mappe Catastali 0.3.0"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
new XElement("metadata", new XElement("name", name)));
foreach (var feature in features.EnumerateArray())
{
if (!feature.TryGetProperty("geometry", out var geometry) || geometry.ValueKind != JsonValueKind.Object) continue;
var properties = feature.TryGetProperty("properties", out var value) ? value : default;
var featureName = FeatureName(properties, GetString(properties, "_lmeCategory") ?? "drawing");
var type = geometry.GetProperty("type").GetString();
var coordinates = geometry.GetProperty("coordinates");
if (type == "Point")
{
root.Add(GpxPoint("wpt", coordinates, featureName));
}
else
{
foreach (var line in EnumerateGpxLines(type, coordinates))
{
root.Add(new XElement("trk", new XElement("name", featureName),
new XElement("trkseg", line.Select(point => GpxPoint("trkpt", point, null)))));
}
}
}
return new XDocument(new XDeclaration("1.0", "utf-8", null), root).ToString();
}
private static XElement GpxPoint(string element, JsonElement coordinate, string? name)
{
var values = coordinate.EnumerateArray().ToArray();
var point = new XElement(element,
new XAttribute("lat", values[1].GetDouble().ToString("G17", CultureInfo.InvariantCulture)),
new XAttribute("lon", values[0].GetDouble().ToString("G17", CultureInfo.InvariantCulture)));
if (!string.IsNullOrWhiteSpace(name)) point.Add(new XElement("name", name));
return point;
}
private static IEnumerable<JsonElement[]> EnumerateGpxLines(string? type, JsonElement coordinates)
{
if (type == "LineString") yield return coordinates.EnumerateArray().ToArray();
else if (type == "Polygon")
foreach (var ring in coordinates.EnumerateArray()) yield return ring.EnumerateArray().ToArray();
else if (type == "MultiLineString")
foreach (var line in coordinates.EnumerateArray()) yield return line.EnumerateArray().ToArray();
else if (type == "MultiPolygon")
foreach (var polygon in coordinates.EnumerateArray())
foreach (var ring in polygon.EnumerateArray()) yield return ring.EnumerateArray().ToArray();
}
private static byte[] BuildPdfReport(JsonElement features, string title)
{
var lines = new List<string>
{
title,
$"Rapporto LME Mappe Catastali - {DateTime.Now:dd/MM/yyyy HH:mm}",
$"Oggetti: {features.GetArrayLength()}",
""
};
var index = 1;
foreach (var feature in features.EnumerateArray())
{
var properties = feature.TryGetProperty("properties", out var value) ? value : default;
var category = GetString(properties, "_lmeCategory") ?? "drawing";
var geometry = feature.TryGetProperty("geometry", out var geo) ? geo.GetProperty("type").GetString() : "-";
lines.Add($"{index++}. {FeatureName(properties, category)} [{category}, {geometry}]");
}
var content = new StringBuilder("BT /F1 16 Tf 48 790 Td ");
foreach (var line in lines.Take(42))
{
var safe = line.Replace("\\", "\\\\").Replace("(", "\\(").Replace(")", "\\)")
.Select(character => character <= 255 ? character : '?').ToArray();
content.Append('(').Append(safe).Append(") Tj 0 -18 Td ");
}
content.Append("ET");
var stream = Encoding.Latin1.GetBytes(content.ToString());
var objects = new[]
{
"<< /Type /Catalog /Pages 2 0 R >>",
"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>",
$"<< /Length {stream.Length} >>\nstream\n{Encoding.Latin1.GetString(stream)}\nendstream",
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>"
};
using var output = new MemoryStream();
using var writer = new StreamWriter(output, Encoding.Latin1, leaveOpen: true) { NewLine = "\n" };
writer.Write("%PDF-1.4\n"); writer.Flush();
var offsets = new List<long> { 0 };
for (var i = 0; i < objects.Length; i++)
{
offsets.Add(output.Position); writer.Write($"{i + 1} 0 obj\n{objects[i]}\nendobj\n"); writer.Flush();
}
var xref = output.Position;
writer.Write($"xref\n0 {objects.Length + 1}\n0000000000 65535 f \n");
foreach (var offset in offsets.Skip(1)) writer.Write($"{offset:0000000000} 00000 n \n");
writer.Write($"trailer << /Size {objects.Length + 1} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF");
writer.Flush();
return output.ToArray();
}
private static string BuildKml(JsonElement features, string documentName)
{
var document = new XElement(Kml + "Document",
@@ -97,6 +213,11 @@ public sealed class MapExportService
var placemark = new XElement(Kml + "Placemark",
new XElement(Kml + "name", FeatureName(properties, category)),
new XElement(Kml + "styleUrl", $"#{NormalizeStyleId(category)}"));
var featureStyle = BuildFeatureStyle(properties);
if (featureStyle is not null)
{
placemark.Add(featureStyle);
}
var extendedData = BuildExtendedData(properties);
if (extendedData is not null)
@@ -142,6 +263,37 @@ public sealed class MapExportService
new XElement(Kml + "width", 3)),
new XElement(Kml + "PolyStyle", new XElement(Kml + "color", fillColor)));
private static XElement? BuildFeatureStyle(JsonElement properties)
{
var color = GetString(properties, "_lmeColor");
var fillColor = GetString(properties, "_lmeFillColor") ?? color;
if (string.IsNullOrWhiteSpace(color) && string.IsNullOrWhiteSpace(fillColor)) return null;
var weight = GetNumber(properties, "_lmeWeight", 3);
var opacity = GetNumber(properties, "_lmeOpacity", 1);
var fillOpacity = GetNumber(properties, "_lmeFillOpacity", 0.2);
return new XElement(Kml + "Style",
new XElement(Kml + "LineStyle",
new XElement(Kml + "color", ToKmlColor(color ?? "#f97316", opacity)),
new XElement(Kml + "width", weight.ToString("0.##", CultureInfo.InvariantCulture))),
new XElement(Kml + "PolyStyle",
new XElement(Kml + "color", ToKmlColor(fillColor ?? "#fb923c", fillOpacity))));
}
private static double GetNumber(JsonElement properties, string name, double fallback) =>
properties.ValueKind == JsonValueKind.Object
&& properties.TryGetProperty(name, out var value)
&& value.ValueKind == JsonValueKind.Number
? value.GetDouble()
: fallback;
private static string ToKmlColor(string html, double opacity)
{
var value = html.TrimStart('#');
if (value.Length != 6) value = "f97316";
var alpha = (int)Math.Round(Math.Clamp(opacity, 0, 1) * 255);
return $"{alpha:x2}{value[4..6]}{value[2..4]}{value[0..2]}";
}
private static XElement? BuildExtendedData(JsonElement properties)
{
if (properties.ValueKind != JsonValueKind.Object)