Geo Fencing - point inside/outside polygon

后端 未结 16 1655
我在风中等你
我在风中等你 2020-11-28 02:15

I would like to determine a polygon and implement an algorithm which would check if a point is inside or outside the polygon.

Does anyone know if there is any exampl

相关标签:
16条回答
  • 2020-11-28 02:53

    I translated c# method in Php and I added many comments to understand code.

    Description of PolygonHelps:
    Check if a point is inside or outside of a polygon. This procedure uses gps coordinates and it works when polygon has a little geographic area.


    INPUT:
    $poly: array of Point: polygon vertices list; [{Point}, {Point}, ...];
    $point: point to check; Point: {"lat" => "x.xxx", "lng" => "y.yyy"}


    When $c is false, the number of intersections with polygon is even, so the point is outside of polygon;
    When $c is true, the number of intersections with polygon is odd, so the point is inside of polygon;
    $n is the number of vertices in polygon;
    For each vertex in polygon, method calculates line through current vertex and previous vertex and check if the two lines have an intersection point.
    $c changes when intersection point exists.
    So, method can return true if point is inside the polygon, else return false.

    class PolygonHelps {
    
        public static function isPointInPolygon(&$poly, $point){
    
            $c = false; 
            $n = $j = count($poly);
    
    
            for ($i = 0, $j = $n - 1; $i < $n; $j = $i++){
    
                if ( ( ( ( $poly[$i]->lat <= $point->lat ) && ( $point->lat < $poly[$j]->lat ) ) 
                    || ( ( $poly[$j]->lat <= $point->lat ) && ( $point->lat < $poly[$i]->lat ) ) ) 
    
                && ( $point->lng <   ( $poly[$j]->lng - $poly[$i]->lng ) 
                                   * ( $point->lat    - $poly[$i]->lat ) 
                                   / ( $poly[$j]->lat - $poly[$i]->lat ) 
                                   +   $poly[$i]->lng ) ){
    
                    $c = !$c;
                }
            }
    
            return $c;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 02:54

    Jan's answer is great.

    Here is the same code using the GeoCoordinate class instead.

    using System.Device.Location;
    

    ...

    public static bool IsPointInPolygon(List<GeoCoordinate> poly, GeoCoordinate point)
    {
        int i, j;
        bool c = false;
        for (i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
        {
            if ((((poly[i].Latitude <= point.Latitude) && (point.Latitude < poly[j].Latitude))
                    || ((poly[j].Latitude <= point.Latitude) && (point.Latitude < poly[i].Latitude)))
                    && (point.Longitude < (poly[j].Longitude - poly[i].Longitude) * (point.Latitude - poly[i].Latitude)
                        / (poly[j].Latitude - poly[i].Latitude) + poly[i].Longitude))
                c = !c;
        }
    
        return c;
    }
    
    0 讨论(0)
  • 2020-11-28 02:55

    By far the best explanation and implementation can be found at Point In Polygon Winding Number Inclusion

    There is even a C++ implementation at the end of the well explained article. This site also contains some great algorithms/solutions for other geometry based problems.

    I have modified and used the C++ implementation and also created a C# implementation. You definitely want to use the Winding Number algorithm as it is more accurate than the edge crossing algorithm and it is very fast.

    0 讨论(0)
  • 2020-11-28 02:55

    the polygon is defined as a sequential list of point pairs A, B, C .... A. no side A-B, B-C ... crosses any other side

    Determine box Xmin, Xmax, Ymin, Ymax

    case 1 the test point P lies outside the box

    case 2 the test point P lies inside the box:

    Determine the 'diameter' D of the box {[Xmin,Ymin] - [Xmax, Ymax]} ( and add a little extra to avoid possible confusion with D being on a side)

    Determine the gradients M of all sides

    Find a gradient Mt most different from all gradients M

    The test line runs from P at gradient Mt a distance D.

    Set the count of intersections to zero

    For each of the sides A-B, B-C test for the intersection of P-D with a side from its start up to but NOT INCLUDING its end. Increment the count of intersections if required. Note that a zero distance from P to the intersection indicates that P is ON a side

    An odd count indicates P is inside the polygon

    0 讨论(0)
  • 2020-11-28 02:56

    After searching the web and trying various implementations and porting them from C++ to C# I finally got my code straight:

            public static bool PointInPolygon(LatLong p, List<LatLong> poly)
        {
            int n = poly.Count();
    
            poly.Add(new LatLong { Lat = poly[0].Lat, Lon = poly[0].Lon });
            LatLong[] v = poly.ToArray();
    
            int wn = 0;    // the winding number counter
    
            // loop through all edges of the polygon
            for (int i = 0; i < n; i++)
            {   // edge from V[i] to V[i+1]
                if (v[i].Lat <= p.Lat)
                {         // start y <= P.y
                    if (v[i + 1].Lat > p.Lat)      // an upward crossing
                        if (isLeft(v[i], v[i + 1], p) > 0)  // P left of edge
                            ++wn;            // have a valid up intersect
                }
                else
                {                       // start y > P.y (no test needed)
                    if (v[i + 1].Lat <= p.Lat)     // a downward crossing
                        if (isLeft(v[i], v[i + 1], p) < 0)  // P right of edge
                            --wn;            // have a valid down intersect
                }
            }
            if (wn != 0)
                return true;
            else
                return false;
    
        }
    
        private static int isLeft(LatLong P0, LatLong P1, LatLong P2)
        {
            double calc = ((P1.Lon - P0.Lon) * (P2.Lat - P0.Lat)
                    - (P2.Lon - P0.Lon) * (P1.Lat - P0.Lat));
            if (calc > 0)
                return 1;
            else if (calc < 0)
                return -1;
            else
                return 0;
        }
    

    The isLeft function was giving me rounding problems and I spent hours without realizing that I was doing the conversion wrong, so forgive me for the lame if block at the end of that function.

    BTW, this is the original code and article: http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm

    0 讨论(0)
  • 2020-11-28 02:59

    Relating to kobers answer I worked it out with more readable clean code and changed the longitudes that crosses the date border:

    public bool IsPointInPolygon(List<PointPosition> polygon, double latitude, double longitude)
    {
      bool isInIntersection = false;
      int actualPointIndex = 0;
      int pointIndexBeforeActual = polygon.Count - 1;
    
      var offset = calculateLonOffsetFromDateLine(polygon);
      longitude = longitude < 0.0 ? longitude + offset : longitude;
    
      foreach (var actualPointPosition in polygon)
      {
        var p1Lat = actualPointPosition.Latitude;
        var p1Lon = actualPointPosition.Longitude;
    
        var p0Lat = polygon[pointIndexBeforeActual].Latitude;
        var p0Lon = polygon[pointIndexBeforeActual].Longitude;
    
        if (p1Lon < 0.0) p1Lon += offset;
        if (p0Lon < 0.0) p0Lon += offset;
    
        // Jordan curve theorem - odd even rule algorithm
        if (isPointLatitudeBetweenPolyLine(p0Lat, p1Lat, latitude)
        && isPointRightFromPolyLine(p0Lat, p0Lon, p1Lat, p1Lon, latitude, longitude))
        {
          isInIntersection = !isInIntersection;
        }
    
        pointIndexBeforeActual = actualPointIndex;
        actualPointIndex++;
      }
    
      return isInIntersection;
    }
    
    private double calculateLonOffsetFromDateLine(List<PointPosition> polygon)
    {
      double offset = 0.0;
      var maxLonPoly = polygon.Max(x => x.Longitude);
      var minLonPoly = polygon.Min(x => x.Longitude);
      if (Math.Abs(minLonPoly - maxLonPoly) > 180)
      {
        offset = 360.0;
      }
    
      return offset;
    }
    
    private bool isPointLatitudeBetweenPolyLine(double polyLinePoint1Lat, double polyLinePoint2Lat, double poiLat)
    {
      return polyLinePoint2Lat <= poiLat && poiLat < polyLinePoint1Lat || polyLinePoint1Lat <= poiLat && poiLat < polyLinePoint2Lat;
    }
    
    private bool isPointRightFromPolyLine(double polyLinePoint1Lat, double polyLinePoint1Lon, double polyLinePoint2Lat, double polyLinePoint2Lon, double poiLat, double poiLon)
    {
      // lon <(lon1-lon2)*(latp-lat2)/(lat1-lat2)+lon2
      return poiLon < (polyLinePoint1Lon - polyLinePoint2Lon) * (poiLat - polyLinePoint2Lat) / (polyLinePoint1Lat - polyLinePoint2Lat) + polyLinePoint2Lon;
    }
    
    0 讨论(0)
提交回复
热议问题