Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bing maps Polygon Search is not accurate

I am having a bit of a problem with the bing maps example Polygon Search found here:

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;



}

I use ViewportPointToLocation to get the mouse co-ordinates and add a pin at my mouse click.

            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);

This is where I use my polygon search to see if I clicked within the polygon.

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

As seen in the picture below, I set a label to "Within delivery area" or "Customer out of area", depending on weather polygonSearch returns true or false.

It doesn't seem to work when dealing around the edge of the polygon. Can someone more experience with this let me know where i lye wrong?

enter image description here

COMPLETE CODE EXAMPLE BELOW:

You will need to reference the Microsoft.Maps.MapControl.WPF.dll to get this working.

I have made just a demo, which contains the bing map control map, a label to tell us our polygon search output, and a checkbox which allows you to search within the polygon.

To make a polygon, simply right click the map to draw, and press "Escape" to end drawing the polygon. Then you can click the "Search address by left click" checkbox and search within the polygon.

As you will see, the polygon search from MSDN returns out of area when we can see that we clicked within the polygon that we just drew!

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);
        }
    }
}

Note: This design will only work to create 1 polygon to search, but you can still see where my polygon search fails visually.

EDIT:

  • With lots of thanks from KeyboardP, we have found that issue does not exists if you fully zoom in, then draw the polygon, then search. But if we draw it, then zoom, we will see the same issue arise.

  • I have also debugged and confirmed that the LocationCollection polygon is the same on different zoom levels

  • List item

like image 487
clamchoda Avatar asked Jun 28 '13 18:06

clamchoda


People also ask

Which is more accurate Bing Maps or Google Maps?

There may be only some slight discrepancies, because of the map's navigation style. Google Maps leads in the market in top 10K, 100K and 1M sites and even the entire web. However, Bing Maps lags far behind and loses to Google Maps in all segments.

What projection does Bing Maps use?

Each of the Bing Maps services uses a projected coordinate system called Mercator Auxiliary Sphere, which uses GCS_WGS_1984 as its geographic coordinate system.

How often is Bing Maps updated?

Bing maps frequently update and expand the geographic areas covered by their imagery, with new updates being released on roughly a monthly basis. Each imagery release typically contains more than 10TB of imagery.

Can you draw a radius on Bing Maps?

Bing Maps V7 does not include a draw circle method. The circle has to be "aproximated" by drawing several small segments. Save this answer.


1 Answers

I wrote this article about 6 years ago. This is a simple point in polygon algorithm which is based on standard 2D geometry and is not a geospatially accurate algorithm. It works great for small polygons that cover the area of a city or smaller. Large polygons will appear to be less accurate. Note that a line between two points on a map, even though it looks straight, in reality the line is curved. A good example of this can be found here: http://alastaira.wordpress.com/2011/06/27/geodesics-on-bing-maps-v7/

As for your issue, if you want to click on a polygon simply use mouse events. If you want to check if a point is in a polygon then you make use of the powerful SQL Spatial library in WPF with will give you geospatially accurate point in polygon calculations. You don't need to connect to a database. All you need the Microsoft.SqlServer.Types.dll which is freely available through SQL Express. You can use it in .NET and with the Bing Maps WPF control. Here is a good starting point: http://ecn.channel9.msdn.com/o9/learn/SQL2008R2TrainingKit/Labs/UsingSpatialDataInManagedCode/Lab.docx

Once you have this working then you can simply create a SQLGeography object out of your polygon and check to see if your point intersects with the polygon.

like image 133
rbrundritt Avatar answered Oct 16 '22 06:10

rbrundritt