Detecting whether a GPS coordinate falls within a polygon on a map

前端 未结 8 1302
野趣味
野趣味 2020-12-07 10:17

As stated in the title, the goal is to have a way for detecting whether a given GPS coordinate falls inside a polygon or not.

The polygon itself can be either convex

相关标签:
8条回答
  • 2020-12-07 11:02

    I thought similarly as shab first (his proposal is called Ray-Casting Algorithm), but had second thoughts like Spacedman:

    ...but all the geometry will have to be redone in spherical coordinates...

    I implemented and tested the mathematically correct way of doing that, e.i. intersecting great circles and determining whether one of the two intersecting points is on both arcs. (Note: I followed the steps described here, but I found several errors: The sign function is missing at the end of step 6 (just before arcsin), and the final test is numerical garbage (as subtraction is badly conditioned); use rather L_1T >= max(L_1a, L_1b) to test whether S1 is on the first arc etc.)

    That also is extremely slow and a numerical nightmare (evaluates ~100 trigonometric functions, among other things); it proved not to be usable in our embedded systems.

    There's a trick, though: If the area you are considering is small enough, just do a standard cartographic projection, e.g. spherical Mercator projection, of each point:

    // latitude, longitude in radians
    x = longitude;
    y = log(tan(pi/4 + latitude/2));
    

    Then, you can apply ray-casting, where the intersection of arcs is checked by this function:

    public bool ArcsIntersecting(double x1, double y1, double x2, double y2, 
      double x3, double y3, double x4, double y4)
        {
    
        double vx1 = x2 - x1;
        double vy1 = y2 - y1;
    
        double vx2 = x4 - x3;
        double vy2 = y4 - y3;
    
        double denom = vx1 * vy2 - vx2 * vy1;
    
        if (denom == 0) { return false; } // edges are parallel
    
        double t1 = (vx2 * (y1 - y3) - vy2 * (x1 - x3)) / denom;
    
        double t2;
    
        if (vx2 != 0) { t2 = (x1 - x3 + t1 * vx1) / vx2; }
        else if (vy2 != 0) { t2 = (y1 - y3 + t1 * vy1) / vy2; }
        else { return false; } // edges are matching
    
        return min(t1, t2) >= 0 && max(t1, t2) <= 1;
    }
    
    0 讨论(0)
  • 2020-12-07 11:02

    JavaScript Version -

    {
    const PI = 3.14159265;
    const TWOPI = 2*PI;
    function isCoordinateInsidePitch(latitude, longitude, latArray, longArray)
    {       
           let angle=0;
           let p1Lat;
           let p1Long;
           let p2Lat;
           let p2Long;
           let n = latArray.length;
    
           for (let i = 0; i < n; i++) {
              p1Lat = latArray[i] - latitude;
              p1Long = longArray[i] - longitude;
              p2Lat = latArray[(i+1)%n] - latitude;
              p2Long = longArray[(i+1)%n] - longitude;
              angle += angle2D(p1Lat,p1Long,p2Lat,p2Long);
           }
    
           return !(Math.abs(angle) < PI);
    }
    
    function angle2D(y1, x1, y2, x2)
    {
       let dtheta,theta1,theta2;
    
       theta1 = Math.atan2(y1,x1);
       theta2 = Math.atan2(y2,x2);
       dtheta = theta2 - theta1;
       while (dtheta > PI)
          dtheta -= TWOPI;
       while (dtheta < -PI)
          dtheta += TWOPI;
    
       return dtheta;
    }
    
    function isValidCoordinate(latitude,longitude)
    {
        return (
        latitude !== '' && longitude !== '' && !isNaN(latitude) 
         && !isNaN(longitude) && latitude > -90 &&
         latitude < 90 && longitude > -180 && longitude < 180
         )
    }
    let latArray = [32.10458, 32.10479, 32.1038, 32.10361];
    let longArray = [34.86448, 34.86529, 34.86563, 34.86486];
    // true
    console.log(isCoordinateInsidePitch(32.104447, 34.865108,latArray, longArray));
    // false
    // isCoordinateInsidePitch(32.104974, 34.864576,latArray, longArray);
    // true
    // isValidCoordinate(0, 0)
    // true
    // isValidCoordinate(32.104974, 34.864576)
    }

    0 讨论(0)
提交回复
热议问题