public Location firstLocation() { return this.polyLines.get(0).first(); }
public Location start() { return this.getPolyLine().first(); }
PossiblePolygon(final PolyLine first) { this.completed = first instanceof Polygon || first.first().equals(first.last()); this.polyLines.add(first); }
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()); }
/** * @return The overall heading of the {@link PolyLine}: the heading between the start point and * the end point. */ public Optional<Heading> overallHeading() { if (this.isPoint()) { logger.warn("Cannot compute a segment's heading when the polyline has zero length : {}", this); return Optional.empty(); } return Optional.ofNullable(this.first().headingTo(this.last())); }
/** * Check if this {@link LineItem} is closed. Closed is defined when the first {@link Location} * is the same as the last {@link Location}. * * @return {@code true} if it's closed. */ public boolean isClosed() { final PolyLine polyLine = asPolyLine(); return polyLine.first().equals(polyLine.last()); }
/** * Test if two {@link PolyLine}s connect by prepending the first {@link PolyLine} (straight * or reversed) to the second one (unchanged). * * @param one * The {@link PolyLine} from which the end will be considered * @param two * The {@link PolyLine} from which the start will be considered * @return ConnectResult: connected = true if the end of one is the same as the start of two * and reversed = true if the {@link PolyLine} one had to be reversed to be able to * connect the end of one to the beginning of two. */ private ConnectResult canPrependFirstToSecond(final PolyLine one, final PolyLine two) { if (one.last().equals(two.first())) { return new ConnectResult(true, false); } else if (one.first().equals(two.first())) { return new ConnectResult(true, true); } else { return new ConnectResult(false, false); } }
@Override public String toString() { final StringList list = new StringList(); this.polyLines.forEach(polyLine -> list.add(polyLine.first() + " -> ")); list.add(this.lastLocation()); return list.join(""); }
public Polygon toPolygon() { if (!this.isCompleted() && this.size() >= 1) { // If that method is called and the PossiblePolygon is not closed (i.e. completed) // we gather the first and end point of the partially completed polyline and throw // an exception. final List<Location> openLocations = new ArrayList<>(); final Location firstLocation = this.polyLines.get(0).first(); final Location lastLocation = this.polyLines.get(this.size() - 1).last(); if (firstLocation != null && lastLocation != null) { openLocations.add(firstLocation); openLocations.add(lastLocation); throw new OpenPolygonException( "Cannot build polygon with multiple polylines. Loop is not closed.", openLocations); } } return new Polygon(new MultiIterable<>(this.polyLines)); }
/** * @return This {@link PolyLine} as Well Known Text */ @Override public String toWkt() { if (this.size() == 1) { // Handle a single location polyLine return new WktLocationConverter().convert(this.first()); } return new WktPolyLineConverter().convert(this); }
/** * @return This {@link PolyLine} as Well Known Binary */ @Override public byte[] toWkb() { if (this.size() == 1) { // Handle a single location polyLine return new WkbLocationConverter().convert(this.first()); } return new WkbPolyLineConverter().convert(this); }
public PolyLine shiftFirstAlongGreatCircle(final Heading initialHeading, final Distance distance) { return new PolyLine(new MultiIterable<>( Iterables.from(this.first().shiftAlongGreatCircle(initialHeading, distance)), this.truncate(1, 0))); }
/** * Append the given {@link PolyLine} to this one, if possible. * * @param other * The {@link PolyLine} to append * @return the new, combined {@link PolyLine} */ public PolyLine append(final PolyLine other) { if (this.last().equals(other.first())) { return new PolyLine(new MultiIterable<>(this, other.truncate(1, 0))); } else { throw new CoreException( "Cannot append {} to {} - the end and start points do not match.", other.toWkt(), this.toWkt()); } }
/** * Prepends the given {@link PolyLine} to this one, if possible. * * @param other * The {@link PolyLine} to prepend * @return the new, combined {@link PolyLine} */ public PolyLine prepend(final PolyLine other) { if (this.first().equals(other.last())) { return new PolyLine(new MultiIterable<>(other, this.truncate(1, 0))); } else { throw new CoreException( "Cannot prepend {} to {} - the end and start points do not match.", other.toWkt(), this.toWkt()); } }
/** * Test if two {@link PolyLine}s connect by appending the second {@link PolyLine} (straight * or reversed) to the first one (unchanged). * * @param one * The {@link PolyLine} from which the end will be considered * @param two * The {@link PolyLine} from which the start will be considered * @return ConnectResult: connected = true if the end of one is the same as the start of two * and reversed = true if the {@link PolyLine} two had to be reversed to be able to * connect the end of one to the beginning of two. */ private ConnectResult canAppendSecondToFirst(final PolyLine one, final PolyLine two) { if (one.last().equals(two.first())) { return new ConnectResult(true, false); } else if (one.last().equals(two.last())) { return new ConnectResult(true, true); } else { return new ConnectResult(false, false); } }
/** * Get the offset from the start of the node's location * * @param node * The location to test * @param occurrenceIndex * In case of a self intersecting polyline (one or more locations appear more than * once), indicate the index at which this method should return the location. 0 would * be first occurrence, 1 second, etc. * @return The offset ratio from the start of the {@link PolyLine} */ public Ratio offsetFromStart(final Location node, final int occurrenceIndex) { final Distance max = this.length(); Distance candidate = Distance.ZERO; Location previous = this.first(); int index = 0; for (final Location location : this) { candidate = candidate.add(previous.distanceTo(location)); if (location.equals(node) && occurrenceIndex == index++) { return Ratio.ratio(candidate.asMeters() / max.asMeters()); } previous = location; } throw new CoreException("The location {} is not a node of the PolyLine", node); }
protected void validateEdgeToNodeLocationAccuracy() { for (final Edge edge : this.atlas.edges()) { final Location startNodeLocation = edge.start().getLocation(); final Location edgeStartLocation = edge.asPolyLine().first(); if (!startNodeLocation.equals(edgeStartLocation)) { throw new CoreException( "Edge {} with start location {} does not match with its start Node {} at location: {}", edge.getIdentifier(), edgeStartLocation, edge.start().getIdentifier(), startNodeLocation); } final Location endNodeLocation = edge.end().getLocation(); final Location edgeEndLocation = edge.asPolyLine().last(); if (!endNodeLocation.equals(edgeEndLocation)) { throw new CoreException( "Edge {} with end location {} does not match with its end Node {} at location: {}", edge.getIdentifier(), edgeEndLocation, edge.end().getIdentifier(), endNodeLocation); } } }
protected void validateNodeToEdgeLocationAccuracy() { for (final Node node : this.atlas.nodes()) { final Location nodeLocation = node.getLocation(); for (final Edge edge : node.outEdges()) { final Location edgeStartLocation = edge.asPolyLine().first(); if (!nodeLocation.equals(edgeStartLocation)) { throw new CoreException( "Edge {} with start location {} does not match with its start Node {} at location: {}", edge.getIdentifier(), edgeStartLocation, edge.start().getIdentifier(), nodeLocation); } } for (final Edge edge : node.inEdges()) { final Location edgeEndLocation = edge.asPolyLine().last(); if (!nodeLocation.equals(edgeEndLocation)) { throw new CoreException( "Edge {} with end location {} does not match with its end Node {} at location: {}", edge.getIdentifier(), edgeEndLocation, edge.end().getIdentifier(), nodeLocation); } } } } }
@Test public void testModifyForwardEdgeWithoutReverseEdge() { this.expectedException.expect(CoreException.class); this.expectedException.expectMessage("have mismatching PolyLines"); final Atlas atlas = this.rule.getAtlasEdge(); final ChangeBuilder changeBuilder = new ChangeBuilder(); final Edge edge = atlas.edge(39001000001L); final PolyLine oldPolyLine = edge.asPolyLine(); final PolyLine newPolyLine = new PolyLine(oldPolyLine.first(), NEW_LOCATION, oldPolyLine.last()); final CompleteEdge bloatedEdge = CompleteEdge.shallowFrom(edge).withPolyLine(newPolyLine); final FeatureChange featureChange = new FeatureChange(ChangeType.ADD, bloatedEdge); changeBuilder.add(featureChange); final Change change = changeBuilder.get(); new ChangeAtlas(atlas, change); }
/** * @return Feature change 1: Update the first location in the edge 39001000001L's polyLine */ private Tuple<FeatureChange, FeatureChange> getFeatureChangeUpdatedEdgePolyLine() { final Atlas atlas = this.rule.getAtlasEdge(); final Edge originalEdge1 = atlas.edge(39001000001L); final Edge originalEdge1Reverse = atlas.edge(-39001000001L); // Forward: final PolyLine originalPolyLine1 = originalEdge1.asPolyLine(); final PolyLine originalPolyLine1Modified = new PolyLine( originalPolyLine1.prepend(new PolyLine(NEW_LOCATION, originalPolyLine1.first()))); final CompleteEdge bloatedEdge1 = CompleteEdge.shallowFrom(originalEdge1) .withPolyLine(originalPolyLine1Modified); final FeatureChange featureChange1 = new FeatureChange(ChangeType.ADD, bloatedEdge1); // Backward final PolyLine originalPolyLine1Reverse = originalEdge1Reverse.asPolyLine(); final PolyLine originalPolyLine1ModifiedReverse = new PolyLine(originalPolyLine1Reverse .append(new PolyLine(originalPolyLine1Reverse.last(), NEW_LOCATION))); final CompleteEdge bloatedEdge1Reverse = CompleteEdge.shallowFrom(originalEdge1Reverse) .withPolyLine(originalPolyLine1ModifiedReverse); final FeatureChange featureChange1Reverse = new FeatureChange(ChangeType.ADD, bloatedEdge1Reverse); return new Tuple<>(featureChange1, featureChange1Reverse); } }