You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C#中使用GMap.net绘制以两点经纬度为焦点的椭圆的技术咨询

在C#中用GMap.NET绘制以两个经纬度为焦点的椭圆

刚好之前折腾过类似的需求,我来给你一步步拆解怎么实现——不管是用GMap.NET自带的工具,还是自己用公式手动算点,都给你讲清楚。

首先得明确一个关键前提:仅给定两个焦点的话,椭圆是不唯一的,因为椭圆的定义是“到两个焦点的距离之和为定值(也就是长轴长度2a)”,所以你得自己指定这个2a的值,而且必须保证2a大于两个焦点之间的距离,不然要么变成线段,要么根本不存在椭圆。


一、用GMap.NET实现的路子

GMap.NET本身没有直接画椭圆的控件,但我们可以用GMapPolygon来模拟——本质就是生成足够多的椭圆顶点,把这些点连起来就成了椭圆(点越多越平滑)。

具体步骤

  1. 先把基础参数定下来
    先把两个焦点的经纬度转成GMap.NET的PointLatLng类型,再设定好长轴的一半a(记得a要大于两焦点距离的一半):

    // 示例:两个焦点的经纬度
    var focus1 = new PointLatLng(39.9042, 116.4074); // 北京
    var focus2 = new PointLatLng(31.2304, 121.4737); // 上海
    // 设定长半轴a,这里我设成焦点距离的1.5倍,你可以根据需求改
    var projection = gMapControl1.MapProvider.Projection;
    double distanceBetweenFoci = projection.GetDistance(focus1, focus2);
    double a = distanceBetweenFoci * 1.5;
    
  2. 计算椭圆的核心参数
    有了焦点和a,就能算出其他关键值:

    double c = distanceBetweenFoci / 2; // 焦点到中心的距离
    double b = Math.Sqrt(Math.Pow(a, 2) - Math.Pow(c, 2)); // 短半轴
    // 椭圆中心(两个焦点的中点)
    var center = new PointLatLng(
        (focus1.Lat + focus2.Lat) / 2,
        (focus1.Lng + focus2.Lng) / 2
    );
    // 计算长轴的旋转角度(焦点连线和x轴的夹角,用弧度)
    GPoint p1 = projection.FromLatLngToPixel(focus1, gMapControl1.Zoom);
    GPoint p2 = projection.FromLatLngToPixel(focus2, gMapControl1.Zoom);
    double angleRadians = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
    
  3. 生成椭圆的顶点集合
    遍历0到2π的角度,每次生成一个点,这里我用100个点,足够平滑了:

    var ellipsePoints = new List<PointLatLng>();
    int steps = 100; // 点的数量,越多越平滑
    for (int i = 0; i < steps; i++)
    {
        double theta = 2 * Math.PI * i / steps;
        // 先算平面坐标下的椭圆点
        double x = a * Math.Cos(theta);
        double y = b * Math.Sin(theta);
        // 把点旋转到长轴的方向
        double rotatedX = x * Math.Cos(angleRadians) - y * Math.Sin(angleRadians);
        double rotatedY = x * Math.Sin(angleRadians) + y * Math.Cos(angleRadians);
        // 转回到经纬度
        var centerPixel = projection.FromLatLngToPixel(center, gMapControl1.Zoom);
        var targetPixel = new GPoint(centerPixel.X + rotatedX, centerPixel.Y + rotatedY);
        var targetLatLng = projection.FromPixelToLatLng(targetPixel, gMapControl1.Zoom);
        ellipsePoints.Add(targetLatLng);
    }
    // 最后把第一个点加回去,让多边形闭合
    ellipsePoints.Add(ellipsePoints[0]);
    
  4. 把椭圆画到地图上
    用GMapPolygon把点集合包装起来,加到地图的Overlay里就行:

    var ellipsePolygon = new GMapPolygon(ellipsePoints, "Focus-Based Ellipse");
    ellipsePolygon.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue)); // 半透明填充
    ellipsePolygon.Stroke = new Pen(Color.Blue, 2); // 边框
    gMapControl1.Overlays.Add(new GMapOverlay("EllipseOverlay") { ellipsePolygon });
    

二、不用GMap.NET内置函数的公式实现

如果不想依赖GMap.NET的投影工具,那核心就是把经纬度转成平面坐标,用平面椭圆的参数方程生成点,再转回到经纬度。适合需要完全自定义逻辑的场景。

核心公式逻辑

  1. 经纬度转局部平面坐标(墨卡托投影简化版)
    小范围区域(几百公里内)用这个简化版足够,误差很小:

    void LatLngToLocalXY(PointLatLng latLng, PointLatLng center, out double x, out double y)
    {
        const double EarthRadius = 6378137; // 地球半径,单位米
        double centerRadLat = center.Lat * Math.PI / 180;
        double radLat = latLng.Lat * Math.PI / 180;
        double radLng = latLng.Lng * Math.PI / 180;
        double centerRadLng = center.Lng * Math.PI / 180;
    
        x = EarthRadius * (radLng - centerRadLng) * Math.Cos(centerRadLat);
        y = EarthRadius * Math.Log(Math.Tan(Math.PI / 4 + radLat / 2)) - EarthRadius * Math.Log(Math.Tan(Math.PI / 4 + centerRadLat / 2));
    }
    
  2. 旋转后的平面椭圆参数方程
    已知两个焦点的平面坐标(x1,y1)、(x2,y2),中心(cx, cy),长半轴a,短半轴b,旋转角度α(焦点连线和x轴的夹角),椭圆上任意点的坐标是:

    x = cx + a*cosθ*cosα - b*sinθ*sinα
    y = cy + a*cosθ*sinα + b*sinθ*cosα
    

    这里的θ从0到2π,步长越小,点越密集。

  3. 平面坐标转回经纬度
    对应上面的转换函数,写个反向的:

    void LocalXYToLatLng(double x, double y, PointLatLng center, out PointLatLng latLng)
    {
        const double EarthRadius = 6378137;
        double centerRadLat = center.Lat * Math.PI / 180;
        double centerRadLng = center.Lng * Math.PI / 180;
    
        double radLng = (x / (EarthRadius * Math.Cos(centerRadLat))) + centerRadLng;
        double expY = Math.Exp((y + EarthRadius * Math.Log(Math.Tan(Math.PI / 4 + centerRadLat / 2))) / EarthRadius);
        double radLat = 2 * Math.Atan(expY) - Math.PI / 2;
    
        latLng = new PointLatLng(radLat * 180 / Math.PI, radLng * 180 / Math.PI);
    }
    
  4. 完整流程

    • 用LatLngToLocalXY把两个焦点转成局部平面坐标
    • 计算c = 0.5 * sqrt((x2-x1)^2 + (y2-y1)^2),设定a > c,算出b = sqrt(a² - c²)
    • 算旋转角度α = atan2(y2-y1, x2-x1)
    • 遍历θ生成平面点,再用LocalXYToLatLng转回经纬度
    • 最后把这些经纬度点用来绘制就行(比如还是用GMapPolygon,或者自己画)

一些注意点

  • 如果是跨大区域的椭圆(比如跨半球),简化的墨卡托投影误差会变大,这时候可能需要用更精确的球面椭圆公式,但一般业务场景下简化版足够用。
  • 一定要保证a > c,不然生成的不是椭圆——a=c时会变成两个焦点之间的线段,a<c的话根本没有符合条件的点。
  • 顶点数量steps可以自己调,50到200个点之间,既能保证平滑,又不会影响性能。

内容的提问来源于stack exchange,提问作者Jorge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:24:19