All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mil.nga.wkb.util.sweep.SweepLine Maven / Gradle / Ivy

Go to download

Library for writing and reading Well-Known Binary Geometries to and from bytes

There is a newer version: 1.0.6
Show newest version
package mil.nga.wkb.util.sweep;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import mil.nga.wkb.geom.LineString;
import mil.nga.wkb.geom.Point;

/**
 * Sweep Line algorithm
 * 
 * @author osbornb
 * @since 1.0.5
 */
public class SweepLine {

	/**
	 * Segment comparator for adding segments to the sweep line in above-below
	 * order
	 */
	private class SegmentComparator implements Comparator {

		/**
		 * Current sweep x value
		 */
		private double x;

		/**
		 * Set the current sweep x value
		 * 
		 * @param x
		 *            x value
		 */
		public void setX(double x) {
			this.x = x;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int compare(Segment segment1, Segment segment2) {

			double y1 = yValueAtX(segment1, x);
			double y2 = yValueAtX(segment2, x);

			int compare;
			if (y1 < y2) {
				compare = -1;
			} else if (y2 < y1) {
				compare = 1;
			} else if (segment1.getRing() < segment2.getRing()) {
				compare = -1;
			} else if (segment2.getRing() < segment1.getRing()) {
				compare = 1;
			} else if (segment1.getEdge() < segment2.getEdge()) {
				compare = -1;
			} else if (segment2.getEdge() < segment1.getEdge()) {
				compare = 1;
			} else {
				compare = 0;
			}

			return compare;
		}

	}

	/**
	 * Polygon rings
	 */
	private List rings;

	/**
	 * Comparator for ordering segments in above-below order
	 */
	private SegmentComparator comparator = new SegmentComparator();

	/**
	 * Tree of segments sorted by above-below order
	 */
	private TreeSet tree = new TreeSet<>(comparator);

	/**
	 * Mapping between ring, edges, and segments
	 */
	private Map> segments = new HashMap<>();

	/**
	 * Constructor
	 * 
	 * @param rings
	 *            polygon rings
	 */
	public SweepLine(List rings) {
		this.rings = rings;
	}

	/**
	 * Add the event to the sweep line
	 * 
	 * @param event
	 *            event
	 * @return added segment
	 */
	public Segment add(Event event) {

		Segment segment = createSegment(event);

		// Add to the tree
		comparator.setX(event.getPoint().getX());
		tree.add(segment);

		// Update the above and below pointers
		Segment next = tree.higher(segment);
		Segment previous = tree.lower(segment);
		if (next != null) {
			segment.setAbove(next);
			next.setBelow(segment);
		}
		if (previous != null) {
			segment.setBelow(previous);
			previous.setAbove(segment);
		}

		// Add to the segments map
		Map edgeMap = segments.get(segment.getRing());
		if (edgeMap == null) {
			edgeMap = new HashMap<>();
			segments.put(segment.getRing(), edgeMap);
		}
		edgeMap.put(segment.getEdge(), segment);

		return segment;
	}

	/**
	 * Create a segment from the event
	 * 
	 * @param event
	 *            event
	 * @return segment
	 */
	private Segment createSegment(Event event) {

		int edgeNumber = event.getEdge();
		int ringNumber = event.getRing();

		LineString ring = rings.get(ringNumber);
		List points = ring.getPoints();

		Point point1 = points.get(edgeNumber);
		Point point2 = points.get((edgeNumber + 1) % points.size());

		Point left = null;
		Point right = null;
		if (xyOrder(point1, point2) < 0) {
			left = point1;
			right = point2;
		} else {
			right = point1;
			left = point2;
		}

		Segment segment = new Segment(edgeNumber, ringNumber, left, right);

		return segment;
	}

	/**
	 * Find the existing event segment
	 * 
	 * @param event
	 *            event
	 * @return segment
	 */
	public Segment find(Event event) {
		return segments.get(event.getRing()).get(event.getEdge());
	}

