using Microsoft.Extensions.Options; using SatelliteProvider.Common.Configs; using SatelliteProvider.Common.DTO; using SatelliteProvider.Common.Enums; using SatelliteProvider.Common.Utils; namespace SatelliteProvider.Services.RouteManagement; public class RoutePointGraphBuilder { private readonly double _maxPointSpacingMeters; public RoutePointGraphBuilder(IOptions processingConfig) { ArgumentNullException.ThrowIfNull(processingConfig); _maxPointSpacingMeters = processingConfig.Value.MaxRoutePointSpacingMeters; } public RoutePointGraph Build(IReadOnlyList userPoints) { ArgumentNullException.ThrowIfNull(userPoints); if (userPoints.Count < 2) { throw new ArgumentException("Route must have at least 2 points", nameof(userPoints)); } var allPoints = new List(); var totalDistance = 0.0; var sequenceNumber = 0; for (int segmentIndex = 0; segmentIndex < userPoints.Count; segmentIndex++) { var current = userPoints[segmentIndex]; var isStart = segmentIndex == 0; var isEnd = segmentIndex == userPoints.Count - 1; var currentGeo = new GeoPoint(current.Latitude, current.Longitude); double? distanceFromPrevious = null; if (allPoints.Count > 0) { var prev = allPoints[^1]; var prevGeo = new GeoPoint(prev.Latitude, prev.Longitude); distanceFromPrevious = GeoUtils.CalculateDistance(prevGeo, currentGeo); totalDistance += distanceFromPrevious.Value; } allPoints.Add(new RoutePointDto { Latitude = current.Latitude, Longitude = current.Longitude, PointType = isStart ? RoutePointType.Start : (isEnd ? RoutePointType.End : RoutePointType.Action), SequenceNumber = sequenceNumber++, SegmentIndex = segmentIndex, DistanceFromPrevious = distanceFromPrevious, }); if (isEnd) { continue; } var next = userPoints[segmentIndex + 1]; var nextGeo = new GeoPoint(next.Latitude, next.Longitude); var intermediates = GeoUtils.CalculateIntermediatePoints(currentGeo, nextGeo, _maxPointSpacingMeters); foreach (var intermediateGeo in intermediates) { var prev = allPoints[^1]; var prevGeo = new GeoPoint(prev.Latitude, prev.Longitude); var distFromPrev = GeoUtils.CalculateDistance(prevGeo, intermediateGeo); totalDistance += distFromPrev; allPoints.Add(new RoutePointDto { Latitude = intermediateGeo.Lat, Longitude = intermediateGeo.Lon, PointType = RoutePointType.Intermediate, SequenceNumber = sequenceNumber++, SegmentIndex = segmentIndex, DistanceFromPrevious = distFromPrev, }); } } return new RoutePointGraph(allPoints, totalDistance); } } public record RoutePointGraph(IReadOnlyList Points, double TotalDistanceMeters);