mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 10:51:15 +00:00
9a53bff92e
Batch 24 of 03-code-quality-refactoring run; closes the run. AZ-375 (C22): GoogleMapsDownloaderV2.DownloadTilesGridAsync now builds a HashSet<(int X, int Y, int Z)> once from existingTiles and tests Contains((x, y, zoomLevel)) per cell. Removes the per-cell FirstOrDefault tolerance scan and the unused _processingConfig .LatLonTolerance reference at this site. AZ-377 (C24): promote Earth + tile-pixel constants to a single home. GeoUtils now exposes EarthRadiusMeters, EarthEquatorial CircumferenceMeters, MetersPerDegreeLatitude as public const. MapConfig adds DefaultTileSizePixels (const) wired as the TileSizePixels property default. TileRepository and Google MapsDownloaderV2 read those constants instead of duplicating the literals 6378137, 40075016.686, 111000.0, and 256. Tests: +6 new (DownloaderRefactorTests, extended GeoUtils RefactorTests). 200/200 unit tests pass. Cumulative K=3 review (batches 22-24): PASS_WITH_WARNINGS, 4 Low findings only — see _docs/03_implementation/reviews/cumulative_review_22-24.md. Tooling fix: scripts/run-tests.sh --unit-only path now restores before testing (was failing on SixLabors resolution in clean container). Stripped stray BOM from MapConfig.cs to satisfy the .editorconfig charset gate. Updates _dependencies_table.md to reflect all 27 03-code-quality- refactoring tasks done; updates _autodev_state.md to refactor phase 5 (test-sync). Co-authored-by: Cursor <cursoragent@cursor.com>
146 lines
5.8 KiB
C#
146 lines
5.8 KiB
C#
using System.Diagnostics;
|
|
using Dapper;
|
|
using Microsoft.Extensions.Logging;
|
|
using Npgsql;
|
|
using SatelliteProvider.Common.Configs;
|
|
using SatelliteProvider.Common.Utils;
|
|
using SatelliteProvider.DataAccess.Models;
|
|
|
|
namespace SatelliteProvider.DataAccess.Repositories;
|
|
|
|
public class TileRepository : ITileRepository
|
|
{
|
|
private const int SlowQueryThresholdMs = 500;
|
|
|
|
private const string ColumnList = @"id, tile_zoom as TileZoom, tile_x as TileX, tile_y as TileY,
|
|
latitude, longitude,
|
|
tile_size_meters as TileSizeMeters, tile_size_pixels as TileSizePixels,
|
|
image_type as ImageType, maps_version as MapsVersion, version,
|
|
file_path as FilePath, created_at as CreatedAt, updated_at as UpdatedAt";
|
|
|
|
private readonly string _connectionString;
|
|
private readonly ILogger<TileRepository> _logger;
|
|
|
|
public TileRepository(string connectionString, ILogger<TileRepository> logger)
|
|
{
|
|
_connectionString = connectionString;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<TileEntity?> GetByIdAsync(Guid id)
|
|
{
|
|
using var connection = new NpgsqlConnection(_connectionString);
|
|
const string sql = $@"
|
|
SELECT {ColumnList}
|
|
FROM tiles
|
|
WHERE id = @Id";
|
|
|
|
return await connection.QuerySingleOrDefaultAsync<TileEntity>(sql, new { Id = id });
|
|
}
|
|
|
|
public async Task<TileEntity?> GetByTileCoordinatesAsync(int tileZoom, int tileX, int tileY)
|
|
{
|
|
using var connection = new NpgsqlConnection(_connectionString);
|
|
const string sql = $@"
|
|
SELECT {ColumnList}
|
|
FROM tiles
|
|
WHERE tile_zoom = @TileZoom AND tile_x = @TileX AND tile_y = @TileY
|
|
ORDER BY updated_at DESC
|
|
LIMIT 1";
|
|
|
|
return await connection.QuerySingleOrDefaultAsync<TileEntity>(sql, new { TileZoom = tileZoom, TileX = tileX, TileY = tileY });
|
|
}
|
|
|
|
public async Task<IEnumerable<TileEntity>> GetTilesByRegionAsync(double latitude, double longitude, double sizeMeters, int zoomLevel)
|
|
{
|
|
using var connection = new NpgsqlConnection(_connectionString);
|
|
|
|
var latRad = latitude * Math.PI / 180.0;
|
|
var metersPerPixel = (GeoUtils.EarthEquatorialCircumferenceMeters * Math.Cos(latRad)) / (Math.Pow(2, zoomLevel) * MapConfig.DefaultTileSizePixels);
|
|
var tileSizeMeters = metersPerPixel * MapConfig.DefaultTileSizePixels;
|
|
|
|
var expandedSizeMeters = sizeMeters + (tileSizeMeters * 2);
|
|
|
|
var latRange = expandedSizeMeters / GeoUtils.MetersPerDegreeLatitude;
|
|
var lonRange = expandedSizeMeters / (GeoUtils.MetersPerDegreeLatitude * Math.Cos(latitude * Math.PI / 180.0));
|
|
|
|
const string sql = $@"
|
|
SELECT {ColumnList}
|
|
FROM tiles
|
|
WHERE latitude BETWEEN @MinLat AND @MaxLat
|
|
AND longitude BETWEEN @MinLon AND @MaxLon
|
|
AND tile_zoom = @TileZoom
|
|
ORDER BY latitude DESC, longitude ASC, updated_at DESC";
|
|
|
|
var stopwatch = Stopwatch.StartNew();
|
|
var tiles = await connection.QueryAsync<TileEntity>(sql, new
|
|
{
|
|
MinLat = latitude - latRange / 2,
|
|
MaxLat = latitude + latRange / 2,
|
|
MinLon = longitude - lonRange / 2,
|
|
MaxLon = longitude + lonRange / 2,
|
|
TileZoom = zoomLevel
|
|
});
|
|
stopwatch.Stop();
|
|
|
|
if (stopwatch.ElapsedMilliseconds > SlowQueryThresholdMs)
|
|
{
|
|
_logger.LogWarning(
|
|
"Slow GetTilesByRegionAsync: {ElapsedMs} ms (threshold {ThresholdMs} ms) for lat={Latitude}, lon={Longitude}, sizeMeters={SizeMeters}, zoom={ZoomLevel}",
|
|
stopwatch.ElapsedMilliseconds, SlowQueryThresholdMs, latitude, longitude, sizeMeters, zoomLevel);
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
|
|
public async Task<Guid> InsertAsync(TileEntity tile)
|
|
{
|
|
using var connection = new NpgsqlConnection(_connectionString);
|
|
const string sql = @"
|
|
INSERT INTO tiles (id, tile_zoom, tile_x, tile_y, latitude, longitude, tile_size_meters,
|
|
tile_size_pixels, image_type, maps_version, version, file_path,
|
|
created_at, updated_at)
|
|
VALUES (@Id, @TileZoom, @TileX, @TileY, @Latitude, @Longitude, @TileSizeMeters,
|
|
@TileSizePixels, @ImageType, @MapsVersion, @Version, @FilePath,
|
|
@CreatedAt, @UpdatedAt)
|
|
ON CONFLICT (latitude, longitude, tile_zoom, tile_size_meters)
|
|
DO UPDATE SET
|
|
file_path = EXCLUDED.file_path,
|
|
tile_x = EXCLUDED.tile_x,
|
|
tile_y = EXCLUDED.tile_y,
|
|
updated_at = EXCLUDED.updated_at
|
|
RETURNING id";
|
|
|
|
return await connection.ExecuteScalarAsync<Guid>(sql, tile);
|
|
}
|
|
|
|
public async Task<int> UpdateAsync(TileEntity tile)
|
|
{
|
|
using var connection = new NpgsqlConnection(_connectionString);
|
|
const string sql = @"
|
|
UPDATE tiles
|
|
SET tile_zoom = @TileZoom,
|
|
tile_x = @TileX,
|
|
tile_y = @TileY,
|
|
latitude = @Latitude,
|
|
longitude = @Longitude,
|
|
tile_size_meters = @TileSizeMeters,
|
|
tile_size_pixels = @TileSizePixels,
|
|
image_type = @ImageType,
|
|
maps_version = @MapsVersion,
|
|
version = @Version,
|
|
file_path = @FilePath,
|
|
updated_at = @UpdatedAt
|
|
WHERE id = @Id";
|
|
|
|
return await connection.ExecuteAsync(sql, tile);
|
|
}
|
|
|
|
public async Task<int> DeleteAsync(Guid id)
|
|
{
|
|
using var connection = new NpgsqlConnection(_connectionString);
|
|
const string sql = "DELETE FROM tiles WHERE id = @Id";
|
|
return await connection.ExecuteAsync(sql, new { Id = id });
|
|
}
|
|
}
|