	/**
	 * Determine if the two segments intersect
	 * 
	 * @param segment1
	 *            segment 1
	 * @param segment2
	 *            segment 2
	 * @return true if intersection, false if not
	 */
	public boolean intersect(Segment segment1, Segment segment2) {

		boolean intersect = false;

		if (segment1 != null && segment2 != null) {

			int ring1 = segment1.getRing();
			int ring2 = segment2.getRing();

			boolean consecutive = ring1 == ring2;
			if (consecutive) {
				int edge1 = segment1.getEdge();
				int edge2 = segment2.getEdge();
				int ringPoints = rings.get(ring1).numPoints();
				consecutive = (edge1 + 1) % ringPoints == edge2
						|| edge1 == (edge2 + 1) % ringPoints;
			}

			if (!consecutive) {

				double left = isLeft(segment1, segment2.getLeftPoint());
				double right = isLeft(segment1, segment2.getRightPoint());

				if (left * right <= 0) {

					left = isLeft(segment2, segment1.getLeftPoint());
					right = isLeft(segment2, segment1.getRightPoint());

					if (left * right <= 0) {
						intersect = true;
					}
				}
			}
		}

		return intersect;
	}

	/**
	 * Remove the segment from the sweep line
	 * 
	 * @param segment
	 *            segment
	 */
	public void remove(Segment segment) {

		boolean removed = tree.remove(segment);
		if (!removed) {
			comparator.setX(segment.getLeftPoint().getX());
			removed = tree.remove(segment);
		}

		if (removed) {

			Segment above = segment.getAbove();
			Segment below = segment.getBelow();
			if (above != null) {
				above.setBelow(below);
			}
			if (below != null) {
				below.setAbove(above);
			}

			segments.get(segment.getRing()).remove(segment.getEdge());
		}

	}

	/**
	 * Get the segment y value at the x location by calculating the line slope
	 * 
	 * @param segment
	 *            segment
	 * @param x
	 *            current point x value
	 * @return segment y value
	 */
	private double yValueAtX(Segment segment, double x) {

		Point left = segment.getLeftPoint();
		Point right = segment.getRightPoint();

		double m = (right.getY() - left.getY()) / (right.getX() - left.getX());
		double b = left.getY() - (m * left.getX());

		double y = (m * x) + b;

		return y;
	}

	/**
	 * XY order of two points
	 * 
	 * @param point1
	 *            point 1
	 * @param point2
	 *            point 2
	 * @return +1 if p1 > p2, -1 if p1 < p2, 0 if equal
	 */
	public static int xyOrder(Point point1, Point point2) {
		int value = 0;
		if (point1.getX() > point2.getX()) {
			value = 1;
		} else if (point1.getX() < point2.getX()) {
			value = -1;
		} else if (point1.getY() > point2.getY()) {
			value = 1;
		} else if (point1.getY() < point2.getY()) {
			value = -1;
		}
		return value;
	}

	/**
	 * Check where the point is (left, on, right) relative to the line segment
	 * 
	 * @param segment
	 *            segment
	 * @param point
	 *            point
	 * @return > 0 if left, 0 if on, < 0 if right
	 */
	private static double isLeft(Segment segment, Point point) {
		return isLeft(segment.getLeftPoint(), segment.getRightPoint(), point);
	}

	/**
	 * Check where point 2 is (left, on, right) relative to the line from point
	 * 0 to point 1
	 * 
	 * @param point0
	 *            point 0
	 * @param point1
	 *            point 1
	 * @param point2
	 *            point 2
	 * @return > 0 if left, 0 if on, < 0 if right
	 */
	private static double isLeft(Point point0, Point point1, Point point2) {
		return (point1.getX() - point0.getX())
				* (point2.getY() - point0.getY())
				- (point2.getX() - point0.getX())
				* (point1.getY() - point0.getY());
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy