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

C#中实现GMap自定义Bitmap标记任意角度旋转的方法

实现GMaps标记的角度旋转(通过输入框控制)

嘿,我来帮你搞定这个箭头标记旋转的问题!核心思路是旋转箭头图片本身,然后更新GMapMarker的图标,因为GMarkerGoogle没有直接的旋转属性,咱们得从Bitmap下手。下面一步步来:

1. 编写Bitmap旋转与裁剪工具方法

首先需要两个辅助方法:一个负责旋转图片,另一个裁剪掉旋转后多余的空白区域(避免标记周围有大片空白点击区)。把这两个方法加到你的窗体类里:

private Bitmap RotateBitmap(Bitmap originalBitmap, float angle)
{
    // 创建足够大的画布容纳旋转后的图像
    int maxSize = (int)Math.Max(originalBitmap.Width, originalBitmap.Height) * 2;
    Bitmap rotatedBitmap = new Bitmap(maxSize, maxSize);
    rotatedBitmap.SetResolution(originalBitmap.HorizontalResolution, originalBitmap.VerticalResolution);

    using (Graphics g = Graphics.FromImage(rotatedBitmap))
    {
        // 设置旋转中心为图片中心点
        g.TranslateTransform(rotatedBitmap.Width / 2, rotatedBitmap.Height / 2);
        g.RotateTransform(angle);
        g.TranslateTransform(-originalBitmap.Width / 2, -originalBitmap.Height / 2);

        // 绘制旋转后的图片,保留透明通道(PNG的透明背景不会丢)
        g.DrawImage(originalBitmap, new Point(0, 0));
        g.ResetTransform();
    }

    // 裁剪掉空白区域,得到紧凑的旋转后图片
    return CropBitmap(rotatedBitmap);
}

private Bitmap CropBitmap(Bitmap bitmap)
{
    Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

    // 扫描上边界
    for (int y = 0; y < bitmap.Height; y++)
    {
        for (int x = 0; x < bitmap.Width; x++)
        {
            if (bitmap.GetPixel(x, y).A != 0)
            {
                bounds.Y = y;
                goto ScanBottom;
            }
        }
    }

ScanBottom:
    // 扫描下边界
    for (int y = bitmap.Height - 1; y >= bounds.Y; y--)
    {
        for (int x = 0; x < bitmap.Width; x++)
        {
            if (bitmap.GetPixel(x, y).A != 0)
            {
                bounds.Height = y - bounds.Y + 1;
                goto ScanLeft;
            }
        }
    }

ScanLeft:
    // 扫描左边界
    for (int x = 0; x < bitmap.Width; x++)
    {
        for (int y = bounds.Y; y < bounds.Y + bounds.Height; y++)
        {
            if (bitmap.GetPixel(x, y).A != 0)
            {
                bounds.X = x;
                goto ScanRight;
            }
        }
    }

ScanRight:
    // 扫描右边界
    for (int x = bitmap.Width - 1; x >= bounds.X; x--)
    {
        for (int y = bounds.Y; y < bounds.Y + bounds.Height; y++)
        {
            if (bitmap.GetPixel(x, y).A != 0)
            {
                bounds.Width = x - bounds.X + 1;
                goto Done;
            }
        }
    }

Done:
    return bitmap.Clone(bounds, bitmap.PixelFormat);
}

2. 改造标记创建代码,保存全局引用

为了点击旋转按钮时能找到当前的标记,需要把GMarkerGoogle和GMapOverlay保存为窗体类的全局变量:

// 在窗体类顶部声明全局变量
private GMarkerGoogle _currentMarker;
private GMapOverlay _markersOverlay;

// 然后修改你原来的创建标记代码:
double lat = Convert.ToDouble(txtLat.Text);
double lng = Convert.ToDouble(txtLong.Text);
map.Position = new PointLatLng(lat, lng);

// 加载原始箭头图片
Bitmap bmpMarker = (Bitmap)Image.FromFile("img/arrow.png");
PointLatLng point = new PointLatLng(lat, lng);
_currentMarker = new GMarkerGoogle(point, bmpMarker);

// 创建Overlay并关联到地图
_markersOverlay = new GMapOverlay("markers");
_markersOverlay.Markers.Add(_currentMarker);
map.Overlays.Add(_markersOverlay);

// 缩放并居中到标记
map.ZoomAndCenterMarkers("markers");
_currentMarker.ToolTipText = map.Position.ToString();

3. 实现旋转按钮的点击事件

给你的旋转按钮(比如btnRotate)绑定点击事件,读取输入框的角度,旋转图片并更新标记:

private void btnRotate_Click(object sender, EventArgs e)
{
    // 先检查标记是否已创建
    if (_currentMarker == null)
    {
        MessageBox.Show("请先加载标记再进行旋转操作!");
        return;
    }

    // 解析输入的角度值
    if (!float.TryParse(txtAngle.Text, out float rotateAngle))
    {
        MessageBox.Show("请输入有效的数字角度值!");
        return;
    }

    // 建议:如果不想每次旋转都读取文件,可以把原始箭头图片保存为全局变量
    Bitmap originalArrow = (Bitmap)Image.FromFile("img/arrow.png");
    // 旋转图片
    Bitmap rotatedArrow = RotateBitmap(originalArrow, rotateAngle);

    // 更新标记的图标并刷新地图
    _currentMarker.Image = rotatedArrow;
    map.Refresh();

    // 释放资源,避免内存泄漏
    originalArrow.Dispose();
}

一些额外的小提示

  • 优化资源管理:可以在窗体加载时就把原始箭头图片加载到全局变量,避免每次旋转都读取本地文件,比如在Form_Load里添加_originalArrow = (Bitmap)Image.FromFile("img/arrow.png");,旋转时直接用这个变量。
  • 旋转方向:角度值是顺时针为正,如果你需要逆时针旋转,输入负数即可。
  • 透明背景保留:上面的方法已经处理了PNG的透明通道,旋转后的箭头不会出现黑边或背景色。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 18:37:27