mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-04-22 08:56:38 +00:00
215 lines
7.9 KiB
C#
215 lines
7.9 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using SatelliteProvider.Common.DTO;
|
|
using SatelliteProvider.Common.Interfaces;
|
|
using SatelliteProvider.Common.Utils;
|
|
using SatelliteProvider.DataAccess.Models;
|
|
using SatelliteProvider.DataAccess.Repositories;
|
|
|
|
namespace SatelliteProvider.Services;
|
|
|
|
public class RouteService : IRouteService
|
|
{
|
|
private readonly IRouteRepository _routeRepository;
|
|
private readonly IRegionService _regionService;
|
|
private readonly ILogger<RouteService> _logger;
|
|
private const double MAX_POINT_SPACING_METERS = 200.0;
|
|
|
|
public RouteService(
|
|
IRouteRepository routeRepository,
|
|
IRegionService regionService,
|
|
ILogger<RouteService> logger)
|
|
{
|
|
_routeRepository = routeRepository;
|
|
_regionService = regionService;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<RouteResponse> CreateRouteAsync(CreateRouteRequest request)
|
|
{
|
|
if (request.Points.Count < 2)
|
|
{
|
|
throw new ArgumentException("Route must have at least 2 points");
|
|
}
|
|
|
|
if (request.RegionSizeMeters < 100 || request.RegionSizeMeters > 10000)
|
|
{
|
|
throw new ArgumentException("Region size must be between 100 and 10000 meters");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(request.Name))
|
|
{
|
|
throw new ArgumentException("Route name is required");
|
|
}
|
|
|
|
_logger.LogInformation("Creating route {RouteId} with {PointCount} original points",
|
|
request.Id, request.Points.Count);
|
|
|
|
var allPoints = new List<RoutePointDto>();
|
|
var totalDistance = 0.0;
|
|
var sequenceNumber = 0;
|
|
|
|
for (int segmentIndex = 0; segmentIndex < request.Points.Count; segmentIndex++)
|
|
{
|
|
var currentPoint = request.Points[segmentIndex];
|
|
var isStart = segmentIndex == 0;
|
|
var isEnd = segmentIndex == request.Points.Count - 1;
|
|
|
|
var geoPoint = new GeoPoint(currentPoint.Latitude, currentPoint.Longitude);
|
|
|
|
double? distanceFromPrevious = null;
|
|
if (allPoints.Count > 0)
|
|
{
|
|
var lastAddedPoint = allPoints[^1];
|
|
var prevGeoPoint = new GeoPoint(lastAddedPoint.Latitude, lastAddedPoint.Longitude);
|
|
distanceFromPrevious = GeoUtils.CalculateDistance(prevGeoPoint, geoPoint);
|
|
totalDistance += distanceFromPrevious.Value;
|
|
}
|
|
|
|
var pointType = isStart ? "start" : (isEnd ? "end" : "action");
|
|
|
|
allPoints.Add(new RoutePointDto
|
|
{
|
|
Latitude = currentPoint.Latitude,
|
|
Longitude = currentPoint.Longitude,
|
|
PointType = pointType,
|
|
SequenceNumber = sequenceNumber++,
|
|
SegmentIndex = segmentIndex,
|
|
DistanceFromPrevious = distanceFromPrevious
|
|
});
|
|
|
|
if (!isEnd)
|
|
{
|
|
var nextPoint = request.Points[segmentIndex + 1];
|
|
var startGeo = new GeoPoint(currentPoint.Latitude, currentPoint.Longitude);
|
|
var endGeo = new GeoPoint(nextPoint.Latitude, nextPoint.Longitude);
|
|
|
|
var intermediatePoints = GeoUtils.CalculateIntermediatePoints(startGeo, endGeo, MAX_POINT_SPACING_METERS);
|
|
|
|
_logger.LogInformation("Segment {SegmentIndex}: Adding {Count} intermediate points",
|
|
segmentIndex, intermediatePoints.Count);
|
|
|
|
foreach (var intermediateGeo in intermediatePoints)
|
|
{
|
|
var lastAddedPoint = allPoints[^1];
|
|
var prevGeo = new GeoPoint(lastAddedPoint.Latitude, lastAddedPoint.Longitude);
|
|
|
|
var distFromPrev = GeoUtils.CalculateDistance(prevGeo, intermediateGeo);
|
|
totalDistance += distFromPrev;
|
|
|
|
allPoints.Add(new RoutePointDto
|
|
{
|
|
Latitude = intermediateGeo.Lat,
|
|
Longitude = intermediateGeo.Lon,
|
|
PointType = "intermediate",
|
|
SequenceNumber = sequenceNumber++,
|
|
SegmentIndex = segmentIndex,
|
|
DistanceFromPrevious = distFromPrev
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
_logger.LogInformation("Route {RouteId}: Total {TotalPoints} points (original + intermediate), distance {Distance:F2}m",
|
|
request.Id, allPoints.Count, totalDistance);
|
|
|
|
var now = DateTime.UtcNow;
|
|
var routeEntity = new RouteEntity
|
|
{
|
|
Id = request.Id,
|
|
Name = request.Name,
|
|
Description = request.Description,
|
|
RegionSizeMeters = request.RegionSizeMeters,
|
|
ZoomLevel = request.ZoomLevel,
|
|
TotalDistanceMeters = totalDistance,
|
|
TotalPoints = allPoints.Count,
|
|
RequestMaps = request.RequestMaps,
|
|
MapsReady = false,
|
|
CreatedAt = now,
|
|
UpdatedAt = now
|
|
};
|
|
|
|
await _routeRepository.InsertRouteAsync(routeEntity);
|
|
|
|
var pointEntities = allPoints.Select(p => new RoutePointEntity
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
RouteId = request.Id,
|
|
SequenceNumber = p.SequenceNumber,
|
|
Latitude = p.Latitude,
|
|
Longitude = p.Longitude,
|
|
PointType = p.PointType,
|
|
SegmentIndex = p.SegmentIndex,
|
|
DistanceFromPrevious = p.DistanceFromPrevious,
|
|
CreatedAt = now
|
|
}).ToList();
|
|
|
|
await _routeRepository.InsertRoutePointsAsync(pointEntities);
|
|
|
|
if (request.RequestMaps)
|
|
{
|
|
_logger.LogInformation("Route {RouteId}: Maps requested. Regions will be processed sequentially by background service.",
|
|
request.Id);
|
|
}
|
|
|
|
_logger.LogInformation("Route {RouteId} created successfully", request.Id);
|
|
|
|
return new RouteResponse
|
|
{
|
|
Id = routeEntity.Id,
|
|
Name = routeEntity.Name,
|
|
Description = routeEntity.Description,
|
|
RegionSizeMeters = routeEntity.RegionSizeMeters,
|
|
ZoomLevel = routeEntity.ZoomLevel,
|
|
TotalDistanceMeters = routeEntity.TotalDistanceMeters,
|
|
TotalPoints = routeEntity.TotalPoints,
|
|
Points = allPoints,
|
|
RequestMaps = routeEntity.RequestMaps,
|
|
MapsReady = routeEntity.MapsReady,
|
|
CsvFilePath = routeEntity.CsvFilePath,
|
|
SummaryFilePath = routeEntity.SummaryFilePath,
|
|
StitchedImagePath = routeEntity.StitchedImagePath,
|
|
CreatedAt = routeEntity.CreatedAt,
|
|
UpdatedAt = routeEntity.UpdatedAt
|
|
};
|
|
}
|
|
|
|
public async Task<RouteResponse?> GetRouteAsync(Guid id)
|
|
{
|
|
var route = await _routeRepository.GetByIdAsync(id);
|
|
if (route == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var points = await _routeRepository.GetRoutePointsAsync(id);
|
|
|
|
return new RouteResponse
|
|
{
|
|
Id = route.Id,
|
|
Name = route.Name,
|
|
Description = route.Description,
|
|
RegionSizeMeters = route.RegionSizeMeters,
|
|
ZoomLevel = route.ZoomLevel,
|
|
TotalDistanceMeters = route.TotalDistanceMeters,
|
|
TotalPoints = route.TotalPoints,
|
|
Points = points.Select(p => new RoutePointDto
|
|
{
|
|
Latitude = p.Latitude,
|
|
Longitude = p.Longitude,
|
|
PointType = p.PointType,
|
|
SequenceNumber = p.SequenceNumber,
|
|
SegmentIndex = p.SegmentIndex,
|
|
DistanceFromPrevious = p.DistanceFromPrevious
|
|
}).ToList(),
|
|
RequestMaps = route.RequestMaps,
|
|
MapsReady = route.MapsReady,
|
|
CsvFilePath = route.CsvFilePath,
|
|
SummaryFilePath = route.SummaryFilePath,
|
|
StitchedImagePath = route.StitchedImagePath,
|
|
CreatedAt = route.CreatedAt,
|
|
UpdatedAt = route.UpdatedAt
|
|
};
|
|
}
|
|
}
|
|
|