diff --git a/src/main/java/org/cts/crs/CoordinateReferenceSystem.java b/src/main/java/org/cts/crs/CoordinateReferenceSystem.java index 09b9ecd2..8e38b7d8 100644 --- a/src/main/java/org/cts/crs/CoordinateReferenceSystem.java +++ b/src/main/java/org/cts/crs/CoordinateReferenceSystem.java @@ -25,6 +25,7 @@ import org.cts.Identifiable; import org.cts.cs.CoordinateSystem; +import org.cts.cs.Extent; import org.cts.datum.Datum; import org.cts.op.projection.Projection; @@ -47,50 +48,54 @@ * * @author Michaël Michaud */ -public interface CoordinateReferenceSystem extends Identifiable { +public interface CoordinateReferenceSystem extends Identifiable, Extent { /** * Coordinate Reference System Type. */ - public enum Type { + enum Type { GEOCENTRIC, GEOGRAPHIC3D, GEOGRAPHIC2D, PROJECTED, VERTICAL, COMPOUND, ENGINEERING } /** - * Returns this CoordinateReferenceSystem Type. - * @return + * Returns this CoordinateReferenceSystem.Type. */ - public Type getType(); + Type getType(); /** * Returns the {@link CoordinateSystem} used by this * CoordinateReferenceSystem. - * @return */ - public CoordinateSystem getCoordinateSystem(); + CoordinateSystem getCoordinateSystem(); /** * Returns the {@link Datum} to which this - * CoordinateReferenceSystem is refering. For compound - * CoordinateReferenceSystem, getDatum returns the the main - * datum, ie the {@link org.cts.datum.GeodeticDatum} (or horizontal Datum). - * @return + * CoordinateReferenceSystem refers. + * For compound CoordinateReferenceSystem, getDatum returns the + * main datum, ie the {@link org.cts.datum.GeodeticDatum} (or horizontal Datum). */ - public Datum getDatum(); + Datum getDatum(); /** * Returns the {@link Projection} to which this - * CoordinateReferenceSystem is refering. It returns null if no + * CoordinateReferenceSystem refers. It returns null if no * projection is defined for this CRS. - * @return + * @return the {@link Projection} used by this + * CoordinateReferenceSystem or null. */ - public Projection getProjection(); + Projection getProjection(); /** - * Returns a WKT representation of the CoordinateReferenceSystem. - * - * @return + * Returns a WKT representation of this CoordinateReferenceSystem. */ - public String toWKT(); + String toWKT(); + + /** + * Returns whether coord is inside this Extent or not. It's up to the user to + * check consistency between coord and extent type. + */ + default boolean isInside(double[] coord) { + return true; + } } diff --git a/src/main/java/org/cts/crs/GeocentricCRS.java b/src/main/java/org/cts/crs/GeocentricCRS.java index f26a4f9e..a911a3a4 100644 --- a/src/main/java/org/cts/crs/GeocentricCRS.java +++ b/src/main/java/org/cts/crs/GeocentricCRS.java @@ -56,11 +56,11 @@ public class GeocentricCRS extends GeodeticCRS { /** * A 3D cartesian {@link CoordinateSystem}. */ - public static final CoordinateSystem XYZ = new CoordinateSystem(new Axis[]{X, Y, Z}, new Unit[]{METER, METER, METER}); + public static final CoordinateSystem XYZ = + new CoordinateSystem(new Axis[]{X, Y, Z}, new Unit[]{METER, METER, METER}); /** - * Returns this CoordinateReferenceSystem Type. - * @return + * @see CoordinateReferenceSystem#getType(). */ @Override public Type getType() { @@ -92,12 +92,14 @@ public GeocentricCRS(Identifier identifier, GeodeticDatum datum, } /** - * @return + * Creates a CoordinateOperation to transform coordinates from this + * GeocentricCRS to the associated Geographic3DCRS. + * * @see GeodeticCRS#toGeographicCoordinateConverter() */ @Override public CoordinateOperation toGeographicCoordinateConverter() { - List ops = new ArrayList(); + List ops = new ArrayList<>(); ops.add(new Geocentric2Geographic(getDatum().getEllipsoid())); if (!getDatum().getPrimeMeridian().equals(PrimeMeridian.GREENWICH)) { ops.add(LongitudeRotation.getLongitudeRotationTo(getDatum().getPrimeMeridian())); @@ -107,12 +109,14 @@ public CoordinateOperation toGeographicCoordinateConverter() { } /** - * @return + * Creates a CoordinateOperation to transform coordinates from the associated + * Geographic3DCRS to this GeocentricCRS. + * * @see GeodeticCRS#fromGeographicCoordinateConverter() */ @Override public CoordinateOperation fromGeographicCoordinateConverter() { - List ops = new ArrayList(); + List ops = new ArrayList<>(); if (!getDatum().getPrimeMeridian().equals(PrimeMeridian.GREENWICH)) { ops.add(LongitudeRotation.getLongitudeRotationFrom(getDatum().getPrimeMeridian())); } @@ -122,9 +126,7 @@ public CoordinateOperation fromGeographicCoordinateConverter() { } /** - * Returns a WKT representation of the geocentric CRS. - * - * @return + * @see CoordinateReferenceSystem#toWKT() */ public String toWKT() { StringBuilder w = new StringBuilder(); diff --git a/src/main/java/org/cts/crs/GeodeticCRS.java b/src/main/java/org/cts/crs/GeodeticCRS.java index cc207473..d65f8af6 100644 --- a/src/main/java/org/cts/crs/GeodeticCRS.java +++ b/src/main/java/org/cts/crs/GeodeticCRS.java @@ -25,11 +25,14 @@ import org.cts.IdentifiableComponent; import org.cts.Identifier; +import org.cts.IllegalCoordinateException; import org.cts.cs.CoordinateSystem; +import org.cts.cs.Extent; import org.cts.datum.GeodeticDatum; import org.cts.op.CoordinateOperation; import org.cts.op.CoordinateOperationException; import org.cts.op.projection.Projection; +import org.cts.units.Unit; /** * A {@link org.cts.crs.CoordinateReferenceSystem} based on a @@ -61,6 +64,8 @@ public Projection getProjection() { */ protected CoordinateSystem coordinateSystem; + private Extent extent; + /** * Creates a new GeodeticCRS. * @@ -89,34 +94,64 @@ public CoordinateSystem getCoordinateSystem() { /** * Returns the {@link org.cts.datum.Datum} to which this - * CoordinateReferenceSystem is refering. + * CoordinateReferenceSystem refers. */ public GeodeticDatum getDatum() { return geodeticDatum; } /** - * Return whether this coord is a valid coord in this - * CoordinateReferenceSystem. + * Return whether the coord is within this CRS's extent or not. * - * @param coord standard coordinate for this CoordinateReferenceSystem - * datums (ex. decimal degrees for geographic datums and meters for vertical - * datums). + * @param coord coordinates to test */ - //@TODO Clarify : geodeticDatum.getExtent().isInside does not use coordinates - // expressed with this CoordinateReferenceSystem - // Do we have to convert coord to a decimal degree or radian Geographic CRS first ? - //public boolean isValid(double[] coord) { - // return geodeticDatum.getExtent().isInside(coord); - //} + public boolean isInside(double[] coord) { + return extent == null || extent.isInside(coord); + } + + /** + * Set this CoordinateReferenceSystem's extent from min and max coordinates + * expressed in this CoordinateReferenceSystem. + * @param min min ordinates along each axis + * @param max max ordinates along each axis + */ + public void setExtent(double[] min, double[] max) { + this.extent = new Extent() { + public String getName() { return ""; } + public boolean isInside(double[] coord) { + for (int i = 0 ; i < Math.min(coord.length, min.length) ; i++) { + if (coord[i] < min[i] || coord[i] > max[i]) return false; + } + return true; + } + }; + } + + /** + * Set this CoordinateReferenceSystem's extent from min and max + * longitude and latitude in degrees + * @param minLon minimum longitude + * @param minLat minimum latitude + * @param maxLon maximum longitude + * @param maxLat maximum latitude + */ + public void setExtent(double minLon, double minLat, double maxLon, double maxLat) + throws CoordinateOperationException, IllegalCoordinateException { + CoordinateOperation op = fromGeographicCoordinateConverter(); + double[] minLocal = op.transform(new double[]{Unit.DEGREE.toBaseUnit(minLat), Unit.DEGREE.toBaseUnit(minLon)}); + double[] maxLocal = op.transform(new double[]{Unit.DEGREE.toBaseUnit(maxLat), Unit.DEGREE.toBaseUnit(maxLon)}); + setExtent(minLocal, maxLocal); + } /** * Creates a CoordinateOperation object to convert coordinates from this * CoordinateReferenceSystem to a {@link org.cts.crs.Geographic3DCRS} based on * the same {@link org.cts.datum.GeodeticDatum}, and using normal SI units in the * following order : latitude (rad), longitude (rad) height (m). - * @return - * @throws org.cts.op.CoordinateOperationException + * + * @throws org.cts.op.CoordinateOperationException if an exception occurs + * during the computation of the CoordinateOperation to be used + * to convert coordinates to the associated Geographic3DCRS */ abstract public CoordinateOperation toGeographicCoordinateConverter() throws CoordinateOperationException; @@ -126,20 +161,21 @@ abstract public CoordinateOperation toGeographicCoordinateConverter() * {@link org.cts.crs.Geographic3DCRS} based on the same {@link org.cts.datum.GeodeticDatum}, * and using normal SI units in the following order : latitude (rad), * longitude (rad) height (m) to this CoordinateReferenceSystem. - * @return - * @throws org.cts.op.CoordinateOperationException + * + * @throws org.cts.op.CoordinateOperationException if an exception occurs + * during the computation of the CoordinateOperation to be used + * to convert coordinates from the associated Geographic3DCRS */ abstract public CoordinateOperation fromGeographicCoordinateConverter() throws CoordinateOperationException; /** - * Returns a WKT representation of the geodetic CRS. + * @see CoordinateReferenceSystem#toWKT() */ public abstract String toWKT(); /** - * Return a String representation of this Datum. - * @return + * Returns a String representation of this Datum. */ @Override public String toString() { @@ -153,7 +189,6 @@ public String toString() { * the {@link GeodeticDatum}, the {@link CoordinateSystem}. * * @param o The object to compare this GeodeticCRS against - * @return */ @Override public boolean equals(Object o) { @@ -177,7 +212,6 @@ public boolean equals(Object o) { /** * Returns the hash code for this GeodeticCRS. - * @return */ @Override public int hashCode() { diff --git a/src/main/java/org/cts/cs/Extent.java b/src/main/java/org/cts/cs/Extent.java index efbfb347..b8e43fb2 100644 --- a/src/main/java/org/cts/cs/Extent.java +++ b/src/main/java/org/cts/cs/Extent.java @@ -37,11 +37,11 @@ public interface Extent { /** * Return the name of this extent. */ - public String getName(); + String getName(); /** - * Return wether coord is inside this Extent or not. It's up to the user to + * Returns wether coord is inside this Extent or not. It's up to the user to * check consistency between coord and extent type. */ - public boolean isInside(double[] coord); + boolean isInside(double[] coord); } diff --git a/src/main/java/org/cts/op/CheckInExtentCoordinateOperation.java b/src/main/java/org/cts/op/CheckInExtentCoordinateOperation.java new file mode 100644 index 00000000..36b3b186 --- /dev/null +++ b/src/main/java/org/cts/op/CheckInExtentCoordinateOperation.java @@ -0,0 +1,67 @@ +package org.cts.op; + +import org.cts.Identifier; +import org.cts.IllegalCoordinateException; +import org.cts.crs.CoordinateReferenceSystem; +import org.cts.cs.Extent; + +import java.util.Arrays; + + +public class CheckInExtentCoordinateOperation extends AbstractCoordinateOperation { + + Extent extent; + + public CheckInExtentCoordinateOperation(CoordinateReferenceSystem crs) { + super(new Identifier(CoordinateOperation.class, "extent of '" + crs.getName() + "'")); + this.extent = crs; + } + + /** + * Check if coord lies in extent. If it is inside, coord is returned as is, + * else, a IllegalCoordinateException is thrown. + * + * @param coord coordinate to check + * @return the same coordinates array + * @throws IllegalCoordinateException if coord does not lie in extent. + * @throws org.cts.op.CoordinateOperationException if this operation + * failed during the transformation process. + */ + public double[] transform(double[] coord) throws IllegalCoordinateException { + if (extent == null || extent.isInside(coord)) return coord; + throw new IllegalCoordinateException("Coord " + Arrays.toString(coord) + " is not within " + getName()); + } + + /** + * Return the inverse CoordinateOperation, or throw a + * NonInvertibleOperationException. If op.inverse() is not null, + *
+     * op.inverse().transform(op.transform(point));
+     * 
should let point unchanged. + */ + public CoordinateOperation inverse() { + return this; + } + + ///** + // * Returns the maximum precision + // */ + //public double getPrecision() { + // return 1E-9; + //} + + ///** + // * @return true if this operation does not change coordinates. + // */ + //public boolean isIdentity() { + // return true; + //} + + /** + * Returns whether coord is consistent with source and target CRS. + */ + //public boolean isInside(double[] coord) { + // if (extent == null || extent.isInside(coord)) return true; + // throw new IllegalCoordinateException("Coordinates " + coord + " does not lie in extent"); + //} +} diff --git a/src/main/java/org/cts/op/CoordinateOperation.java b/src/main/java/org/cts/op/CoordinateOperation.java index 5b3c76ae..aa55c108 100644 --- a/src/main/java/org/cts/op/CoordinateOperation.java +++ b/src/main/java/org/cts/op/CoordinateOperation.java @@ -49,7 +49,7 @@ public interface CoordinateOperation extends Identifiable { * @throws org.cts.op.CoordinateOperationException if this operation * failed during the transformation process. */ - public double[] transform(double[] coord) throws IllegalCoordinateException, CoordinateOperationException; + double[] transform(double[] coord) throws IllegalCoordinateException, CoordinateOperationException; /** * Return the inverse CoordinateOperation, or throw a @@ -58,7 +58,7 @@ public interface CoordinateOperation extends Identifiable { * op.inverse().transform(op.transform(point)); * should let point unchanged. */ - public CoordinateOperation inverse() throws NonInvertibleOperationException; + CoordinateOperation inverse() throws NonInvertibleOperationException; /** * Return the precision of the transformation.

Precision is a double @@ -70,10 +70,10 @@ public interface CoordinateOperation extends Identifiable { * value of an ulp (units in the last place) for a double value equals to * 6378137.0 (Earth semi-major axis). */ - public double getPrecision(); + double getPrecision(); /** * @return true if this operation does not change coordinates. */ - public boolean isIdentity(); + boolean isIdentity(); } \ No newline at end of file diff --git a/src/main/java/org/cts/op/CoordinateOperationFactory.java b/src/main/java/org/cts/op/CoordinateOperationFactory.java index f5785c18..c48bf717 100644 --- a/src/main/java/org/cts/op/CoordinateOperationFactory.java +++ b/src/main/java/org/cts/op/CoordinateOperationFactory.java @@ -60,8 +60,9 @@ public final class CoordinateOperationFactory { * * @param source the (non null) source geodetic coordinate reference system * @param target the (non null) target geodetic coordinate reference system - * @return - * @throws org.cts.op.CoordinateOperationException + * + * @throws org.cts.op.CoordinateOperationException if an Exception occurs during the + * CoordinateOperation computation. */ public static Set createCoordinateOperations( GeodeticCRS source, GeodeticCRS target) throws CoordinateOperationException { @@ -71,7 +72,7 @@ public static Set createCoordinateOperations( if (target == null) { throw new IllegalArgumentException("The target CRS must not be null"); } - Set opList = new HashSet(); + Set opList = new HashSet<>(); GeodeticDatum sourceDatum = source.getDatum(); if (sourceDatum == null) { LOG.warn(source.getName() + " has no Geodetic Datum"); @@ -149,7 +150,7 @@ private static void addCoordinateOperations( // We get registered transformation from source GeodeticDatum to target GeodeticDatum // There maybe more than one transformations available. - Set datumTransformations = new HashSet(2); + Set datumTransformations = new HashSet<>(2); // If source CRS or target CRS is 3D, we need to use a 3D Geocentric transformation // from source Datum to target Datum @@ -184,6 +185,7 @@ private static void addCoordinateOperations( GeocentricTransformationSequence newSequence = new GeocentricTransformationSequence( new Identifier(CoordinateOperation.class, source.getCode() + " to " + target.getCode() + " through " + datumTransformation.getName()), + new CheckInExtentCoordinateOperation(source), source.toGeographicCoordinateConverter(), new LongitudeRotation(source.getDatum().getPrimeMeridian().getLongitudeFromGreenwichInRadians()), new Geographic2Geocentric(source.getDatum().getEllipsoid()), @@ -224,6 +226,7 @@ private static void addCoordinateOperations( opList.add(new CoordinateOperationSequence( new Identifier(CoordinateOperationSequence.class, source.getCode() + " to " + target.getCode() + " through " + datumTf.getName()), + new CheckInExtentCoordinateOperation(source), source.toGeographicCoordinateConverter(), datumTf, target.fromGeographicCoordinateConverter())); @@ -241,9 +244,12 @@ private static void addCoordinateOperations( /** * Returns {@link org.cts.op.CoordinateOperation}s including operations of a particular type. + * @param ops a collection of CoordinateOperation + * @param clazz include CoordinateOperations of this class */ - public static Set includeFilter(Collection ops, Class clazz) { - Set list = new HashSet(); + public static Set includeFilter(Collection ops, + Class clazz) { + Set list = new HashSet<>(); for (CoordinateOperation op : ops) { if (clazz.isAssignableFrom(op.getClass())) list.add(op); else if (op instanceof CoordinateOperationSequence) { @@ -262,12 +268,12 @@ else if (op instanceof CoordinateOperationSequence) { /** * Returns {@link org.cts.op.CoordinateOperation}s excluding sequence containing a particular operation type. - * @param ops - * @param clazz - * @return + * @param ops a collection of CoordinateOperation + * @param clazz exlude CoordinateOperations of this class */ - public static Set excludeFilter(Collection ops, Class clazz) { - Set list = new HashSet(); + public static Set excludeFilter(Collection ops, + Class clazz) { + Set list = new HashSet<>(); for (CoordinateOperation op : ops) { if (clazz.isAssignableFrom(op.getClass())) continue; if (op instanceof CoordinateOperationSequence) { @@ -286,8 +292,7 @@ public static Set excludeFilter(Collection ops) { CoordinateOperation preciseOp = null; @@ -303,8 +308,7 @@ public static CoordinateOperation getMostPrecise(Collection ops) { CoordinateOperation preciseOp = null; diff --git a/src/main/java/org/cts/op/transformation/GridBasedTransformation.java b/src/main/java/org/cts/op/transformation/GridBasedTransformation.java index 6e5152f3..6c635199 100644 --- a/src/main/java/org/cts/op/transformation/GridBasedTransformation.java +++ b/src/main/java/org/cts/op/transformation/GridBasedTransformation.java @@ -23,10 +23,13 @@ */ package org.cts.op.transformation; +import org.cts.op.CoordinateOperation; + /** * Marker for transformation based on grids like NTv2 or * vertical transformations * @author Michaël Michaud */ -public interface GridBasedTransformation { +public interface GridBasedTransformation extends CoordinateOperation { + } diff --git a/src/test/java/org/cts/op/CheckCRSExtentTest.java b/src/test/java/org/cts/op/CheckCRSExtentTest.java new file mode 100644 index 00000000..d05e0b56 --- /dev/null +++ b/src/test/java/org/cts/op/CheckCRSExtentTest.java @@ -0,0 +1,102 @@ +package org.cts.op; + +import org.cts.CRSFactory; +import org.cts.IllegalCoordinateException; +import org.cts.crs.CRSException; +import org.cts.crs.GeodeticCRS; +import org.cts.registry.EPSGRegistry; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +public class CheckCRSExtentTest { + + // Lambert 93 (2154) + // Projected bounds: + // -378305.81 6093283.21 + // 1212610.74 7186901.68 + // WGS84 bounds: + // -9.86 41.15 + // 10.38 51.56 + @Test + void testExtentInLocalCRS() throws CRSException, CoordinateOperationException, IllegalCoordinateException { + CRSFactory crsFactory = new CRSFactory(); + crsFactory.getRegistryManager().addRegistry(new EPSGRegistry()); + GeodeticCRS crs1 = (GeodeticCRS) crsFactory.getCRS("EPSG:2154"); + GeodeticCRS crs2 = (GeodeticCRS) crsFactory.getCRS("EPSG:4326"); + CoordinateOperation op = CoordinateOperationFactory.createCoordinateOperations(crs1, crs2).iterator().next(); + crs1.setExtent(new double[]{-378305.81, 6093283.21}, new double[]{1212610.74,7186901.68}); + System.out.println(Arrays.toString(op.transform(new double[]{0.0, 7000000.0}))); + assertTrue(true); + } + + @Test + void testExtentInGeographicCRS() throws CRSException, CoordinateOperationException, IllegalCoordinateException { + CRSFactory crsFactory = new CRSFactory(); + crsFactory.getRegistryManager().addRegistry(new EPSGRegistry()); + GeodeticCRS crs1 = (GeodeticCRS) crsFactory.getCRS("EPSG:2154"); + GeodeticCRS crs2 = (GeodeticCRS) crsFactory.getCRS("EPSG:4326"); + CoordinateOperation op = CoordinateOperationFactory.createCoordinateOperations(crs1, crs2).iterator().next(); + //crs1.setExtent(new double[]{-378305.81, 6093283.21}, new double[]{1212610.74,7186901.68}); + crs1.setExtent(-9.86, 41.15, 10.38, 51.56); + System.out.println(Arrays.toString(op.transform(new double[]{0.0, 7000000.0}))); + assertTrue(true); + } + + @Test + void testExtentNotInLocalCRS() throws CRSException, CoordinateOperationException, IllegalCoordinateException, IOException { + CRSFactory crsFactory = new CRSFactory(); + crsFactory.getRegistryManager().addRegistry(new EPSGRegistry()); + GeodeticCRS crs1 = (GeodeticCRS) crsFactory.getCRS("EPSG:2154"); + GeodeticCRS crs2 = (GeodeticCRS) crsFactory.getCRS("EPSG:4326"); + CoordinateOperation op = CoordinateOperationFactory.createCoordinateOperations(crs1, crs2).iterator().next(); + crs1.setExtent(new double[]{-378305.81, 6093283.21}, new double[]{1212610.74,7186901.68}); + Exception exception = assertThrows(IllegalCoordinateException.class, () -> { + System.out.println(Arrays.toString(op.transform(new double[]{-400000.0, 7000000.0}))); + }); + } + + @Test + void testExtentNotInGeographicCRS() throws CRSException, CoordinateOperationException, IllegalCoordinateException { + CRSFactory crsFactory = new CRSFactory(); + crsFactory.getRegistryManager().addRegistry(new EPSGRegistry()); + GeodeticCRS crs1 = (GeodeticCRS) crsFactory.getCRS("EPSG:2154"); + GeodeticCRS crs2 = (GeodeticCRS) crsFactory.getCRS("EPSG:4326"); + CoordinateOperation op = CoordinateOperationFactory.createCoordinateOperations(crs1, crs2).iterator().next(); + //crs1.setExtent(new double[]{-378305.81, 6093283.21}, new double[]{1212610.74,7186901.68}); + crs1.setExtent(-9.86, 41.15, 10.38, 51.56); + Exception exception = assertThrows(IllegalCoordinateException.class, () -> { + System.out.println(Arrays.toString(op.transform(new double[]{-400000.0, 7000000.0}))); + }); + } + + @Test + void testExtentInWGSCRS() throws CRSException, CoordinateOperationException, IllegalCoordinateException, IOException { + CRSFactory crsFactory = new CRSFactory(); + crsFactory.getRegistryManager().addRegistry(new EPSGRegistry()); + GeodeticCRS crs1 = (GeodeticCRS) crsFactory.getCRS("EPSG:2154"); + GeodeticCRS crs2 = (GeodeticCRS) crsFactory.getCRS("EPSG:4326"); + CoordinateOperation op = CoordinateOperationFactory.createCoordinateOperations(crs2, crs1).iterator().next(); + crs2.setExtent(-9.86, 41.15, 10.38, 51.56); + System.out.println(Arrays.toString(op.transform(new double[]{1.0, 45.0}))); + assertTrue(true); + } + + @Test + void testExtentNotInWGSCRS() throws CRSException, CoordinateOperationException, IllegalCoordinateException, IOException { + CRSFactory crsFactory = new CRSFactory(); + crsFactory.getRegistryManager().addRegistry(new EPSGRegistry()); + GeodeticCRS crs1 = (GeodeticCRS) crsFactory.getCRS("EPSG:2154"); + GeodeticCRS crs2 = (GeodeticCRS) crsFactory.getCRS("EPSG:4326"); + CoordinateOperation op = CoordinateOperationFactory.createCoordinateOperations(crs2, crs1).iterator().next(); + crs2.setExtent(-9.86, 41.15, 10.38, 51.56); + Exception exception = assertThrows(IllegalCoordinateException.class, () -> { + System.out.println(Arrays.toString(op.transform(new double[]{-10.0, 45.0}))); + }); + } +}