【问题标题】:Bing maps Polygon Search is not accurate必应地图多边形搜索不准确
【发布时间】:2013-06-28 18:59:49
【问题描述】:

我在此处找到的 bing 地图示例多边形搜索有点问题:

Polygon search is about halfway down this link.

  public bool polygonSearch(LocationCollection points, double lat, double lon)
{
    MapPolygon poly = new MapPolygon();

    int i = 0;
    int j = points.Count - 1;
    bool inPoly = false;

    for (i = 0; i < points.Count; i++)
    {
        if (points[i].Longitude < lon && points[j].Longitude >= lon || points[j].Longitude < lon && points[i].Longitude >= lon)
        {
            if (points[i].Latitude + (lon - points[i].Longitude) / (points[j].Longitude - points[i].Longitude) * (points[j].Latitude - points[i].Latitude) < lat)
            {
                inPoly = !inPoly;

            }
        }
        j = i;
    }

    return inPoly;



}

我使用ViewportPointToLocation 来获取鼠标坐标并在我的鼠标点击时添加一个图钉。

            Point mousePosition = e.GetPosition(myMap);
            Microsoft.Maps.MapControl.WPF.Location pinLocation = myMap.ViewportPointToLocation(mousePosition);
            // Convert the mouse coordinates to a location on the map 


            // The pushpin to add to the map.
            Pushpin pin = new Pushpin();
            pin.Location = pinLocation;
            pin.Content = "Cust";
            pin.Heading = 0;

            // Adds the pushpin to the map
            myMap.Children.Add(pin);

这是我使用多边形搜索来查看我是否在多边形内单击的地方。

polygonSearch(polygon.Locations, pin.Location.Latitude, pin.Location.Longitude))

如下图所示,我将标签设置为“在送货区域内”或“客户在区域外”,具体取决于天气多边形搜索返回真或假。

在处理多边形边缘时似乎不起作用。有更多经验的人可以告诉我我哪里错了吗?

下面的完整代码示例:

您需要引用 Microsoft.Maps.MapControl.WPF.dll 才能使其正常工作。

我只做了一个演示,其中包含 bing 地图控制地图、一个告诉我们多边形搜索输出的标签,以及一个允许您在多边形内搜索的复选框。

要制作多边形,只需右键单击要绘制的地图,然后按“退出”即可结束多边形的绘制。然后您可以单击“左键搜索地址”复选框并在多边形内搜索。

正如您将看到的,当我们看到我们在刚刚绘制的多边形内单击时,来自 MSDN 的多边形搜索返回区域外!

MainWindow.xaml

<Window x:Class="PolygonSearch.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:m="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF"
        Title="MainWindow" Height="350" Width="525" KeyDown="Window_KeyDown">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="100*"/>
        </Grid.RowDefinitions>
        <StackPanel>
        <Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="10,10,0,0" Name="lbl_arearsult" Grid.Row="0" VerticalAlignment="Top" />
        <CheckBox Content="Search Address by left click" Height="16" HorizontalAlignment="Left" Margin="10,10,0,0" Name="chk_search" VerticalAlignment="Top" />
        </StackPanel>
        <m:Map x:Name="myMap" Grid.Row="1" CredentialsProvider="your_bing_map_key" Mode="AerialWithLabels" MouseLeftButtonDown="myMap_MouseDown" MouseRightButtonDown="myMap_MouseRightButtonDown" KeyDown="myMap_KeyDown" />

    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;

using Microsoft.Maps.MapControl.WPF;

