@Override public Distance length() { return this.start().distanceTo(this.end()); }
/** * @return True if this segment is exactly east west (the two latitudes are the same) */ public boolean isEastWest() { return start().hasSameLatitudeAs(end()); }
/** * @return True if this segment is exactly north south (the two longitudes are the same) */ public boolean isNorthSouth() { return start().hasSameLongitudeAs(end()); }
@Override public boolean isPoint() { return start().equals(end()); }
@Override public Segment reversed() { return new Segment(end(), start()); }
/** * @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())); }
/** * Convenience method to gather all {@link Location}s for a list of segments. * * @param segments * target segments * @return a list of {@link Location}s for the given segments */ public static List<Location> asList(final Iterable<Segment> segments) { final List<Location> result = new ArrayList<>(); Iterables.stream(segments).forEach(segment -> { if (result.isEmpty() || !result.get(result.size() - 1).equals(segment.start())) { result.add(segment.start()); } result.add(segment.end()); }); return result; }
/** * @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); }
if (candidate.isGreaterThanOrEqualTo(target)) final Tuple<Angle, Location> tuple = Tuple.createTuple(candidate, first.end()); result.add(tuple);
/** * 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(); }
protected long longitudeSpan() { return this.end().getLongitude().asDm7() - this.start().getLongitude().asDm7(); } }
protected long latitudeSpan() { return this.end().getLatitude().asDm7() - this.start().getLatitude().asDm7(); }
/** * 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()); }
return new SnappedLocation(origin, shape.end(), shape);
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)); }