This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new ccb51c0 More reliable detecting of Java2D geometries backed by single-precision floating point numbers. ccb51c0 is described below commit ccb51c02dde7065bc72e601def4e4755935b32e5 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sun Dec 12 19:10:30 2021 +0100 More reliable detecting of Java2D geometries backed by single-precision floating point numbers. --- .../sis/internal/feature/j2d/EmptyShape.java | 4 +- .../apache/sis/internal/feature/j2d/Factory.java | 5 +- .../sis/internal/feature/j2d/ShapeProperties.java | 4 +- .../sis/internal/feature/j2d/ShapeWrapper.java | 11 ++- .../apache/sis/internal/feature/j2d/Wrapper.java | 5 +- .../sis/internal/feature/jts/ShapeAdapter.java | 22 +++++- .../sis/internal/feature/jts/ShapeConverter.java | 4 +- .../internal/referencing/j2d/AbstractShape.java | 82 ++++++++++++++++++++++ .../internal/referencing/j2d/ShapeUtilities.java | 16 +---- .../sis/internal/referencing/j2d/package-info.java | 2 +- .../referencing/j2d/AbstractShapeTest.java} | 38 ++++++---- .../referencing/j2d/ShapeUtilitiesTest.java | 13 +--- .../sis/test/suite/ReferencingTestSuite.java | 3 +- 13 files changed, 155 insertions(+), 54 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/EmptyShape.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/EmptyShape.java index 94642c3..5ef0e34 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/EmptyShape.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/EmptyShape.java @@ -16,13 +16,13 @@ */ package org.apache.sis.internal.feature.j2d; -import java.awt.Shape; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.util.NoSuchElementException; +import org.apache.sis.internal.referencing.j2d.AbstractShape; /** @@ -33,7 +33,7 @@ import java.util.NoSuchElementException; * @since 1.1 * @module */ -public final class EmptyShape implements Shape, PathIterator { +public final class EmptyShape extends AbstractShape implements PathIterator { /** * The unique empty shape instance. */ diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java index 6434c64..607d7f1 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Factory.java @@ -29,6 +29,7 @@ import org.apache.sis.setup.GeometryLibrary; import org.apache.sis.internal.feature.Geometries; import org.apache.sis.internal.feature.GeometryType; import org.apache.sis.internal.feature.GeometryWrapper; +import org.apache.sis.internal.referencing.j2d.AbstractShape; import org.apache.sis.internal.referencing.j2d.ShapeUtilities; import org.apache.sis.internal.util.CollectionsExt; import org.apache.sis.math.Vector; @@ -243,7 +244,7 @@ public final class Factory extends Geometries<Shape> { } boolean isFloat = true; for (final Shape geometry : shapes) { - if (!ShapeUtilities.isFloat(geometry)) { + if (!AbstractShape.isFloat(geometry)) { isFloat = false; break; } @@ -286,7 +287,7 @@ public final class Factory extends Geometries<Shape> { if (geometry == null) { boolean isFloat = true; for (final Object component : data) { - isFloat = ShapeUtilities.isFloat(component); + isFloat = AbstractShape.isFloat(component); if (!isFloat) break; } final Path2D path = createPath(isFloat, 20); diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeProperties.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeProperties.java index 6ccb702..8cdc881 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeProperties.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeProperties.java @@ -23,7 +23,7 @@ import java.lang.reflect.Array; import java.awt.Shape; import java.awt.geom.PathIterator; import java.awt.geom.IllegalPathStateException; -import org.apache.sis.internal.referencing.j2d.ShapeUtilities; +import org.apache.sis.internal.referencing.j2d.AbstractShape; import org.apache.sis.util.StringBuilders; @@ -96,7 +96,7 @@ final class ShapeProperties { private List<?> coordinates(final double flatness) { final PathIterator it = geometry.getPathIterator(null, flatness); isPolygon = true; - if (ShapeUtilities.isFloat(geometry)) { + if (AbstractShape.isFloat(geometry)) { return coordinatesAsFloats(it); } else { return coordinatesAsDoubles(it); diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeWrapper.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeWrapper.java index d8487a3..835b027 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeWrapper.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/ShapeWrapper.java @@ -22,6 +22,7 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.PathIterator; import java.awt.geom.AffineTransform; +import org.apache.sis.internal.referencing.j2d.AbstractShape; /** @@ -33,7 +34,7 @@ import java.awt.geom.AffineTransform; * @since 1.2 * @module */ -abstract class ShapeWrapper implements Shape { +abstract class ShapeWrapper extends AbstractShape { /** * The source of coordinate values. */ @@ -49,6 +50,14 @@ abstract class ShapeWrapper implements Shape { } /** + * Returns {@code true} if this shape backed by primitive {@code float} values. + */ + @Override + protected boolean isFloat() { + return isFloat(source); + } + + /** * Returns a rectangle that completely encloses this {@code Shape}. * This is not necessarily the smallest bounding box if an accurate * computation would be too expansive. diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java index a37af34..d9d2b82 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/j2d/Wrapper.java @@ -34,6 +34,7 @@ import org.apache.sis.internal.feature.GeometryWithCRS; import org.apache.sis.internal.feature.GeometryWrapper; import org.apache.sis.internal.filter.sqlmm.SQLMM; import org.apache.sis.internal.referencing.j2d.ShapeUtilities; +import org.apache.sis.internal.referencing.j2d.AbstractShape; import org.apache.sis.internal.jdk9.JDK9; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.Debug; @@ -163,7 +164,7 @@ final class Wrapper extends GeometryWithCRS<Shape> { * Implementation of {@link #mergePolylines(Iterator)} also shared by {@link PointWrapper}. */ static Shape mergePolylines(Object next, final Iterator<?> polylines) { - boolean isFloat = ShapeUtilities.isFloat(next); + boolean isFloat = AbstractShape.isFloat(next); Path2D path = isFloat ? new Path2D.Float() : new Path2D.Double(); boolean lineTo = false; add: for (;;) { @@ -191,7 +192,7 @@ add: for (;;) { /* * Convert the path from single-precision to double-precision if needed. */ - if (isFloat && !ShapeUtilities.isFloat(next)) { + if (isFloat && !AbstractShape.isFloat(next)) { path = new Path2D.Double(path); isFloat = false; } diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeAdapter.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeAdapter.java index 57551cf..e2bbf57 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeAdapter.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeAdapter.java @@ -23,12 +23,16 @@ import java.awt.geom.PathIterator; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import org.apache.sis.internal.feature.j2d.EmptyShape; +import org.apache.sis.internal.referencing.j2d.AbstractShape; import org.apache.sis.internal.referencing.j2d.IntervalRectangle; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Point; /** @@ -43,7 +47,7 @@ import org.locationtech.jts.geom.LinearRing; * @since 1.2 * @module */ -final class ShapeAdapter implements Shape { +final class ShapeAdapter extends AbstractShape { /** * A lightweight JTS geometry factory using the default * {@link org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory}. @@ -69,6 +73,22 @@ final class ShapeAdapter implements Shape { } /** + * Returns {@code true} if this shape backed by primitive {@code float} values. + */ + @Override + protected boolean isFloat() { + final CoordinateSequence cs; + if (geometry instanceof Point) { + cs = ((Point) geometry).getCoordinateSequence(); + } else if (geometry instanceof LineString) { + cs = ((LineString) geometry).getCoordinateSequence(); + } else { + return super.isFloat(); + } + return Factory.isFloat(cs); + } + + /** * Returns an integer rectangle that completely encloses the shape. * There is no guarantee that the rectangle is the smallest bounding box that encloses the shape. */ diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeConverter.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeConverter.java index 2103f85..2b29d73 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeConverter.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/ShapeConverter.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.awt.geom.PathIterator; import java.awt.geom.IllegalPathStateException; import org.apache.sis.internal.jdk9.JDK9; -import org.apache.sis.internal.referencing.j2d.ShapeUtilities; +import org.apache.sis.internal.referencing.j2d.AbstractShape; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; @@ -115,7 +115,7 @@ abstract class ShapeConverter { } final PathIterator iterator = shape.getPathIterator(null, flatness); final ShapeConverter converter; - if (ShapeUtilities.isFloat(shape)) { + if (AbstractShape.isFloat(shape)) { converter = new ShapeConverter.Float(factory, iterator); } else { converter = new ShapeConverter.Double(factory, iterator); diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AbstractShape.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AbstractShape.java new file mode 100644 index 0000000..a32e3be --- /dev/null +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AbstractShape.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.referencing.j2d; + +import java.awt.Shape; + + +/** + * Base class for some (not all) shape implementations in Apache SIS. + * This base class provides a mechanism for determining if a shape stores + * coordinate values as simple-precision or double-precision floating point numbers. + * + * @author Martin Desruisseaux (MPO, IRD, Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +public abstract class AbstractShape implements Shape { + /** + * Base classes of Java2D implementations known to store coordinates + * as single-precision floating point numbers. + */ + private static final Class<?>[] FLOAT_SHAPES = { + java.awt.geom.Point2D.Float.class, + java.awt.geom.Line2D.Float.class, + java.awt.geom.QuadCurve2D.Float.class, + java.awt.geom.CubicCurve2D.Float.class, + java.awt.geom.Rectangle2D.Float.class, + java.awt.geom.RoundRectangle2D.Float.class, + java.awt.geom.Arc2D.Float.class, + java.awt.geom.Ellipse2D.Float.class, + java.awt.geom.Path2D.Float.class + }; + + /** + * Creates a new shape. + */ + protected AbstractShape() { + } + + /** + * Returns {@code true} if this shape backed by primitive {@code float} values. + * + * @return {@code true} if this shape is backed by {@code float} coordinate values. + */ + protected boolean isFloat() { + return false; + } + + /** + * Returns {@code true} if the given shape is presumed backed by primitive {@code float} values. + * The given object should be an instance of {@link Shape} or {@link Point2D}. + * + * @param shape the shape for which to determine the backing primitive type. + * @return {@code true} if the given shape is presumed backed by {@code float} coordinate values. + */ + public static boolean isFloat(final Object shape) { + if (shape instanceof AbstractShape) { + return ((AbstractShape) shape).isFloat(); + } + for (final Class<?> c : FLOAT_SHAPES) { + if (c.isInstance(shape)) { + return true; + } + } + return false; + } +} diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/ShapeUtilities.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/ShapeUtilities.java index 7915e2a..a6ecc95 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/ShapeUtilities.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/ShapeUtilities.java @@ -35,7 +35,7 @@ import static java.lang.Double.isInfinite; * Static methods operating on shapes from the {@link java.awt.geom} package. * * @author Martin Desruisseaux (MPO, IRD, Geomatys) - * @version 1.0 + * @version 1.2 * @since 0.5 * @module */ @@ -408,7 +408,7 @@ public final class ShapeUtilities extends Static { final int code = it.currentSegment(buffer); it.next(); if (it.isDone()) { - if (isFloat(path)) { + if (AbstractShape.isFloat(path)) { switch (code) { case PathIterator.SEG_LINETO: return new Line2D.Float((float) x1, (float) y1, (float) buffer[0], (float) buffer[1]); case PathIterator.SEG_QUADTO: return new QuadCurve2D.Float((float) x1, (float) y1, (float) buffer[0], (float) buffer[1], (float) buffer[2], (float) buffer[3]); @@ -427,16 +427,4 @@ public final class ShapeUtilities extends Static { } return path; } - - /** - * Returns {@code true} if the given shape is presumed backed by primitive {@code float} values. - * The given object should be an instance of {@link Shape} or {@link Point2D}. - * This method use heuristic rules based on class name used in Java2D library. - * - * @param path the shape for which to determine the backing primitive type. - * @return {@code true} if the given shape is presumed backed by {@code float} type. - */ - public static boolean isFloat(final Object path) { - return path.getClass().getSimpleName().equals("Float"); - } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java index 3d4904d..24dbb46 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java @@ -26,7 +26,7 @@ * may change in incompatible ways in any future version without notice. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * * @see org.apache.sis.internal.feature.j2d * diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/AbstractShapeTest.java similarity index 57% copy from core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java copy to core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/AbstractShapeTest.java index 3d4904d..5df0e55 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/package-info.java +++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/AbstractShapeTest.java @@ -14,23 +14,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.sis.internal.referencing.j2d; + +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; +import org.apache.sis.test.TestCase; +import org.junit.Test; + +import static org.opengis.test.Assert.*; + /** - * A set of helper classes having a dependency to Java2D. - * We keep those classes in a separated package for making easier to identify - * which parts may need to be replaced in a JavaFX applications. - * - * <strong>Do not use!</strong> - * - * This package is for internal use by SIS only. Classes in this package - * may change in incompatible ways in any future version without notice. + * Tests the {@link AbstractShape} class. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 - * - * @see org.apache.sis.internal.feature.j2d - * - * @since 0.3 + * @version 1.2 + * @since 1.2 * @module */ -package org.apache.sis.internal.referencing.j2d; +public final strictfp class AbstractShapeTest extends TestCase { + /** + * Tests {@link ShapeUtilities#isFloat(Object)}. + */ + @Test + public void testIsFloat() { + assertTrue (AbstractShape.isFloat(new Point2D.Float())); + assertFalse(AbstractShape.isFloat(new Point2D.Double())); + assertTrue (AbstractShape.isFloat(new Line2D.Float())); + assertFalse(AbstractShape.isFloat(new Line2D.Double())); + } +} diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/ShapeUtilitiesTest.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/ShapeUtilitiesTest.java index 4d94f74..a0653a9 100644 --- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/ShapeUtilitiesTest.java +++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/j2d/ShapeUtilitiesTest.java @@ -33,7 +33,7 @@ import static org.opengis.test.Assert.*; * Values in this test were determined empirically by running {@link ShapeUtilitiesViewer}. * * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.2 * @since 0.5 * @module */ @@ -241,15 +241,4 @@ public final strictfp class ShapeUtilitiesTest extends TestCase { assertEquals("CtrlP2", new Point2D.Double(8, 6), ((CubicCurve2D) p).getCtrlP2()); assertEquals("P2", new Point2D.Double(9, 4), ((CubicCurve2D) p).getP2()); } - - /** - * Tests {@link ShapeUtilities#isFloat(Object)}. - */ - @Test - public void test() { - assertTrue (ShapeUtilities.isFloat(new Point2D.Float())); - assertFalse(ShapeUtilities.isFloat(new Point2D.Double())); - assertTrue (ShapeUtilities.isFloat(new Line2D.Float())); - assertFalse(ShapeUtilities.isFloat(new Line2D.Double())); - } } diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java index c10e4bc..261adfd 100644 --- a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java +++ b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java @@ -25,13 +25,14 @@ import org.junit.BeforeClass; * All tests from the {@code sis-referencing} module, in rough dependency order. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */ @Suite.SuiteClasses({ org.apache.sis.internal.referencing.LazySetTest.class, org.apache.sis.internal.referencing.FormulasTest.class, + org.apache.sis.internal.referencing.j2d.AbstractShapeTest.class, org.apache.sis.internal.referencing.j2d.ShapeUtilitiesTest.class, org.apache.sis.internal.referencing.AxisDirectionsTest.class, org.apache.sis.internal.referencing.VerticalDatumTypesTest.class,