XAML-Map-Control/MapControl/Shared/AzimuthalProjection.cs

102 lines
3.3 KiB
C#
Raw Normal View History

2026-01-19 21:38:18 +01:00
using System;
namespace MapControl
{
public abstract class AzimuthalProjection : MapProjection
{
protected AzimuthalProjection()
2026-01-19 21:38:18 +01:00
{
EnableCenterUpdates();
2026-01-19 21:38:18 +01:00
}
public readonly struct ProjectedPoint
2026-01-19 21:38:18 +01:00
{
public double X { get; }
public double Y { get; }
public double CosC { get; }
public ProjectedPoint(double centerLatitude, double centerLongitude, double latitude, double longitude)
{
var phi = latitude * Math.PI / 180d;
var phi1 = centerLatitude * Math.PI / 180d;
var dLambda = (longitude - centerLongitude) * Math.PI / 180d; // λ - λ0
var cosPhi = Math.Cos(phi);
var sinPhi = Math.Sin(phi);
var cosPhi1 = Math.Cos(phi1);
var sinPhi1 = Math.Sin(phi1);
var cosLambda = Math.Cos(dLambda);
var sinLambda = Math.Sin(dLambda);
2026-01-21 14:15:26 +01:00
var cosC = sinPhi1 * sinPhi + cosPhi1 * cosPhi * cosLambda; // (5-3)
2026-01-19 21:38:18 +01:00
X = cosPhi * sinLambda;
Y = cosPhi1 * sinPhi - sinPhi1 * cosPhi * cosLambda;
2026-01-21 14:15:26 +01:00
CosC = Math.Min(Math.Max(cosC, -1d), 1d); // protect against rounding errors
}
public (double, double) RelativeScale(double radialScale, double perpendicularScale)
2026-01-21 14:15:26 +01:00
{
var scaleX = radialScale;
var scaleY = perpendicularScale;
2026-01-21 14:15:26 +01:00
if (scaleX != scaleY)
2026-01-21 14:15:26 +01:00
{
var s = Math.Sqrt(X * X + Y * Y);
// sin and cos of azimuth from projection center, i.e. Atan2(Y/X)
var cos = X / s;
var sin = Y / s;
var x1 = scaleX * cos;
var y1 = scaleY * sin;
var x2 = scaleX * sin;
var y2 = scaleY * cos;
scaleX = Math.Sqrt(x1 * x1 + y1 * y1);
scaleY = Math.Sqrt(x2 * x2 + y2 * y2);
2026-01-21 14:15:26 +01:00
}
return (scaleX, scaleY);
}
}
protected ProjectedPoint GetProjectedPoint(double latitude, double longitude)
{
return new ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude);
2026-01-19 21:38:18 +01:00
}
protected Location GetLocation(double x, double y, double rho, double sinC)
{
2026-01-21 12:08:12 +01:00
var cos2C = 1d - sinC * sinC;
if (cos2C < 0d)
{
return null;
}
var cosC = Math.Sqrt(cos2C);
2026-01-19 21:38:18 +01:00
var phi1 = Center.Latitude * Math.PI / 180d;
var cosPhi1 = Math.Cos(phi1);
var sinPhi1 = Math.Sin(phi1);
var phi = Math.Asin(cosC * sinPhi1 + y * sinC * cosPhi1 / rho); // (20-14)
2026-01-21 12:08:12 +01:00
double u, v;
if (Center.Latitude == 90d) // (20-16)
{
u = x;
v = -y;
}
else if (Center.Latitude == -90d) // (20-17)
{
u = x;
v = y;
}
else // (20-15)
{
u = x * sinC;
v = rho * cosPhi1 * cosC - y * sinPhi1 * sinC;
}
2026-01-19 21:38:18 +01:00
2026-01-21 12:08:12 +01:00
return new Location(
phi * 180d / Math.PI,
Math.Atan2(u, v) * 180d / Math.PI + Center.Longitude);
2026-01-19 21:38:18 +01:00
}
}
}