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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 04:00:56