This is an automated email from the ASF dual-hosted git repository. amanin pushed a commit to branch refactor/sql-store in repository https://gitbox.apache.org/repos/asf/sis.git
commit bea8309f0e3ca8e8bf2c4d11c599d60a8686d01c Author: Alexis Manin <[email protected]> AuthorDate: Thu Oct 10 11:16:48 2019 +0200 fix(Feature): Add CRS resolution strategy for filter operations. --- .../java/org/apache/sis/filter/CRSMatching.java | 106 ++++++++++++++++++++ .../java/org/apache/sis/filter/ST_Intersects.java | 110 +++++++++++++++------ .../java/org/apache/sis/filter/package-info.java | 45 ++++++++- .../test/java/org/apache/sis/filter/SQLMMTest.java | 43 +++++++- .../sis/internal/feature/GeometriesTestCase.java | 2 +- .../main/java/org/apache/sis/referencing/CRS.java | 80 +++++++-------- 6 files changed, 307 insertions(+), 79 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java b/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java new file mode 100644 index 0000000..23dfdf2 --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/CRSMatching.java @@ -0,0 +1,106 @@ +package org.apache.sis.filter; + +import java.util.Optional; + +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.util.FactoryException; + +import org.apache.sis.referencing.CRS; +import org.apache.sis.util.Utilities; +import org.apache.sis.util.collection.BackingStoreException; + +import static org.apache.sis.referencing.IdentifiedObjects.getIdentifierOrName; + +/** + * TODO: improve CRS conversion/suggestion by allowing user to input a geographic region of interest. + */ +@FunctionalInterface +public interface CRSMatching { + + static CRSMatching left(final CoordinateReferenceSystem source) { + if (source == null) { + return other -> { + if (other == null) return new NullMatch(); + else throw new IllegalArgumentException("No match can be established with given CRS because source one is null."); + }; + } else { + return other -> { + if (other == null) throw new IllegalArgumentException("No match can be established with previous CRS because input one is null"); + final CoordinateReferenceSystem commonCrs = CRS.suggestCommonTarget(null, source, other); + if (commonCrs == null) throw new IllegalArgumentException(String.format( + "No common space can be found between %s and %s", + getIdentifierOrName(source), getIdentifierOrName(other) + )); + return new DefaultMatch(commonCrs, source, other); + }; + } + } + + Match right(final CoordinateReferenceSystem other); + + interface Match { + Optional<CoordinateReferenceSystem> getCommonCRS(); + Optional<CoordinateOperation> fromLeft(); + Optional<CoordinateOperation> fromRight(); + } + + final class NullMatch implements Match { + + @Override + public Optional<CoordinateReferenceSystem> getCommonCRS() { + return Optional.empty(); + } + + @Override + public Optional<CoordinateOperation> fromLeft() { + return Optional.empty(); + } + + @Override + public Optional<CoordinateOperation> fromRight() { + return Optional.empty(); + } + } + + final class DefaultMatch implements Match { + + final CoordinateReferenceSystem commonCRS; + final Optional<CoordinateOperation> fromLeft; + final Optional<CoordinateOperation> fromRight; + + public DefaultMatch(CoordinateReferenceSystem commonCRS, CoordinateReferenceSystem left, CoordinateReferenceSystem right) { + this.commonCRS = commonCRS; + try { + fromLeft = createOp(left, commonCRS); + fromRight = createOp(right, commonCRS); + } catch (FactoryException e) { + throw new BackingStoreException(e); + } + } + + @Override + public Optional<CoordinateReferenceSystem> getCommonCRS() { + return Optional.of(commonCRS); + } + + @Override + public Optional<CoordinateOperation> fromLeft() { + return fromLeft; + } + + @Override + public Optional<CoordinateOperation> fromRight() { + return fromRight; + } + } + + static Optional<CoordinateOperation> createOp(final CoordinateReferenceSystem source, final CoordinateReferenceSystem target) throws FactoryException { + if (Utilities.equalsIgnoreMetadata(source, target)) { + return Optional.empty(); + } else { + final CoordinateOperation op = CRS.findOperation(source, target, null); + return Optional.of(op); + } + } +} diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java index 407d291..f5ddf46 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Intersects.java @@ -1,5 +1,7 @@ package org.apache.sis.filter; +import java.util.AbstractMap; +import java.util.Map; import java.util.function.Predicate; import org.opengis.filter.FilterVisitor; @@ -7,8 +9,14 @@ import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Literal; import org.opengis.filter.spatial.Intersects; import org.opengis.geometry.Geometry; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.TransformException; +import org.opengis.util.FactoryException; import org.apache.sis.internal.feature.GeometryWrapper; +import org.apache.sis.internal.feature.jts.JTS; +import org.apache.sis.util.collection.BackingStoreException; import org.locationtech.jts.geom.prep.PreparedGeometry; import org.locationtech.jts.geom.prep.PreparedGeometryFactory; @@ -38,11 +46,12 @@ public class ST_Intersects implements Intersects { ensureNonNull("Left operand", left); ensureNonNull("Right operand", right); if (left instanceof Literal && right instanceof Literal) { - intersects = constantOp((Literal) left, (Literal) right); + final boolean constantResult = nonOptimizedIntersects(null); + intersects = it -> constantResult; } else if (left instanceof Literal) { - intersects = intersect(right, (Literal) left); + intersects = intersect((Literal) left, right); } else if (right instanceof Literal) { - intersects = intersect(left, (Literal) right); + intersects = intersect((Literal) right, left); } else intersects = this::nonOptimizedIntersects; } @@ -50,46 +59,83 @@ public class ST_Intersects implements Intersects { final Object leftEval = left.evaluate(candidate); final Object rightEval = right.evaluate(candidate); if (leftEval == null || rightEval == null) return false; - return toJTS(leftEval).intersects(toJTS(rightEval)); + + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> leftEntry = toJTS(leftEval); + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> rightEntry = toJTS(rightEval); + final CRSMatching.Match match = CRSMatching.left(leftEntry.getValue()).right(rightEntry.getValue()); + + final org.locationtech.jts.geom.Geometry leftGeom = match.fromLeft() + .map(op -> transformSilently(leftEntry.getKey(), op)) + .orElse(leftEntry.getKey()); + final org.locationtech.jts.geom.Geometry rightGeom = match.fromRight() + .map(op -> transformSilently(rightEntry.getKey(), op)) + .orElse(rightEntry.getKey()); + + return leftGeom.intersects(rightGeom); } - private static org.locationtech.jts.geom.Geometry toJTS(Object value) { - if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry; - if (value instanceof org.locationtech.jts.geom.Geometry) return (org.locationtech.jts.geom.Geometry) value; - throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); + private static org.locationtech.jts.geom.Geometry transformSilently(org.locationtech.jts.geom.Geometry target, CoordinateOperation op) { + try { + return JTS.transform(target, op); + } catch (TransformException e) { + throw new BackingStoreException(e); + } } - private Predicate constantOp(Literal left, Literal right) { - final boolean result = left.getValue() != null - && right.getValue() != null - && toJTS(left.getValue()).intersects(toJTS(right.getValue())); - return it -> result; + private static Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> toJTS(Object value) { + CoordinateReferenceSystem crs = null; + if (value instanceof Geometry) crs = ((Geometry) value).getCoordinateReferenceSystem(); + if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry; + if (value instanceof org.locationtech.jts.geom.Geometry) { + final org.locationtech.jts.geom.Geometry geom = (org.locationtech.jts.geom.Geometry) value; + if (crs == null) { + try { + crs = JTS.getCoordinateReferenceSystem(geom); + } catch (FactoryException e) { + throw new BackingStoreException("Cannot extract CRS from operand", e); + } + } + return new AbstractMap.SimpleImmutableEntry<>(geom, crs); + } + throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); } - private static Predicate intersect(Expression left, Literal right) { - Object value = right.getValue(); + private static Predicate intersect(Literal left, Expression right) { + Object value = left.getValue(); ensureNonNull("Literal value", value); // TODO: make more consistent strategy once Geometry API is stable. - if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry; - if (value instanceof org.locationtech.jts.geom.Geometry) { - final PreparedGeometry pg = new PreparedGeometryFactory().create((org.locationtech.jts.geom.Geometry) value); + try { + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> leftEntry = toJTS(value); + final CRSMatching crsMatching = CRSMatching.left(leftEntry.getValue()); + final PreparedGeometry optimizedGeom = new PreparedGeometryFactory().create((org.locationtech.jts.geom.Geometry) value); return it -> { - Object val = left.evaluate(it); + Object val = right.evaluate(it); if (val == null) return false; - return pg.intersects(toJTS(val)); + final Map.Entry<org.locationtech.jts.geom.Geometry, CoordinateReferenceSystem> rightEntry = toJTS(val); + final CRSMatching.Match match = crsMatching.right(rightEntry.getValue()); + final org.locationtech.jts.geom.Geometry rightGeom = match.fromRight() + .map(op -> transformSilently(rightEntry.getKey(), op)) + .orElse(rightEntry.getKey()); + return match.fromLeft() + .map(op -> transformSilently(leftEntry.getKey(), op)) + .map(geom -> geom.intersects(rightGeom)) + .orElseGet(() -> optimizedGeom.intersects(rightGeom)); }; - } else if (value instanceof Geometry) { - final Geometry geom = (Geometry) value; - return it -> { - final Geometry newVal = left.evaluate(it, Geometry.class); - if (newVal == null) { - final Object testVal = left.evaluate(it); - if (testVal == null) return false; - throw new UnsupportedOperationException("Unsupported geometry type: "+testVal.getClass().getCanonicalName()); - } - return geom.intersects(newVal); - }; - } else throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); + } catch (UnsupportedOperationException e) { + if (value instanceof Geometry) { + final Geometry geom = (Geometry) value; + return it -> { + final Geometry newVal = left.evaluate(it, Geometry.class); + if (newVal == null) { + final Object testVal = left.evaluate(it); + if (testVal == null) return false; + throw new UnsupportedOperationException("Unsupported geometry type: "+testVal.getClass().getCanonicalName()); + } + return geom.intersects(newVal); + }; + } + } + throw new UnsupportedOperationException("Unsupported geometry type: "+value.getClass().getCanonicalName()); } @Override diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/package-info.java b/core/sis-feature/src/main/java/org/apache/sis/filter/package-info.java index 7291456..cc40a87 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/filter/package-info.java +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/package-info.java @@ -19,12 +19,53 @@ * Filters features according their properties. * A <cite>filter expression</cite> is a construct used to constraint a feature set to a subset. * + * All operations in this package try to follow rules of both following standards: + * <ul> + * <li><a href="https://www.iso.org/fr/standard/53698.html">ISO/IEC 13249-3:2011 - SQLMM</a></li> + * <li><a href="http://docs.opengeospatial.org/is/09-026r2/09-026r2.html">OGC® Filter Encoding 2.0 Encoding Standard</a></li> + * </ul> + * + * <div class="section">General considerations:</div> + * <div class="section">Coordinate reference system handling:</div> + * As stated by Filter encoding 2.0.2, section 7.8.4, heterogeneous coordinate reference systems must be handled by + * libraries, one way or another. The standard does not define any strategy. As Apache-SIS contains a powerful + * transform system, we'll try to handle differences in the following way: + * <ul> + * <li> + * If all evaluated geometries define a srid, but their not the same, we'll try to project them in a common + * space. The strategy will be guided by {@link org.apache.sis.referencing.CRS#suggestCommonTarget(org.opengis.metadata.extent.GeographicBoundingBox, org.opengis.referencing.crs.CoordinateReferenceSystem...) Referencing utility method}. + * If it cannot provide a common space, we will fail any ongoing operation. + * </li> + * <li> + * Missing information: + * <ul> + * <li>If no geometry contains any srid, consider they're defined in the same space, and proceed</li> + * <li>If one geometry define a CRS but the other do not, consider that an ambiguity resides: fail.</li> + * </ul> + * </li> + * </ul> + * + * <div class="section">Optimisations</div> + * For now, few to no optimisation is done in the operators. Most of important ones would require one of the two + * following things: + * <ul> + * <li> + * Context information: Filters does not know in advance the feature type they're operating upon, which is + * vital to define some calculus parameters, as property value conversion strategy, spatial system changes, etc. + * Such information would allow operators to prepare data at initialisation time. + * </li> + * <li> + * User hints: some operations could be set faster at the cost of precision. To activate such things, it would + * require user consent. Most naïve example is spatial reference system conversion, which could be de-activated + * for systems with nearly equal parameters (see {@link org.apache.sis.util.Utilities#equalsApproximately(java.lang.Object, java.lang.Object)}. + * </li> + * </ul> + * extra-information for + * the operator, as well as hints from the user to allow sac * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * @version 1.0 * - * @see <a href="http://docs.opengeospatial.org/is/09-026r2/09-026r2.html">OGC® Filter Encoding 2.0 Encoding Standard</a> - * * @since 1.0 * @module */ diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java index 75a2fbc..8b2cda5 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java @@ -25,6 +25,7 @@ import org.opengis.filter.expression.Literal; import org.opengis.filter.expression.PropertyName; import org.opengis.geometry.Envelope; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.ProjectedCRS; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.geometry.GeneralEnvelope; @@ -40,6 +41,7 @@ import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Point; +import static org.apache.sis.internal.feature.GeometriesTestCase.expectFailFast; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -170,22 +172,53 @@ public class SQLMMTest extends TestCase { }); final Literal lring = factory.literal(ring); - ST_Intersects st = new ST_Intersects(new Expression[]{factory.literal(gf.createPoint(new Coordinate(2, 4))), lring}); + ST_Intersects st = intersects(factory.literal(gf.createPoint(new Coordinate(2, 4))), lring); // Ensure argument nullity does not modify behavior assertFalse("Unexpected intersection", st.evaluate(null)); assertFalse("Unexpected intersection", st.evaluate(new Object())); // Border should intersect final Feature f = mock(); - f.setPropertyValue(P_NAME, gf.createPoint(second)); + final Point point = gf.createPoint(second); + f.setPropertyValue(P_NAME, point); final PropertyName geomName = factory.property(P_NAME); - st = new ST_Intersects(new Expression[]{geomName, lring}); + st = intersects(geomName, lring); assertTrue("Border point should intersect triangle", st.evaluate(f)); // Ensure inverting expression does not modify behavior. - st = new ST_Intersects(new Expression[]{lring, geomName}); + st = intersects(lring, geomName); assertTrue("Border point should intersect triangle", st.evaluate(f)); - // TODO: add CRS checking tests. + // Ensure CRS conversion works as expected (see package-info). + // Missing + point.setUserData(CommonCRS.defaultGeographic()); + expectFailFast(() -> intersects(geomName, lring).evaluate(f), IllegalArgumentException.class); + final Literal lPoint = factory.literal(point); + expectFailFast(() -> intersects(lPoint, lring).evaluate(null), IllegalArgumentException.class); + + // Disjoint + final ProjectedCRS nadUtm = CommonCRS.NAD27.universal(32, 37); + final ProjectedCRS wgsUtm = CommonCRS.WGS84.universal(-2, 4); + + point.setUserData(nadUtm); + ring.setUserData(wgsUtm); + expectFailFast(() -> intersects(geomName, lring).evaluate(f), IllegalArgumentException.class); + expectFailFast(() -> intersects(lPoint, lring).evaluate(null), IllegalArgumentException.class); + + // TODO: activate back after fixing CRS.suggestCommonTarget + // utm domain contained in CRS:84 +// ring.setUserData(CommonCRS.defaultGeographic()); +// assertTrue("Intersection should be found when CRS are compatible", intersects(geomName, lring).evaluate(f)); +// assertTrue("Intersection should be found when CRS are compatible", intersects(lPoint, lring).evaluate(null)); + + // Common base CRS +// ring.setUserData(CommonCRS.WGS84.universal(0, 0)); +// point.setUserData(CommonCRS.WGS84.universal(7, 8)); +// assertTrue("Intersection should be found when CRS are compatible", intersects(geomName, lring).evaluate(f)); +// assertTrue("Intersection should be found when CRS are compatible", intersects(lPoint, lring).evaluate(null)); + } + + private static ST_Intersects intersects(final Expression left, Expression right) { + return new ST_Intersects(new Expression[]{left, right}); } /** diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java index 7aa7a44..f49d481 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java @@ -194,7 +194,7 @@ public abstract strictfp class GeometriesTestCase extends TestCase { expectFailFast(() -> factory.tryConvertToGeometry(wrapped3d, NONE), IllegalArgumentException.class); } - private static void expectFailFast(Callable whichMustFail, Class<? extends Exception>... expectedErrorTypes) { + public static void expectFailFast(Callable whichMustFail, Class<? extends Exception>... expectedErrorTypes) { try { final Object result = whichMustFail.call(); fail("Fail fast expected, but successfully returned "+result); diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java index 1b9b49d..a8912d4 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java @@ -16,82 +16,84 @@ */ package org.apache.sis.referencing; -import java.util.Map; -import java.util.List; import java.util.ArrayList; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.logging.LogRecord; -import org.opengis.util.FactoryException; + import org.opengis.geometry.Envelope; -import org.opengis.referencing.NoSuchAuthorityCodeException; +import org.opengis.geometry.Geometry; +import org.opengis.metadata.citation.Citation; +import org.opengis.metadata.extent.BoundingPolygon; +import org.opengis.metadata.extent.Extent; +import org.opengis.metadata.extent.GeographicBoundingBox; +import org.opengis.metadata.extent.GeographicExtent; import org.opengis.referencing.IdentifiedObject; -import org.opengis.referencing.cs.CartesianCS; -import org.opengis.referencing.cs.EllipsoidalCS; -import org.opengis.referencing.cs.AxisDirection; -import org.opengis.referencing.cs.CoordinateSystem; -import org.opengis.referencing.cs.CoordinateSystemAxis; +import org.opengis.referencing.NoSuchAuthorityCodeException; +import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CRSFactory; -import org.opengis.referencing.crs.SingleCRS; import org.opengis.referencing.crs.CompoundCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.referencing.crs.CRSAuthorityFactory; +import org.opengis.referencing.crs.EngineeringCRS; +import org.opengis.referencing.crs.GeneralDerivedCRS; import org.opengis.referencing.crs.GeodeticCRS; import org.opengis.referencing.crs.GeographicCRS; -import org.opengis.referencing.crs.GeneralDerivedCRS; import org.opengis.referencing.crs.ProjectedCRS; +import org.opengis.referencing.crs.SingleCRS; import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.crs.VerticalCRS; -import org.opengis.referencing.crs.EngineeringCRS; +import org.opengis.referencing.cs.AxisDirection; +import org.opengis.referencing.cs.CartesianCS; +import org.opengis.referencing.cs.CoordinateSystem; +import org.opengis.referencing.cs.CoordinateSystemAxis; +import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.datum.Datum; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.OperationNotFoundException; -import org.opengis.metadata.citation.Citation; -import org.opengis.metadata.extent.Extent; -import org.opengis.metadata.extent.BoundingPolygon; -import org.opengis.metadata.extent.GeographicBoundingBox; -import org.opengis.metadata.extent.GeographicExtent; import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.OperationNotFoundException; import org.opengis.referencing.operation.TransformException; -import org.apache.sis.measure.Units; +import org.opengis.util.FactoryException; + import org.apache.sis.geometry.Envelopes; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.internal.referencing.AxisDirections; +import org.apache.sis.internal.referencing.CoordinateOperations; +import org.apache.sis.internal.referencing.DefinitionVerifier; import org.apache.sis.internal.referencing.EllipsoidalHeightCombiner; import org.apache.sis.internal.referencing.PositionalAccuracyConstant; -import org.apache.sis.internal.referencing.CoordinateOperations; import org.apache.sis.internal.referencing.ReferencingUtilities; -import org.apache.sis.internal.referencing.DefinitionVerifier; import org.apache.sis.internal.referencing.Resources; import org.apache.sis.internal.system.DefaultFactories; -import org.apache.sis.internal.system.Modules; import org.apache.sis.internal.system.Loggers; +import org.apache.sis.internal.system.Modules; import org.apache.sis.internal.util.Numerics; -import org.apache.sis.referencing.cs.AxisFilter; -import org.apache.sis.referencing.cs.CoordinateSystems; -import org.apache.sis.referencing.cs.DefaultVerticalCS; +import org.apache.sis.measure.Units; +import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; +import org.apache.sis.metadata.iso.extent.Extents; +import org.apache.sis.referencing.crs.DefaultCompoundCRS; +import org.apache.sis.referencing.crs.DefaultEngineeringCRS; import org.apache.sis.referencing.crs.DefaultGeographicCRS; import org.apache.sis.referencing.crs.DefaultProjectedCRS; import org.apache.sis.referencing.crs.DefaultVerticalCRS; -import org.apache.sis.referencing.crs.DefaultCompoundCRS; -import org.apache.sis.referencing.crs.DefaultEngineeringCRS; +import org.apache.sis.referencing.cs.AxisFilter; +import org.apache.sis.referencing.cs.CoordinateSystems; +import org.apache.sis.referencing.cs.DefaultVerticalCS; +import org.apache.sis.referencing.factory.GeodeticObjectFactory; +import org.apache.sis.referencing.factory.UnavailableFactoryException; import org.apache.sis.referencing.operation.AbstractCoordinateOperation; import org.apache.sis.referencing.operation.CoordinateOperationContext; -import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory; import org.apache.sis.referencing.operation.DefaultConversion; -import org.apache.sis.referencing.factory.GeodeticObjectFactory; -import org.apache.sis.referencing.factory.UnavailableFactoryException; -import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; -import org.apache.sis.metadata.iso.extent.Extents; -import org.apache.sis.util.resources.Errors; -import org.apache.sis.util.logging.Logging; -import org.apache.sis.util.logging.WarningListener; +import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory; import org.apache.sis.util.ArgumentChecks; -import org.apache.sis.util.Utilities; import org.apache.sis.util.Static; +import org.apache.sis.util.Utilities; +import org.apache.sis.util.logging.Logging; +import org.apache.sis.util.logging.WarningListener; +import org.apache.sis.util.resources.Errors; // Branch-dependent imports -import org.opengis.geometry.Geometry; /** @@ -431,7 +433,7 @@ public final class CRS extends Static { * Suggests a coordinate reference system which could be a common target for coordinate operations having the * given sources. This method compares the {@linkplain #getGeographicBoundingBox(CoordinateReferenceSystem) * domain of validity} of all given CRSs. If a CRS has a domain of validity that contains the domain of all other - * CRS, than that CRS is returned. Otherwise this method verifies if a {@linkplain GeneralDerivedCRS#getBaseCRS() + * CRS, then that CRS is returned. Otherwise this method verifies if a {@linkplain GeneralDerivedCRS#getBaseCRS() * base CRS} (usually a {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS geographic CRS} instance) * would be suitable. If no suitable CRS is found, then this method returns {@code null}. *
