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
commit f039368a03a140d08700cab4af7ef082d45c4dc6 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Nov 25 10:51:24 2021 +0100 Add an `Extents.union(…)` method and modify implementations of `intersection` methods for sharing code. --- .../apache/sis/metadata/iso/extent/Extents.java | 83 +++++++++++++++------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java index 81d51da..4ab7000 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java @@ -20,6 +20,8 @@ import java.util.Date; import java.util.List; import java.util.ArrayList; import java.util.Locale; +import java.util.function.Function; +import java.util.function.BiConsumer; import javax.measure.Unit; import org.opengis.geometry.Envelope; import org.opengis.geometry.DirectPosition; @@ -44,6 +46,7 @@ import org.opengis.referencing.datum.VerticalDatumType; import org.opengis.referencing.operation.TransformException; import org.apache.sis.internal.metadata.ReferencingServices; import org.apache.sis.metadata.InvalidMetadataException; +import org.apache.sis.metadata.iso.ISOMetadata; import org.apache.sis.measure.Longitude; import org.apache.sis.measure.MeasurementRange; import org.apache.sis.measure.Range; @@ -75,7 +78,7 @@ import org.opengis.geometry.Geometry; * </ul> * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * * @see org.apache.sis.geometry.Envelopes * @@ -548,6 +551,27 @@ public final class Extents extends Static { } /** + * Returns the union of the given geographic bounding boxes. If any of the arguments is {@code null}, + * then this method returns the other argument (which may be null). Otherwise this method returns a box + * which is the union of the two given boxes. + * + * <p>This method never modify the given boxes, but may return directly one of the given arguments + * if it already represents the union result.</p> + * + * @param b1 the first bounding box, or {@code null}. + * @param b2 the second bounding box, or {@code null}. + * @return the union (may be any of the {@code b1} or {@code b2} argument if unchanged), + * or {@code null} if the two given boxes are null. + * + * @see DefaultGeographicBoundingBox#add(GeographicBoundingBox) + * + * @since 1.2 + */ + public static GeographicBoundingBox union(final GeographicBoundingBox b1, final GeographicBoundingBox b2) { + return apply(b1, b2, DefaultGeographicBoundingBox::new, DefaultGeographicBoundingBox::add); + } + + /** * Returns the intersection of the given geographic bounding boxes. If any of the arguments is {@code null}, * then this method returns the other argument (which may be null). Otherwise this method returns a box which * is the intersection of the two given boxes. If there is no intersection, the returned bounding box contains @@ -568,13 +592,7 @@ public final class Extents extends Static { * @since 0.4 */ public static GeographicBoundingBox intersection(final GeographicBoundingBox b1, final GeographicBoundingBox b2) { - if (b1 == null) return b2; - if (b2 == null || b2 == b1) return b1; - final DefaultGeographicBoundingBox box = new DefaultGeographicBoundingBox(b1); - box.intersect(b2); - if (box.equals(b1, ComparisonMode.BY_CONTRACT)) return b1; - if (box.equals(b2, ComparisonMode.BY_CONTRACT)) return b2; - return box; + return apply(b1, b2, DefaultGeographicBoundingBox::new, DefaultGeographicBoundingBox::intersect); } /** @@ -618,13 +636,7 @@ public final class Extents extends Static { * @since 0.8 */ public static VerticalExtent intersection(final VerticalExtent e1, final VerticalExtent e2) { - if (e1 == null) return e2; - if (e2 == null || e2 == e1) return e1; - final DefaultVerticalExtent extent = new DefaultVerticalExtent(e1); - extent.intersect(e2); - if (extent.equals(e1, ComparisonMode.BY_CONTRACT)) return e1; - if (extent.equals(e2, ComparisonMode.BY_CONTRACT)) return e2; - return extent; + return apply(e1, e2, DefaultVerticalExtent::new, DefaultVerticalExtent::intersect); } /** @@ -647,13 +659,7 @@ public final class Extents extends Static { * @since 0.8 */ public static TemporalExtent intersection(final TemporalExtent e1, final TemporalExtent e2) { - if (e1 == null) return e2; - if (e2 == null || e2 == e1) return e1; - final DefaultTemporalExtent extent = new DefaultTemporalExtent(e1); - extent.intersect(e2); - if (extent.equals(e1, ComparisonMode.BY_CONTRACT)) return e1; - if (extent.equals(e2, ComparisonMode.BY_CONTRACT)) return e2; - return extent; + return apply(e1, e2, DefaultTemporalExtent::new, DefaultTemporalExtent::intersect); } /** @@ -680,12 +686,35 @@ public final class Extents extends Static { * @since 0.8 */ public static Extent intersection(final Extent e1, final Extent e2) { + return apply(e1, e2, DefaultExtent::new, DefaultExtent::intersect); + } + + /** + * Implementation of {@code intersection(…)} and {@code union(…)} methods. + * + * <div class="note"><b>Note:</b> + * the <var>C</var> parameter type should be {@code <C extends ISOMetadata & I>}. + * But this is not allowed by current Java compiler, because of complexity. See + * <a href="https://bugs.openjdk.java.net/browse/JDK-4899305">JDK-4899305</a>.</div> + * + * @param <I> the metadata interface. + * @param <C> the metadata implementation class. Shall implement {@code <I>}. + * @param e1 the first extent, or {@code null}. + * @param e2 the second extent, or {@code null}. + * @param constructor copy constructor of metadata implementation class. + * @param operator the union or intersection operator to apply. + * @return + */ + @SuppressWarnings("unchecked") // Workaround for Java above-cited compiler restriction. + private static <I, C extends ISOMetadata> I apply(final I e1, final I e2, + final Function<I,C> constructor, final BiConsumer<C,I> operator) + { if (e1 == null) return e2; if (e2 == null || e2 == e1) return e1; - final DefaultExtent extent = new DefaultExtent(e1); - extent.intersect(e2); - if (extent.equals(e1, ComparisonMode.BY_CONTRACT)) return e1; - if (extent.equals(e2, ComparisonMode.BY_CONTRACT)) return e2; - return extent; + final C result = constructor.apply(e1); + operator.accept(result, e2); + if (result.equals(e1, ComparisonMode.BY_CONTRACT)) return e1; + if (result.equals(e2, ComparisonMode.BY_CONTRACT)) return e2; + return (I) result; } }