Проверка точки внутри многоугольника в Android

На днях я сделал класс на Java, чтобы вычислить, находится ли point(X,Y) внутри многоугольника. (X и Y это double, т.к. это будут геокоординаты).

Я знаю, что в Java есть класс Polygon, но мне пришлось использовать Path2D и Point2D, потому что Polygon не допускает double, только целые числа :(

Как только я сделал многоугольник в Path2D, я использовал метод contains (он был у Path2D), и моя проблема была решена.

Но теперь я хочу импортировать на Android, и проблема здесь, потому что Path2D нужно импортировать:

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

а в андроиде awt не существует, так что не могу пользоваться.

Итак, есть ли класс, похожий на Path2D, в котором есть метод contains? или я должен рассчитать сам?

Вот как я это сделал на Java, используя 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);                
}

person Shudy    schedule 04.04.2013    source источник
comment
Может быть, вы могли бы просто умножить все значения double на 10 000 и использовать их с классом Java Polygon?   -  person Carlos P    schedule 20.04.2016


Ответы (4)


Вы можете использовать мою простую библиотеку именно для этого: https://github.com/snatik/polygon-contains-point.

Подготовить полигон:

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

И проверьте, находится ли точка внутри многоугольника:

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

Он работает с типами float и с полигонами, содержащими дырки :)

person sromku    schedule 04.04.2013
comment
Привет @sromku ! У меня есть один вопрос, мне нужно прочитать файл KML, чтобы получить координаты, но это может быть более 100 точек... так как я могу использовать Builder для всех этих точек? Потому что моя идея состоит в том, чтобы прочитать kml, получить точки в векторе (например), а затем построить многоугольник ... так ... я не знаю, как использовать так, как вы :( Можете ли вы мне помочь ?Спасибо!!(код работает отлично!) - person Shudy; 10.04.2013
comment
Обратите внимание, что этот код не проходит тестовый случай, когда вы попадаете прямо в вершину со странным наклонным лучом, который он использует. - person Tatarize; 26.04.2016
comment
В этой библиотеке есть проблемы даже с самыми простыми формами, проверьте список проблем, прежде чем рассматривать. github.com/sromku/polygon-contains-point/issues - person fnieto - Fernando Nieto; 09.07.2016
comment
@KaveeshKanwal да, будет - person sromku; 11.03.2018
comment
@sromku В случае координат карты правильный ли порядок новой точки (широта, долгота)? или новая точка (долгота, широта)? - person Inosh Perera; 16.03.2018

Вы можете использовать Google Maps PolyUtil:

import com.google.maps.android.PolyUtil;

boolean inside = PolyUtil.containsLocation(new LatLng(...), poly, true);
person fnieto - Fernando Nieto    schedule 09.07.2016

Вот как я это сделал на Android. Он основан на этой программе Java (алгоритм литья лучей): https://gis.stackexchange.com/questions/42879/check-if-lat-long-point-is-in-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);
            }

     }
person ylag75    schedule 14.09.2014
comment
Пожалуйста, поделитесь ссылкой, чтобы получить класс Polygon - person ; 28.04.2015

Извините @sromku, я спросил себя (я никогда не пользовался такими вещами)

Вот как я решил, если у кого-то есть такой же вопрос

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();
person Shudy    schedule 10.04.2013
comment
Откуда вы импортируете Polygon.Builder(); ? - person Muhammad Saqib; 10.05.2020
comment
благодаря @sromku. github. com/sromku/polygon-contains-point/blob/master/src/main/ - person n-y; 11.02.2021