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
相关产品推荐
相关产品推荐

