package edu.oregonstate.cartography.simplefeatures; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.util.ArrayList; public class LineString extends Geometry { //Stores Point objects in an ArrayList called linePoints ArrayList<Point> points = new ArrayList<>(); //Returns the number of points public int getNumPoints() { return points.size(); } //Returns point at specified index public Point getPointN(int index) { return points.get(index); } public Point getFirstPoint() { return points.isEmpty() ? null : points.get(0); } public Point getLastPoint() { return points.isEmpty() ? null : points.get(points.size() - 1); } //Points of line field public ArrayList<Point> getPoints() { return points; } /** * Returns the length of this line. * * @return */ public double getLength() { int nPoints = getNumPoints(); if (nPoints < 2) { return 0; } double length = 0; Point p1 = getPointN(0); for (int i = 1; i < nPoints; i++) { Point p2 = getPointN(i); double dx = p1.getX() - p2.getX(); double dy = p1.getY() - p2.getY(); length += Math.sqrt(dx * dx + dy * dy); p1 = p2; } return length; } //Tests if first and last point are identical and returns true if they are //If true, line is closed public boolean isClosed() { Point point1 = points.get(0); Point point2 = points.get(points.size() - 1); return (point1.getX() == point2.getX() && point1.getY() == point2.getY()); } //Appends a passed point to list of existing points public void addPoint(Point addedPoint) { points.add(addedPoint); } public void addPointIfDifferentFromLast(Point p) { int nbrPoints = getNumPoints(); if (nbrPoints == 0) { this.addPoint(p); } else { Point lastPoint = getPointN(nbrPoints - 1); if (!lastPoint.isSameLocation(p)) { points.add(p); } } } //Calls the toString method of each point and concatenates returned strings //to a single string and returns the concatenated string @Override public String toString() { String lineDesc = /*"Attributes:\n" + super.toString() + */"Line:" + "\n"; for (Point point : points) { String desc = point.toString(); lineDesc += desc + "\n"; } return lineDesc; } @Override public void paint(Graphics2D g2d) { //Sets color of lines g2d.setColor(Color.green); //Creates a GeneralPath shape GeneralPath.Double path = new GeneralPath.Double(); //Retrieves the first point Point firstPoint = getPointN(0); //Pass x and y coordinates of the first point in path.moveTo() path.moveTo(firstPoint.getX(), firstPoint.getY()); //Starts at index 1 and iterates over all remaining points, then //calls path.lineTo for (int i = 1; i < points.size(); i++) { Point nextPoint = getPointN(i); path.lineTo(nextPoint.getX(), nextPoint.getY()); } //Paints line g2d.draw(path); } @Override public Rectangle2D getBoundingBox() { //Returns null if linePoints ArrayList does not have any points if (points.size() < 1) { return null; } //Retrieves bounding box of first point of line Point firstPoint = getPointN(0); //Assign bounding box to variable Rectangle2D bb = firstPoint.getBoundingBox(); //Iterate over remaining points and retrieve bounding boxes for (Point point : points) { //creates new bounding box that includes previous points and current point bb = bb.createUnion(point.getBoundingBox()); } return bb; } public LineString densify(double maxSegmentLength) { LineString denseLine = new LineString(); if (getNumPoints() == 0) { return denseLine; } // copy first point Point p1 = getPointN(0); denseLine.addPoint(p1); // densify line segment for (int i = 1; i < getNumPoints(); i++) { Point p2 = getPointN(i); double x1 = p1.getX(); double y1 = p1.getY(); double x2 = p2.getX(); double y2 = p2.getY(); double segmentLength = Math.hypot(x1 - x2, y1 - y2); if (segmentLength > maxSegmentLength) { double dx = (x2 - x1) / segmentLength; double dy = (y2 - y1) / segmentLength; int nbrIntermediatePoints = (int) (segmentLength / maxSegmentLength); for (int j = 0; j < nbrIntermediatePoints; j++) { double x = x1 + j * dx * maxSegmentLength; double y = y1 + j * dy * maxSegmentLength; denseLine.addPoint(new Point(x, y)); } } // add last point denseLine.addPoint(p2); p1 = p2; } return denseLine; } public void smoothPointAttribute(int filterSize, String sourceAttributeName, String targetAttributeName) { if (filterSize % 2 != 1 || filterSize <= 0) { throw new IllegalArgumentException("filter size must be positive odd number"); } if (getNumPoints() == 0 || filterSize == 1) { return; } int halfFilterSize = filterSize / 2; int nbrPoints = getNumPoints(); for (int i = 0; i < nbrPoints; i++) { double total = 0; int nbrPointsInFilter = 0; for (int j = -halfFilterSize; j <= halfFilterSize; j++) { int ptID = i + j; if (ptID >= 0 && ptID < nbrPoints) { Point pt = getPointN(ptID); total += pt.getAttribute(sourceAttributeName).doubleValue(); nbrPointsInFilter++; } } double smoothValue = total / nbrPointsInFilter; getPointN(i).setAttribute(targetAttributeName, smoothValue); } } /* public static void main (String[]args) { LineString line = new LineString(); for (int i = 0; i < 10; i++) { Point p = new Point(i, i); p.setAttribute("in", i*i); line.addPoint(p); } line.smoothPointAttribute(5, "in", "out"); System.out.println(line); } */ /** * Invert order of points. */ public void invert() { int nPoints = getNumPoints(); for (int i = 0; i < nPoints / 2; i++) { Point p1 = getPointN(i); Point p2 = getPointN(nPoints - i - 1); points.set(nPoints - i - 1, p1); points.set(i, p2); } } /** * Append line * * @param l2 */ public void append(LineString l2) { if (l2 == null || l2.getNumPoints() == 0) { return; } int nPoints = l2.getNumPoints(); for (int i = 0; i < nPoints; i++) { Point p = l2.getPointN(i); addPointIfDifferentFromLast(p); } } /** * Joins two lines. Inverts order of points in lines if necessary. Does not * modify passed lines. * * @param l1 * @param l2 * @param centralPt * @return */ public static LineString concatenateLines(LineString l1, LineString l2, Point centralPt) { if (l1.getFirstPoint().isSameLocation(centralPt)) { l1.invert(); } if (!l2.getFirstPoint().isSameLocation(centralPt)) { l2.invert(); } LineString combinedLine = new LineString(); combinedLine.append(l1); combinedLine.append(l2); return combinedLine; } }