| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
package org.locationtech.jts.precision; |
| 14 |
|
| 15 |
import org.locationtech.jts.geom.Geometry; |
| 16 |
import org.locationtech.jts.geom.GeometryFactory; |
| 17 |
import org.locationtech.jts.geom.Polygonal; |
| 18 |
import org.locationtech.jts.geom.PrecisionModel; |
| 19 |
import org.locationtech.jts.geom.util.GeometryEditor; |
| 20 |
|
| 21 |
/** |
| 22 |
* Reduces the precision of a {@link Geometry} |
| 23 |
* according to the supplied {@link PrecisionModel}, |
| 24 |
* ensuring that the result is topologically valid. |
| 25 |
* |
| 26 |
* @version 1.12 |
| 27 |
*/ |
| 28 |
public class GeometryPrecisionReducer |
| 29 |
{ |
| 30 |
/** |
| 31 |
* Convenience method for doing precision reduction |
| 32 |
* on a single geometry, |
| 33 |
* with collapses removed |
| 34 |
* and keeping the geometry precision model the same, |
| 35 |
* and preserving polygonal topology. |
| 36 |
* |
| 37 |
* @param g the geometry to reduce |
| 38 |
* @param precModel the precision model to use |
| 39 |
* @return the reduced geometry |
| 40 |
*/ |
| 41 |
public static Geometry reduce(Geometry g, PrecisionModel precModel) |
| 42 |
{ |
| 43 |
GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(precModel); |
| 44 |
return reducer.reduce(g); |
| 45 |
} |
| 46 |
|
| 47 |
/** |
| 48 |
* Convenience method for doing pointwise precision reduction |
| 49 |
* on a single geometry, |
| 50 |
* with collapses removed |
| 51 |
* and keeping the geometry precision model the same, |
| 52 |
* but NOT preserving valid polygonal topology. |
| 53 |
* |
| 54 |
* @param g the geometry to reduce |
| 55 |
* @param precModel the precision model to use |
| 56 |
* @return the reduced geometry |
| 57 |
*/ |
| 58 |
public static Geometry reducePointwise(Geometry g, PrecisionModel precModel) |
| 59 |
{ |
| 60 |
GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(precModel); |
| 61 |
reducer.setPointwise(true); |
| 62 |
return reducer.reduce(g); |
| 63 |
} |
| 64 |
|
| 65 |
private PrecisionModel targetPM; |
| 66 |
private boolean removeCollapsed = true; |
| 67 |
private boolean changePrecisionModel = false; |
| 68 |
private boolean isPointwise = false; |
| 69 |
|
| 70 |
public GeometryPrecisionReducer(PrecisionModel pm) |
| 71 |
{ |
| 72 |
targetPM = pm; |
| 73 |
} |
| 74 |
|
| 75 |
/** |
| 76 |
* Sets whether the reduction will result in collapsed components |
| 77 |
* being removed completely, or simply being collapsed to an (invalid) |
| 78 |
* Geometry of the same type. |
| 79 |
* The default is to remove collapsed components. |
| 80 |
* |
| 81 |
* @param removeCollapsed if <code>true</code> collapsed components will be removed |
| 82 |
*/ |
| 83 |
public void setRemoveCollapsedComponents(boolean removeCollapsed) |
| 84 |
{ |
| 85 |
this.removeCollapsed = removeCollapsed; |
| 86 |
} |
| 87 |
|
| 88 |
/** |
| 89 |
* Sets whether the {@link PrecisionModel} of the new reduced Geometry |
| 90 |
* will be changed to be the {@link PrecisionModel} supplied to |
| 91 |
* specify the precision reduction. |
| 92 |
* <p> |
| 93 |
* The default is to <b>not</b> change the precision model |
| 94 |
* |
| 95 |
* @param changePrecisionModel if <code>true</code> the precision model of the created Geometry will be the |
| 96 |
* the precisionModel supplied in the constructor. |
| 97 |
*/ |
| 98 |
public void setChangePrecisionModel(boolean changePrecisionModel) |
| 99 |
{ |
| 100 |
this.changePrecisionModel = changePrecisionModel; |
| 101 |
} |
| 102 |
|
| 103 |
/** |
| 104 |
* Sets whether the precision reduction will be done |
| 105 |
* in pointwise fashion only. |
| 106 |
* Pointwise precision reduction reduces the precision |
| 107 |
* of the individual coordinates only, but does |
| 108 |
* not attempt to recreate valid topology. |
| 109 |
* This is only relevant for geometries containing polygonal components. |
| 110 |
* |
| 111 |
* @param isPointwise if reduction should be done pointwise only |
| 112 |
*/ |
| 113 |
public void setPointwise(boolean isPointwise) |
| 114 |
{ |
| 115 |
this.isPointwise = isPointwise; |
| 116 |
} |
| 117 |
|
| 118 |
public Geometry reduce(Geometry geom) |
| 119 |
{ |
| 120 |
Geometry reducePW = reducePointwise(geom); |
| 121 |
if (isPointwise) |
| 122 |
return reducePW; |
| 123 |
|
| 124 |
|
| 125 |
if (! (reducePW instanceof Polygonal)) |
| 126 |
return reducePW; |
| 127 |
|
| 128 |
|
| 129 |
if (reducePW.isValid()) return reducePW; |
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
return fixPolygonalTopology(reducePW); |
| 134 |
} |
| 135 |
|
| 136 |
private Geometry reducePointwise(Geometry geom) |
| 137 |
{ |
| 138 |
GeometryEditor geomEdit; |
| 139 |
if (changePrecisionModel) { |
| 140 |
GeometryFactory newFactory = createFactory(geom.getFactory(), targetPM); |
| 141 |
geomEdit = new GeometryEditor(newFactory); |
| 142 |
} |
| 143 |
else |
| 144 |
|
| 145 |
geomEdit = new GeometryEditor(); |
| 146 |
|
| 147 |
/** |
| 148 |
* For polygonal geometries, collapses are always removed, in order |
| 149 |
* to produce correct topology |
| 150 |
*/ |
| 151 |
boolean finalRemoveCollapsed = removeCollapsed; |
| 152 |
if (geom.getDimension() >= 2) |
| 153 |
finalRemoveCollapsed = true; |
| 154 |
|
| 155 |
Geometry reduceGeom = geomEdit.edit(geom, |
| 156 |
new PrecisionReducerCoordinateOperation(targetPM, finalRemoveCollapsed)); |
| 157 |
|
| 158 |
return reduceGeom; |
| 159 |
} |
| 160 |
|
| 161 |
private Geometry fixPolygonalTopology(Geometry geom) |
| 162 |
{ |
| 163 |
/** |
| 164 |
* If precision model was *not* changed, need to flip |
| 165 |
* geometry to targetPM, buffer in that model, then flip back |
| 166 |
*/ |
| 167 |
Geometry geomToBuffer = geom; |
| 168 |
if (! changePrecisionModel) { |
| 169 |
geomToBuffer = changePM(geom, targetPM); |
| 170 |
} |
| 171 |
|
| 172 |
Geometry bufGeom = geomToBuffer.buffer(0); |
| 173 |
|
| 174 |
Geometry finalGeom = bufGeom; |
| 175 |
if (! changePrecisionModel) { |
| 176 |
|
| 177 |
finalGeom = geom.getFactory().createGeometry(bufGeom); |
| 178 |
} |
| 179 |
return finalGeom; |
| 180 |
} |
| 181 |
|
| 182 |
/** |
| 183 |
* Duplicates a geometry to one that uses a different PrecisionModel, |
| 184 |
* without changing any coordinate values. |
| 185 |
* |
| 186 |
* @param geom the geometry to duplicate |
| 187 |
* @param newPM the precision model to use |
| 188 |
* @return the geometry value with a new precision model |
| 189 |
*/ |
| 190 |
private Geometry changePM(Geometry geom, PrecisionModel newPM) |
| 191 |
{ |
| 192 |
GeometryEditor geomEditor = createEditor(geom.getFactory(), newPM); |
| 193 |
|
| 194 |
return geomEditor.edit(geom, new GeometryEditor.NoOpGeometryOperation()); |
| 195 |
} |
| 196 |
|
| 197 |
private GeometryEditor createEditor(GeometryFactory geomFactory, PrecisionModel newPM) |
| 198 |
{ |
| 199 |
|
| 200 |
if (geomFactory.getPrecisionModel() == newPM) |
| 201 |
return new GeometryEditor(); |
| 202 |
|
| 203 |
GeometryFactory newFactory = createFactory(geomFactory, newPM); |
| 204 |
GeometryEditor geomEdit = new GeometryEditor(newFactory); |
| 205 |
return geomEdit; |
| 206 |
} |
| 207 |
|
| 208 |
private GeometryFactory createFactory(GeometryFactory inputFactory, PrecisionModel pm) |
| 209 |
{ |
| 210 |
GeometryFactory newFactory |
| 211 |
= new GeometryFactory(pm, |
| 212 |
inputFactory.getSRID(), |
| 213 |
inputFactory.getCoordinateSequenceFactory()); |
| 214 |
return newFactory; |
| 215 |
} |
| 216 |
|
| 217 |
} |
| 218 |
|