ComponentJumpChecker.java
/*
* Copyright (c) 2023 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.simplify;
import java.util.Collection;
import org.locationtech.jts.algorithm.RayCrossingCounter;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.LineSegment;
/**
* Checks if simplifying (flattening) line sections or segments
* would cause them to "jump" over other components in the geometry.
*
* @author mdavis
*
*/
class ComponentJumpChecker {
//TODO: use a spatial index?
private Collection<TaggedLineString> components;
public ComponentJumpChecker(Collection<TaggedLineString> taggedLines) {
components = taggedLines;
}
/**
* Checks if a line section jumps a component if flattened.
*
* Assumes start <= end.
*
* @param line the line containing the section being flattened
* @param start start index of the section
* @param end end index of the section
* @param seg the flattening segment
* @return true if the flattened section jumps a component
*/
public boolean hasJump(TaggedLineString line, int start, int end, LineSegment seg) {
Envelope sectionEnv = computeEnvelope(line, start, end);
for (TaggedLineString comp : components) {
//-- don't test component against itself
if (comp == line)
continue;
Coordinate compPt = comp.getComponentPoint();
if (sectionEnv.intersects(compPt)) {
if (hasJumpAtComponent(compPt, line, start, end, seg)) {
return true;
}
}
}
return false;
}
/**
* Checks if two consecutive segments jumps a component if flattened.
* The segments are assumed to be consecutive.
* (so the seg1.p1 = seg2.p0).
* The flattening segment must be the segment between seg1.p0 and seg2.p1.
*
* @param line the line containing the section being flattened
* @param seg1 the first replaced segment
* @param seg2 the next replaced segment
* @param seg the flattening segment
* @return true if the flattened segment jumps a component
*/
public boolean hasJump(TaggedLineString line, LineSegment seg1, LineSegment seg2, LineSegment seg) {
Envelope sectionEnv = computeEnvelope(seg1, seg2);
for (TaggedLineString comp : components) {
//-- don't test component against itself
if (comp == line)
continue;
Coordinate compPt = comp.getComponentPoint();
if (sectionEnv.intersects(compPt)) {
if (hasJumpAtComponent(compPt, seg1, seg2, seg)) {
return true;
}
}
}
return false;
}
private static boolean hasJumpAtComponent(Coordinate compPt, TaggedLineString line, int start, int end, LineSegment seg) {
int sectionCount = crossingCount(compPt, line, start, end);
int segCount = crossingCount(compPt, seg);
boolean hasJump = sectionCount % 2 != segCount % 2;
return hasJump;
}
private static boolean hasJumpAtComponent(Coordinate compPt, LineSegment seg1, LineSegment seg2, LineSegment seg) {
int sectionCount = crossingCount(compPt, seg1, seg2);
int segCount = crossingCount(compPt, seg);
boolean hasJump = sectionCount % 2 != segCount % 2;
return hasJump;
}
private static int crossingCount(Coordinate compPt, LineSegment seg) {
RayCrossingCounter rcc = new RayCrossingCounter(compPt);
rcc.countSegment(seg.p0, seg.p1);
return rcc.getCount();
}
private static int crossingCount(Coordinate compPt, LineSegment seg1, LineSegment seg2) {
RayCrossingCounter rcc = new RayCrossingCounter(compPt);
rcc.countSegment(seg1.p0, seg1.p1);
rcc.countSegment(seg2.p0, seg2.p1);
return rcc.getCount();
}
private static int crossingCount(Coordinate compPt, TaggedLineString line, int start, int end) {
RayCrossingCounter rcc = new RayCrossingCounter(compPt);
for (int i = start; i < end; i++) {
rcc.countSegment(line.getCoordinate(i), line.getCoordinate(i + 1));
}
return rcc.getCount();
}
private static Envelope computeEnvelope(LineSegment seg1, LineSegment seg2) {
Envelope env = new Envelope();
env.expandToInclude(seg1.p0);
env.expandToInclude(seg1.p1);
env.expandToInclude(seg2.p0);
env.expandToInclude(seg2.p1);
return env;
}
private static Envelope computeEnvelope(TaggedLineString line, int start, int end) {
Envelope env = new Envelope();
for (int i = start; i <= end; i++) {
env.expandToInclude(line.getCoordinate(i));
}
return env;
}
}