Changeset 15008 in josm


Ignore:
Timestamp:
2019-04-21T07:43:00+02:00 (5 years ago)
Author:
GerdP
Message:

fix #17614:

  • don't test integer bounding box of intersection area, use doubles
  • in case of multiple intersections, check each individual intersection
  • introduce new constant INTERSECTION_EPS_EAST_NORTH, the value 1e-4 is just a reasonable small value, might be decreased
  • improve javadoc to make clear that east/north space is assumed
  • small refactorings to fix SonarLint issues
Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/WaySegment.java

    r13030 r15008  
    1313     * The way.
    1414     */
    15     public Way way;
     15    public final Way way;
    1616
    1717    /**
     
    1919     * index <code>lowerIndex + 1</code>.
    2020     */
    21     public int lowerIndex;
     21    public final int lowerIndex;
    2222
    2323    /**
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java

    r14437 r15008  
    272272                for (int j = i + 1; j < allPolygons.size(); j++) {
    273273                    PolyData pd2 = allPolygons.get(j);
    274                     if (!checkProblemMap(crossingPolyMap, pd1, pd2)) {
    275                         if (hasIntersectionWay(pd2, intersectionWays))
    276                             checkPolygonsForSharedNodes(r, pd1, pd2, sharedNodes);
     274                    if (!checkProblemMap(crossingPolyMap, pd1, pd2) && hasIntersectionWay(pd2, intersectionWays)) {
     275                        checkPolygonsForSharedNodes(r, pd1, pd2, sharedNodes);
    277276                    }
    278277                }
     
    472471            Area a1 = new Area(pd1.get());
    473472            Area a2 = new Area(pd2.get());
    474             PolygonIntersection areaRes = Geometry.polygonIntersection(a1, a2, 1e-6);
     473            PolygonIntersection areaRes = Geometry.polygonIntersection(a1, a2);
    475474            if (areaRes == PolygonIntersection.OUTSIDE)
    476475                return ExtPolygonIntersection.OUTSIDE;
     
    842841
    843842            for (PolyData pd : polygons) {
    844                 if (processOuterWay(level, polygons, result, pd) == null) {
    845                     return null;
    846                 }
     843                processOuterWay(level, polygons, result, pd);
    847844            }
    848845
     
    850847        }
    851848
    852         private Object processOuterWay(int level, List<PolyData> polygons, List<PolygonLevel> result, PolyData pd) {
     849        private void processOuterWay(int level, List<PolyData> polygons, List<PolygonLevel> result, PolyData pd) {
    853850            List<PolyData> inners = findInnerWaysCandidates(pd, polygons);
    854851
     
    865862                result.add(pol);
    866863            }
    867             return result;
    868864        }
    869865
  • trunk/src/org/openstreetmap/josm/tools/Geometry.java

    r14521 r15008  
    22package org.openstreetmap.josm.tools;
    33
    4 import java.awt.Rectangle;
    54import java.awt.geom.Area;
    65import java.awt.geom.Line2D;
    76import java.awt.geom.Path2D;
     7import java.awt.geom.PathIterator;
     8import java.awt.geom.Rectangle2D;
    89import java.math.BigDecimal;
    910import java.math.MathContext;
     
    7273    }
    7374
     75    /** threshold value for size of intersection area given in east/north space */
     76    public static final double INTERSECTION_EPS_EAST_NORTH = 1e-4;
     77
    7478    /**
    7579     * Will find all intersection and add nodes there for list of given ways.
     
    578582        Area a1 = getArea(first);
    579583        Area a2 = getArea(second);
    580         return polygonIntersection(a1, a2);
    581     }
    582 
    583     /**
    584      * Tests if two polygons intersect.
     584        return polygonIntersection(a1, a2, INTERSECTION_EPS_EAST_NORTH);
     585    }
     586
     587    /**
     588     * Tests if two polygons intersect. It is assumed that the area is given in East North points.
    585589     * @param a1 Area of first polygon
    586590     * @param a2 Area of second polygon
     
    589593     */
    590594    public static PolygonIntersection polygonIntersection(Area a1, Area a2) {
    591         return polygonIntersection(a1, a2, 1.0);
     595        return polygonIntersection(a1, a2, INTERSECTION_EPS_EAST_NORTH);
    592596    }
    593597
     
    604608        inter.intersect(a2);
    605609
    606         Rectangle bounds = inter.getBounds();
    607 
    608         if (inter.isEmpty() || bounds.getHeight()*bounds.getWidth() <= eps) {
     610        if (inter.isEmpty() || !checkIntersection(inter, eps)) {
    609611            return PolygonIntersection.OUTSIDE;
    610612        } else if (a2.getBounds2D().contains(a1.getBounds2D()) && inter.equals(a1)) {
     
    618620
    619621    /**
     622     * Check an intersection area which might describe multiple small polygons.
     623     * Return true if any of the polygons is bigger than the given threshold.
     624     * @param inter the intersection area
     625     * @param eps an area threshold, everything below is considered an empty intersection
     626     * @return true if any of the polygons is bigger than the given threshold
     627     */
     628    private static boolean checkIntersection(Area inter, double eps) {
     629        PathIterator pit = inter.getPathIterator(null);
     630        double[] res = new double[6];
     631        Rectangle2D r = new Rectangle2D.Double();
     632        while (!pit.isDone()) {
     633            int type = pit.currentSegment(res);
     634            switch (type) {
     635            case PathIterator.SEG_MOVETO:
     636                r = new Rectangle2D.Double(res[0], res[1], 0, 0);
     637                break;
     638            case PathIterator.SEG_LINETO:
     639                r.add(res[0], res[1]);
     640                break;
     641            case PathIterator.SEG_CLOSE:
     642                if (r.getWidth() > eps || r.getHeight() > eps)
     643                    return true;
     644                break;
     645            default:
     646                break;
     647            }
     648            pit.next();
     649        }
     650        return false;
     651    }
     652
     653    /**
    620654     * Tests if point is inside a polygon. The polygon can be self-intersecting. In such case the contains function works in xor-like manner.
    621655     * @param polygonNodes list of nodes from polygon path.
     
    778812     *
    779813     * @param p1 first point
    780      * @param p2 Common endpoint
     814     * @param common Common end point
    781815     * @param p3 third point
    782816     * @return Angle in radians (-pi, pi]
    783817     */
    784     public static double getCornerAngle(EastNorth p1, EastNorth p2, EastNorth p3) {
     818    public static double getCornerAngle(EastNorth p1, EastNorth common, EastNorth p3) {
    785819
    786820        CheckParameterUtil.ensure(p1, "p1", EastNorth::isValid);
    787         CheckParameterUtil.ensure(p2, "p2", EastNorth::isValid);
     821        CheckParameterUtil.ensure(common, "p2", EastNorth::isValid);
    788822        CheckParameterUtil.ensure(p3, "p3", EastNorth::isValid);
    789823
    790         Double result = getSegmentAngle(p2, p1) - getSegmentAngle(p2, p3);
     824        double result = getSegmentAngle(common, p1) - getSegmentAngle(common, p3);
    791825        if (result <= -Math.PI) {
    792826            result += 2 * Math.PI;
  • trunk/test/unit/org/openstreetmap/josm/tools/GeometryTest.java

    r13712 r15008  
    33
    44import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertNotEquals;
    56
    67import java.io.FileInputStream;
     
    159160        assertEquals(new EastNorth(150, 266d + 2d/3d), Geometry.getCentroidEN(Arrays.asList(en1, en2, en3)));
    160161    }
     162
     163
     164    /**
     165     * Test of {@link Geometry#polygonIntersection} method with two triangles.
     166     */
     167    @Test
     168    public void testPolygonIntersectionTriangles() {
     169        Node node1 = new Node(new LatLon(0.0, 1.0));
     170        Node node2 = new Node(new LatLon(0.0, 2.0));
     171        Node node3 = new Node(new LatLon(5.0, 1.5));
     172        List<Node> poly1 = Arrays.asList(node1, node2, node3, node1);
     173        Node node4 = new Node(new LatLon(10.0, 1.0));
     174        Node node5 = new Node(new LatLon(10.0, 2.0));
     175        Node node6 = new Node(new LatLon(5.000001, 1.5));
     176        List<Node> poly2 = Arrays.asList(node4, node5, node6, node4);
     177        // no intersection, not even touching
     178        assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
     179
     180        node5.setCoor(new LatLon(5.0, 1.5));
     181        // touching in a single point with two different nodes
     182        assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
     183
     184        node5.setCoor(new LatLon(4.99999999, 1.5));
     185        // now node5 lies inside way1, intersection is a very small area, in OSM precision nodes are equal
     186        assertEquals(node5.getCoor().getRoundedToOsmPrecision(), node3.getCoor().getRoundedToOsmPrecision());
     187        assertEquals(Geometry.PolygonIntersection.CROSSING, Geometry.polygonIntersection(poly1, poly2));
     188
     189        node5.setCoor(new LatLon(4.9999999, 1.5));
     190        // intersection area is too big to ignore
     191        assertNotEquals(node5.getCoor().getRoundedToOsmPrecision(), node3.getCoor().getRoundedToOsmPrecision());
     192        assertEquals(Geometry.PolygonIntersection.CROSSING, Geometry.polygonIntersection(poly1, poly2));
     193    }
     194
     195    /**
     196     * Test of {@link Geometry#polygonIntersection} method with two V-shapes
     197     */
     198    @Test
     199    public void testPolygonIntersectionVShapes() {
     200        Node node1 = new Node(new LatLon(1.0, 1.0));
     201        Node node2 = new Node(new LatLon(2.0, 2.0));
     202        Node node3 = new Node(new LatLon(0.9, 1.0));
     203        Node node4 = new Node(new LatLon(2.0, 0.0));
     204        List<Node> poly1 = Arrays.asList(node1, node2, node3, node4, node1);
     205        Node node5 = new Node(new LatLon(3.0, 1.0));
     206        Node node6 = new Node(new LatLon(2.0, 2.0)); // like node2
     207        Node node7 = new Node(new LatLon(3.1, 1.0));
     208        Node node8 = new Node(new LatLon(2.0, 0.0)); // like node4
     209        List<Node> poly2 = Arrays.asList(node5, node6, node7, node8, node5);
     210
     211        // touching in two points but not overlapping
     212        assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
     213
     214        // touching in one point, small overlap at the other
     215        node6.setCoor(new LatLon(1.9999999, 2.0));
     216        assertEquals(Geometry.PolygonIntersection.CROSSING, Geometry.polygonIntersection(poly1, poly2));
     217
     218        // two small overlaps, but clearly visible because lines are crossing
     219        node6.setCoor(new LatLon(1.99999999, 2.0));
     220        node8.setCoor(new LatLon(1.99999999, 0.0));
     221        assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
     222    }
    161223}
Note: See TracChangeset for help on using the changeset viewer.