Test of Point inside polygon in Android

2019-01-11 15:30发布

问题:

The other day I did a class in Java to calculate if a point(X,Y) is inside a polygon. (X and Y are double, because will be geo-coordinates).

I know that Java has the class Polygon, but I had to use Path2D and Point2D, because Polygon don't allow double's, just integers :(

Once I have the polygon done in Path2D, I used the method contains (Path2D had it), and my problem was solved.

But now, I want to import to Android, and the problem is here, because Path2D needs to import:

import java.awt.geom.Path2D;
import java.awt.geom.Point2D;

and in Android don't exist awt, so I can't use.

So, is there any class similar to Path2D that had contains method? or I have to calculate by myself?

Here is how I did in Java using Path2D:

private void ConstructPolygon(Vector<Point2D> coodinates)
{       
    this.polygon.moveTo(coodinates.get(0).getX(), coodinates.get(0).getY());        

    //System.out.println(coodinates.get(0).getX() + "   " + coodinates.get(0).getY());
    //System.out.println("asda");

    for(int i = 1; i < this.num_points; i++)
    {
        //System.out.println(coodinates.get(i).getX() + "   " + coodinates.get(i).getY());
        this.polygon.lineTo(coodinates.get(i).getX(), coodinates.get(i).getY());
    }
    this.polygon.closePath();
}
public boolean InsideCity(Point2D punto)
{
    return this.polygon.contains(punto);                
}

回答1:

You can use my simple library exactly for this: https://github.com/snatik/polygon-contains-point.

Prepare polygon:

Polygon polygon = Polygon.Builder()
    .addVertex(new Point(1, 3))
    .addVertex(new Point(2, 8))
    .addVertex(new Point(5, 4))
    .addVertex(new Point(5, 9))
    .addVertex(new Point(7, 5))
    .addVertex(new Point(6, 1))
    .addVertex(new Point(3, 1))
    .build();

And check whereas the point is inside the polygon:

Point point = new Point(4.5f, 7);
boolean contains = polygon.contains(point);

It works with float types and with polygons that contain holes :)



回答2:

You can use Google Maps PolyUtil:

import com.google.maps.android.PolyUtil;

boolean inside = PolyUtil.containsLocation(new LatLng(...), poly, true);


回答3:

Here is how I did it in Android. It is based on this java program (ray casting algorithm) : https://gis.stackexchange.com/questions/42879/check-if-lat-long-point-is-within-a-set-of-polygons-using-google-maps/46720#46720

    public boolean pointInPolygon(LatLng point, Polygon polygon) {
        // ray casting alogrithm http://rosettacode.org/wiki/Ray-casting_algorithm
        int crossings = 0;
        List<LatLng> path = polygon.getPoints();
        path.remove(path.size()-1); //remove the last point that is added automatically by getPoints()

        // for each edge
        for (int i=0; i < path.size(); i++) {
            LatLng a = path.get(i);
            int j = i + 1;
            //to close the last edge, you have to take the first point of your polygon
            if (j >= path.size()) {
                j = 0;
            }
            LatLng b = path.get(j);
            if (rayCrossesSegment(point, a, b)) {
                crossings++;
            }
        }

        // odd number of crossings?
        return (crossings % 2 == 1);
     }

    public boolean rayCrossesSegment(LatLng point, LatLng a,LatLng b) {
                // Ray Casting algorithm checks, for each segment, if the point is 1) to the left of the segment and 2) not above nor below the segment. If these two conditions are met, it returns true
                double px = point.longitude,
                py = point.latitude,
                ax = a.longitude,
                ay = a.latitude,
                bx = b.longitude,
                by = b.latitude;
            if (ay > by) {
                ax = b.longitude;
                ay = b.latitude;
                bx = a.longitude;
                by = a.latitude;
            }
            // alter longitude to cater for 180 degree crossings
            if (px < 0 || ax <0 || bx <0) { px += 360; ax+=360; bx+=360; }
            // if the point has the same latitude as a or b, increase slightly py
            if (py == ay || py == by) py += 0.00000001;


            // if the point is above, below or to the right of the segment, it returns false
            if ((py > by || py < ay) || (px > Math.max(ax, bx))){ 
                return false;
            }
            // if the point is not above, below or to the right and is to the left, return true
            else if (px < Math.min(ax, bx)){ 
                return true;
            }
            // if the two above conditions are not met, you have to compare the slope of segment [a,b] (the red one here) and segment [a,p] (the blue one here) to see if your point is to the left of segment [a,b] or not
            else {
                double red = (ax != bx) ? ((by - ay) / (bx - ax)) : Double.POSITIVE_INFINITY;
                double blue = (ax != px) ? ((py - ay) / (px - ax)) : Double.POSITIVE_INFINITY;
                return (blue >= red);
            }

     }


回答4:

Sorry @sromku I asked my self (I never used this type of things)

That's how I solved if anyone have the same question

Builder poly2 = new Polygon.Builder();
    for(int i = 0; i< xpoints.length;i++){
        poly2.addVertex(new Point(xpoints[i],ypoints[i]));
    }
    Polygon polygon2 = poly2.build();