mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-04-22 11:16:38 +00:00
added stitching
This commit is contained in:
@@ -3,8 +3,12 @@ using Microsoft.Extensions.Options;
|
||||
using SatelliteProvider.Common.Configs;
|
||||
using SatelliteProvider.Common.DTO;
|
||||
using SatelliteProvider.Common.Interfaces;
|
||||
using SatelliteProvider.Common.Utils;
|
||||
using SatelliteProvider.DataAccess.Models;
|
||||
using SatelliteProvider.DataAccess.Repositories;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace SatelliteProvider.Services;
|
||||
|
||||
@@ -122,9 +126,14 @@ public class RegionService : IRegionService
|
||||
|
||||
var csvPath = Path.Combine(readyDir, $"region_{id}_ready.csv");
|
||||
var summaryPath = Path.Combine(readyDir, $"region_{id}_summary.txt");
|
||||
var stitchedImagePath = Path.Combine(readyDir, $"region_{id}_stitched.jpg");
|
||||
|
||||
await GenerateCsvFileAsync(csvPath, tiles, cancellationToken);
|
||||
await GenerateSummaryFileAsync(summaryPath, id, region, tiles, tilesDownloaded, tilesReused, processingStartTime, cancellationToken);
|
||||
|
||||
_logger.LogInformation("Stitching tiles for region {RegionId}", id);
|
||||
await StitchTilesAsync(tiles, region.Latitude, region.Longitude, region.ZoomLevel, stitchedImagePath, cancellationToken);
|
||||
|
||||
await GenerateSummaryFileAsync(summaryPath, id, region, tiles, tilesDownloaded, tilesReused, stitchedImagePath, processingStartTime, cancellationToken);
|
||||
|
||||
region.Status = "completed";
|
||||
region.CsvFilePath = csvPath;
|
||||
@@ -146,6 +155,96 @@ public class RegionService : IRegionService
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> StitchTilesAsync(
|
||||
List<TileMetadata> tiles,
|
||||
double centerLatitude,
|
||||
double centerLongitude,
|
||||
int zoomLevel,
|
||||
string outputPath,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (tiles.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No tiles to stitch");
|
||||
}
|
||||
|
||||
var tileSizePixels = tiles.First().TileSizePixels;
|
||||
|
||||
var tileCoords = tiles.Select(t =>
|
||||
{
|
||||
var (x, y) = GeoUtils.WorldToTilePos(new GeoPoint(t.Latitude, t.Longitude), zoomLevel);
|
||||
return (x, y, t.FilePath);
|
||||
}).ToList();
|
||||
|
||||
var minX = tileCoords.Min(t => t.x);
|
||||
var maxX = tileCoords.Max(t => t.x);
|
||||
var minY = tileCoords.Min(t => t.y);
|
||||
var maxY = tileCoords.Max(t => t.y);
|
||||
|
||||
var gridWidth = maxX - minX + 1;
|
||||
var gridHeight = maxY - minY + 1;
|
||||
var imageWidth = gridWidth * tileSizePixels;
|
||||
var imageHeight = gridHeight * tileSizePixels;
|
||||
|
||||
_logger.LogInformation("Stitching {Count} tiles into {Width}x{Height} image (grid: {GridWidth}x{GridHeight})",
|
||||
tiles.Count, imageWidth, imageHeight, gridWidth, gridHeight);
|
||||
|
||||
using var stitchedImage = new Image<Rgb24>(imageWidth, imageHeight);
|
||||
|
||||
foreach (var (x, y, filePath) in tileCoords)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
_logger.LogWarning("Tile file not found: {FilePath}", filePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
using var tileImage = await Image.LoadAsync<Rgb24>(filePath, cancellationToken);
|
||||
|
||||
var destX = (x - minX) * tileSizePixels;
|
||||
var destY = (y - minY) * tileSizePixels;
|
||||
|
||||
stitchedImage.Mutate(ctx => ctx.DrawImage(tileImage, new Point(destX, destY), 1f));
|
||||
}
|
||||
|
||||
var (centerTileX, centerTileY) = GeoUtils.WorldToTilePos(new GeoPoint(centerLatitude, centerLongitude), zoomLevel);
|
||||
|
||||
var n = Math.Pow(2.0, zoomLevel);
|
||||
var centerTilePixelX = ((centerLongitude + 180.0) / 360.0 * n - centerTileX) * tileSizePixels;
|
||||
var centerTilePixelY = ((1.0 - Math.Log(Math.Tan(centerLatitude * Math.PI / 180.0) + 1.0 / Math.Cos(centerLatitude * Math.PI / 180.0)) / Math.PI) / 2.0 * n - centerTileY) * tileSizePixels;
|
||||
|
||||
var crossX = (int)Math.Round((centerTileX - minX) * tileSizePixels + centerTilePixelX);
|
||||
var crossY = (int)Math.Round((centerTileY - minY) * tileSizePixels + centerTilePixelY);
|
||||
|
||||
_logger.LogInformation("Drawing red cross at pixel position ({X}, {Y}) for coordinates ({Lat}, {Lon})",
|
||||
crossX, crossY, centerLatitude, centerLongitude);
|
||||
|
||||
var red = new Rgb24(255, 0, 0);
|
||||
stitchedImage.Mutate(ctx =>
|
||||
{
|
||||
for (int i = -5; i < 5; i++)
|
||||
{
|
||||
var hx = crossX + i;
|
||||
var vy = crossY + i;
|
||||
|
||||
if (hx >= 0 && hx < imageWidth && crossY >= 0 && crossY < imageHeight)
|
||||
{
|
||||
stitchedImage[hx, crossY] = red;
|
||||
}
|
||||
|
||||
if (crossX >= 0 && crossX < imageWidth && vy >= 0 && vy < imageHeight)
|
||||
{
|
||||
stitchedImage[crossX, vy] = red;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await stitchedImage.SaveAsJpegAsync(outputPath, cancellationToken);
|
||||
_logger.LogInformation("Stitched image saved to {OutputPath}", outputPath);
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
private async Task GenerateCsvFileAsync(string filePath, List<TileMetadata> tiles, CancellationToken cancellationToken)
|
||||
{
|
||||
var orderedTiles = tiles.OrderByDescending(t => t.Latitude).ThenBy(t => t.Longitude).ToList();
|
||||
@@ -166,6 +265,7 @@ public class RegionService : IRegionService
|
||||
List<TileMetadata> tiles,
|
||||
int tilesDownloaded,
|
||||
int tilesReused,
|
||||
string stitchedImagePath,
|
||||
DateTime startTime,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -190,6 +290,8 @@ public class RegionService : IRegionService
|
||||
summary.AppendLine();
|
||||
summary.AppendLine("Files Created:");
|
||||
summary.AppendLine($"- CSV: {Path.GetFileName(filePath).Replace("_summary.txt", "_ready.csv")}");
|
||||
summary.AppendLine($"- Stitched Image: {Path.GetFileName(stitchedImagePath)}");
|
||||
summary.AppendLine($"- Stitched Image Path: {stitchedImagePath}");
|
||||
summary.AppendLine($"- Summary: {Path.GetFileName(filePath)}");
|
||||
|
||||
await File.WriteAllTextAsync(filePath, summary.ToString(), cancellationToken);
|
||||
|
||||
Reference in New Issue
Block a user