mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-04-22 07:06:39 +00:00
make structure
add tests
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace SatelliteProvider.Common.Configs;
|
||||
|
||||
public class MapConfig
|
||||
{
|
||||
public string Service { get; set; } = null!;
|
||||
public string ApiKey { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace SatelliteProvider.Common.DTO;
|
||||
|
||||
public class Direction
|
||||
{
|
||||
public double Distance { get; set; }
|
||||
public double Azimuth { get; set; }
|
||||
|
||||
public Direction() { }
|
||||
|
||||
public Direction(double distance, double azimuth)
|
||||
{
|
||||
Distance = distance;
|
||||
Azimuth = azimuth;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Distance:F2}, {Azimuth:F1} deg";
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace SatelliteProvider.Common.DTO;
|
||||
|
||||
public class GeoPoint
|
||||
{
|
||||
const double PRECISION_TOLERANCE = 0.00005;
|
||||
public double Lat { get; }
|
||||
public double Lon { get; }
|
||||
|
||||
public GeoPoint() { }
|
||||
|
||||
public GeoPoint(double lat, double lon)
|
||||
{
|
||||
Lat = lat;
|
||||
Lon = lon;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Lat:F4}, {Lon:F4}";
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not GeoPoint point) return false;
|
||||
return ReferenceEquals(this, obj) || Equals(point);
|
||||
}
|
||||
|
||||
private bool Equals(GeoPoint point) =>
|
||||
Math.Abs(Lat - point.Lat) < PRECISION_TOLERANCE && Math.Abs(Lon - point.Lon) < PRECISION_TOLERANCE;
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(Lat, Lon);
|
||||
|
||||
public static bool operator ==(GeoPoint left, GeoPoint right) => Equals(left, right);
|
||||
public static bool operator !=(GeoPoint left, GeoPoint right) => !Equals(left, right);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using SatelliteProvider.Common.Utils;
|
||||
|
||||
namespace SatelliteProvider.Common.DTO;
|
||||
|
||||
public class SatTile
|
||||
{
|
||||
public int X { get; }
|
||||
public int Y { get; }
|
||||
public int Zoom { get; }
|
||||
public GeoPoint LeftTop { get; }
|
||||
public GeoPoint BottomRight { get; }
|
||||
public string Url { get; set; }
|
||||
|
||||
|
||||
public SatTile(int x, int y, int zoom, string url)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Zoom = zoom;
|
||||
Url = url;
|
||||
|
||||
LeftTop = GeoUtils.TileToWorldPos(x, y, zoom);
|
||||
BottomRight = GeoUtils.TileToWorldPos(x + 1, y + 1, zoom);
|
||||
}
|
||||
|
||||
public string FileName => $"{X}.{Y}.{Zoom}.jpg";
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Tile[X={X}, Y={Y}, TL=({LeftTop.Lat:F6}, {LeftTop.Lon:F6}), BR=({BottomRight.Lat:F6}, {BottomRight.Lon:F6})]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using SatelliteProvider.Common.DTO;
|
||||
|
||||
namespace SatelliteProvider.Common.Interfaces;
|
||||
|
||||
public interface ISatelliteDownloader
|
||||
{
|
||||
Task GetTiles(GeoPoint geoPoint, double radiusM, int zoomLevel, CancellationToken token = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,86 @@
|
||||
using SatelliteProvider.Common.DTO;
|
||||
|
||||
namespace SatelliteProvider.Common.Utils;
|
||||
|
||||
public static class GeoUtils
|
||||
{
|
||||
private const double EARTH_RADIUS = 6378137;
|
||||
|
||||
public static (int x, int y) WorldToTilePos(GeoPoint point, int zoom)
|
||||
{
|
||||
var latRad = point.Lat * Math.PI / 180.0;
|
||||
var n = Math.Pow(2.0, zoom);
|
||||
var xTile = (int)Math.Floor((point.Lon + 180.0) / 360.0 * n);
|
||||
var yTile = (int)Math.Floor((1.0 - Math.Log(Math.Tan(latRad) + 1.0 / Math.Cos(latRad)) / Math.PI) / 2.0 * n);
|
||||
return (xTile, yTile);
|
||||
}
|
||||
|
||||
public static double ToRadians(double degrees) => degrees * Math.PI / 180.0;
|
||||
public static double ToDegrees(double radians) => radians * 180.0 / Math.PI;
|
||||
|
||||
public static Direction DirectionTo(this GeoPoint p1, GeoPoint p2)
|
||||
{
|
||||
var lat1Rad = ToRadians(p1.Lat);
|
||||
var lat2Rad = ToRadians(p2.Lat);
|
||||
var dLon = ToRadians(p2.Lon - p1.Lon);
|
||||
var dLat = ToRadians(p2.Lat - p1.Lat);
|
||||
|
||||
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
|
||||
Math.Cos(lat1Rad) * Math.Cos(lat2Rad) *
|
||||
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
|
||||
var c = 2 * Math.Asin(Math.Sqrt(a));
|
||||
var distance = EARTH_RADIUS * c;
|
||||
|
||||
var y = Math.Sin(dLon) * Math.Cos(lat2Rad);
|
||||
var x = Math.Cos(lat1Rad) * Math.Sin(lat2Rad) -
|
||||
Math.Sin(lat1Rad) * Math.Cos(lat2Rad) * Math.Cos(dLon);
|
||||
var azimuthRadians = Math.Atan2(y, x);
|
||||
var azimuth = (ToDegrees(azimuthRadians) + 360) % 360;
|
||||
|
||||
return new Direction
|
||||
{
|
||||
Distance = distance,
|
||||
Azimuth = azimuth
|
||||
};
|
||||
}
|
||||
|
||||
public static GeoPoint GoDirection(this GeoPoint startPoint, Direction direction)
|
||||
{
|
||||
var angularDistance = direction.Distance / EARTH_RADIUS;
|
||||
var azimuthRadians = ToRadians(direction.Azimuth);
|
||||
var startLatRad = ToRadians(startPoint.Lat);
|
||||
var startLonRad = ToRadians(startPoint.Lon);
|
||||
|
||||
var destLatRad = Math.Asin(Math.Sin(startLatRad) * Math.Cos(angularDistance) +
|
||||
Math.Cos(startLatRad) * Math.Sin(angularDistance) * Math.Cos(azimuthRadians));
|
||||
|
||||
var destLonRad = startLonRad + Math.Atan2(Math.Sin(azimuthRadians) * Math.Sin(angularDistance) * Math.Cos(startLatRad),
|
||||
Math.Cos(angularDistance) - Math.Sin(startLatRad) * Math.Sin(destLatRad));
|
||||
|
||||
return new GeoPoint(ToDegrees(destLatRad), ToDegrees(destLonRad));
|
||||
}
|
||||
|
||||
public static GeoPoint TileToWorldPos(int x, int y, int zoom)
|
||||
{
|
||||
var n = Math.Pow(2.0, zoom);
|
||||
var lonDeg = x / n * 360.0 - 180.0;
|
||||
var latRad = Math.Atan(Math.Sinh(Math.PI * (1.0 - 2.0 * y / n)));
|
||||
var latDeg = latRad * 180.0 / Math.PI;
|
||||
return new GeoPoint(latDeg, lonDeg);
|
||||
}
|
||||
|
||||
public static (double minLat, double maxLat, double minLon, double maxLon) GetBoundingBox(GeoPoint centerGeoPoint, double radiusM)
|
||||
{
|
||||
var latRad = centerGeoPoint.Lat * Math.PI / 180.0;
|
||||
|
||||
var latDiff = (radiusM / EARTH_RADIUS) * (180.0 / Math.PI);
|
||||
var minLat = Math.Max(centerGeoPoint.Lat - latDiff, -90.0);
|
||||
var maxLat = Math.Min(centerGeoPoint.Lat + latDiff, 90.0);
|
||||
|
||||
var lonDiff = (radiusM / (EARTH_RADIUS * Math.Cos(latRad))) * (180.0 / Math.PI);
|
||||
var minLon = Math.Max(centerGeoPoint.Lon - lonDiff, -180.0);
|
||||
var maxLon = Math.Min(centerGeoPoint.Lon + lonDiff, 180.0);
|
||||
|
||||
return (minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user