/*
* 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.densify;
import java.util.ArrayList;
import java.util.List;
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.LinearRing;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.Polygon;
import com.revolsys.geometry.model.Polygonal;
import com.revolsys.geometry.model.Punctual;
import com.revolsys.geometry.model.segment.Segment;
/**
* Densifies a {@link Geometry} by inserting extra vertices along the line segments
* contained in the geometry.
* All segments in the created densified geometry will be no longer than
* than the given distance tolerance.
* Densified polygonal geometries are guaranteed to be topologically correct.
* The coordinates created during densification respect the input geometry's
* scale.
* <p>
* <b>Note:</b> At some future point this class will
* offer a variety of densification strategies.
*
* @author Martin Davis
*/
public class Densifier {
private static Lineal densify(final Lineal lineal, final double distanceTolerance) {
final List<LineString> lines = new ArrayList<>();
for (final LineString line : lineal.lineStrings()) {
final LineString newLine = densify(line, distanceTolerance);
lines.add(newLine);
}
final GeometryFactory geometryFactory = lineal.getGeometryFactory();
return geometryFactory.lineal(lines);
}
private static LinearRing densify(final LinearRing line, final double distanceTolerance) {
final List<Point> points = densifyPoints(line, distanceTolerance);
final GeometryFactory geometryFactory = line.getGeometryFactory();
return geometryFactory.linearRing(points);
}
private static LineString densify(final LineString line, final double distanceTolerance) {
final List<Point> points = densifyPoints(line, distanceTolerance);
final GeometryFactory geometryFactory = line.getGeometryFactory();
return geometryFactory.lineString(points);
}
private static Polygon densify(final Polygon polygon, final double distanceTolerance) {
// Attempt to fix invalid geometries
final GeometryFactory geometryFactory = polygon.getGeometryFactory();
final List<LinearRing> rings = new ArrayList<>();
for (final LinearRing ring : polygon.rings()) {
final LinearRing newRing = densify(ring, distanceTolerance);
rings.add(newRing);
}
final Polygon newPolygon = geometryFactory.polygon(rings);
return (Polygon)newPolygon.buffer(0);
}
private static Polygonal densify(final Polygonal polygonal, final double distanceTolerance) {
final List<Polygon> polygons = new ArrayList<>();
for (final Polygon polygon : polygonal.getPolygons()) {
final Polygon newPolygon = densify(polygon, distanceTolerance);
polygons.add(newPolygon);
}
final GeometryFactory geometryFactory = polygonal.getGeometryFactory();
final Polygonal newMultiPolygon = geometryFactory.polygonal(polygons);
return (Polygonal)newMultiPolygon.buffer(0);
}
/**
* Densifies a geometry using a given distance tolerance,
* and respecting the input geometry's scale.
*
* @param geometry the geometry to densify
* @param distanceTolerance the distance tolerance to densify
* @return the densified geometry
*/
@SuppressWarnings("unchecked")
public static <V extends Geometry> V densify(final V geometry, final double distanceTolerance) {
if (distanceTolerance <= 0.0) {
throw new IllegalArgumentException("Tolerance must be positive");
} else if (geometry == null || geometry.isEmpty()) {
return geometry;
} else {
if (geometry instanceof Punctual) {
return geometry;
} else if (geometry instanceof LinearRing) {
return (V)densify((LinearRing)geometry, distanceTolerance);
} else if (geometry instanceof LineString) {
return (V)densify((LineString)geometry, distanceTolerance);
} else if (geometry instanceof Polygon) {
return (V)densify((Polygon)geometry, distanceTolerance);
} else if (geometry instanceof Lineal) {
return (V)densify((Lineal)geometry, distanceTolerance);
} else if (geometry instanceof Polygonal) {
return (V)densify((Polygonal)geometry, distanceTolerance);
} else if (geometry.isGeometryCollection()) {
return (V)densifyCollection(geometry, distanceTolerance);
} else {
throw new UnsupportedOperationException("Unknown geometry type " + geometry.getClass());
}
}
}
private static Geometry densifyCollection(final Geometry geometryCollection,
final double distanceTolerance) {
final List<Geometry> geometries = new ArrayList<>();
for (final Geometry part : geometryCollection.geometries()) {
final Geometry newGeometry = densify(part, distanceTolerance);
geometries.add(newGeometry);
}
final Geometry newGeometry = geometryCollection.getGeometryFactory().geometry(geometries);
return newGeometry;
}
/**
* Densifies a coordinate sequence.
*
* @param pts
* @param distanceTolerance
* @return the densified coordinate sequence
*/
private static List<Point> densifyPoints(final LineString line, final double distanceTolerance) {
final List<Point> points = new ArrayList<>();
for (final Segment segment : line.segments()) {
if (points.isEmpty()) {
points.add(segment.getPoint(0));
}
final double length = segment.getLength();
if (length > 0) {
final int densifiedSegCount = (int)(length / distanceTolerance) + 1;
if (densifiedSegCount > 1) {
final double densifiedSegLen = length / densifiedSegCount;
for (int j = 1; j < densifiedSegCount; j++) {
final double segFract = j * densifiedSegLen / length;
final Point point = segment.pointAlong(segFract);
if (!segment.isEndPoint(point)) {
points.add(point);
}
}
}
points.add(segment.getPoint(1));
}
}
return points;
}
}