/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.operation.overlay;
import java.util.ArrayList;
import java.util.List;
import com.revolsys.geometry.algorithm.PointLocator;
import com.revolsys.geometry.geomgraph.DirectedEdge;
import com.revolsys.geometry.geomgraph.DirectedEdgeStar;
import com.revolsys.geometry.geomgraph.Edge;
import com.revolsys.geometry.geomgraph.Label;
import com.revolsys.geometry.geomgraph.Node;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.util.Assert;
/**
* Forms JTS LineStrings out of a the graph of {@link DirectedEdge}s
* created by an {@link OverlayOp}.
*
* @version 1.7
*/
public class LineBuilder {
private final GeometryFactory geometryFactory;
private final List<Edge> lineEdgesList = new ArrayList<>();
private final OverlayOp op;
private final List<LineString> resultLineList = new ArrayList<>();
public LineBuilder(final OverlayOp op, final GeometryFactory geometryFactory,
final PointLocator ptLocator) {
this.op = op;
this.geometryFactory = geometryFactory;
}
/**
* @return a list of the LineStrings in the result of the specified overlay operation
*/
public List<LineString> build(final int opCode) {
findCoveredLineEdges();
collectLines(opCode);
// labelIsolatedLines(lineEdgesList);
buildLines(opCode);
return this.resultLineList;
}
private void buildLines(final int opCode) {
for (final Edge e : this.lineEdgesList) {
final LineString line = this.geometryFactory.lineString(e.getLine());
this.resultLineList.add(line);
e.setInResult(true);
}
}
/**
* Collect edges from Area inputs which should be in the result but
* which have not been included in a result area.
* This happens ONLY:
* <ul>
* <li>during an intersection when the boundaries of two
* areas touch in a line segment
* <li> OR as a result of a dimensional collapse.
* </ul>
*/
private void collectBoundaryTouchEdge(final DirectedEdge de, final int opCode,
final List<Edge> edges) {
final Label label = de.getLabel();
if (de.isLineEdge()) {
return; // only interested in area edges
}
if (de.isVisited()) {
return; // already processed
}
if (de.isInteriorAreaEdge()) {
return; // added to handle dimensional collapses
}
if (de.getEdge().isInResult()) {
return; // if the edge linework is already included, don't include it
// again
}
// sanity check for labelling of result edgerings
Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
// include the linework if it's in the result of the operation
if (OverlayOp.isResultOfOp(label, opCode) && opCode == OverlayOp.INTERSECTION) {
edges.add(de.getEdge());
de.setVisitedEdge(true);
}
}
/**
* Collect line edges which are in the result.
* Line edges are in the result if they are not part of
* an area boundary, if they are in the result of the overlay operation,
* and if they are not covered by a result area.
*
* @param de the directed edge to test
* @param opCode the overlap operation
* @param edges the list of included line edges
*/
private void collectLineEdge(final DirectedEdge de, final int opCode, final List<Edge> edges) {
final Label label = de.getLabel();
final Edge e = de.getEdge();
// include L edges which are in the result
if (de.isLineEdge()) {
if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
// Debug.println("de: " + de.getLabel());
// Debug.println("edge: " + e.getLabel());
edges.add(e);
de.setVisitedEdge(true);
}
}
}
private void collectLines(final int opCode) {
for (final Object element : this.op.getGraph().getEdgeEnds()) {
final DirectedEdge de = (DirectedEdge)element;
collectLineEdge(de, opCode, this.lineEdgesList);
collectBoundaryTouchEdge(de, opCode, this.lineEdgesList);
}
}
/**
* Find and mark L edges which are "covered" by the result area (if any).
* L edges at nodes which also have A edges can be checked by checking
* their depth at that node.
* L edges at nodes which do not have A edges can be checked by doing a
* point-in-polygon test with the previously computed result areas.
*/
private void findCoveredLineEdges() {
// first set covered for all L edges at nodes which have A edges too
for (final Object element : this.op.getGraph().getNodes()) {
final Node node = (Node)element;
// node.print(System.out);
((DirectedEdgeStar)node.getEdges()).findCoveredLineEdges();
}
/**
* For all L edges which weren't handled by the above,
* use a point-in-poly test to determine whether they are covered
*/
for (final Object element : this.op.getGraph().getEdgeEnds()) {
final DirectedEdge de = (DirectedEdge)element;
final Edge e = de.getEdge();
if (de.isLineEdge() && !e.isCoveredSet()) {
final boolean isCovered = this.op.isCoveredByA(de.getCoordinate());
e.setCovered(isCovered);
}
}
}
}