WPF中如何将经纬度正确映射到500*500静态地图像素坐标?
问题描述
我拥有一张尺寸为500*500px的静态世界地图,需求是接收用户输入的经纬度,并在地图上用圆点标记对应位置。我使用了以下GoogleMapsAPIProjection类中的公式进行经纬度与像素坐标的互转,但目前输入任意经纬度都会显示错误位置。
投影转换代码
using System; using System.Collections.Generic; using System.Drawing; using System.Text; namespace MapControl.ProjectionFolder { public class GoogleMapsAPIProjection : IProjection { private readonly double PixelTileSize = 256d; private readonly double DegreesToRadiansRatio = 180d / Math.PI; private readonly double RadiansToDegreesRatio = Math.PI / 180d; private readonly PointF PixelGlobeCenter; private readonly double XPixelsToDegreesRatio; private readonly double YPixelsToRadiansRatio; public GoogleMapsAPIProjection(double zoomLevel) { var pixelGlobeSize = this.PixelTileSize * Math.Pow(2d, zoomLevel); this.XPixelsToDegreesRatio = pixelGlobeSize / 360d; this.YPixelsToRadiansRatio = pixelGlobeSize / (2d * Math.PI); var halfPixelGlobeSize = Convert.ToSingle(pixelGlobeSize / 2d); this.PixelGlobeCenter = new PointF(halfPixelGlobeSize, halfPixelGlobeSize); } #region IProjection Members public PointF FromCoordinatesToPixel(PointF coordinates) { var x = Math.Round(this.PixelGlobeCenter.X + (coordinates.X * this.XPixelsToDegreesRatio)); var f = Math.Min(Math.Max(Math.Sin(coordinates.Y * RadiansToDegreesRatio), -0.9999d), 0.9999d); var y = Math.Round(this.PixelGlobeCenter.Y + .5d * Math.Log((1d + f) / (1d - f)) * -this.YPixelsToRadiansRatio); return new PointF(Convert.ToSingle(x), Convert.ToSingle(y)); } public PointF FromPixelToCoordinates(PointF pixel) { var longitude = (pixel.X - this.PixelGlobeCenter.X) / this.XPixelsToDegreesRatio; var latitude = (2 * Math.Atan(Math.Exp((pixel.Y - this.PixelGlobeCenter.Y) / -this.YPixelsToRadiansRatio)) - Math.PI / 2) * DegreesToRadiansRatio; return new PointF(Convert.ToSingle(latitude), Convert.ToSingle(longitude)); } #endregion } }
XAML代码
<Window x:Class="MapControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MapControl" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <ScrollViewer HorizontalScrollBarVisibility="Auto" Grid.Row="4" Width="500" Height="500" Background="Gray"> <Grid Width="500" Height="500"> <Image Margin="0,0,0,0" x:Name="MapImage" Source="Desktop\screenmap.png" Stretch="Fill" Width="500" Height="500" ></Image> <Ellipse Canvas.Top="50" Canvas.Left="50" Fill="Red" Height="5" Width="5" Visibility="Collapsed" StrokeThickness="4" x:Name="CircleEllipse" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,0" /> </Grid> </ScrollViewer> </Window>
调用代码
using MapControl.ProjectionFolder; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace MapControl { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); GoogleMapsAPIProjection googleApiProjObj = new GoogleMapsAPIProjection(0); float x = 18.29F; float y = 73.57F; System.Drawing.PointF p1 = new System.Drawing.PointF(x, y); System.Drawing.PointF p2 = googleApiProjObj.FromCoordinatesToPixel(p1); //CircleEllipse.Margin = new Thickness(longitudePixels,latitudePixels, 0, 0); CircleEllipse.Margin = new Thickness(p2.X, p2.Y, 0, 0); CircleEllipse.Visibility = Visibility.Visible; } } }
问题排查与修正方案
1. 经纬度参数顺序错误
GoogleMapsAPIProjection.FromCoordinatesToPixel方法中,coordinates.X代表经度,coordinates.Y代表纬度。但原调用代码里把纬度(18.29)传给了X,经度(73.57)传给了Y,顺序颠倒,这是定位错误的核心原因。
2. 缩放级别与地图尺寸不匹配
当zoomLevel=0时,Google Maps的全球地图基准尺寸是256256px,但你的静态地图是500500px,需要把投影计算出的256px范围坐标,按比例映射到500px画布上:
// 计算缩放比例 double scale = 500d / 256d; // 映射到500px地图的坐标 float mappedX = (float)(p2.X * scale); float mappedY = (float)(p2.Y * scale);
3. 控件定位偏移问题
Ellipse用Margin定位时默认是左上角对齐,而我们需要圆点中心对准目标坐标,所以要减去椭圆半径(宽高5px,半径2.5px):
CircleEllipse.Margin = new Thickness(mappedX - 2.5, mappedY - 2.5, 0, 0);
修正后的完整调用代码
using MapControl.ProjectionFolder; using System; using System.Windows; using System.Windows.Shapes; namespace MapControl { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); GoogleMapsAPIProjection googleApiProjObj = new GoogleMapsAPIProjection(0); // 正确顺序:X是经度,Y是纬度 float longitude = 73.57F; float latitude = 18.29F; System.Drawing.PointF coordinates = new System.Drawing.PointF(longitude, latitude); System.Drawing.PointF pixelCoords = googleApiProjObj.FromCoordinatesToPixel(coordinates); // 缩放适配500*500地图 double scale = 500d / 256d; float mappedX = (float)(pixelCoords.X * scale); float mappedY = (float)(pixelCoords.Y * scale); // 修正定位偏移,让圆点中心对准坐标点 CircleEllipse.Margin = new Thickness(mappedX - CircleEllipse.Width/2, mappedY - CircleEllipse.Height/2, 0, 0); CircleEllipse.Visibility = Visibility.Visible; } } }
额外注意事项
- 确认静态地图采用Web墨卡托投影(Google Maps使用的投影),若投影不一致,即使修正上述问题,定位仍会偏差。
- 可将地图
Stretch属性设为None或Uniform,避免图片拉伸变形导致坐标映射不准。
内容的提问来源于stack exchange,提问作者Souvik Roy
相关产品推荐
相关产品推荐

