Bing maps Polygon Search is not accurate
Asked Answered
O

2

8

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

Ocd answered 28/6, 2013 at 18:59 Comment(14)
No one has ever noticed this issue?Ocd
Could you please provide us with the contents (you can omit actual latlongs if you're worried about privacy) of your polygonSearch method? We can't troubleshoot a piece of code we can't see.Circumstantiate
going to add a full code sample here now, thank you for your time.Ocd
@Circumstantiate I have added a demoOcd
@ChrisBuckler Does it work at all or only when it's not near the edges?Ezana
@Ezana It works good just not near the edges. I notice when I am fully zoomed in I need to be about an inch deep into the polygon to be considered "In area". I have done multiple tests including looking up the lat / long returned by the viewport mouseclick function , and it is indeed correct :(Ocd
@ChrisBuckler - I just downloaded the SDK (microsoft.com/en-us/download/…) and was able to replicate your error. Initially I couldn't but it seems to happen if you create the polygon whilst zoomed out and then zoom in to select the position. For me, if I create the polygon when zoomed in, it works fine; even near the edges. So the issue seems to be the zoom position when the polygon was created. Just a guess atm but it might be related to the StrokeThickness not resizing as you zoom in.Ezana
@KeyboardP thank you I too just tried your exact same test. I am very excited to have a point of interest to work with. I just tried using stroke thickeness to 0 but I still get the same results. Going to play with the zoom a bit and see if anything pops out.Ocd
@ChrisBuckler This is an interesting problem. I tried varying stroke thicknesses but not much difference although I did notice that you can reverse the effect and have "within distance area" whilst clicking just outside the border.Ezana
I have even tried re-drawing my shapes every time the map view change and I still have the same results :(Ocd
Should I repost my questions since we have determined it to be with zoom level opposed to polygon search?Ocd
I wouldn't repost it just yet as it might not be that for sure, although you could edit the post with the extra details.Ezana
@ChrisBuckler, the problem here is that you're using bing maps. :)Pocked
Wow, I got this... Thank you all so much for your help I will post exactly what was happening and exactly how to get this working as expected. I just need a break after ripping my hair out >.<Ocd
O
1

I fixed this by converting all my polygon lat / long to a Point object on the screen using LocationToViewPortpoint function, as well as the point I'm testing for intersection, and use the X and Y values instead of lat / long in my polygon search.

Ocd answered 6/7, 2013 at 5:37 Comment(1)
@john The equivalent for the Ajax 7.0 version would be tryLocationToPixel. Found here - msdn.microsoft.com/en-us/library/gg427609.aspxOcd
D
6

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.

Disown answered 3/7, 2013 at 12:50 Comment(4)
Thank you rbrundritt this all makes good sense. I am going to implement what you suggest and I will get back here asap. Just finishing up a few side jobs at work.Ocd
Do you have an example link of creating a SQLGeography object (polygon) to search a given point in, WITHOUT the use of the databse? Everything Im comming accross requires MS SQL Tables to query against.Ocd
After going through your lab, reading the documentaion, I still have the same issue when using SQLGeography.STIntersects() ... As described here - #17458617Ocd
I also converted you ToGeodesic to C# and I still have the same issueOcd
O
1

I fixed this by converting all my polygon lat / long to a Point object on the screen using LocationToViewPortpoint function, as well as the point I'm testing for intersection, and use the X and Y values instead of lat / long in my polygon search.

Ocd answered 6/7, 2013 at 5:37 Comment(1)
@john The equivalent for the Ajax 7.0 version would be tryLocationToPixel. Found here - msdn.microsoft.com/en-us/library/gg427609.aspxOcd

© 2022 - 2024 — McMap. All rights reserved.