jts icon indicating copy to clipboard operation
jts copied to clipboard

GeometryPrecisionReducer does not change factory/PrecisionModel of output

Open ch-k opened this issue 4 years ago • 1 comments

I'm using the GeometryPrecisionReducer to adjust the precision of a geometry. This works but the precision model of the output geometry is not changed although this is explicitly enabled with setChangePrecisionModel(true). It seems that under the hood a NoOpGeometryOperation is used. This does nothing but returning the input geometry.

Here is a unit test to demonstrate (my) expectation:

void testChangePrecisionModel() throws ParseException {
    PrecisionModel precisionModel = new PrecisionModel(1000);
    GeometryPrecisionReducer precisionReducer = new GeometryPrecisionReducer(precisionModel);
    precisionReducer.setChangePrecisionModel(true);

    Geometry geom = new WKTReader().read("LINESTRING (612955.157 5975669.491, 579339.471 5936922.488)");
    System.out.println("PM orig: " + geom.getFactory().getPrecisionModel());

    Geometry geomReduced = precisionReducer.reduce(geom);

    System.out.println("PM redu: " + geomReduced.getFactory().getPrecisionModel());
    assertEquals(precisionModel.getType(),  geomReduced.getFactory().getPrecisionModel().getType());
  }

ch-k avatar Sep 10 '21 08:09 ch-k

This applies to Point LineString, MultiPoint with just one item, MultiLineString with just one item. For Polygon the factory is changed but not for its LinearRing components. For the GeometryCollection based geometries the factory is only changed for the parent geometry, not for its items.

  /**
   * Geometry precision reducer changes precision model only for GeometryCollections and Polygons
   * see https://github.com/NetTopologySuite/NetTopologySuite/issues/719
   */
  public void testNTS719() {
    checkPrecisionModelChange("POLYGON ((0.001 0.001, 0.001 10.001, 10.001 10.001, 10.001 0.001, 0.001 0.001))");
    checkPrecisionModelChange("MULTIPOINT ((1.234 5.678))");
    checkPrecisionModelChange("MULTIPOINT ((1.234 5.678), (1.235 4.678))");
    checkPrecisionModelChange("MULTILINESTRING ((1.234 5.678, 1.235 4.678))");
    checkPrecisionModelChange("MULTILINESTRING ((1.234 5.678, 1.235 4.678), (1.236 4.578, 1.235 4.678))");
    checkPrecisionModelChange("POINT (1.234 5.678)");
    checkPrecisionModelChange("GEOMETRYCOLLECTION(POINT (1.234 5.678), LINESTRING(1.234 5.678, 1.235 4.678))");
  }

  public void checkPrecisionModelChange(String wkt) {
    Geometry geom = read(wkt);

    final double scale = 100;
    PrecisionModel pmTarget = new PrecisionModel(scale);
    GeometryPrecisionReducer gpr = new GeometryPrecisionReducer(pmTarget);
    gpr.setChangePrecisionModel(true);

    Geometry geomReduced = gpr.reduce(geom);
    geomReduced.apply(new FactoryChangeFilter(scale));
    assertEquals("PM not changed for " + wkt, scale, geomReduced.getFactory().getPrecisionModel().getScale());

  }

  class FactoryChangeFilter implements GeometryComponentFilter {

    final double _scale;
    FactoryChangeFilter(double scale) {
      _scale = scale;
    }
    @Override
    public void filter(Geometry geom) {
      PrecisionModel pm = geom.getPrecisionModel();
      assertEquals("PM not changed for " + geom.toString(), _scale, pm.getScale());
    }
  }

A possible fix would be to change GeometryEditor.NoOpGeometryOperation to this;

  /**
   * A GeometryEditorOperation which does not modify
   * the input geometry.
   * This can be used for simple changes of
   * GeometryFactory (including PrecisionModel and SRID).
   *
   * @author mbdavis
   *
   */
  public static class NoOpGeometryOperation
  implements GeometryEditorOperation
  {
  	public Geometry edit(Geometry geometry, GeometryFactory factory)
  	{
      return factory != geometry.getFactory()
        ? factory.createGeometry(geometry)
        : geometry;
  	}
  }

FObermaier avatar Aug 28 '24 13:08 FObermaier