
mil.nga.wkb.util.sweep.SweepLine Maven / Gradle / Ivy
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 - 2025 Weber Informatics LLC | Privacy Policy