com.vividsolutions.jts.operation.union.UnaryUnionOp Maven / Gradle / Ivy
The newest version!
/*
* 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.vividsolutions.jts.operation.union;
import java.util.*;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.util.*;
import com.vividsolutions.jts.operation.linemerge.LineMerger;
import com.vividsolutions.jts.operation.overlay.OverlayOp;
import com.vividsolutions.jts.operation.overlay.snap.SnapIfNeededOverlayOp;
/**
* Unions a Collection
of {@link Geometry}s or a single Geometry
* (which may be a {@link GeoometryCollection}) together.
* By using this special-purpose operation over a collection of geometries
* it is possible to take advantage of various optimizations to improve performance.
* Heterogeneous {@link GeometryCollection}s are fully supported.
*
* The result obeys the following contract:
*
* - Unioning a set of {@link Polygon}s has the effect of
* merging the areas (i.e. the same effect as
* iteratively unioning all individual polygons together).
*
*
- Unioning a set of {@link LineString}s has the effect of noding
* and dissolving the input linework.
* In this context "fully noded" means that there will be
* an endpoint or node in the result
* for every endpoint or line segment crossing in the input.
* "Dissolved" means that any duplicate (i.e. coincident) line segments or portions
* of line segments will be reduced to a single line segment in the result.
* This is consistent with the semantics of the
* {@link Geometry#union(Geometry)} operation.
* If merged linework is required, the {@link LineMerger} class can be used.
*
*
- Unioning a set of {@link Point}s has the effect of merging
* all identical points (producing a set with no duplicates).
*
*
* UnaryUnion always operates on the individual components of MultiGeometries.
* So it is possible to use it to "clean" invalid self-intersecting MultiPolygons
* (although the polygon components must all still be individually valid.)
*
* @author mbdavis
*
*/
public class UnaryUnionOp
{
/**
* Computes the geometric union of a {@link Collection}
* of {@link Geometry}s.
*
* @param geoms a collection of geometries
* @return the union of the geometries,
* or null
if the input is empty
*/
public static Geometry union(Collection geoms)
{
UnaryUnionOp op = new UnaryUnionOp(geoms);
return op.union();
}
/**
* Computes the geometric union of a {@link Collection}
* of {@link Geometry}s.
*
* If no input geometries were provided but a {@link GeometryFactory} was provided,
* an empty {@link GeometryCollection} is returned.
*
* @param geoms a collection of geometries
* @param geomFact the geometry factory to use if the collection is empty
* @return the union of the geometries,
* or an empty GEOMETRYCOLLECTION
*/
public static Geometry union(Collection geoms, GeometryFactory geomFact)
{
UnaryUnionOp op = new UnaryUnionOp(geoms, geomFact);
return op.union();
}
/**
* Constructs a unary union operation for a {@link Geometry}
* (which may be a {@link GeometryCollection}).
*
* @param geom a geometry to union
* @return the union of the elements of the geometry
* or an empty GEOMETRYCOLLECTION
*/
public static Geometry union(Geometry geom)
{
UnaryUnionOp op = new UnaryUnionOp(geom);
return op.union();
}
private List polygons = new ArrayList();
private List lines = new ArrayList();
private List points = new ArrayList();
private GeometryFactory geomFact = null;
/**
* Constructs a unary union operation for a {@link Collection}
* of {@link Geometry}s.
*
* @param geoms a collection of geometries
* @param geomFact the geometry factory to use if the collection is empty
*/
public UnaryUnionOp(Collection geoms, GeometryFactory geomFact)
{
this.geomFact = geomFact;
extract(geoms);
}
/**
* Constructs a unary union operation for a {@link Collection}
* of {@link Geometry}s, using the {@link GeometryFactory}
* of the input geometries.
*
* @param geoms a collection of geometries
*/
public UnaryUnionOp(Collection geoms)
{
extract(geoms);
}
/**
* Constructs a unary union operation for a {@link Geometry}
* (which may be a {@link GeometryCollection}).
* @param geom
*/
public UnaryUnionOp(Geometry geom)
{
extract(geom);
}
private void extract(Collection geoms)
{
for (Iterator i = geoms.iterator(); i.hasNext();) {
Geometry geom = (Geometry) i.next();
extract(geom);
}
}
private void extract(Geometry geom)
{
if (geomFact == null)
geomFact = geom.getFactory();
/*
PolygonExtracter.getPolygons(geom, polygons);
LineStringExtracter.getLines(geom, lines);
PointExtracter.getPoints(geom, points);
*/
GeometryExtracter.extract(geom, Polygon.class, polygons);
GeometryExtracter.extract(geom, LineString.class, lines);
GeometryExtracter.extract(geom, Point.class, points);
}
/**
* Gets the union of the input geometries.
* If no input geometries were provided but a {@link GeometryFactory} was provided,
* an empty {@link GeometryCollection} is returned.
* Otherwise, the return value is null
.
*
* @return a Geometry containing the union,
* or an empty GEOMETRYCOLLECTION if no geometries were provided in the input,
* or null
if no GeometryFactory was provided
*/
public Geometry union()
{
if (geomFact == null) {
return null;
}
/**
* For points and lines, only a single union operation is
* required, since the OGC model allowings self-intersecting
* MultiPoint and MultiLineStrings.
* This is not the case for polygons, so Cascaded Union is required.
*/
Geometry unionPoints = null;
if (points.size() > 0) {
Geometry ptGeom = geomFact.buildGeometry(points);
unionPoints = unionNoOpt(ptGeom);
}
Geometry unionLines = null;
if (lines.size() > 0) {
Geometry lineGeom = geomFact.buildGeometry(lines);
unionLines = unionNoOpt(lineGeom);
}
Geometry unionPolygons = null;
if (polygons.size() > 0) {
unionPolygons = CascadedPolygonUnion.union(polygons);
}
/**
* Performing two unions is somewhat inefficient,
* but is mitigated by unioning lines and points first
*/
Geometry unionLA = unionWithNull(unionLines, unionPolygons);
Geometry union = null;
if (unionPoints == null)
union = unionLA;
else if (unionLA == null)
union = unionPoints;
else
union = PointGeometryUnion.union((Puntal) unionPoints, unionLA);
if (union == null)
return geomFact.createGeometryCollection(null);
return union;
}
/**
* Computes the union of two geometries,
* either of both of which may be null.
*
* @param g0 a Geometry
* @param g1 a Geometry
* @return the union of the input(s)
* or null if both inputs are null
*/
private Geometry unionWithNull(Geometry g0, Geometry g1)
{
if (g0 == null && g1 == null)
return null;
if (g1 == null)
return g0;
if (g0 == null)
return g1;
return g0.union(g1);
}
/**
* Computes a unary union with no extra optimization,
* and no short-circuiting.
* Due to the way the overlay operations
* are implemented, this is still efficient in the case of linear
* and puntal geometries.
* Uses robust version of overlay operation
* to ensure identical behaviour to the union(Geometry) operation.
*
* @param g0 a geometry
* @return the union of the input geometry
*/
private Geometry unionNoOpt(Geometry g0)
{
Geometry empty = geomFact.createPoint((Coordinate) null);
return SnapIfNeededOverlayOp.overlayOp(g0, empty, OverlayOp.UNION);
}
}