namespace PolygonSearch
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        LocationCollection drawPolyPoints = new LocationCollection();

        public MainWindow()
        {
            InitializeComponent();
        }



        public bool polygonSearch(LocationCollection points, double lat, double lon)
        {
            MapPolygon poly = new MapPolygon();

            int i = 0;
            int j = points.Count - 1;
            bool inPoly = false;

            for (i = 0; i < points.Count; i++)
            {
                if (points[i].Longitude < lon && points[j].Longitude >= lon || points[j].Longitude < lon && points[i].Longitude >= lon)
                {
                    if (points[i].Latitude + (lon - points[i].Longitude) / (points[j].Longitude - points[i].Longitude) * (points[j].Latitude - points[i].Latitude) < lat)
                    {
                        inPoly = !inPoly;

                    }
                }
                j = i;
            }

            return inPoly;



        }

        private void myMap_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Escape)
            {
                MapPolygon polygon = new MapPolygon();
                polygon.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Blue);
                polygon.Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green);
                polygon.StrokeThickness = 5;
                polygon.Opacity = 0.7;
                polygon.Locations = drawPolyPoints;
                polygon.Tag = "1388_q3_polygon_5";
                myMap.Children.Add(polygon);
                //drawPolyPoints.Clear();

                for (int p = 0; p < myMap.Children.Count; p++)
                {
                    object entity = myMap.Children[p];
                    if (entity is Microsoft.Maps.MapControl.WPF.Pushpin)
                    {
                        if (((Microsoft.Maps.MapControl.WPF.Pushpin)entity).Content.ToString() == "Vertice")
                            myMap.Children.Remove(((Microsoft.Maps.MapControl.WPF.Pushpin)entity));
                    }


                }
            }
        }

        private void myMap_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (chk_search.IsChecked == true)
            {
                Point mousePosition = e.GetPosition(myMap);
                Microsoft.Maps.MapControl.WPF.Location pinLocation = myMap.ViewportPointToLocation(mousePosition);
                // Convert the mouse coordinates to a location on the map 


                // The pushpin to add to the map.
                Pushpin pin = new Pushpin();
                pin.Location = pinLocation;
                pin.Content = "Cust";
                pin.Heading = 0;

                // Adds the pushpin to the map
                myMap.Children.Add(pin);

                bool inArea = false;
                for (int p = 0; p < myMap.Children.Count; p++)
                {
                    object entity = myMap.Children[p];


                    if (entity is Microsoft.Maps.MapControl.WPF.MapPolygon)
                    {
                        if (polygonSearch(((Microsoft.Maps.MapControl.WPF.MapPolygon)entity).Locations, pin.Location.Latitude, pin.Location.Longitude))
                        {
                            string[] quadAttributes = ((Microsoft.Maps.MapControl.WPF.MapPolygon)entity).Tag.ToString().Split('_');

                            lbl_arearsult.Content = "Within delivery area ";

                            inArea = true;
                            break;
                        }
                        else
                        {

                            inArea = false;

                        }

                    }
                }
                if (inArea != true)
                    lbl_arearsult.Content = "Customer out of area. ";
            }

        }

        private void myMap_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point mousePosition = e.GetPosition(myMap);
            Microsoft.Maps.MapControl.WPF.Location pinLocation = myMap.ViewportPointToLocation(mousePosition);
            // Convert the mouse coordinates to a location on the map 

            // The pushpin to add to the map.
            Pushpin pin = new Pushpin();
            pin.Location = pinLocation;
            pin.Content = "Vertice";

            // Adds the pushpin to the map
            myMap.Children.Add(pin);
            drawPolyPoints.Add(pin.Location);
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            myMap_KeyDown(sender, e);
        }
    }
}

注意:此设计仅适用于创建 1 个要搜索的多边形,但您仍然可以直观地看到我的多边形搜索失败的地方。

编辑:

  • 非常感谢 KeyboardP,我们发现如果您完全放大,然后绘制多边形,然后搜索,则不存在该问题。但是如果我们绘制它,然后缩放,我们会看到同样的问题。

  • 我还调试并确认 LocationCollection 多边形在不同缩放级别上是相同的

  • 列表项

【问题讨论】:

  • 没人注意到这个问题吗?
  • 能否向我们提供您的 polygonSearch 方法的内容(如果您担心隐私,可以省略实际的纬度)?我们无法对看不到的一段代码进行故障排除。
  • 现在要在此处添加完整的代码示例,感谢您抽出宝贵时间。
  • @mech 我添加了一个演示
  • @ChrisBuckler 它是完全起作用还是仅在它不靠近边缘时起作用?

标签: c# wpf bing-api bing-maps


【解决方案1】:

我在大约 6 年前写了这篇文章。这是基于标准二维几何的多边形算法中的简单点,不是地理空间精确算法。它适用于覆盖城市或更小的区域的小多边形。大多边形似乎不太准确。请注意,地图上两点之间的线,即使看起来很直,但实际上它是弯曲的。一个很好的例子可以在这里找到:http://alastaira.wordpress.com/2011/06/27/geodesics-on-bing-maps-v7/

至于你的问题,如果你想点击一个多边形,只需使用鼠标事件。如果您想检查一个点是否在多边形中,那么您可以使用 WPF 中强大的 SQL Spatial 库,它将在多边形计算中为您提供地理空间上准确的点。您不需要连接到数据库。您只需要通过 SQL Express 免费获得的 Microsoft.SqlServer.Types.dll。您可以在 .NET 和 Bing Maps WPF 控件中使用它。这是一个很好的起点:http://ecn.channel9.msdn.com/o9/learn/SQL2008R2TrainingKit/Labs/UsingSpatialDataInManagedCode/Lab.docx

一旦你完成了这项工作,你就可以简单地从你的多边形中创建一个 SQLGeography 对象并检查你的点是否与多边形相交。

【讨论】:

  • 谢谢你 rbrundrit 这一切都说得通。我将实施你的建议,我会尽快回到这里。刚刚完成了一些兼职工作。
  • 您是否有创建 SQLGeography 对象(多边形)以在不使用数据库的情况下搜索给定点的示例链接?我遇到的所有事情都需要 MS SQL 表来查询。
  • 经过您的实验室,阅读文档后,我在使用 SQLGeography.STIntersects() 时仍然遇到同样的问题...如此处所述 - stackoverflow.com/questions/17458117/…
  • 我也将您的 ToGeodesic 转换为 C#,但我仍然遇到同样的问题
【解决方案2】:

我通过使用LocationToViewPortpoint 函数将我的所有多边形纬度/经度转换为屏幕上的Point 对象以及我正在测试相交的点来解决此问题,并使用 X 和 Y 值而不是我的多边形搜索中的纬度/经度。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-03
  • 2023-03-30
  • 2016-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
相关资源
最近更新 更多