first region implementation

This commit is contained in:
Anton Martynenko
2025-10-28 15:56:16 +01:00
parent 12f3bf890a
commit bbb112940d
10 changed files with 576 additions and 0 deletions
+84
View File
@@ -6,6 +6,7 @@ using SatelliteProvider.DataAccess;
using SatelliteProvider.DataAccess.Models;
using SatelliteProvider.DataAccess.Repositories;
using SatelliteProvider.Common.Configs;
using SatelliteProvider.Common.DTO;
using SatelliteProvider.Common.Interfaces;
using SatelliteProvider.Services;
@@ -26,6 +27,11 @@ builder.Services.AddSingleton<GoogleMapsDownloader>();
builder.Services.AddSingleton<GoogleMapsDownloaderV2>();
builder.Services.AddSingleton<ITileService, TileService>();
var processingConfig = builder.Configuration.GetSection("ProcessingConfig").Get<ProcessingConfig>() ?? new ProcessingConfig();
builder.Services.AddSingleton<IRegionRequestQueue>(sp => new RegionRequestQueue(processingConfig.QueueCapacity));
builder.Services.AddSingleton<IRegionService, RegionService>();
builder.Services.AddHostedService<RegionProcessingService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
@@ -60,6 +66,12 @@ if (!migrator.RunMigrations())
throw new Exception("Database migration failed. Application cannot start.");
}
var storageConfig = app.Configuration.GetSection("StorageConfig").Get<StorageConfig>() ?? new StorageConfig();
Directory.CreateDirectory(storageConfig.TilesDirectory);
Directory.CreateDirectory(storageConfig.ReadyDirectory);
logger.LogInformation("Storage directories created: Tiles={TilesDir}, Ready={ReadyDir}",
storageConfig.TilesDirectory, storageConfig.ReadyDirectory);
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
@@ -82,6 +94,12 @@ app.MapPost("/api/satellite/upload", UploadImage)
app.MapPost("/api/satellite/tiles/download", DownloadSingleTile)
.WithOpenApi(op => new(op) { Summary = "TEMPORARY: Download single tile at specified coordinates" });
app.MapPost("/api/satellite/request", RequestRegion)
.WithOpenApi(op => new(op) { Summary = "Request tiles for a region" });
app.MapGet("/api/satellite/region/{id:guid}", GetRegionStatus)
.WithOpenApi(op => new(op) { Summary = "Get region status and file paths" });
app.Run();
IResult GetSatelliteTilesByLatLon(double lat, double lon, double squareSideMeters)
@@ -154,6 +172,54 @@ async Task<IResult> DownloadSingleTile([FromBody] DownloadTileRequest request, G
}
}
async Task<IResult> RequestRegion([FromBody] RequestRegionRequest request, IRegionService regionService, ILogger<Program> logger)
{
try
{
if (request.SizeMeters < 100 || request.SizeMeters > 10000)
{
return Results.BadRequest(new { error = "Size must be between 100 and 10000 meters" });
}
logger.LogInformation("Region request received: ID={Id}, Lat={Lat}, Lon={Lon}, Size={Size}m, Zoom={Zoom}",
request.Id, request.Latitude, request.Longitude, request.SizeMeters, request.ZoomLevel);
var status = await regionService.RequestRegionAsync(
request.Id,
request.Latitude,
request.Longitude,
request.SizeMeters,
request.ZoomLevel);
return Results.Ok(status);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to request region");
return Results.Problem(detail: ex.Message, statusCode: 500);
}
}
async Task<IResult> GetRegionStatus(Guid id, IRegionService regionService, ILogger<Program> logger)
{
try
{
var status = await regionService.GetRegionStatusAsync(id);
if (status == null)
{
return Results.NotFound(new { error = $"Region {id} not found" });
}
return Results.Ok(status);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to get region status");
return Results.Problem(detail: ex.Message, statusCode: 500);
}
}
public record GetSatelliteTilesResponse
{
public List<SatelliteTile> Tiles { get; set; } = new();
@@ -228,6 +294,24 @@ public record DownloadTileResponse
public DateTime UpdatedAt { get; set; }
}
public record RequestRegionRequest
{
[Required]
public Guid Id { get; set; }
[Required]
public double Latitude { get; set; }
[Required]
public double Longitude { get; set; }
[Required]
public double SizeMeters { get; set; }
[Required]
public int ZoomLevel { get; set; } = 18;
}
public class ParameterDescriptionFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)