namespace SatelliteProvider.Common.Configs; public class StorageConfig { public string TilesDirectory { get; set; } = "/tiles"; public string ReadyDirectory { get; set; } = "/ready"; public string GetTileSubdirectoryPath(int zoomLevel, int tileX, int tileY) { var xBucket = tileX / 1000; var yBucket = tileY / 1000; return Path.Combine(TilesDirectory, zoomLevel.ToString(), xBucket.ToString(), yBucket.ToString()); } public string GetTileFilePath(int zoomLevel, int tileX, int tileY, string timestamp) { var subdirectory = GetTileSubdirectoryPath(zoomLevel, tileX, tileY); var fileName = $"tile_{zoomLevel}_{tileX}_{tileY}_{timestamp}.jpg"; return Path.Combine(subdirectory, fileName); } // Inverse of GetTileFilePath: parses tile_{zoom}_{x}_{y}_{ts}.jpg. // Co-located with the writer per AZ-366 / C13 so format changes can never // drift between the two ends. Pure: no I/O, no logging, no exceptions for // malformed input — caller decides how to react to a `false` return. public static bool TryExtractTileCoordinates(string filePath, out int tileX, out int tileY) { tileX = -1; tileY = -1; ArgumentNullException.ThrowIfNull(filePath); var filename = Path.GetFileNameWithoutExtension(filePath); var parts = filename.Split('_'); if (parts.Length >= 4 && parts[0] == "tile" && int.TryParse(parts[2], out var x) && int.TryParse(parts[3], out var y)) { tileX = x; tileY = y; return true; } return false; } }