using SkiaSharp; namespace EarthQuake.Core; /// /// 緯度経度と画面座標の変換 /// public static class GeomTransform { public const int Zoom = 50; private static readonly SKPoint Offset = new(135, (float)TranslateFromLat(35)); public const int Height = 150; private const double MercatorLimit = 85.05112877980659; /// /// 緯度経度の点を計算された位置に変換します。 /// /// 点(X:経度, Y:緯度) /// 画面上の座標 public static SKPoint Translate(SKPoint point) => Translate(point.X, point.Y); public static SKPoint Translate(float lon, float lat) { var x = (lon - Offset.X) * Zoom; var y = -((float)TranslateFromLat(lat) - Offset.Y) * Zoom; return new SKPoint(x, y); } /// /// 経度緯度から計算された位置に変換します。 /// /// 経度 /// 緯度 /// 画面上の座標 public static SKPoint Translate(double lon, double lat) { var x = (float)(lon - Offset.X) * Zoom; var y = -(float)(TranslateFromLat(lat) - Offset.Y) * Zoom; return new SKPoint(x, y); } /// /// 画面上の座標からオフセットを戻します。( /// /// 画面上X /// 画面上Y /// オフセットのない画面座標 public static SKPoint TranslateToNonTransform(float x, float y) => new(x / Zoom + Offset.X, Offset.Y - y / Zoom); /// /// 緯度から計算された位置に変換します。 /// /// 緯度 /// 画面上Y private static double TranslateFromLat(double latitude) => Mercator(latitude); /// /// メルカトル図法 /// /// 緯度 /// 画面上Y public static double Mercator(double latitude) => latitude <= -MercatorLimit ? -Height : latitude >= MercatorLimit ? Height : Math.Log(Math.Tan((90 + latitude) * Math.PI / 360)) * Height / Math.PI; /// /// ミラー図法 /// /// 緯度 /// 画面上Y public static double Mirror(double latitude) => 1.25 * Math.Asinh(Math.Tan(0.8 * latitude * Math.PI / 360)) * Height; public static int RealIndex(int value) { return value >= 0 ? value : -value - 1; } }