private static Segment candidateSegment(final List<PolyLine> candidates, final int polyLineIndex, final int segmentIndex) { return new Segment(candidates.get(polyLineIndex).get(segmentIndex), segmentIndex < candidates.get(polyLineIndex).size() - 1 ? candidates.get(polyLineIndex).get(segmentIndex + 1) : candidates.get(polyLineIndex).first()); }
/** * @param segment * The {@link Segment} to test * @return True if one of the segments of this {@link PolyLine} is the provided {@link Segment} */ public boolean contains(final Segment segment) { final List<Segment> segments = this.segments(); for (final Segment thisSegment : segments) { if (thisSegment.equals(segment)) { return true; } } return false; }
final Segment variable = new Segment(shape.start(), origin); final double dotProduct = shape.dotProduct(variable); if (dotProduct <= 0) return new SnappedLocation(origin, shape.start(), shape); if (dotProduct >= shape.dotProduct(shape)) // NOSONAR return new SnappedLocation(origin, shape.end(), shape); / (shape.dotProductLength() * variable.dotProductLength()); final double offsetDistance = cosAlpha * variable.dotProductLength(); final double latitudeAsDm7 = shape.start().getLatitude().asDm7() + offsetDistance / shape.dotProductLength() * shape.latitudeSpan(); final double longitudeAsDm7 = shape.start().getLongitude().asDm7() + offsetDistance / shape.dotProductLength() * shape.longitudeSpan(); final Location snapped = new Location(Latitude.dm7(Math.round(latitudeAsDm7)), Longitude.dm7(Math.round(longitudeAsDm7)));
/** * @return The location of the biggest Angle in this {@link PolyLine} */ public Optional<Location> maximumAngleLocation() { final List<Segment> segments = segments(); if (segments.isEmpty() || segments.size() == 1) { return Optional.empty(); } Angle maximum = Angle.NONE; Location maximumAngleLocation = null; for (int i = 1; i < segments.size(); i++) { final Segment first = segments.get(i - 1); final Segment second = segments.get(i); final Optional<Heading> firstHeading = first.heading(); final Optional<Heading> secondHeading = second.heading(); if (firstHeading.isPresent() && secondHeading.isPresent()) { final Angle candidate = firstHeading.get().difference(secondHeading.get()); if (candidate.isGreaterThan(maximum) || maximumAngleLocation == null) { maximum = candidate; maximumAngleLocation = first.end(); } } } return Optional.ofNullable(maximumAngleLocation); }
/** * For an point defined by the two surrounding segments, return the angle and location of that * point if that point is not part of a curve, and the angle between the two segments is less * than headingThreshold. * * @param beforeAngle * the segment directly before the point in question * @param afterAngle * the segment directly after the point in question * @param curvedLocations * the locations of all curved segments in the polygon * @return an empty optional if the point is part of a curve, or if the angle is greater than or * equal to headingThreshold. Otherwise, a tuple containing the location of the point * and the angle between beforeAnge and afterAngle */ private Optional<Tuple<Angle, Location>> getSpikyAngleLocation(final Segment beforeAngle, final Segment afterAngle, final Set<Location> curvedLocations) { if (!curvedLocations.contains(afterAngle.end()) && !curvedLocations.contains(beforeAngle.start())) { final Angle difference = this.getDifferenceInHeadings(beforeAngle, afterAngle.reversed(), Angle.MAXIMUM); if (difference.isLessThan(headingThreshold)) { return Optional.of(Tuple.createTuple(difference, afterAngle.start())); } } return Optional.empty(); }
/** * Tests if this {@link Polygon} wraps (geometrically contains) a {@link Segment} * * @param segment * The {@link Segment} to test * @return True if this {@link Polygon} wraps (geometrically contains) the provided * {@link Segment} */ public boolean fullyGeometricallyEncloses(final Segment segment) { final Set<Location> intersections = this.intersections(segment); for (final Location intersection : intersections) { if (!intersection.equals(segment.start()) && !intersection.equals(segment.end())) { // This is a non-end intersection return false; } } return this.fullyGeometricallyEncloses(segment.middle()); }
@Test public void testIntersection() { Assert.assertEquals(null, new Segment(Location.TEST_1, Location.TEST_2) .intersection(new Segment(Location.TEST_4, Location.TEST_3))); Assert.assertEquals(false, new Segment(Location.TEST_1, Location.TEST_2) .intersects(new Segment(Location.TEST_4, Location.TEST_3))); Assert.assertEquals( new Location(Latitude.degrees(37.3273389), Longitude.degrees(-122.0287109)), new Segment(Location.TEST_1, Location.TEST_3) .intersection(new Segment(Location.TEST_4, Location.TEST_2))); Assert.assertEquals(true, new Segment(Location.TEST_1, Location.TEST_3) .intersects(new Segment(Location.TEST_4, Location.TEST_2))); } }
if (curvedSectionEnd.equals(beforeAndAfter.getSecond())) locations.add(curvedSectionEnd.start()); curvedSectionIndex++; if (curvedSectionIndex >= curvedSections.size()) locations.add(beforeAndAfter.getFirst().end()); if (curvedSectionStart.equals(beforeAndAfter.getFirst())) locations.add(curvedSectionStart.end());
/** * @return The {@link Segment}'s {@link Heading}. In case the segment is the same start and end * locations, then the result is empty. */ public Optional<Heading> heading() { if (this.isPoint()) { logger.warn( "Cannot compute a segment's heading when the segment is a point with same start and end {}", this.start()); return Optional.empty(); } return Optional.of(this.start().headingTo(this.end())); }
/** * Gets the difference in headings between firstSegment and secondSegment, returning * defaultAngle if either segments are a point. * * @param firstSegment * the first segment to compare * @param secondSegment * the second segment to compare * @param defaultAngle * the default value to return * @return the difference between firstSegment.heading() and secondSegment.heading() if neither * segment is a single point (same start and end nodes), or defaultAngle if either * segment is a single point */ private Angle getDifferenceInHeadings(final Segment firstSegment, final Segment secondSegment, final Angle defaultAngle) { return firstSegment.heading() .flatMap(first -> secondSegment.heading().map(first::difference)) .orElse(defaultAngle); }
public Location offsetFromStart(final Ratio ratio) { final Distance length = length(); final Distance stop = length.scaleBy(ratio); Distance accumulated = Distance.ZERO; final List<Segment> segments = this.segments(); for (final Segment segment : segments) { if (accumulated.add(segment.length()).isGreaterThan(stop)) { // This is the proper segment final Ratio segmentRatio = Ratio.ratio( stop.substract(accumulated).asMeters() / segment.length().asMeters()); return segment.offsetFromStart(segmentRatio); } if (accumulated.add(segment.length()).equals(stop)) { return segment.end(); } accumulated = accumulated.add(segment.length()); } throw new CoreException("This exception should never be thrown."); }
@Test public void testPolygonWith45DegreeZeroAreaPartContains() { // Shape is a triangle, with a zero area line protruding from one of the corners on an // incline final Polygon polygon = Polygon.wkt("POLYGON ((-0.0065127 0.0214697, -0.0092975 0.0054797," + " -0.0233112 -0.0085339, 0.0027398 0.0175171, -0.0065127 0.0214697))"); final Location middleZeroAreaPart = polygon.segmentForIndex(1).middle(); final Location endpointZeroAreaPart = polygon.segmentForIndex(1).end(); final Location middleThirdSegment = polygon.segmentForIndex(2).middle(); // Locations on the zero area part are still on the boundary, and therefore contained Assert.assertTrue(polygon.fullyGeometricallyEncloses(middleZeroAreaPart)); // see awt definition of contains Assert.assertFalse(polygon.fullyGeometricallyEncloses(endpointZeroAreaPart)); Assert.assertTrue(polygon.fullyGeometricallyEncloses(middleThirdSegment)); }
@Test public void testSegment() { final Segment shape = new Segment(Location.TEST_6, Location.TEST_1); final Location origin = Location.TEST_2; Assert.assertEquals(Location.forString("37.3268107,-122.030562"), origin.snapTo(shape)); Assert.assertEquals(Location.TEST_6, shape.snapFrom(Location.TEST_3)); Assert.assertEquals(Location.TEST_1, shape.snapFrom(Location.EIFFEL_TOWER)); }
/** * @return True if the {@link PolyLine} self intersects at locations other than shape points. */ public boolean selfIntersects() { // See comments on algorithm in selfIntersections() final boolean isPolygon = this instanceof Polygon; final List<Segment> segments = this.segments().stream() .filter(segment -> !segment.isPoint()).collect(Collectors.toList()); for (int i = 0; i < segments.size() - 2; i++) { final int limit = isPolygon && i == 0 ? segments.size() - 1 : segments.size(); for (int j = i + 2; j < limit; j++) { if (segments.get(i).intersection(segments.get(j)) != null) { return true; } } } return false; }
public Set<Location> intersections(final Segment candidate) { final Set<Location> result = new HashSet<>(); final List<Segment> segments = this.segments(); segments.forEach(segment -> { final Location intersection = segment.intersection(candidate); if (intersection != null) { result.add(intersection); } }); return result; }