CommonBitsRemover.java
/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.precision;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateFilter;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequenceFilter;
import org.locationtech.jts.geom.Geometry;
/**
* Removes common most-significant mantissa bits
* from one or more {@link Geometry}s.
* <p>
* The CommonBitsRemover "scavenges" precision
* which is "wasted" by a large displacement of the geometry
* from the origin.
* For example, if a small geometry is displaced from the origin
* by a large distance,
* the displacement increases the significant figures in the coordinates,
* but does not affect the <i>relative</i> topology of the geometry.
* Thus the geometry can be translated back to the origin
* without affecting its topology.
* In order to compute the translation without affecting
* the full precision of the coordinate values,
* the translation is performed at the bit level by
* removing the common leading mantissa bits.
* <p>
* If the geometry envelope already contains the origin,
* the translation procedure cannot be applied.
* In this case, the common bits value is computed as zero.
* <p>
* If the geometry crosses the Y axis but not the X axis
* (and <i>mutatis mutandum</i>),
* the common bits for Y are zero,
* but the common bits for X are non-zero.
*
* @version 1.7
*/
public class CommonBitsRemover
{
private Coordinate commonCoord;
private CommonCoordinateFilter ccFilter = new CommonCoordinateFilter();
public CommonBitsRemover()
{
}
/**
* Add a geometry to the set of geometries whose common bits are
* being computed. After this method has executed the
* common coordinate reflects the common bits of all added
* geometries.
*
* @param geom a Geometry to test for common bits
*/
public void add(Geometry geom)
{
geom.apply(ccFilter);
commonCoord = ccFilter.getCommonCoordinate();
}
/**
* The common bits of the Coordinates in the supplied Geometries.
*/
public Coordinate getCommonCoordinate() { return commonCoord; }
/**
* Removes the common coordinate bits from a Geometry.
* The coordinates of the Geometry are changed.
*
* @param geom the Geometry from which to remove the common coordinate bits
* @return the shifted Geometry
*/
public Geometry removeCommonBits(Geometry geom)
{
if (commonCoord.x == 0.0 && commonCoord.y == 0.0)
return geom;
Coordinate invCoord = new Coordinate(commonCoord);
invCoord.x = -invCoord.x;
invCoord.y = -invCoord.y;
Translater trans = new Translater(invCoord);
geom.apply(trans);
geom.geometryChanged();
return geom;
}
/**
* Adds the common coordinate bits back into a Geometry.
* The coordinates of the Geometry are changed.
*
* @param geom the Geometry to which to add the common coordinate bits
*/
public void addCommonBits(Geometry geom)
{
Translater trans = new Translater(commonCoord);
geom.apply(trans);
geom.geometryChanged();
}
static class CommonCoordinateFilter
implements CoordinateFilter
{
private CommonBits commonBitsX = new CommonBits();
private CommonBits commonBitsY = new CommonBits();
public void filter(Coordinate coord)
{
commonBitsX.add(coord.x);
commonBitsY.add(coord.y);
}
public Coordinate getCommonCoordinate()
{
return new Coordinate(
commonBitsX.getCommon(),
commonBitsY.getCommon());
}
}
static class Translater
implements CoordinateSequenceFilter
{
Coordinate trans = null;
public Translater(Coordinate trans)
{
this.trans = trans;
}
public void filter(CoordinateSequence seq, int i) {
double xp = seq.getOrdinate(i, 0) + trans.x;
double yp = seq.getOrdinate(i, 1) + trans.y;
seq.setOrdinate(i, 0, xp);
seq.setOrdinate(i, 1, yp);
}
public boolean isDone() {
return false;
}
public boolean isGeometryChanged() {
return true;
}
}
}