diff --git a/SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj b/SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj
index 86dc555..b403f78 100644
--- a/SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj
+++ b/SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/SatelliteProvider.IntegrationTests/Program.cs b/SatelliteProvider.IntegrationTests/Program.cs
index fb7156d..baf92c8 100644
--- a/SatelliteProvider.IntegrationTests/Program.cs
+++ b/SatelliteProvider.IntegrationTests/Program.cs
@@ -37,7 +37,7 @@ class Program
// await RouteTests.RunRouteWithRegionProcessingAndStitching(httpClient);
await RouteTests.RunComplexRouteWithStitching(httpClient);
-
+ await RouteTests.RunComplexRouteWithStitchingAndGeofences(httpClient);
// await RouteTests.RunExtendedRouteEast(httpClient);
Console.WriteLine();
diff --git a/SatelliteProvider.IntegrationTests/RouteTests.cs b/SatelliteProvider.IntegrationTests/RouteTests.cs
index 099b1d9..901c173 100644
--- a/SatelliteProvider.IntegrationTests/RouteTests.cs
+++ b/SatelliteProvider.IntegrationTests/RouteTests.cs
@@ -289,6 +289,7 @@ public static class RouteTests
Console.WriteLine("✓ Route with Region Processing and Stitching Test: PASSED");
}
+
public static async Task RunComplexRouteWithStitching(HttpClient httpClient)
{
Console.WriteLine();
@@ -296,6 +297,227 @@ public static class RouteTests
Console.WriteLine("======================================================================================");
Console.WriteLine();
+ var routeId = Guid.NewGuid();
+ var request = new CreateRouteRequest
+ {
+ Id = routeId,
+ Name = "Complex Route with 10 Points + 2 Geofences",
+ Description = "Test route with 10 action points and 2 geofence regions for complex map stitching",
+ RegionSizeMeters = 300.0,
+ ZoomLevel = 18,
+ RequestMaps = true,
+ Points = new List
+ {
+ new() { Lat = 48.276067180586544, Lon = 37.38445758819581 },
+ new() { Lat = 48.27074009522731, Lon = 37.374029159545906 },
+ new() { Lat = 48.263312668696855, Lon = 37.37707614898682 },
+ new() { Lat = 48.26539817051818, Lon = 37.36587524414063 },
+ new() { Lat = 48.25851283439989, Lon = 37.35952377319337 },
+ new() { Lat = 48.254426906081555, Lon = 37.374801635742195 },
+ new() { Lat = 48.25914140977405, Lon = 37.39068031311036 },
+ new() { Lat = 48.25354110233028, Lon = 37.401752471923835 },
+ new() { Lat = 48.25902712391726, Lon = 37.416257858276374 },
+ new() { Lat = 48.26828345053738, Lon = 37.402009963989265 }
+ },
+ };
+
+ Console.WriteLine("Step 1: Creating complex route with RequestMaps=true and 2 geofences");
+ Console.WriteLine($" Action Points: {request.Points.Count}");
+ Console.WriteLine($" NO Geofences Regions");
+ Console.WriteLine($" Region Size: {request.RegionSizeMeters}m");
+ Console.WriteLine($" Zoom Level: {request.ZoomLevel}");
+ Console.WriteLine($" Request Maps: {request.RequestMaps}");
+ Console.WriteLine();
+ Console.WriteLine("Route Path:");
+ for (int i = 0; i < request.Points.Count; i++)
+ {
+ Console.WriteLine($" {i + 1}. ({request.Points[i].Lat}, {request.Points[i].Lon})");
+ }
+ Console.WriteLine();
+
+ var routeResponse = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions);
+
+ if (!routeResponse.IsSuccessStatusCode)
+ {
+ var errorContent = await routeResponse.Content.ReadAsStringAsync();
+ throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}");
+ }
+
+ var route = await routeResponse.Content.ReadFromJsonAsync(JsonOptions);
+
+ if (route == null)
+ {
+ throw new Exception("No route data returned");
+ }
+
+ Console.WriteLine($"✓ Route created with {route.TotalPoints} total points");
+ Console.WriteLine($" Distance: {route.TotalDistanceMeters:F2}m");
+ Console.WriteLine($" Request Maps: {route.RequestMaps}");
+ Console.WriteLine($" Maps Ready: {route.MapsReady}");
+ Console.WriteLine();
+
+ var startPoints = route.Points.Count(p => p.PointType == "start");
+ var endPoints = route.Points.Count(p => p.PointType == "end");
+ var actionPoints = route.Points.Count(p => p.PointType == "action");
+ var intermediatePoints = route.Points.Count(p => p.PointType == "intermediate");
+
+ Console.WriteLine("Point Type Distribution:");
+ Console.WriteLine($" Start: {startPoints}");
+ Console.WriteLine($" Action: {actionPoints}");
+ Console.WriteLine($" Intermediate: {intermediatePoints}");
+ Console.WriteLine($" End: {endPoints}");
+ Console.WriteLine();
+
+ if (startPoints != 1)
+ {
+ throw new Exception($"Expected 1 start point, got {startPoints}");
+ }
+
+ if (endPoints != 1)
+ {
+ throw new Exception($"Expected 1 end point, got {endPoints}");
+ }
+
+ if (actionPoints != 8)
+ {
+ throw new Exception($"Expected 8 action points (excluding start/end), got {actionPoints}");
+ }
+
+ if (!route.RequestMaps)
+ {
+ throw new Exception("Expected RequestMaps to be true");
+ }
+
+ if (route.MapsReady)
+ {
+ throw new Exception("Expected MapsReady to be false initially");
+ }
+
+ Console.WriteLine("Step 2: Waiting for complex route maps to be ready");
+ Console.WriteLine(" (Processing route point regions + 2 geofence regions SEQUENTIALLY to avoid API throttling)");
+ Console.WriteLine(" (This will take several minutes as each region is processed one at a time)");
+ Console.WriteLine();
+
+ RouteResponseModel? finalRoute = null;
+ int maxAttempts = 240;
+ int pollInterval = 3000;
+
+ for (int attempt = 0; attempt < maxAttempts; attempt++)
+ {
+ await Task.Delay(pollInterval);
+
+ var getResponse = await httpClient.GetAsync($"/api/satellite/route/{routeId}");
+
+ if (!getResponse.IsSuccessStatusCode)
+ {
+ throw new Exception($"Failed to get route status: {getResponse.StatusCode}");
+ }
+
+ var currentRoute = await getResponse.Content.ReadFromJsonAsync(JsonOptions);
+
+ if (currentRoute == null)
+ {
+ throw new Exception("No route returned");
+ }
+
+ if (currentRoute.MapsReady)
+ {
+ finalRoute = currentRoute;
+ Console.WriteLine($"✓ Complex route maps ready in approximately {attempt * pollInterval / 1000}s");
+ break;
+ }
+
+ if (attempt % 10 == 0)
+ {
+ Console.WriteLine($" Waiting... (attempt {attempt + 1}/{maxAttempts})");
+ }
+
+ if (attempt == maxAttempts - 1)
+ {
+ throw new Exception($"Timeout: Complex route maps did not become ready in {maxAttempts * pollInterval / 1000}s");
+ }
+ }
+
+ if (finalRoute == null)
+ {
+ throw new Exception("Final route is null");
+ }
+
+ Console.WriteLine();
+
+ Console.WriteLine("Step 3: Verifying generated files");
+
+ if (string.IsNullOrEmpty(finalRoute.CsvFilePath))
+ {
+ throw new Exception("CSV file path is null or empty");
+ }
+
+ if (string.IsNullOrEmpty(finalRoute.SummaryFilePath))
+ {
+ throw new Exception("Summary file path is null or empty");
+ }
+
+ if (string.IsNullOrEmpty(finalRoute.StitchedImagePath))
+ {
+ throw new Exception("Stitched image path is null or empty");
+ }
+
+ if (!File.Exists(finalRoute.CsvFilePath))
+ {
+ throw new Exception($"CSV file not found: {finalRoute.CsvFilePath}");
+ }
+
+ if (!File.Exists(finalRoute.SummaryFilePath))
+ {
+ throw new Exception($"Summary file not found: {finalRoute.SummaryFilePath}");
+ }
+
+ if (!File.Exists(finalRoute.StitchedImagePath))
+ {
+ throw new Exception($"Stitched image not found: {finalRoute.StitchedImagePath}");
+ }
+
+ var csvLines = await File.ReadAllLinesAsync(finalRoute.CsvFilePath);
+ var uniqueTileCount = csvLines.Length - 1;
+
+ var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath);
+
+ Console.WriteLine("Files Generated:");
+ Console.WriteLine($" ✓ CSV: {Path.GetFileName(finalRoute.CsvFilePath)} ({uniqueTileCount} tiles)");
+ Console.WriteLine($" ✓ Summary: {Path.GetFileName(finalRoute.SummaryFilePath)}");
+ Console.WriteLine($" ✓ Stitched Map: {Path.GetFileName(finalRoute.StitchedImagePath)} ({stitchedInfo.Length / 1024:F2} KB)");
+ Console.WriteLine();
+
+ Console.WriteLine("Route Summary:");
+ Console.WriteLine($" Route ID: {finalRoute.Id}");
+ Console.WriteLine($" Total Points: {finalRoute.TotalPoints}");
+ Console.WriteLine($" Action Points: {actionPoints}");
+ Console.WriteLine($" Distance: {finalRoute.TotalDistanceMeters:F2}m");
+ Console.WriteLine($" Geofence Regions: 2");
+ Console.WriteLine($" Unique Tiles: {uniqueTileCount}");
+ Console.WriteLine($" Maps Ready: {finalRoute.MapsReady}");
+ Console.WriteLine();
+
+ if (uniqueTileCount < 10)
+ {
+ throw new Exception($"Expected at least 10 unique tiles for complex route with geofences, got {uniqueTileCount}");
+ }
+
+ if (stitchedInfo.Length < 1024)
+ {
+ throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes");
+ }
+
+ Console.WriteLine("✓ Complex Route with 10 Points + 2 Geofences Test: PASSED");
+ }
+
+ public static async Task RunComplexRouteWithStitchingAndGeofences(HttpClient httpClient)
+ {
+ Console.WriteLine();
+ Console.WriteLine("Test: Complex Route with 10 Points + 2 Geofences - Region Processing and Stitching");
+ Console.WriteLine("======================================================================================");
+ Console.WriteLine();
+
var routeId = Guid.NewGuid();
var request = new CreateRouteRequest
{
@@ -347,7 +569,7 @@ public static class RouteTests
for (int i = 0; i < request.Geofences.Polygons.Count; i++)
{
var polygon = request.Geofences.Polygons[i];
- Console.WriteLine($" {i + 1}. NW: ({polygon.NorthWest.Lat}, {polygon.NorthWest.Lon}) -> SE: ({polygon.SouthEast.Lat}, {polygon.SouthEast.Lon})");
+ Console.WriteLine($" {i + 1}. NW: ({polygon.NorthWest?.Lat}, {polygon.NorthWest?.Lon}) -> SE: ({polygon.SouthEast?.Lat}, {polygon.SouthEast?.Lon})");
}
Console.WriteLine();
Console.WriteLine("Route Path:");
diff --git a/SatelliteProvider.Services/RouteProcessingService.cs b/SatelliteProvider.Services/RouteProcessingService.cs
index 6e24f2a..491bd8b 100644
--- a/SatelliteProvider.Services/RouteProcessingService.cs
+++ b/SatelliteProvider.Services/RouteProcessingService.cs
@@ -326,54 +326,49 @@ public class RouteProcessingService : BackgroundService
string? stitchedImagePath = null;
if (route.RequestMaps)
{
- var geofenceTileBounds = new List<(Guid RegionId, int MinX, int MinY, int MaxX, int MaxY)>();
+ int? minX = null, minY = null, maxX = null, maxY = null;
- foreach (var geofenceId in geofenceRegionIds)
+ if (geofenceRegionIds.Count > 0)
{
- var region = await _regionRepository.GetByIdAsync(geofenceId);
- if (region != null && !string.IsNullOrEmpty(region.CsvFilePath) && File.Exists(region.CsvFilePath))
+ foreach (var geofenceId in geofenceRegionIds)
{
- _logger.LogInformation("Route {RouteId}: Loading geofence region {RegionId} tile bounds",
- routeId, region.Id);
-
- var csvLines = await File.ReadAllLinesAsync(region.CsvFilePath, cancellationToken);
- int? minX = null, minY = null, maxX = null, maxY = null;
-
- foreach (var line in csvLines.Skip(1))
+ var region = await _regionRepository.GetByIdAsync(geofenceId);
+ if (region != null && !string.IsNullOrEmpty(region.CsvFilePath) && File.Exists(region.CsvFilePath))
{
- var parts = line.Split(',');
- if (parts.Length >= 3)
+ var csvLines = await File.ReadAllLinesAsync(region.CsvFilePath, cancellationToken);
+
+ foreach (var line in csvLines.Skip(1))
{
- if (double.TryParse(parts[0], out var lat) && double.TryParse(parts[1], out var lon))
+ var parts = line.Split(',');
+ if (parts.Length >= 3)
{
- var tile = GeoUtils.WorldToTilePos(new Common.DTO.GeoPoint { Lat = lat, Lon = lon }, route.ZoomLevel);
- minX = minX == null ? tile.x : Math.Min(minX.Value, tile.x);
- minY = minY == null ? tile.y : Math.Min(minY.Value, tile.y);
- maxX = maxX == null ? tile.x : Math.Max(maxX.Value, tile.x);
- maxY = maxY == null ? tile.y : Math.Max(maxY.Value, tile.y);
+ if (double.TryParse(parts[0], out var lat) && double.TryParse(parts[1], out var lon))
+ {
+ var tile = GeoUtils.WorldToTilePos(new Common.DTO.GeoPoint { Lat = lat, Lon = lon }, route.ZoomLevel);
+ minX = minX == null ? tile.x : Math.Min(minX.Value, tile.x);
+ minY = minY == null ? tile.y : Math.Min(minY.Value, tile.y);
+ maxX = maxX == null ? tile.x : Math.Max(maxX.Value, tile.x);
+ maxY = maxY == null ? tile.y : Math.Max(maxY.Value, tile.y);
+ }
}
}
}
-
- if (minX.HasValue && minY.HasValue && maxX.HasValue && maxY.HasValue)
- {
- geofenceTileBounds.Add((region.Id, minX.Value, minY.Value, maxX.Value, maxY.Value));
- _logger.LogInformation("Route {RouteId}: Geofence {RegionId} tile bounds: X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
- routeId, region.Id, minX.Value, maxX.Value, minY.Value, maxY.Value);
- }
}
- else
+
+ if (minX.HasValue && minY.HasValue && maxX.HasValue && maxY.HasValue)
{
- _logger.LogWarning("Route {RouteId}: Geofence region {RegionId} CSV not found",
- routeId, geofenceId);
+ _logger.LogInformation("Route {RouteId}: Combined geofence tile bounds: X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
+ routeId, minX.Value, maxX.Value, minY.Value, maxY.Value);
}
}
- _logger.LogInformation("Route {RouteId}: Starting stitching with {GeofenceCount} geofence regions",
- routeId, geofenceTileBounds.Count);
-
stitchedImagePath = Path.Combine(readyDir, $"route_{routeId}_stitched.jpg");
- await StitchRouteTilesAsync(allTiles.Values.ToList(), stitchedImagePath, route.ZoomLevel, geofenceTileBounds, cancellationToken);
+
+ var geofenceBounds = (minX.HasValue && minY.HasValue && maxX.HasValue && maxY.HasValue)
+ ? (minX.Value, minY.Value, maxX.Value, maxY.Value)
+ : ((int, int, int, int)?)null;
+
+ await StitchRouteTilesAsync(allTiles.Values.ToList(), stitchedImagePath, route.ZoomLevel, geofenceBounds, cancellationToken);
}
var summaryPath = Path.Combine(readyDir, $"route_{routeId}_summary.txt");
@@ -460,7 +455,7 @@ public class RouteProcessingService : BackgroundService
List tiles,
string outputPath,
int zoomLevel,
- List<(Guid RegionId, int MinX, int MinY, int MaxX, int MaxY)> geofenceTileBounds,
+ (int MinX, int MinY, int MaxX, int MaxY)? geofenceBounds,
CancellationToken cancellationToken)
{
if (tiles.Count == 0)
@@ -559,46 +554,42 @@ public class RouteProcessingService : BackgroundService
}
}
- if (geofenceTileBounds.Count > 0)
+ if (geofenceBounds.HasValue)
{
- _logger.LogInformation("Drawing {Count} geofence borders on image {Width}x{Height} (grid: minX={MinX}, minY={MinY})",
- geofenceTileBounds.Count, imageWidth, imageHeight, minX, minY);
+ var (geoMinX, geoMinY, geoMaxX, geoMaxY) = geofenceBounds.Value;
- foreach (var (regionId, geoMinX, geoMinY, geoMaxX, geoMaxY) in geofenceTileBounds)
+ _logger.LogInformation("Drawing geofence border on image {Width}x{Height} (grid: minX={MinX}, minY={MinY})",
+ imageWidth, imageHeight, minX, minY);
+ _logger.LogInformation("Geofence tile range - X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
+ geoMinX, geoMaxX, geoMinY, geoMaxY);
+
+ var x1 = (geoMinX - minX) * tileSizePixels;
+ var y1 = (geoMinY - minY) * tileSizePixels;
+ var x2 = (geoMaxX - minX + 1) * tileSizePixels - 1;
+ var y2 = (geoMaxY - minY + 1) * tileSizePixels - 1;
+
+ _logger.LogInformation("Geofence pixel coords before clipping - ({X1},{Y1}) to ({X2},{Y2})",
+ x1, y1, x2, y2);
+
+ x1 = Math.Max(0, Math.Min(x1, imageWidth - 1));
+ y1 = Math.Max(0, Math.Min(y1, imageHeight - 1));
+ x2 = Math.Max(0, Math.Min(x2, imageWidth - 1));
+ y2 = Math.Max(0, Math.Min(y2, imageHeight - 1));
+
+ if (x1 >= 0 && y1 >= 0 && x2 < imageWidth && y2 < imageHeight && x2 > x1 && y2 > y1)
{
- _logger.LogInformation("Geofence {RegionId}: Tile range - X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
- regionId, geoMinX, geoMaxX, geoMinY, geoMaxY);
+ _logger.LogInformation("Drawing geofence border at pixel coords ({X1},{Y1}) to ({X2},{Y2})",
+ x1, y1, x2, y2);
- var x1 = (geoMinX - minX) * tileSizePixels;
- var y1 = (geoMinY - minY) * tileSizePixels;
- var x2 = (geoMaxX - minX + 1) * tileSizePixels - 1;
- var y2 = (geoMaxY - minY + 1) * tileSizePixels - 1;
+ DrawRectangleBorder(stitchedImage, x1, y1, x2, y2, new Rgb24(255, 255, 0));
- _logger.LogInformation("Geofence {RegionId}: Pixel coords before clipping - ({X1},{Y1}) to ({X2},{Y2})",
- regionId, x1, y1, x2, y2);
-
- x1 = Math.Max(0, Math.Min(x1, imageWidth - 1));
- y1 = Math.Max(0, Math.Min(y1, imageHeight - 1));
- x2 = Math.Max(0, Math.Min(x2, imageWidth - 1));
- y2 = Math.Max(0, Math.Min(y2, imageHeight - 1));
-
- if (x1 >= 0 && y1 >= 0 && x2 < imageWidth && y2 < imageHeight && x2 > x1 && y2 > y1)
- {
- _logger.LogInformation("Geofence {RegionId}: Drawing border at pixel coords ({X1},{Y1}) to ({X2},{Y2})",
- regionId, x1, y1, x2, y2);
-
- DrawRectangleBorder(stitchedImage, x1, y1, x2, y2, new Rgb24(255, 255, 0));
-
- _logger.LogInformation("Successfully drew geofence border for region {RegionId}", regionId);
- }
- else
- {
- _logger.LogWarning("Geofence {RegionId}: Border out of bounds or invalid - ({X1},{Y1}) to ({X2},{Y2}), image size: {Width}x{Height}",
- regionId, x1, y1, x2, y2, imageWidth, imageHeight);
- }
+ _logger.LogInformation("Successfully drew geofence border");
+ }
+ else
+ {
+ _logger.LogWarning("Geofence border out of bounds or invalid - ({X1},{Y1}) to ({X2},{Y2}), image size: {Width}x{Height}",
+ x1, y1, x2, y2, imageWidth, imageHeight);
}
-
- _logger.LogInformation("Completed drawing all geofence borders, now saving image...");
}
await stitchedImage.SaveAsJpegAsync(outputPath, cancellationToken);
@@ -684,21 +675,6 @@ public class RouteProcessingService : BackgroundService
return (-1, -1);
}
- private static (double NorthLat, double SouthLat, double WestLon, double EastLon) CalculateGeofenceCorners(
- double centerLat,
- double centerLon,
- double halfSizeMeters)
- {
- var center = new Common.DTO.GeoPoint { Lat = centerLat, Lon = centerLon };
-
- var north = GeoUtils.GoDirection(center, new Common.DTO.Direction { Distance = halfSizeMeters, Azimuth = 0 });
- var south = GeoUtils.GoDirection(center, new Common.DTO.Direction { Distance = halfSizeMeters, Azimuth = 180 });
- var east = GeoUtils.GoDirection(center, new Common.DTO.Direction { Distance = halfSizeMeters, Azimuth = 90 });
- var west = GeoUtils.GoDirection(center, new Common.DTO.Direction { Distance = halfSizeMeters, Azimuth = 270 });
-
- return (north.Lat, south.Lat, west.Lon, east.Lon);
- }
-
private static void DrawRectangleBorder(Image image, int x1, int y1, int x2, int y2, Rgb24 color)
{
const int thickness = 5;
diff --git a/SatelliteProvider.Services/RouteService.cs b/SatelliteProvider.Services/RouteService.cs
index 3dd4cc2..95efe5f 100644
--- a/SatelliteProvider.Services/RouteService.cs
+++ b/SatelliteProvider.Services/RouteService.cs
@@ -192,24 +192,28 @@ public class RouteService : IRouteService
throw new ArgumentException("Geofence northWest latitude must be greater than southEast latitude");
}
- var center = GeoUtils.CalculateCenter(polygon.NorthWest, polygon.SouthEast);
- var diagonalDistance = GeoUtils.CalculatePolygonDiagonalDistance(polygon.NorthWest, polygon.SouthEast);
- var geofenceRegionSize = Math.Max(diagonalDistance * 0.6, request.RegionSizeMeters);
-
- var geofenceRegionId = Guid.NewGuid();
+ var geofenceRegions = CreateGeofenceRegionGrid(polygon.NorthWest, polygon.SouthEast, request.RegionSizeMeters);
- _logger.LogInformation("Route {RouteId}: Requesting geofence region {RegionId} at center ({Lat}, {Lon}) with size {Size}m",
- request.Id, geofenceRegionId, center.Lat, center.Lon, geofenceRegionSize);
+ _logger.LogInformation("Route {RouteId}: Created grid of {Count} regions to cover geofence area",
+ request.Id, geofenceRegions.Count);
- await _regionService.RequestRegionAsync(
- geofenceRegionId,
- center.Lat,
- center.Lon,
- geofenceRegionSize,
- request.ZoomLevel,
- stitchTiles: false);
-
- await _routeRepository.LinkRouteToRegionAsync(request.Id, geofenceRegionId, isGeofence: true);
+ foreach (var geofencePoint in geofenceRegions)
+ {
+ var geofenceRegionId = Guid.NewGuid();
+
+ _logger.LogInformation("Route {RouteId}: Requesting geofence region {RegionId} at ({Lat}, {Lon}) with size {Size}m",
+ request.Id, geofenceRegionId, geofencePoint.Lat, geofencePoint.Lon, request.RegionSizeMeters);
+
+ await _regionService.RequestRegionAsync(
+ geofenceRegionId,
+ geofencePoint.Lat,
+ geofencePoint.Lon,
+ request.RegionSizeMeters,
+ request.ZoomLevel,
+ stitchTiles: false);
+
+ await _routeRepository.LinkRouteToRegionAsync(request.Id, geofenceRegionId, isGeofence: true);
+ }
}
}
@@ -278,5 +282,36 @@ public class RouteService : IRouteService
UpdatedAt = route.UpdatedAt
};
}
+
+ private List CreateGeofenceRegionGrid(GeoPoint northWest, GeoPoint southEast, double regionSizeMeters)
+ {
+ var regions = new List();
+
+ var northPoint = new GeoPoint(northWest.Lat, (northWest.Lon + southEast.Lon) / 2);
+ var southPoint = new GeoPoint(southEast.Lat, (northWest.Lon + southEast.Lon) / 2);
+ var heightMeters = GeoUtils.CalculateDistance(northPoint, southPoint);
+
+ var westPoint = new GeoPoint((northWest.Lat + southEast.Lat) / 2, northWest.Lon);
+ var eastPoint = new GeoPoint((northWest.Lat + southEast.Lat) / 2, southEast.Lon);
+ var widthMeters = GeoUtils.CalculateDistance(westPoint, eastPoint);
+
+ var numLatSteps = Math.Max(1, (int)Math.Ceiling(heightMeters / regionSizeMeters));
+ var numLonSteps = Math.Max(1, (int)Math.Ceiling(widthMeters / regionSizeMeters));
+
+ var latStep = (northWest.Lat - southEast.Lat) / numLatSteps;
+ var lonStep = (southEast.Lon - northWest.Lon) / numLonSteps;
+
+ for (int latIdx = 0; latIdx < numLatSteps; latIdx++)
+ {
+ for (int lonIdx = 0; lonIdx < numLonSteps; lonIdx++)
+ {
+ var lat = northWest.Lat - (latIdx + 0.5) * latStep;
+ var lon = northWest.Lon + (lonIdx + 0.5) * lonStep;
+ regions.Add(new GeoPoint(lat, lon));
+ }
+ }
+
+ return regions;
+ }
}
diff --git a/SatelliteProvider.Tests/GoogleMapsDownloaderTests.cs b/SatelliteProvider.Tests/GoogleMapsDownloaderTests.cs
index 3b7bd20..b46fd00 100644
--- a/SatelliteProvider.Tests/GoogleMapsDownloaderTests.cs
+++ b/SatelliteProvider.Tests/GoogleMapsDownloaderTests.cs
@@ -13,7 +13,7 @@ namespace SatelliteProvider.Tests;
public class DummyTests
{
[Fact]
- public async Task Dummy_ShouldWork()
+ public void Dummy_ShouldWork()
{
Assert.Equal(1, 1);
}