/*
* 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.elevation.tin.quadedge;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Lineal;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.PointList;
import com.revolsys.geometry.model.Polygonal;
import com.revolsys.geometry.model.impl.BoundingBoxDoubleXY;
/**
* A utility class which creates Conforming Delaunay Trianglulations
* from collections of points and linear constraints, and extract the resulting
* triangulation edges or triangles as geometries.
*
* @author Martin Davis
*
*/
public class QuadEdgeConformingDelaunayTinBuilder {
/**
* Extracts the unique {@link Coordinates}s from the given {@link Geometry}.
* @param geom the geometry to extract from
* @return a List of the unique Coordinates
*/
public static PointList extractUniqueCoordinates(final Geometry geom) {
if (geom == null) {
return new PointList();
}
return unique(geom.vertices(), geom.getVertexCount());
}
private static List<LineSegmentDoubleData> newConstraintSegments(final Geometry geom) {
final List<LineString> lines = geom.getGeometryComponents(LineString.class);
final List<LineSegmentDoubleData> constraintSegs = new ArrayList<>();
for (final LineString line : lines) {
newConstraintSegments(line, constraintSegs);
}
return constraintSegs;
}
private static void newConstraintSegments(final LineString line,
final List<LineSegmentDoubleData> constraintSegs) {
final int vertexCount = line.getVertexCount();
if (vertexCount > 0) {
double x1 = line.getX(0);
double y1 = line.getY(0);
double z1 = line.getZ(0);
for (int vertexIndex = 1; vertexIndex < vertexCount; vertexIndex++) {
final double x2 = line.getX(vertexIndex);
final double y2 = line.getY(vertexIndex);
final double z2 = line.getZ(vertexIndex);
constraintSegs.add(new LineSegmentDoubleData(x1, y1, z1, x2, y2, z2));
x1 = x2;
y1 = y2;
z1 = z2;
}
}
}
public static PointList unique(final Iterable<? extends Point> points, final int vertexCount) {
final Point[] pointArray = new Point[vertexCount];
int vertexIndex = 0;
for (final Point point : points) {
pointArray[vertexIndex++] = point.newPoint();
}
Arrays.sort(pointArray);
final PointList coordList = new PointList(pointArray, false);
return coordList;
}
private Geometry constraintLines;
private final Map<ConstraintVertex, ConstraintVertex> constraintVertexMap = new TreeMap<>();
private List<Point> sitePoints;
private QuadEdgeSubdivision subdivision = null;
private double tolerance = 0.0;
public QuadEdgeConformingDelaunayTinBuilder() {
}
/**
* Gets the edges of the computed triangulation as a {@link Lineal}.
*
* @param geomFact the geometry factory to use to create the output
* @return the edges of the triangulation
*/
public Lineal getEdgesLineal(final GeometryFactory geomFact) {
init();
return this.subdivision.getEdgesLineal(geomFact);
}
/**
* Gets the QuadEdgeSubdivision which models the computed triangulation.
*
* @return the subdivision containing the triangulation
*/
public QuadEdgeSubdivision getSubdivision() {
init();
return this.subdivision;
}
/**
* Gets the faces of the computed triangulation as a {@link Polygonal}.
*
* @param geomFact the geometry factory to use to create the output
* @return the faces of the triangulation
*/
public Polygonal getTrianglesPolygonal(final GeometryFactory geomFact) {
init();
return this.subdivision.getTrianglesPolygonal(geomFact);
}
private void init() {
if (this.subdivision != null) {
return;
}
final List<Point> points = this.sitePoints;
final BoundingBox siteBoundingBox = BoundingBoxDoubleXY.newBoundingBox(points);
List<LineSegmentDoubleData> segments = new ArrayList<>();
if (this.constraintLines != null) {
siteBoundingBox.expandToInclude(this.constraintLines.getBoundingBox());
initVertices(this.constraintLines);
segments = newConstraintSegments(this.constraintLines);
}
final List<ConstraintVertex> sites = newSiteVertices(this.sitePoints);
final ConformingDelaunayTriangulator cdt = new ConformingDelaunayTriangulator(sites,
GeometryFactory.fixed(0, 3, this.tolerance, this.tolerance, 0));
cdt.setConstraints(segments, new ArrayList<>(this.constraintVertexMap.values()));
cdt.buildTin();
cdt.enforceConstraints();
this.subdivision = cdt.getSubdivision();
}
private void initVertices(final Geometry geom) {
for (final Point point : geom.vertices()) {
final ConstraintVertex vertex = new ConstraintVertex(point);
this.constraintVertexMap.put(vertex, vertex);
}
}
private List<ConstraintVertex> newSiteVertices(final Collection<Point> coords) {
final List<ConstraintVertex> verts = new ArrayList<>();
for (final Point coord : coords) {
if (this.constraintVertexMap.containsKey(coord)) {
continue;
}
verts.add(new ConstraintVertex(coord));
}
return verts;
}
/**
* Sets the linear constraints to be conformed to.
* All linear components in the input will be used as constraints.
* The constraint vertices do not have to be disjoint from
* the site vertices.
* The constraints must not contain duplicate segments (up to orientation).
*
* @param constraintLines the lines to constraint to
*/
public void setConstraints(final Geometry constraintLines) {
this.constraintLines = constraintLines;
}
/**
* Sets the sites (point or vertices) which will be triangulated.
* All vertices of the given geometry will be used as sites.
* The site vertices do not have to contain the constraint
* vertices as well; any site vertices which are
* identical to a constraint vertex will be removed
* from the site vertex set.
*
* @param geom the geometry from which the sites will be extracted.
*/
public void setSites(final Geometry geom) {
this.sitePoints = extractUniqueCoordinates(geom);
}
/**
* Sets the snapping tolerance which will be used
* to improved the robustness of the triangulation computation.
* A tolerance of 0.0 specifies that no snapping will take place.
*
* @param tolerance the tolerance distance to use
*/
public void setTolerance(final double tolerance) {
this.tolerance = tolerance;
}
}