com.vividsolutions.jts.precision.GeometryPrecisionReducer Maven / Gradle / Ivy
Show all versions of JTSplus Show documentation
/*
* 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.precision;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.util.*;
/**
* Reduces the precision of a {@link Geometry}
* according to the supplied {@link PrecisionModel},
* ensuring that the result is topologically valid.
*
* @version 1.12
*/
public class GeometryPrecisionReducer
{
/**
* Convenience method for doing precision reduction
* on a single geometry,
* with collapses removed
* and keeping the geometry precision model the same,
* and preserving polygonal topology.
*
* @param g the geometry to reduce
* @param precModel the precision model to use
* @return the reduced geometry
*/
public static Geometry reduce(Geometry g, PrecisionModel precModel)
{
GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(precModel);
return reducer.reduce(g);
}
/**
* Convenience method for doing pointwise precision reduction
* on a single geometry,
* with collapses removed
* and keeping the geometry precision model the same,
* but NOT preserving valid polygonal topology.
*
* @param g the geometry to reduce
* @param precModel the precision model to use
* @return the reduced geometry
*/
public static Geometry reducePointwise(Geometry g, PrecisionModel precModel)
{
GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(precModel);
reducer.setPointwise(true);
return reducer.reduce(g);
}
private PrecisionModel targetPM;
private boolean removeCollapsed = true;
private boolean changePrecisionModel = false;
private boolean isPointwise = false;
public GeometryPrecisionReducer(PrecisionModel pm)
{
targetPM = pm;
}
/**
* Sets whether the reduction will result in collapsed components
* being removed completely, or simply being collapsed to an (invalid)
* Geometry of the same type.
* The default is to remove collapsed components.
*
* @param removeCollapsed if true
collapsed components will be removed
*/
public void setRemoveCollapsedComponents(boolean removeCollapsed)
{
this.removeCollapsed = removeCollapsed;
}
/**
* Sets whether the {@link PrecisionModel} of the new reduced Geometry
* will be changed to be the {@link PrecisionModel} supplied to
* specify the precision reduction.
*
* The default is to not change the precision model
*
* @param changePrecisionModel if true
the precision model of the created Geometry will be the
* the precisionModel supplied in the constructor.
*/
public void setChangePrecisionModel(boolean changePrecisionModel)
{
this.changePrecisionModel = changePrecisionModel;
}
/**
* Sets whether the precision reduction will be done
* in pointwise fashion only.
* Pointwise precision reduction reduces the precision
* of the individual coordinates only, but does
* not attempt to recreate valid topology.
* This is only relevant for geometries containing polygonal components.
*
* @param isPointwise if reduction should be done pointwise only
*/
public void setPointwise(boolean isPointwise)
{
this.isPointwise = isPointwise;
}
public Geometry reduce(Geometry geom)
{
Geometry reducePW = reducePointwise(geom);
if (isPointwise)
return reducePW;
//TODO: handle GeometryCollections containing polys
if (! (reducePW instanceof Polygonal))
return reducePW;
// Geometry is polygonal - test if topology needs to be fixed
if (reducePW.isValid()) return reducePW;
// hack to fix topology.
// TODO: implement snap-rounding and use that.
return fixPolygonalTopology(reducePW);
}
private Geometry reducePointwise(Geometry geom)
{
GeometryEditor geomEdit;
if (changePrecisionModel) {
GeometryFactory newFactory = createFactory(geom.getFactory(), targetPM);
geomEdit = new GeometryEditor(newFactory);
}
else
// don't change geometry factory
geomEdit = new GeometryEditor();
/**
* For polygonal geometries, collapses are always removed, in order
* to produce correct topology
*/
boolean finalRemoveCollapsed = removeCollapsed;
if (geom.getDimension() >= 2)
finalRemoveCollapsed = true;
Geometry reduceGeom = geomEdit.edit(geom,
new PrecisionReducerCoordinateOperation(targetPM, finalRemoveCollapsed));
return reduceGeom;
}
private Geometry fixPolygonalTopology(Geometry geom)
{
/**
* If precision model was *not* changed, need to flip
* geometry to targetPM, buffer in that model, then flip back
*/
Geometry geomToBuffer = geom;
if (! changePrecisionModel) {
geomToBuffer = changePM(geom, targetPM);
}
Geometry bufGeom = geomToBuffer.buffer(0);
Geometry finalGeom = bufGeom;
if (! changePrecisionModel) {
// a slick way to copy the geometry with the original precision factory
finalGeom = geom.getFactory().createGeometry(bufGeom);
}
return finalGeom;
}
/**
* Duplicates a geometry to one that uses a different PrecisionModel,
* without changing any coordinate values.
*
* @param geom the geometry to duplicate
* @param newPM the precision model to use
* @return the geometry value with a new precision model
*/
private Geometry changePM(Geometry geom, PrecisionModel newPM)
{
GeometryEditor geomEditor = createEditor(geom.getFactory(), newPM);
// this operation changes the PM for the entire geometry tree
return geomEditor.edit(geom, new GeometryEditor.NoOpGeometryOperation());
}
private GeometryEditor createEditor(GeometryFactory geomFactory, PrecisionModel newPM)
{
// no need to change if precision model is the same
if (geomFactory.getPrecisionModel() == newPM)
return new GeometryEditor();
// otherwise create a geometry editor which changes PrecisionModel
GeometryFactory newFactory = createFactory(geomFactory, newPM);
GeometryEditor geomEdit = new GeometryEditor(newFactory);
return geomEdit;
}
private GeometryFactory createFactory(GeometryFactory inputFactory, PrecisionModel pm)
{
GeometryFactory newFactory
= new GeometryFactory(pm,
inputFactory.getSRID(),
inputFactory.getCoordinateSequenceFactory());
return newFactory;
}
}