better geo fences and points on the map

This commit is contained in:
Anton Martynenko
2025-11-19 17:59:52 +01:00
parent a148df1697
commit eff7ca4dba
5 changed files with 135 additions and 53 deletions
@@ -196,7 +196,7 @@ public class RouteProcessingService : BackgroundService
var allRegionIdsForStitching = routeRegionIds.Concat(completedGeofenceRegions.Select(r => r.Id)).Distinct();
var geofenceRegionIdsForBorders = completedGeofenceRegions.Select(r => r.Id).ToList();
await GenerateRouteMapsAsync(routeId, route, allRegionIdsForStitching, geofenceRegionIdsForBorders, cancellationToken);
await GenerateRouteMapsAsync(routeId, route, allRegionIdsForStitching, geofenceRegionIdsForBorders, routePointsList, cancellationToken);
return;
}
@@ -246,6 +246,7 @@ public class RouteProcessingService : BackgroundService
DataAccess.Models.RouteEntity route,
IEnumerable<Guid> regionIds,
List<Guid> geofenceRegionIds,
List<DataAccess.Models.RoutePointEntity> routePoints,
CancellationToken cancellationToken)
{
try
@@ -326,11 +327,15 @@ public class RouteProcessingService : BackgroundService
string? stitchedImagePath = null;
if (route.RequestMaps)
{
int? minX = null, minY = null, maxX = null, maxY = null;
var geofencePolygonBounds = new List<(int MinX, int MinY, int MaxX, int MaxY)>();
if (geofenceRegionIds.Count > 0)
var geofencesByPolygon = await _routeRepository.GetGeofenceRegionsByPolygonAsync(routeId);
foreach (var (polygonIndex, polygonRegionIds) in geofencesByPolygon.OrderBy(kvp => kvp.Key))
{
foreach (var geofenceId in geofenceRegionIds)
int? minX = null, minY = null, maxX = null, maxY = null;
foreach (var geofenceId in polygonRegionIds)
{
var region = await _regionRepository.GetByIdAsync(geofenceId);
if (region != null && !string.IsNullOrEmpty(region.CsvFilePath) && File.Exists(region.CsvFilePath))
@@ -357,18 +362,15 @@ public class RouteProcessingService : BackgroundService
if (minX.HasValue && minY.HasValue && maxX.HasValue && maxY.HasValue)
{
_logger.LogInformation("Route {RouteId}: Combined geofence tile bounds: X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
routeId, minX.Value, maxX.Value, minY.Value, maxY.Value);
geofencePolygonBounds.Add((minX.Value, minY.Value, maxX.Value, maxY.Value));
_logger.LogInformation("Route {RouteId}: Polygon {PolygonIndex} tile bounds: X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
routeId, polygonIndex, minX.Value, maxX.Value, minY.Value, maxY.Value);
}
}
stitchedImagePath = Path.Combine(readyDir, $"route_{routeId}_stitched.jpg");
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);
await StitchRouteTilesAsync(allTiles.Values.ToList(), stitchedImagePath, route.ZoomLevel, geofencePolygonBounds, routePoints, cancellationToken);
}
var summaryPath = Path.Combine(readyDir, $"route_{routeId}_summary.txt");
@@ -455,7 +457,8 @@ public class RouteProcessingService : BackgroundService
List<TileInfo> tiles,
string outputPath,
int zoomLevel,
(int MinX, int MinY, int MaxX, int MaxY)? geofenceBounds,
List<(int MinX, int MinY, int MaxX, int MaxY)> geofencePolygonBounds,
List<DataAccess.Models.RoutePointEntity> routePoints,
CancellationToken cancellationToken)
{
if (tiles.Count == 0)
@@ -554,44 +557,66 @@ public class RouteProcessingService : BackgroundService
}
}
if (geofenceBounds.HasValue)
if (geofencePolygonBounds.Count > 0)
{
var (geoMinX, geoMinY, geoMaxX, geoMaxY) = geofenceBounds.Value;
_logger.LogInformation("Drawing {Count} geofence polygon borders on image {Width}x{Height} (grid: minX={MinX}, minY={MinY})",
geofencePolygonBounds.Count, imageWidth, imageHeight, minX, minY);
_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)
for (int i = 0; i < geofencePolygonBounds.Count; i++)
{
_logger.LogInformation("Drawing geofence border at pixel coords ({X1},{Y1}) to ({X2},{Y2})",
x1, y1, x2, y2);
var (geoMinX, geoMinY, geoMaxX, geoMaxY) = geofencePolygonBounds[i];
DrawRectangleBorder(stitchedImage, x1, y1, x2, y2, new Rgb24(255, 255, 0));
_logger.LogInformation("Polygon {Index}: Tile range - X=[{MinX}..{MaxX}], Y=[{MinY}..{MaxY}]",
i, geoMinX, geoMaxX, geoMinY, geoMaxY);
_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);
var x1 = (geoMinX - minX) * tileSizePixels;
var y1 = (geoMinY - minY + 1) * tileSizePixels;
var x2 = (geoMaxX - minX + 2) * tileSizePixels - 1;
var y2 = (geoMaxY - minY + 1) * tileSizePixels - 1;
_logger.LogInformation("Polygon {Index}: Pixel coords before clipping - ({X1},{Y1}) to ({X2},{Y2})",
i, 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("Polygon {Index}: Drawing border at pixel coords ({X1},{Y1}) to ({X2},{Y2})",
i, x1, y1, x2, y2);
DrawRectangleBorder(stitchedImage, x1, y1, x2, y2, new Rgb24(255, 255, 0));
_logger.LogInformation("Polygon {Index}: Successfully drew geofence border", i);
}
else
{
_logger.LogWarning("Polygon {Index}: Border out of bounds or invalid - ({X1},{Y1}) to ({X2},{Y2}), image size: {Width}x{Height}",
i, x1, y1, x2, y2, imageWidth, imageHeight);
}
}
}
_logger.LogInformation("Drawing {Count} route point crosses on map", routePoints.Count);
foreach (var point in routePoints)
{
var geoPoint = new Common.DTO.GeoPoint { Lat = point.Latitude, Lon = point.Longitude };
var (tileX, tileY) = GeoUtils.WorldToTilePos(geoPoint, zoomLevel);
var pixelX = (tileX - minX) * tileSizePixels + tileSizePixels / 2;
var pixelY = (tileY - minY) * tileSizePixels + tileSizePixels / 2;
if (pixelX >= 0 && pixelX < imageWidth && pixelY >= 0 && pixelY < imageHeight)
{
DrawCross(stitchedImage, pixelX, pixelY, new Rgb24(255, 0, 0), 50);
}
}
_logger.LogInformation("Finished drawing route point crosses");
await stitchedImage.SaveAsJpegAsync(outputPath, cancellationToken);
var totalPossibleTiles = gridWidth * gridHeight;
@@ -702,6 +727,34 @@ public class RouteProcessingService : BackgroundService
}
}
}
private static void DrawCross(Image<Rgb24> image, int centerX, int centerY, Rgb24 color, int armLength)
{
const int thickness = 10;
int halfThickness = thickness / 2;
for (int dx = -armLength; dx <= armLength; dx++)
{
for (int t = -halfThickness; t <= halfThickness; t++)
{
int x = centerX + dx;
int y = centerY + t;
if (x >= 0 && x < image.Width && y >= 0 && y < image.Height)
image[x, y] = color;
}
}
for (int dy = -armLength; dy <= armLength; dy++)
{
for (int t = -halfThickness; t <= halfThickness; t++)
{
int x = centerX + t;
int y = centerY + dy;
if (x >= 0 && x < image.Width && y >= 0 && y < image.Height)
image[x, y] = color;
}
}
}
}
public class TileInfo