/** * OrbisGIS is a java GIS application dedicated to research in GIScience. * OrbisGIS is developed by the GIS group of the DECIDE team of the * Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>. * * The GIS group of the DECIDE team is located at : * * Laboratoire Lab-STICC – CNRS UMR 6285 * Equipe DECIDE * UNIVERSITÉ DE BRETAGNE-SUD * Institut Universitaire de Technologie de Vannes * 8, Rue Montaigne - BP 561 56017 Vannes Cedex * * OrbisGIS is distributed under GPL 3 license. * * Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488) * Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285) * * This file is part of OrbisGIS. * * OrbisGIS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * OrbisGIS 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with * OrbisGIS. If not, see <http://www.gnu.org/licenses/>. * * For more information, please consult: <http://www.orbisgis.org/> * or contact directly: * info_at_ orbisgis.org */ package org.orbisgis.mapeditor.map.geometryUtils; import com.vividsolutions.jts.algorithm.RobustLineIntersector; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Set; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateFilter; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.CoordinateSequenceFilter; import com.vividsolutions.jts.geom.CoordinateSequences; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.TopologyException; import com.vividsolutions.jts.geom.util.LinearComponentExtracter; import com.vividsolutions.jts.noding.IntersectionAdder; import com.vividsolutions.jts.noding.MCIndexNoder; import com.vividsolutions.jts.noding.NodedSegmentString; import com.vividsolutions.jts.noding.Noder; import com.vividsolutions.jts.operation.distance.DistanceOp; import com.vividsolutions.jts.operation.distance.GeometryLocation; import com.vividsolutions.jts.operation.polygonize.Polygonizer; import com.vividsolutions.jts.operation.union.UnaryUnionOp; import org.xnap.commons.i18n.I18n; import org.xnap.commons.i18n.I18nFactory; /** * This utility class contains editing methods for JTS {@link Geometry} objects. * * Geometry objects are unmodifiable; this class allows you to "modify" a * Geometry in a sense -- the modified Geometry is returned as a new Geometry. * The new method * <code>Geometry.isValid()</code> of the returned objects should be checked. * * @author Erwan bocher */ public final class GeometryEdit { private static final GeometryFactory FACTORY = new GeometryFactory(); public static final double PRECISION = 10E-6; private static final I18n I18N = I18nFactory.getI18n(GeometryEdit.class, Locale.getDefault(), I18nFactory.FALLBACK); /** * Interpolate a linestring according start and end coordinates z value. If * the z is NaN return the input linestring * * @param lineString * @param startz * @param endz * @return linestring */ public static LineString linearZInterpolation(LineString lineString, double startz, double endz) { if (Double.isNaN(startz) || Double.isNaN(endz)) { return lineString; } else { double length = lineString.getLength(); lineString.apply(new LinearZInterpolationFilter(startz, endz, length)); return lineString; } } /** * Interpolate a linestring according start and end coordinates z value. If * the z is NaN return the input linestring * * @param lineString * @return */ public static LineString linearZInterpolation(LineString lineString) { double startz = lineString.getStartPoint().getCoordinate().z; double endz = lineString.getEndPoint().getCoordinate().z; return linearZInterpolation(lineString, startz, endz); } public static MultiLineString linearZInterpolation(MultiLineString multiLineString) { int nbGeom = multiLineString.getNumGeometries(); LineString[] lines = new LineString[nbGeom]; for (int i = 0; i < nbGeom; i++) { LineString subGeom = (LineString) multiLineString.getGeometryN(i); double startz = subGeom.getStartPoint().getCoordinates()[0].z; double endz = subGeom.getEndPoint().getCoordinates()[0].z; double length = subGeom.getLength(); subGeom.apply(new LinearZInterpolationFilter(startz, endz, length)); lines[i] = subGeom; } return FACTORY.createMultiLineString(lines); } private static class LinearZInterpolationFilter implements CoordinateSequenceFilter { private boolean done = false; private double startZ = 0; private double endZ = 0; private double dZ = 0; private final double length; private int seqSize = 0; private double sumLenght = 0; LinearZInterpolationFilter(double startZ, double endZ, double length) { this.startZ = startZ; this.endZ = endZ; this.length = length; } @Override public void filter(CoordinateSequence seq, int i) { if (i == 0) { seqSize = seq.size(); dZ = endZ - startZ; } else if (i == seqSize) { done = true; } else { Coordinate coord = seq.getCoordinate(i); Coordinate previousCoord = seq.getCoordinate(i - 1); sumLenght += coord.distance(previousCoord); seq.setOrdinate(i, 2, startZ + dZ * sumLenght / length); } } @Override public boolean isGeometryChanged() { return true; } @Override public boolean isDone() { return done; } } /** * Updates all z values by a new value using the specified first and the * last coordinates. * * @param geom * @param startZ * @param endZ * @return */ public static Geometry force3DStartEnd(Geometry geom, final double startZ, final double endZ) { final double bigD = geom.getLength(); final double z = endZ - startZ; final Coordinate coordEnd = geom.getCoordinates()[geom.getCoordinates().length - 1]; geom.apply(new CoordinateSequenceFilter() { boolean done = false; @Override public boolean isGeometryChanged() { return true; } @Override public boolean isDone() { return done; } @Override public void filter(CoordinateSequence seq, int i) { double x = seq.getX(i); double y = seq.getY(i); if (i == 0) { seq.setOrdinate(i, 0, x); seq.setOrdinate(i, 1, y); seq.setOrdinate(i, 2, startZ); } else if (i == seq.size() - 1) { seq.setOrdinate(i, 0, x); seq.setOrdinate(i, 1, y); seq.setOrdinate(i, 2, endZ); } else { double smallD = seq.getCoordinate(i).distance(coordEnd); double factor = smallD / bigD; seq.setOrdinate(i, 0, x); seq.setOrdinate(i, 1, y); seq.setOrdinate(i, 2, startZ + (factor * z)); } if (i == seq.size()) { done = true; } } }); return geom; } /** * Reverses a multilinestring according to z value. The z first point must * be greater than the z end point * * @param multiLineString * @return */ public static MultiLineString reverse3D(MultiLineString multiLineString) { int num = multiLineString.getNumGeometries(); LineString[] lineStrings = new LineString[num]; for (int i = 0; i < multiLineString.getNumGeometries(); i++) { lineStrings[i] = reverse3D((LineString) multiLineString.getGeometryN(i)); } return FACTORY.createMultiLineString(lineStrings); } /** * Reverses a LineString according to the z value. The z of the first point * must be lower than the z of the end point. * * @param lineString * @return */ public static LineString reverse3D(LineString lineString) { CoordinateSequence seq = lineString.getCoordinateSequence(); double startZ = seq.getCoordinate(0).z; double endZ = seq.getCoordinate(seq.size() - 1).z; if (!Double.isNaN(startZ) && !Double.isNaN(endZ) && startZ < endZ) { CoordinateSequences.reverse(seq); return FACTORY.createLineString(seq); } return lineString; } /** * Reverse a linestring or a multilinetring according to z value. The z * first point must be greater than the z end point * * @param geometry * @return */ public static Geometry reverse3D(Geometry geometry) { if (geometry instanceof MultiLineString) { return reverse3D((MultiLineString) geometry); } else if (geometry instanceof LineString) { return reverse3D((LineString) geometry); } return geometry; } /** * Converts a xyz geometry to xy. * * @param geom * @return */ public static Geometry force2D(Geometry geom) { // return new Geometry2DTransformer().transform(geom); geom.apply(new CoordinateSequenceFilter() { private boolean done = false; @Override public boolean isGeometryChanged() { return true; } @Override public boolean isDone() { return done; } @Override public void filter(CoordinateSequence seq, int i) { seq.setOrdinate(i, 2, Double.NaN); if (i == seq.size()) { done = true; } } }); return geom; } /** * Adds a new z value to each vertex of the Geometry. * * If the geometry doesn't contain a z (ie NaN value) a z equal to zero is * added. The boolean argument is used to set if the z value must be added * (true) or if the z value must replace all existing z (false). * * @param geom * @param value * @param addZ * @return */ public static Geometry force3D(Geometry geom, final double value, final boolean addZ) { geom.apply(new CoordinateSequenceFilter() { private boolean done = false; @Override public boolean isGeometryChanged() { return true; } @Override public boolean isDone() { return done; } @Override public void filter(CoordinateSequence seq, int i) { Coordinate coord = seq.getCoordinate(i); double z = coord.z; if (addZ) { if (Double.isNaN(z)) { z = 0; } seq.setOrdinate(i, 2, z + value); } else { seq.setOrdinate(i, 2, value); } if (i == seq.size()) { done = true; } } }); return geom; } /** * Split a linestring with a point The point must be on the linestring * * @param line * @param pointToSplit * @return */ public static MultiLineString splitLineWithPoint(LineString line, Point pointToSplit) { return FACTORY.createMultiLineString(splitLineStringWithPoint(line, pointToSplit, PRECISION)); } /** * Splits a LineString using a Point. * * @param line * @param pointToSplit * @return */ public static MultiLineString splitLineWithPoint(LineString line, Point pointToSplit, double tolerance) { return FACTORY.createMultiLineString(splitLineStringWithPoint(line, pointToSplit, tolerance)); } /** * Splits a LineString using a Point. * * @param line * @param pointToSplit * @return */ public static LineString[] splitLineStringWithPoint(LineString line, Point pointToSplit) { return splitLineStringWithPoint(line, pointToSplit, PRECISION); } /** * Splits a LineString using a Point, with a distance tolerance. * * @param line * @param pointToSplit * @param tolerance * @return */ public static LineString[] splitLineStringWithPoint(LineString line, Point pointToSplit, double tolerance) { Coordinate[] coords = line.getCoordinates(); Coordinate firstCoord = coords[0]; Coordinate lastCoord = coords[coords.length - 1]; Coordinate coordToSplit = pointToSplit.getCoordinate(); if ((coordToSplit.distance(firstCoord) <= PRECISION) || (coordToSplit.distance(lastCoord) <= PRECISION)) { return new LineString[]{line}; } else { ArrayList<Coordinate> firstLine = new ArrayList<Coordinate>(); firstLine.add(coords[0]); ArrayList<Coordinate> secondLine = new ArrayList<Coordinate>(); GeometryLocation geometryLocation = getVertexToSnap(line, pointToSplit, tolerance); if (geometryLocation != null) { int segmentIndex = geometryLocation.getSegmentIndex(); Coordinate coord = geometryLocation.getCoordinate(); int index = -1; for (int i = 1; i < coords.length; i++) { index = i - 1; if (index < segmentIndex) { firstLine.add(coords[i]); } else if (index == segmentIndex) { coord.z = CoordinatesUtils.interpolate(coords[i - 1], coords[i], coord); firstLine.add(coord); secondLine.add(coord); if (!coord.equals2D(coords[i])) { secondLine.add(coords[i]); } } else { secondLine.add(coords[i]); } } LineString lineString1 = FACTORY.createLineString(firstLine.toArray(new Coordinate[firstLine.size()])); LineString lineString2 = FACTORY.createLineString(secondLine.toArray(new Coordinate[secondLine.size()])); return new LineString[]{lineString1, lineString2}; } } return null; } /** * Splits a MultilineString using a point. * * @param multiLineString * @param pointToSplit * @return */ public static MultiLineString splitMultiLineStringWithPoint(MultiLineString multiLineString, Point pointToSplit) { return splitMultiLineStringWithPoint(multiLineString, pointToSplit, PRECISION); } /** * Splits a MultilineString using a point. * * @param multiLineString * @param pointToSplit * @param tolerance * @return */ public static MultiLineString splitMultiLineStringWithPoint(MultiLineString multiLineString, Point pointToSplit, double tolerance) { ArrayList<LineString> linestrings = new ArrayList<LineString>(); boolean notChanged = true; int nb = multiLineString.getNumGeometries(); for (int i = 0; i < nb; i++) { LineString subGeom = (LineString) multiLineString.getGeometryN(i); LineString[] result = splitLineStringWithPoint(subGeom, pointToSplit, tolerance); if (result != null) { Collections.addAll(linestrings, result); notChanged = false; } else { linestrings.add(subGeom); } } if (!notChanged) { return FACTORY.createMultiLineString(linestrings.toArray(new LineString[linestrings.size()])); } return null; } /** * Split a geometry at intersections and return a list of sublinestrings * * @param tobeNodes * */ public static Geometry geometryNoders(Geometry tobeNodes) { Noder noder = new MCIndexNoder(new IntersectionAdder(new RobustLineIntersector())); noder.computeNodes(createNodedSegmentStrings(tobeNodes)); Collection<NodedSegmentString> nodedSegStrings = noder.getNodedSubstrings(); return fromSegmentStrings(nodedSegStrings); } /** * Extracts all the 1-dimensional ({@link LineString}) components from a * {@link Geometry}. Store them as a list of({@link NodedSegmentString}) * * @param geom * @return */ public static List<NodedSegmentString> createNodedSegmentStrings(Geometry geom) { List<NodedSegmentString> segs = new ArrayList<NodedSegmentString>(); List<LineString> lines = LinearComponentExtracter.getLines(geom); for (LineString line : lines) { segs.add(new NodedSegmentString(line.getCoordinates(), null)); } return segs; } /** * Convert a list of {@link NodedSegmentString})to a * ({@link MultiLineString}) * * @param segStrings * @return */ public static Geometry fromSegmentStrings(Collection<NodedSegmentString> segStrings) { LineString[] lines = new LineString[segStrings.size()]; int index = 0; for (NodedSegmentString ss : segStrings) { LineString line = FACTORY.createLineString(ss.getCoordinates()); lines[index++] = line; } return FACTORY.createMultiLineString(lines); } /** * Splits the specified lineString with another lineString. * * @param lineString * @param lineString * */ public static Geometry splitLineStringWithLine(LineString input, LineString cut) { return input.difference(cut); } /** * Splits the specified MultiLineString with another lineString. * * @param MultiLineString * @param lineString * */ public static MultiLineString splitMultiLineStringWithLine(MultiLineString input, LineString cut) { ArrayList<Geometry> geometries = new ArrayList<Geometry>(); Geometry lines = input.difference(cut); for (int i = 0; i < lines.getNumGeometries(); i++) { geometries.add(lines.getGeometryN(i)); } return FACTORY.createMultiLineString(geometries.toArray(new LineString[geometries.size()])); } /** * Gets the coordinate of a Geometry that is the nearest of a given Point, * with a distance tolerance. * * @param g * @param p * @param tolerance * @return */ public static GeometryLocation getVertexToSnap(Geometry g, Point p, double tolerance) { DistanceOp distanceOp = new DistanceOp(g, p); GeometryLocation snapedPoint = distanceOp.nearestLocations()[0]; if (tolerance == 0 || snapedPoint.getCoordinate().distance(p.getCoordinate()) <= tolerance) { return snapedPoint; } return null; } /** * Gets the coordinate of a Geometry that is the nearest of a given Point. * * @param g * @param p * @return */ public static GeometryLocation getVertexToSnap(Geometry g, Point p) { return getVertexToSnap(g, p, PRECISION); } /** * Inserts a vertex into a LineString. * * @param lineString * @param vertexPoint * @return * @throws com.vividsolutions.jts.geom.TopologyException */ public static Geometry insertVertexInLineString(LineString lineString, Point vertexPoint) throws TopologyException { return insertVertexInLineString(lineString, vertexPoint, -1); } /** * Inserts a vertex into a LineString with a given tolerance. * * @param lineString * @param vertexPoint * @param tolerance * @return * @throws com.vividsolutions.jts.geom.TopologyException */ public static LineString insertVertexInLineString(LineString lineString, Point vertexPoint, double tolerance) throws TopologyException { GeometryLocation geomLocation = getVertexToSnap(lineString, vertexPoint, tolerance); if (geomLocation != null) { Coordinate[] coords = lineString.getCoordinates(); int index = geomLocation.getSegmentIndex(); Coordinate coord = geomLocation.getCoordinate(); if (!CoordinatesUtils.contains2D(coords, coord)) { Coordinate[] ret = new Coordinate[coords.length + 1]; System.arraycopy(coords, 0, ret, 0, index + 1); ret[index + 1] = coord; System.arraycopy(coords, index + 1, ret, index + 2, coords.length - (index + 1)); return FACTORY.createLineString(ret); } return null; } else { return null; } } /** * Inserts a vertex into a linearRing. * * @param lineString * @param vertexPoint * @return */ public static LinearRing insertVertexInLinearRing(LineString lineString, Point vertexPoint) { return insertVertexInLinearRing(lineString, vertexPoint, -1); } /** * Inserts a vertex into a LinearRing with a given tolerance. * * @param lineString * @param vertexPoint * @param tolerance * @return */ public static LinearRing insertVertexInLinearRing(LineString lineString, Point vertexPoint, double tolerance) { GeometryLocation geomLocation = getVertexToSnap(lineString, vertexPoint, tolerance); if (geomLocation != null) { Coordinate[] coords = lineString.getCoordinates(); int index = geomLocation.getSegmentIndex(); Coordinate coord = geomLocation.getCoordinate(); if (!CoordinatesUtils.contains2D(coords, coord)) { Coordinate[] ret = new Coordinate[coords.length + 1]; System.arraycopy(coords, 0, ret, 0, index + 1); ret[index + 1] = coord; System.arraycopy(coords, index + 1, ret, index + 2, coords.length - (index + 1)); return FACTORY.createLinearRing(ret); } return null; } else { return null; } } /** * Inserts a vertex into a polygon. * * @param polygon * @param vertexPoint * @return * @throws TopologyException */ public static Geometry insertVertexInPolygon(Polygon polygon, Point vertexPoint) throws TopologyException { return insertVertexInPolygon(polygon, vertexPoint, -1); } /** * Inserts a vertex into a Polygon with a given tolerance. * * @param polygon * @param vertexPoint * @param tolerance * @return * @throws TopologyException */ public static Polygon insertVertexInPolygon(Polygon polygon, Point vertexPoint, double tolerance) throws TopologyException { LinearRing inserted = insertVertexInLinearRing(polygon.getExteriorRing(), vertexPoint, tolerance); if (inserted != null) { LinearRing[] holes = new LinearRing[polygon.getNumInteriorRing()]; for (int i = 0; i < holes.length; i++) { holes[i] = FACTORY.createLinearRing(polygon.getInteriorRingN(i).getCoordinates()); } Polygon ret = FACTORY.createPolygon(inserted, holes); if (!ret.isValid()) { throw new TopologyException(I18N.tr("Geometry is not valid")); } return ret; } for (int i = 0; i < polygon.getNumInteriorRing(); i++) { inserted = insertVertexInLinearRing(polygon.getInteriorRingN(i), vertexPoint, tolerance); if (inserted != null) { LinearRing[] holes = new LinearRing[polygon.getNumInteriorRing()]; for (int h = 0; h < holes.length; h++) { if (h == i) { holes[h] = inserted; } else { holes[h] = FACTORY.createLinearRing(polygon.getInteriorRingN(h).getCoordinates()); } } Polygon ret = FACTORY.createPolygon(FACTORY.createLinearRing(polygon.getExteriorRing().getCoordinates()), holes); if (!ret.isValid()) { throw new TopologyException(I18N.tr("Geometry is not valid")); } return ret; } } return null; } /** * Inserts a Point into a MultiPoint geometry. * * @param g * @param vertexPoint * @return */ public static Geometry insertVertexInMultipoint(Geometry g, Point vertexPoint) { ArrayList<Point> geoms = new ArrayList<Point>(); for (int i = 0; i < g.getNumGeometries(); i++) { Point geom = (Point) g.getGeometryN(i); geoms.add(geom); } geoms.add(FACTORY.createPoint(new Coordinate(vertexPoint.getX(), vertexPoint.getY()))); return FACTORY.createMultiPoint(GeometryFactory.toPointArray(geoms)); } /** * Inserts a Point into a geometry. * * @param geom * @param point * @return * @throws TopologyException */ public static Geometry insertVertex(Geometry geom, Point point) throws TopologyException { return insertVertex(geom, point, -1); } /** * Returns a new geometry based on an existing one, with a specific point as * a new vertex. * * @param geometry * @param vertexPoint * @param tolerance * @return Null if the vertex cannot be inserted * @throws GeometryException If the vertex can be inserted but it makes the * geometry to be in an invalid shape */ public static Geometry insertVertex(Geometry geometry, Point vertexPoint, double tolerance) throws TopologyException { if (geometry instanceof MultiPoint) { return insertVertexInMultipoint(geometry, vertexPoint); } else if (geometry instanceof LineString) { return insertVertexInLineString((LineString) geometry, vertexPoint, tolerance); } else if (geometry instanceof MultiLineString) { LineString[] linestrings = new LineString[geometry.getNumGeometries()]; boolean any = false; for (int i = 0; i < geometry.getNumGeometries(); i++) { LineString line = (LineString) geometry.getGeometryN(i); LineString inserted = insertVertexInLineString(line, vertexPoint, tolerance); if (inserted != null) { linestrings[i] = inserted; any = true; } else { linestrings[i] = line; } } if (any) { return FACTORY.createMultiLineString(linestrings); } else { return null; } } else if (geometry instanceof Polygon) { return insertVertexInPolygon((Polygon) geometry, vertexPoint, tolerance); } else if (geometry instanceof MultiPolygon) { Polygon[] polygons = new Polygon[geometry.getNumGeometries()]; boolean any = false; for (int i = 0; i < geometry.getNumGeometries(); i++) { Polygon polygon = (Polygon) geometry.getGeometryN(i); Polygon inserted = insertVertexInPolygon(polygon, vertexPoint, tolerance); if (inserted != null) { any = true; polygons[i] = inserted; } else { polygons[i] = polygon; } } if (any) { return FACTORY.createMultiPolygon(polygons); } else { return null; } } throw new UnsupportedOperationException(I18N.tr("Unknown geometry type: {0}",geometry.getGeometryType())); } /** * Splits a Polygon with a LineString. * * @param polygon * @param lineString * @return */ public static Collection<Polygon> splitPolygonizer(Polygon polygon, LineString lineString) { Set<LineString> segments = GeometryConvert.toSegmentsLineString(polygon.getExteriorRing()); segments.add(lineString); int holes = polygon.getNumInteriorRing(); for (int i = 0; i < holes; i++) { segments.addAll(GeometryConvert.toSegmentsLineString(polygon.getInteriorRingN(i))); } // Perform union of all extracted LineStrings (the edge-noding process) UnaryUnionOp uOp = new UnaryUnionOp(segments); Geometry union = uOp.union(); // Create polygons from unioned LineStrings Polygonizer polygonizer = new Polygonizer(); polygonizer.add(union); Collection<Polygon> polygons = polygonizer.getPolygons(); if (polygons.size() > 1) { return polygons; } return null; } /** * Splits a Polygon using a LineString. * * @param polygon * @param lineString * @return */ public static Geometry splitPolygonWithLine(Polygon polygon, LineString lineString) { Collection<Polygon> pols = polygonWithLineSplitter(polygon, lineString); if (pols != null) { return FACTORY.buildGeometry(pols); } return null; } /** * Splits a Polygon using a LineString. * * @param polygon * @param lineString * @return */ public static Collection<Polygon> polygonWithLineSplitter(Polygon polygon, LineString lineString) { Collection<Polygon> polygons = splitPolygonizer(polygon, lineString); if (polygons != null && polygons.size() > 1) { List<Polygon> pols = new ArrayList<Polygon>(); for (Polygon pol : polygons) { if (polygon.contains(pol.getInteriorPoint())) { pols.add(pol); } } return pols; } return null; } /** * Splits a MultiPolygon using a LineString. * * @param multiPolygon * @param lineString * @return */ public static Geometry splitMultiPolygonWithLine(MultiPolygon multiPolygon, LineString lineString) { ArrayList<Polygon> allPolygons = new ArrayList<Polygon>(); for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { Collection<Polygon> polygons = splitPolygonizer((Polygon) multiPolygon.getGeometryN(i), lineString); if (polygons != null) { allPolygons.addAll(polygons); } } if (!allPolygons.isEmpty()) { return FACTORY.buildGeometry(allPolygons); } return null; } /** * Removes a vertex from a JTS geometry. * * @param vertexIndex * @param g * @param minNumVertex * @return * * @throws TopologyException */ public static Coordinate[] removeVertex(int vertexIndex, Geometry g, int minNumVertex) throws TopologyException { Coordinate[] coords = g.getCoordinates(); if (coords.length <= minNumVertex) { throw new TopologyException( I18N.tr("Too few vertices")); } Coordinate[] newCoords = new Coordinate[coords.length - 1]; for (int i = 0; i < vertexIndex; i++) { newCoords[i] = new Coordinate(coords[i].x, coords[i].y, coords[i].z); } if (vertexIndex != coords.length - 1) { for (int i = vertexIndex + 1; i < coords.length; i++) { newCoords[i - 1] = new Coordinate(coords[i].x, coords[i].y, coords[i].z); } } return newCoords; } /** * Removes a vertex from a MultiPoint. * * @param geometry * @param vertexIndex * @return * @throws TopologyException */ public static MultiPoint removeVertex(MultiPoint geometry, int vertexIndex) throws TopologyException { return FACTORY.createMultiPoint(removeVertex(vertexIndex, geometry, 1)); } /** * Removes a vertex from a LineString. * * @param geometry * @param vertexIndex * @return * @throws TopologyException */ public static LineString removeVertex(LineString geometry, int vertexIndex) throws TopologyException { return FACTORY.createLineString(removeVertex(vertexIndex, geometry, 2)); } /** * Moves a geometry according to a distance displacement in x and y. * * @param geometry * @param displacement * @return */ public static Geometry moveGeometry(Geometry geometry, final double[] displacement) { geometry.apply(new CoordinateFilter() { @Override public void filter(Coordinate coordinate) { coordinate.x += displacement[0]; coordinate.y += displacement[1]; } }); return geometry; } /** * Moves a geometry according to start and end coordinates. * * @param geometry * @param start * @param end * @return */ public static Geometry moveGeometry(Geometry geometry, Coordinate start, Coordinate end) { double xDisplacement = end.x - start.x; double yDisplacement = end.y - start.y; return moveGeometry(geometry, new double[]{xDisplacement, yDisplacement}); } /** * Cuts a Polygon with a Polygon. * * @param polygon * @param extrudePolygon * @return */ public static List<Polygon> cutPolygonWithPolygon(Polygon polygon, Polygon extrudePolygon) { Geometry geom = polygon.difference(extrudePolygon); ArrayList<Polygon> polygons = new ArrayList<Polygon>(); for (int i = 0; i < geom.getNumGeometries(); i++) { Polygon subGeom = (Polygon) geom.getGeometryN(i); polygons.add(subGeom); } return polygons; } /** * Cut a MultiPolygon with a Polygon. * * @param multiPolygon * @param extrudePolygon * @return */ public static MultiPolygon cutMultiPolygonWithPolygon(MultiPolygon multiPolygon, Polygon extrudePolygon) { ArrayList<Polygon> polygons = new ArrayList<Polygon>(); for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { Polygon subGeom = (Polygon) multiPolygon.getGeometryN(i); if (extrudePolygon.intersects(subGeom)) { List<Polygon> result = cutPolygonWithPolygon(subGeom, extrudePolygon); polygons.addAll(result); } else { polygons.add(subGeom); } } return FACTORY.createMultiPolygon(polygons.toArray(new Polygon[polygons.size()])); } /** * Remove holes in a polygon or multipolygon. * * @param geometry * @return */ public static Geometry removeHole(Geometry geometry) { if (geometry instanceof Polygon) { return removeHolePolygon((Polygon) geometry); } else if (geometry instanceof MultiPolygon) { return removeHoleMultiPolygon((MultiPolygon) geometry); } return null; } /** * Create a new multiPolygon without hole. * * @param multiPolygon * @return */ public static MultiPolygon removeHoleMultiPolygon(MultiPolygon multiPolygon) { int num = multiPolygon.getNumGeometries(); Polygon[] polygons = new Polygon[num]; for (int i = 0; i < num; i++) { polygons[i] = removeHolePolygon((Polygon) multiPolygon.getGeometryN(i)); } return multiPolygon.getFactory().createMultiPolygon(polygons); } /** * Create a new polygon without hole. * * @param polygon * @return */ public static Polygon removeHolePolygon(Polygon polygon) { return new Polygon((LinearRing) polygon.getExteriorRing(), null, polygon.getFactory()); } /** * Private constructor for utility class. */ private GeometryEdit() { } }