You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2017/10/27 12:58:40 UTC
svn commit: r1813531 [1/3] - in /sis/trunk: ./
core/sis-feature/src/main/java/org/apache/sis/feature/builder/
core/sis-feature/src/main/java/org/apache/sis/internal/feature/
core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/
core/sis-r...
Author: desruisseaux
Date: Fri Oct 27 12:58:39 2017
New Revision: 1813531
URL: http://svn.apache.org/viewvc?rev=1813531&view=rev
Log:
Merge from JDK7 branch.
Added:
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java
- copied, changed from r1813524, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftMethod.java
- copied unchanged from r1813524, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftMethod.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransform2D.java
- copied unchanged from r1813524, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransform2D.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java
- copied unchanged from r1813524, sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/CoordinateOperationsTest.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java
- copied, changed from r1813524, sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/AbridgedMolodenskyTransformTest.java
sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/
- copied from r1813524, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
- copied unchanged from r1813524, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Query.java
- copied, changed from r1813524, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/Query.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedQueryException.java
- copied unchanged from r1813524, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedQueryException.java
Modified:
sis/trunk/ (props changed)
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java
sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultBoundingPolygon.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyTransform2D.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractEnvelopeTest.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/EnvelopesTest.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/Shapes2DTest.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/TransformTestCase.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/HardCodedConversions.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java
sis/trunk/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java
sis/trunk/ide-project/NetBeans/nbproject/genfiles.properties
sis/trunk/ide-project/NetBeans/nbproject/project.xml
sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/FeaturesWrapper.java
sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/AttributeNames.java
sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
sis/trunk/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Store.java
Propchange: sis/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Oct 27 12:58:39 2017
@@ -1,5 +1,5 @@
/sis/branches/Android:1430670-1480699
/sis/branches/JDK6:1394364-1758914
-/sis/branches/JDK7:1394913-1812269
-/sis/branches/JDK8:1584960-1812266
+/sis/branches/JDK7:1394913-1813524
+/sis/branches/JDK8:1584960-1813514
/sis/branches/JDK9:1773327-1803064
Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -201,6 +201,10 @@ public class FeatureTypeBuilder extends
/**
* Creates a new builder instance using the given feature type as a template.
+ * This constructor initializes the list of {@linkplain #properties() properties}, the
+ * {@linkplain #getSuperTypes() super types} and {@link #isAbstract() isAbstract} flag
+ * to values inferred from the given template. The properties list will contain properties
+ * declared explicitely in the given template, not including properties inherited from super types.
*
* <div class="warning"><b>Warning:</b>
* The {@code template} argument type will be changed to {@code FeatureType} if and when such interface
@@ -264,6 +268,7 @@ public class FeatureTypeBuilder extends
/**
* Sets all properties of this builder to the values of the given feature type.
* This builder is {@linkplain #clear() cleared} before the properties of the given type are copied.
+ * The copy is performed as documented in the {@linkplain #FeatureTypeBuilder(DefaultFeatureType) constructor}.
*
* <div class="warning"><b>Warning:</b>
* The {@code template} argument type will be changed to {@code FeatureType} if and when such interface
@@ -609,10 +614,12 @@ public class FeatureTypeBuilder extends
/**
* Returns a view of all attributes and associations added to the {@code FeatureType} to build.
+ * This list contains only properties declared explicitely to this builder;
+ * it does not include properties inherited from {@linkplain #getSuperTypes() super-types}.
* The returned list is <cite>live</cite>: changes in this builder are reflected in that list and conversely.
* However the returned list allows only {@linkplain List#remove(Object) remove} operations;
* new attributes or associations can be added only by calls to one of the {@code addAttribute(…)}
- * or {@code addAssociation(…)} methods.
+ * or {@code addAssociation(…)} methods. Removal operations never affect the super-types.
*
* @return a live list over the properties declared to this builder.
*
Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -23,6 +23,8 @@ import com.esri.core.geometry.MultiPath;
import com.esri.core.geometry.Polyline;
import com.esri.core.geometry.Polygon;
import com.esri.core.geometry.Point;
+import com.esri.core.geometry.WktImportFlags;
+import com.esri.core.geometry.OperatorImportFromWkt;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.setup.GeometryLibrary;
import org.apache.sis.math.Vector;
@@ -178,4 +180,12 @@ final class ESRI extends Geometries<Geom
}
return path;
}
+
+ /**
+ * Parses the given WKT.
+ */
+ @Override
+ public Object parseWKT(final String wkt) {
+ return OperatorImportFromWkt.local().execute(WktImportFlags.wktImportDefaults, Geometry.Type.Unknown, wkt, null);
+ }
}
Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -284,6 +284,14 @@ public abstract class Geometries<G> {
}
/**
+ * Parses the given WKT.
+ *
+ * @param wkt the WKT to parse.
+ * @return the geometry object for the given WKT.
+ */
+ public abstract Object parseWKT(String wkt);
+
+ /**
* Returns an error message for an unsupported geometry object.
*
* @param dimension number of dimensions (2 or 3) requested for the geometry object.
Copied: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java (from r1813524, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java?p2=sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java&p1=sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java&r1=1813524&r2=1813531&rev=1813531&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/GeometryWrapper.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -16,17 +16,9 @@
*/
package org.apache.sis.internal.feature;
-import java.util.Set;
import java.util.Objects;
-import org.opengis.geometry.Boundary;
-import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.Geometry;
-import org.opengis.geometry.Precision;
-import org.opengis.geometry.TransfiniteSet;
-import org.opengis.geometry.complex.Complex;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
/**
@@ -60,50 +52,7 @@ public final class GeometryWrapper imple
this.envelope = envelope;
}
- /**
- * Returns the geometry CRS, which is taken from the envelope CRS.
- *
- * @return the geometry CRS.
- */
- @Override
- public CoordinateReferenceSystem getCoordinateReferenceSystem() {
- return envelope.getCoordinateReferenceSystem();
- }
-
- /**
- * Returns the envelope specified at construction time.
- */
- @Override public Envelope getEnvelope() {
- return envelope;
- }
-
- @Override public Precision getPrecision() {throw new UnsupportedOperationException();}
- @Override public Geometry getMbRegion() {throw new UnsupportedOperationException();}
- @Override public DirectPosition getRepresentativePoint() {throw new UnsupportedOperationException();}
- @Override public Boundary getBoundary() {throw new UnsupportedOperationException();}
- @Override public Complex getClosure() {throw new UnsupportedOperationException();}
- @Override public boolean isSimple() {throw new UnsupportedOperationException();}
- @Override public boolean isCycle() {throw new UnsupportedOperationException();}
- @Override public double distance(Geometry geometry) {throw new UnsupportedOperationException();}
- @Override public int getDimension(DirectPosition point) {throw new UnsupportedOperationException();}
- @Override public int getCoordinateDimension() {throw new UnsupportedOperationException();}
- @Override public Set<Complex> getMaximalComplex() {throw new UnsupportedOperationException();}
- @Override public DirectPosition getCentroid() {throw new UnsupportedOperationException();}
- @Override public Geometry getConvexHull() {throw new UnsupportedOperationException();}
- @Override public Geometry getBuffer(double distance) {throw new UnsupportedOperationException();}
- @Override public boolean isMutable() {throw new UnsupportedOperationException();}
- @Override public Geometry toImmutable() {throw new UnsupportedOperationException();}
@Override public Geometry clone() throws CloneNotSupportedException {throw new CloneNotSupportedException();}
- @Override public boolean contains(TransfiniteSet pointSet) {throw new UnsupportedOperationException();}
- @Override public boolean contains(DirectPosition point) {throw new UnsupportedOperationException();}
- @Override public boolean intersects(TransfiniteSet pointSet) {throw new UnsupportedOperationException();}
- @Override public boolean equals(TransfiniteSet pointSet) {throw new UnsupportedOperationException();}
- @Override public TransfiniteSet union(TransfiniteSet pointSet) {throw new UnsupportedOperationException();}
- @Override public TransfiniteSet intersection(TransfiniteSet pointSet) {throw new UnsupportedOperationException();}
- @Override public TransfiniteSet difference(TransfiniteSet pointSet) {throw new UnsupportedOperationException();}
- @Override public TransfiniteSet symmetricDifference(TransfiniteSet ps) {throw new UnsupportedOperationException();}
- @Override public Geometry transform(CoordinateReferenceSystem crs) {throw new UnsupportedOperationException();}
- @Override public Geometry transform(CoordinateReferenceSystem crs, MathTransform tr) {throw new UnsupportedOperationException();}
@Override
public boolean equals(final Object obj) {
Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -146,4 +146,12 @@ final class JTS extends Geometries<Objec
final Object tryMergePolylines(final Object first, final Iterator<?> polylines) {
throw unsupported(2); // TODO - see class javadoc
}
+
+ /**
+ * Parses the given WKT.
+ */
+ @Override
+ public Object parseWKT(final String wkt) {
+ throw unsupported(2);
+ }
}
Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -214,4 +214,12 @@ final class Java2D extends Geometries<Sh
}
return ShapeUtilities.toPrimitive(path);
}
+
+ /**
+ * Parses the given WKT.
+ */
+ @Override
+ public Object parseWKT(final String wkt) {
+ throw unsupported(2);
+ }
}
Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultBoundingPolygon.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultBoundingPolygon.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultBoundingPolygon.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultBoundingPolygon.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -47,7 +47,7 @@ import org.opengis.metadata.extent.Bound
* @author Touraïvane (IRD)
* @author Cédric Briançon (Geomatys)
* @author Guilhem Legal (Geomatys)
- * @version 0.3
+ * @version 0.8
* @since 0.3
* @module
*/
@@ -77,6 +77,7 @@ public class DefaultBoundingPolygon exte
* @param polygon the sets of points defining the bounding polygon.
*/
public DefaultBoundingPolygon(final Geometry polygon) {
+ super(true);
polygons = singleton(polygon, Geometry.class);
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -45,8 +45,9 @@ import static org.apache.sis.util.Argume
import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
import static org.apache.sis.util.StringBuilders.trimFractionalPart;
import static org.apache.sis.math.MathFunctions.epsilonEqual;
-import static org.apache.sis.math.MathFunctions.isNegative;
import static org.apache.sis.math.MathFunctions.isPositive;
+import static org.apache.sis.math.MathFunctions.isNegative;
+import static org.apache.sis.math.MathFunctions.isNegativeZero;
/**
@@ -819,10 +820,12 @@ public abstract class AbstractEnvelope i
// Not the excluded case, go to next dimension.
continue;
}
- // If this envelope does not span the anti-meridian but the given envelope
- // does, we don't contain the given envelope except in the special case
- // where the envelope spanning is equals or greater than the axis spanning
- // (including the case where this envelope expands to infinities).
+ /*
+ * If this envelope does not span the anti-meridian but the given envelope does,
+ * then this envelope does not contain the given envelope except in the special
+ * case where the envelope spanning is equals or greater than the axis spanning
+ * (including the case where this envelope expands to infinities).
+ */
if ((lower0 == Double.NEGATIVE_INFINITY && upper0 == Double.POSITIVE_INFINITY) ||
(upper0 - lower0 >= getSpan(getAxis(getCoordinateReferenceSystem(), i))))
{
@@ -846,6 +849,14 @@ public abstract class AbstractEnvelope i
continue;
}
}
+ } else if (isNegativeZero(upper0 - lower0)) {
+ /* !upperCnd
+ * ────────┬────────
+ * ┌─┼────┐
+ * └─┼────┘
+ * ────────┴────────
+ * !lowerCnd */
+ continue;
}
return false;
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -32,9 +32,10 @@ import org.apache.sis.util.Emptiable;
import static java.lang.Double.NaN;
import static java.lang.Double.isNaN;
import static java.lang.Double.doubleToLongBits;
+import static org.apache.sis.math.MathFunctions.isSameSign;
import static org.apache.sis.math.MathFunctions.isPositive;
import static org.apache.sis.math.MathFunctions.isNegative;
-import static org.apache.sis.math.MathFunctions.isSameSign;
+import static org.apache.sis.math.MathFunctions.isNegativeZero;
import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
import static org.apache.sis.internal.referencing.Formulas.isPoleToPole;
@@ -768,13 +769,15 @@ public class Envelope2D extends Rectangl
if (!isNegativeUnsafe(span1) || isNegativeUnsafe(span0)) {
continue;
}
- if (span0 >= AbstractEnvelope.getSpan(getAxis(getCoordinateReferenceSystem(), i))) {
+ if (span0 >= AbstractEnvelope.getSpan(getAxis(crs, i))) {
continue;
}
} else if (minCondition != maxCondition) {
if (isNegative(span0) && isPositive(span1)) {
continue;
}
+ } else if (isNegativeZero(span0)) {
+ continue;
}
return false;
}
@@ -879,8 +882,13 @@ public class Envelope2D extends Rectangl
min1 = rect.getY();
span1 = (env != null) ? env.height : rect.getHeight();
}
- final double max0 = min0 + span0;
- final double max1 = min1 + span1;
+ /*
+ * The purpose for (min != 0) test before addition is to preserve the sign of zero.
+ * In the [0 … -0] range, the span is -0. But computing max = 0 + -0 result in +0,
+ * while we need max = -0 in this case.
+ */
+ final double max0 = (min0 != 0) ? min0 + span0 : span0;
+ final double max1 = (min1 != 0) ? min1 + span1 : span1;
double min = Math.max(min0, min1);
double max = Math.min(max0, max1);
/*
@@ -903,10 +911,10 @@ public class Envelope2D extends Rectangl
}
if (intersect == 0 || intersect == 3) {
final double csSpan = AbstractEnvelope.getSpan(getAxis(crs, i));
- if (span1 >= csSpan) {
+ if (span1 >= csSpan || isNegativeZero(span1)) {
min = min0;
max = max0;
- } else if (span0 >= csSpan) {
+ } else if (span0 >= csSpan || isNegativeZero(span0)) {
min = min1;
max = max1;
} else {
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -21,10 +21,10 @@ package org.apache.sis.geometry;
* support Java2D (e.g. Android), or applications that do not need it may want to avoid to
* force installation of the Java2D module (e.g. JavaFX/SWT).
*/
+import java.util.Set;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.cs.RangeMeaning;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -40,6 +40,7 @@ import org.apache.sis.util.ComparisonMod
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
import org.apache.sis.internal.referencing.CoordinateOperations;
import org.apache.sis.internal.referencing.DirectPositionView;
@@ -86,7 +87,7 @@ import static org.apache.sis.util.String
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.5
+ * @version 0.8
*
* @see org.apache.sis.metadata.iso.extent.Extents
* @see CRS
@@ -102,15 +103,6 @@ public final class Envelopes extends Sta
}
/**
- * Returns {@code true} if the given axis is of kind "Wrap Around".
- *
- * @see AbstractEnvelope#isWrapAround(CoordinateSystemAxis)
- */
- static boolean isWrapAround(final CoordinateSystemAxis axis) {
- return RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning());
- }
-
- /**
* Invoked when a recoverable exception occurred. Those exceptions must be minor enough
* that they can be silently ignored in most cases.
*/
@@ -446,6 +438,7 @@ public final class Envelopes extends Sta
if (envelope == null) {
return null;
}
+ boolean isOperationComplete = true;
final CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
if (sourceCRS != null) {
final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
@@ -465,6 +458,7 @@ public final class Envelopes extends Sta
throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), e);
}
if (!mt.isIdentity()) {
+ isOperationComplete = false;
envelope = transform(mt, envelope);
}
}
@@ -575,7 +569,7 @@ public final class Envelopes extends Sta
long isWrapAroundAxis = 0;
long dimensionBitMask = 1;
final int dimension = targetCS.getDimension();
- for (int i=0; i<dimension; i++, dimensionBitMask <<= 1) {
+poles: for (int i=0; i<dimension; i++, dimensionBitMask <<= 1) {
final CoordinateSystemAxis axis = targetCS.getAxis(i);
if (axis == null) { // Should never be null, but check as a paranoiac safety.
continue;
@@ -607,9 +601,9 @@ public final class Envelopes extends Sta
* lost dimensions. So we don't log any warning in this case.
*/
if (dimension >= mt.getSourceDimensions()) {
- recoverableException(Envelopes.class, exception);
+ warning = exception;
}
- return transformed;
+ break poles;
}
targetPt = new GeneralDirectPosition(mt.getSourceDimensions());
for (int j=0; j<dimension; j++) {
@@ -646,7 +640,7 @@ public final class Envelopes extends Sta
* axis have been included in the envelope (in which case the next step after this
* loop doesn't need to be executed for that axis).
*/
- if ((includedMinValue & includedMaxValue & dimensionBitMask) == 0 && isWrapAround(axis)) {
+ if ((includedMinValue & includedMaxValue & dimensionBitMask) == 0 && CoordinateOperations.isWrapAround(axis)) {
isWrapAroundAxis |= dimensionBitMask;
}
// Restore 'targetPt' to its initial state, which is equals to 'centerPt'.
@@ -666,7 +660,7 @@ public final class Envelopes extends Sta
while (isWrapAroundAxis != 0) {
final int wrapAroundDimension = Long.numberOfTrailingZeros(isWrapAroundAxis);
dimensionBitMask = 1 << wrapAroundDimension;
- isWrapAroundAxis &= ~dimensionBitMask; // Clear now the bit, for the next iteration.
+ isWrapAroundAxis &= ~dimensionBitMask; // Clear now the bit, for the next iteration.
final CoordinateSystemAxis wrapAroundAxis = targetCS.getAxis(wrapAroundDimension);
final double min = wrapAroundAxis.getMinimumValue();
final double max = wrapAroundAxis.getMaximumValue();
@@ -689,8 +683,8 @@ public final class Envelopes extends Sta
for (int c=0; c<4; c++) {
/*
* Set the ordinate value along the axis having the singularity point
- * (cases c=0 and c=2). If the envelope did not included that point,
- * then skip completly this case and the next one, i.e. skip c={0,1}
+ * (cases c=0 and c=2). If the envelope did not included that point,
+ * then skip completely this case and the next one, i.e. skip c={0,1}
* or skip c={2,3}.
*/
double value = max;
@@ -722,6 +716,21 @@ public final class Envelopes extends Sta
targetPt.setOrdinate(wrapAroundDimension, centerPt[wrapAroundDimension]);
}
}
+ /*
+ * At this point we finished envelope transformation. Verify if some ordinates need to be "wrapped around"
+ * as a result of the coordinate operation. This is usually the longitude axis where the source CRS uses
+ * the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. We do not wrap
+ * around if the source and target axes use the same range (e.g. the longitude stay [-180 … +180]°) in order
+ * to reduce the risk of discontinuities. If the user really wants unconditional wrap around, (s)he can call
+ * GeneralEnvelope.normalize().
+ */
+ final Set<Integer> wrapAroundChanges;
+ if (isOperationComplete && operation instanceof AbstractCoordinateOperation) {
+ wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
+ } else {
+ wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
+ }
+ transformed.normalize(targetCS, 0, wrapAroundChanges.size(), wrapAroundChanges.iterator());
if (warning != null) {
recoverableException(Envelopes.class, warning);
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -22,6 +22,7 @@ package org.apache.sis.geometry;
* force installation of the Java2D module (e.g. JavaFX/SWT).
*/
import java.util.Arrays;
+import java.util.Iterator;
import java.io.Serializable;
import java.lang.reflect.Field;
import org.opengis.referencing.cs.RangeMeaning;
@@ -35,8 +36,9 @@ import org.opengis.metadata.extent.Geogr
import org.apache.sis.util.resources.Errors;
import static org.apache.sis.util.ArgumentChecks.*;
-import static org.apache.sis.math.MathFunctions.isNegative;
import static org.apache.sis.math.MathFunctions.isSameSign;
+import static org.apache.sis.math.MathFunctions.isNegative;
+import static org.apache.sis.math.MathFunctions.isNegativeZero;
/**
@@ -110,7 +112,7 @@ import static org.apache.sis.math.MathFu
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.5
+ * @version 0.8
*
* @see Envelope2D
* @see org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox
@@ -806,10 +808,10 @@ public class GeneralEnvelope extends Arr
*/
final double min, max;
final double csSpan = getSpan(getAxis(crs, i));
- if (span1 >= csSpan) {
+ if (span1 >= csSpan || isNegativeZero(span1)) { // Negative zero if [+0 … -0] range.
min = min0;
max = max0;
- } else if (span0 >= csSpan) {
+ } else if (span0 >= csSpan || isNegativeZero(span0)) {
min = min1;
max = max1;
} else {
@@ -886,53 +888,68 @@ public class GeneralEnvelope extends Arr
* @see AbstractDirectPosition#normalize()
*/
public boolean normalize() {
+ if (crs == null) {
+ return false;
+ }
+ final int beginIndex = beginIndex();
+ return normalize(crs.getCoordinateSystem(), beginIndex, endIndex() - beginIndex, null);
+ }
+
+ /**
+ * Normalizes only the dimensions returned by the given iterator, or all dimensions if the iterator is null.
+ * This is used for normalizing the result of a coordinate operation where a wrap around axis does not
+ * necessarily means that the ordinates need to be normalized along that axis.
+ *
+ * @param cs the coordinate system of this envelope CRS (as an argument because sometime already known).
+ * @param beginIndex index of the first ordinate value in {@link #ordinates} array. Non-zero for sub-envelopes.
+ * @param count number of coordinates, i.e. this envelope dimensions.
+ * @param dimensions the dimensions to check for normalization, or {@code null} for all dimensions.
+ * @return {@code true} if this envelope has been modified as a result of this method call.
+ */
+ final boolean normalize(final CoordinateSystem cs, final int beginIndex, final int count, final Iterator<Integer> dimensions) {
boolean changed = false;
- if (crs != null) {
- final int d = ordinates.length >>> 1;
- final int beginIndex = beginIndex();
- final int dimension = endIndex() - beginIndex;
- final CoordinateSystem cs = crs.getCoordinateSystem();
- for (int i=0; i<dimension; i++) {
- final int iLower = beginIndex + i;
- final int iUpper = iLower + d;
- final CoordinateSystemAxis axis = cs.getAxis(i);
- final double minimum = axis.getMinimumValue();
- final double maximum = axis.getMaximumValue();
- final RangeMeaning rm = axis.getRangeMeaning();
- if (RangeMeaning.EXACT.equals(rm)) {
- if (ordinates[iLower] < minimum) {ordinates[iLower] = minimum; changed = true;}
- if (ordinates[iUpper] > maximum) {ordinates[iUpper] = maximum; changed = true;}
- } else if (RangeMeaning.WRAPAROUND.equals(rm)) {
- final double csSpan = maximum - minimum;
- if (csSpan > 0 && csSpan < Double.POSITIVE_INFINITY) {
- double o1 = ordinates[iLower];
- double o2 = ordinates[iUpper];
- if (Math.abs(o2-o1) >= csSpan) {
- /*
- * If the range exceed the CS span, then we have to replace it by the
- * full span, otherwise the range computed by the "else" block is too
- * small. The full range will typically be [-180 … 180]°. However we
- * make a special case if the two bounds are multiple of the CS span,
- * typically [0 … 360]°. In this case the [0 … -0]° range matches the
- * original values and is understood by GeneralEnvelope as a range
- * spanning all the world.
- */
- if (o1 != minimum || o2 != maximum) {
- if ((o1 % csSpan) == 0 && (o2 % csSpan) == 0) {
- ordinates[iLower] = +0.0;
- ordinates[iUpper] = -0.0;
- } else {
- ordinates[iLower] = minimum;
- ordinates[iUpper] = maximum;
- }
- changed = true;
+ final int d = ordinates.length >>> 1;
+ for (int j=0; j<count; j++) {
+ final int i = (dimensions != null) ? dimensions.next() : j;
+ final int iLower = beginIndex + i;
+ final int iUpper = iLower + d;
+ final CoordinateSystemAxis axis = cs.getAxis(i);
+ final double minimum = axis.getMinimumValue();
+ final double maximum = axis.getMaximumValue();
+ final RangeMeaning rm = axis.getRangeMeaning();
+ if (RangeMeaning.EXACT.equals(rm)) {
+ if (ordinates[iLower] < minimum) {ordinates[iLower] = minimum; changed = true;}
+ if (ordinates[iUpper] > maximum) {ordinates[iUpper] = maximum; changed = true;}
+ } else if (RangeMeaning.WRAPAROUND.equals(rm)) {
+ final double csSpan = maximum - minimum;
+ if (csSpan > 0 && csSpan < Double.POSITIVE_INFINITY) {
+ double o1 = ordinates[iLower];
+ double o2 = ordinates[iUpper];
+ if (Math.abs(o2-o1) >= csSpan) {
+ /*
+ * If the range exceed the CS span, then we have to replace it by the
+ * full span, otherwise the range computed by the "else" block is too
+ * small. The full range will typically be [-180 … 180]°. However we
+ * make a special case if the two bounds are multiple of the CS span,
+ * typically [0 … 360]°. In this case the [0 … -0]° range matches the
+ * original values and is understood by GeneralEnvelope as a range
+ * spanning all the world.
+ */
+ if (o1 != minimum || o2 != maximum) {
+ if ((o1 % csSpan) == 0 && (o2 % csSpan) == 0) {
+ ordinates[iLower] = +0.0;
+ ordinates[iUpper] = -0.0;
+ } else {
+ ordinates[iLower] = minimum;
+ ordinates[iUpper] = maximum;
}
- } else {
- o1 = Math.floor((o1 - minimum) / csSpan) * csSpan;
- o2 = Math.floor((o2 - minimum) / csSpan) * csSpan;
- if (o1 != 0) {ordinates[iLower] -= o1; changed = true;}
- if (o2 != 0) {ordinates[iUpper] -= o2; changed = true;}
+ changed = true;
}
+ } else {
+ o1 = Math.floor((o1 - minimum) / csSpan) * csSpan;
+ o2 = Math.floor((o2 - minimum) / csSpan) * csSpan;
+ if (o1 != 0) {ordinates[iLower] -= o1; changed = true;}
+ if (o2 != 0) {ordinates[iUpper] -= o2; changed = true;}
}
}
}
@@ -940,8 +957,6 @@ public class GeneralEnvelope extends Arr
return changed;
}
- // Note: As of JDK 1.6.0_31, using {@linkplain #getLower(int)} in the first line crash the
- // Javadoc tools, maybe because getLower/getUpper are defined in a non-public parent class.
/**
* Ensures that <var>lower</var> <= <var>upper</var> for every dimensions.
* If a {@linkplain #getUpper(int) upper ordinate value} is less than a
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -16,6 +16,7 @@
*/
package org.apache.sis.geometry;
+import java.util.Set;
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.awt.geom.Ellipse2D;
@@ -32,6 +33,9 @@ import org.opengis.referencing.operation
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.internal.referencing.j2d.ShapeUtilities;
+import org.apache.sis.internal.referencing.j2d.IntervalRectangle;
+import org.apache.sis.internal.referencing.CoordinateOperations;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
@@ -351,9 +355,9 @@ public final class Shapes2D extends Stat
D1=D2;
}
if (destination != null) {
- destination.setRect(xmin, ymin, xmax-xmin, ymax-ymin);
+ destination.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
} else {
- destination = new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin);
+ destination = new IntervalRectangle(xmin, ymin, xmax, ymax);
}
/*
* Note: a previous version had an "assert" statement here comparing our calculation
@@ -539,8 +543,8 @@ public final class Shapes2D extends Stat
* Forget any axes that are not of kind "WRAPAROUND". Then get the final
* bit pattern indicating which points to test. Iterate over that bits.
*/
- if ((toTest & 0x33333333) != 0 && !Envelopes.isWrapAround(targetCS.getAxis(0))) toTest &= 0xCCCCCCCC;
- if ((toTest & 0xCCCCCCCC) != 0 && !Envelopes.isWrapAround(targetCS.getAxis(1))) toTest &= 0x33333333;
+ if ((toTest & 0x33333333) != 0 && !CoordinateOperations.isWrapAround(targetCS.getAxis(0))) toTest &= 0xCCCCCCCC;
+ if ((toTest & 0xCCCCCCCC) != 0 && !CoordinateOperations.isWrapAround(targetCS.getAxis(1))) toTest &= 0x33333333;
while (toTest != 0) {
final int border = Integer.numberOfTrailingZeros(toTest);
final int bitMask = 1 << border;
@@ -569,6 +573,42 @@ public final class Shapes2D extends Stat
}
}
}
+ /*
+ * At this point we finished envelope transformation. Verify if some ordinates need to be "wrapped around"
+ * as a result of the coordinate operation. This is usually the longitude axis where the source CRS uses
+ * the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. In such case we
+ * set the rectangle to the full range (we do not use the mechanism documented in Envelope2D) because most
+ * Rectangle2D implementations do not support spanning the anti-meridian. This results in larger rectangle
+ * than what would be possible with GeneralEnvelope or Envelope2D, but we try to limit the situation where
+ * this expansion is applied.
+ */
+ final Set<Integer> wrapAroundChanges;
+ if (operation instanceof AbstractCoordinateOperation) {
+ wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
+ } else {
+ wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
+ }
+ for (int dim : wrapAroundChanges) { // Empty in the vast majority of cases.
+ final CoordinateSystemAxis axis = targetCS.getAxis(dim);
+ final double minimum = axis.getMinimumValue();
+ final double maximum = axis.getMaximumValue();
+ final double o1, o2;
+ if (dim == 0) {
+ o1 = destination.getMinX();
+ o2 = destination.getMaxX();
+ } else {
+ o1 = destination.getMinY();
+ o2 = destination.getMaxY();
+ }
+ if (o1 < minimum || o2 > maximum) {
+ final double span = maximum - minimum;
+ if (dim == 0) {
+ destination.setRect(minimum, destination.getY(), span, destination.getHeight());
+ } else {
+ destination.setRect(destination.getX(), minimum, destination.getWidth(), span);
+ }
+ }
+ }
if (warning != null) {
Envelopes.recoverableException(Shapes2D.class, warning);
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/CoordinateOperations.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -16,25 +16,57 @@
*/
package org.apache.sis.internal.referencing;
+import java.util.Set;
+import java.util.Objects;
+import java.util.Collections;
+import javax.measure.UnitConverter;
+import javax.measure.IncommensurableException;
+import org.opengis.referencing.cs.RangeMeaning;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.GeneralDerivedCRS;
+import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
+import org.apache.sis.internal.metadata.AxisDirections;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.Modules;
import org.apache.sis.internal.system.SystemListener;
+import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.jdk9.JDK9;
/**
* The default coordinate operation factory, provided in a separated class for deferring class loading
- * until first needed.
+ * until first needed. Contains also utility methods related to coordinate operations.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
* @since 0.7
* @module
*/
public final class CoordinateOperations extends SystemListener {
/**
- * The factory.
+ * Cached values or {@link #wrapAroundChanges wrapAroundChanges(…)}, created when first needed.
+ * Indices are bit masks computed by {@link #changes changes(…)}. Since the most common "wrap around" axes
+ * are longitude at dimension 0 or 1, and some measurement of time (in climatology) at dimension 2 or 3,
+ * then the most likely values are (binary digits):
+ *
+ * {@preformat text
+ * 0000 0100 1000
+ * 0001 0101 1001
+ * 0010 0110 1010
+ * }
+ *
+ * The last decimal value is 10 (binary {@code 1010}); we don't need to cache more.
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static final Set<Integer>[] CACHE = new Set[11];
+
+ /**
+ * The system-wide default factory.
*/
private static volatile DefaultCoordinateOperationFactory factory;
@@ -73,4 +105,157 @@ public final class CoordinateOperations
}
return c;
}
+
+ /**
+ * Returns {@code true} if the given axis is of kind "Wrap Around".
+ * Defined here because used together with {@link #wrapAroundChanges wrapAroundChanges(…)}.
+ *
+ * @param axis the axis to test.
+ * @return {@code true} if the given axis has "wrap around" range meaning.
+ */
+ public static boolean isWrapAround(final CoordinateSystemAxis axis) {
+ return RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning());
+ }
+
+ /**
+ * Returns indices of target dimensions where "wrap around" may happen as a result of a coordinate operation.
+ * This is usually the longitude axis when the source CRS uses the [-180 … +180]° range and the target CRS
+ * uses the [0 … 360]° range, or the converse.
+ *
+ * @param op the coordinate operation for which to get "wrap around" target dimensions.
+ * @return target dimensions where "wrap around" may happen, or an empty set if none.
+ *
+ * @see AbstractCoordinateOperation#getWrapAroundChanges()
+ */
+ public static Set<Integer> wrapAroundChanges(final CoordinateOperation op) {
+ if (op instanceof AbstractCoordinateOperation) {
+ return ((AbstractCoordinateOperation) op).getWrapAroundChanges();
+ } else if (op != null) {
+ final CoordinateReferenceSystem source, target;
+ if ((source = op.getSourceCRS()) != null &&
+ (target = op.getTargetCRS()) != null)
+ {
+ return wrapAroundChanges(source, target.getCoordinateSystem());
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
+ * Computes indices of target dimensions where "wrap around" may happen as a result of a coordinate operation.
+ * This is usually the longitude axis when the source CRS uses the [-180 … +180]° range and the target CRS uses
+ * the [0 … 360]° range, or the converse.
+ *
+ * @param source the source of the coordinate operation.
+ * @param target the target of the coordinate operation.
+ * @return target dimensions where "wrap around" may happen, or an empty set if none.
+ */
+ public static Set<Integer> wrapAroundChanges(CoordinateReferenceSystem source, final CoordinateSystem target) {
+ long changes = changes(source.getCoordinateSystem(), target);
+ while (source instanceof GeneralDerivedCRS) {
+ source = ((GeneralDerivedCRS) source).getBaseCRS();
+ changes |= changes(source.getCoordinateSystem(), target);
+ }
+ final boolean useCache = (changes >= 0 && changes < CACHE.length);
+ if (useCache) {
+ /*
+ * In most cases we have an existing instance. Since WrapAroundAxes are immutable, if we got a reference,
+ * the object should be okay ("published" in memory model terminology) even if we did not synchronized.
+ * The reference however may not be visible in the array, but we will check later in a synchronized block.
+ */
+ final Set<Integer> existing = CACHE[(int) changes];
+ if (existing != null) {
+ return existing;
+ }
+ }
+ /*
+ * No existing instance found. Expand the 'changes' bit mask in an array of integers in order to create an
+ * unmodifiable List<Integer>. The list is for public API; internally, Apache SIS will use toBitMask(…).
+ */
+ long r = changes;
+ final Integer[] indices = new Integer[Long.bitCount(r)];
+ for (int i=0; i<indices.length; i++) {
+ final int dim = Long.numberOfTrailingZeros(r);
+ indices[i] = dim;
+ r &= ~(1L << dim);
+ }
+ final Set<Integer> dimensions = JDK9.setOf(indices);
+ if (useCache) {
+ synchronized (CACHE) {
+ final Set<Integer> existing = CACHE[(int) changes];
+ if (existing != null) {
+ return existing;
+ }
+ CACHE[(int) changes] = dimensions;
+ }
+ }
+ return dimensions;
+ }
+
+ /**
+ * Returns the packed indices of target dimensions where ordinate values may need to be wrapped around.
+ * This method matches target coordinate system axes having {@link RangeMeaning#WRAPAROUND} with source
+ * axes, then verifies if the range of values changed (taking unit conversions in account). A target
+ * dimension {@code i} may need to "wrap around" the coordinate values if the {@code 1 << i} bit is set.
+ * If there is no change, then the value is zero.
+ */
+ private static long changes(final CoordinateSystem source, final CoordinateSystem target) {
+ long changes = 0;
+ if (source != target) { // Optimization for a common case.
+ /*
+ * Get the dimensions of all axes in the source coordinate system as bit fields.
+ * We create this list first because we may iterate more than once on those axes
+ * and we want to clear the axes that we already matched. We use a bitmask for
+ * efficiency, with the bits of dimensions to consider set to 1.
+ *
+ * Note: a previous version was creating a list of "wraparound" axes only. We removed that filter
+ * because a target wraparound axis may match a source infinite axis. For example when converting
+ * dates on a temporal axis (with infinite span toward past and future) to months on a climatology
+ * axis (January to December months without year), the same cycle is repeated after every 12 months
+ * even if the source axis had no cycle.
+ */
+ long isWrapAroundAxis = (1 << source.getDimension()) - 1;
+ /*
+ * For each "wrap around" axis in the target CRS, search a matching axis in the source CRS
+ * which is also "wrap around", is colinear and uses compatible unit of measurement. There
+ * is usually at most one "wrap around" axis, but this code is nevertheless generic enough
+ * for an arbitrary amount of axes.
+ */
+ final int dim = Math.min(Long.SIZE, target.getDimension());
+compare: for (int i=0; i<dim; i++) {
+ final CoordinateSystemAxis axis = target.getAxis(i);
+ if (isWrapAround(axis)) {
+ long candidates = isWrapAroundAxis;
+ do {
+ final long mask = Long.lowestOneBit(candidates);
+ final CoordinateSystemAxis src = source.getAxis(Long.numberOfTrailingZeros(mask));
+ if (Objects.equals(AxisDirections.absolute(src .getDirection()),
+ AxisDirections.absolute(axis.getDirection())))
+ {
+ try {
+ final UnitConverter c = src.getUnit().getConverterToAny(axis.getUnit());
+ final double minimum = axis.getMinimumValue();
+ final double maximum = axis.getMaximumValue();
+ final double tolerance = (maximum - minimum) * Numerics.COMPARISON_THRESHOLD;
+ if (!Numerics.epsilonEqual(c.convert(src.getMinimumValue()), minimum, tolerance) ||
+ !Numerics.epsilonEqual(c.convert(src.getMaximumValue()), maximum, tolerance))
+ {
+ changes |= (1 << i);
+ }
+ isWrapAroundAxis &= ~mask; // We are done with this source axis.
+ if (isWrapAroundAxis == 0) {
+ break compare; // Useless to continue if there is no more source axis.
+ }
+ continue compare; // Match next pair of wrap around axes.
+ } catch (IncommensurableException e) {
+ // Ignore (should not happen often). We will try to match another pair of axes.
+ }
+ }
+ candidates &= ~mask; // Unable to use that axis. Check if we can use another one.
+ } while (candidates != 0);
+ }
+ }
+ }
+ return changes;
+ }
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -31,7 +31,7 @@ import org.apache.sis.util.ArgumentCheck
* used in double-double arithmetic.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 0.8
* @since 0.5
* @module
*/
@@ -181,6 +181,26 @@ final class AffineMatrix implements Exte
}
/**
+ * Compares this matrix with the given object for equality, including error terms (if any).
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj instanceof AffineMatrix) {
+ final AffineMatrix other = (AffineMatrix) obj;
+ return transform.equals(other.transform) && Arrays.equals(errors, other.errors);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for this matrix.
+ */
+ @Override
+ public int hashCode() {
+ return (transform.hashCode() * 31 + Arrays.hashCode(errors)) ^ (int) serialVersionUID;
+ }
+
+ /**
* Returns a string representation of this matrix.
*
* @return a string representation of this matrix.
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -57,7 +57,7 @@ import org.apache.sis.referencing.operat
* </table>
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.6
+ * @version 0.8
* @since 0.5
* @module
*/
@@ -283,6 +283,23 @@ public final class Affine extends Abstra
}
/**
+ * Returns parameter values for an identity transform of the given input and output dimensions.
+ * Callers can modify the returned parameters if desired.
+ *
+ * @param dimension the number of source and target dimensions.
+ * @return parameters for an identity transform of the given dimensions.
+ *
+ * @since 0.8
+ */
+ public static ParameterValueGroup identity(int dimension) {
+ final ParameterValueGroup values = TensorParameters.WKT1.createValueGroup(
+ Collections.singletonMap(NAME_KEY, Constants.AFFINE));
+ values.parameter(Constants.NUM_COL).setValue(++dimension);
+ values.parameter(Constants.NUM_ROW).setValue( dimension);
+ return values;
+ }
+
+ /**
* Returns the parameter values for the given matrix. This method is invoked by implementations of
* {@link org.apache.sis.referencing.operation.transform.AbstractMathTransform#getParameterValues()}.
*
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -226,7 +226,8 @@ public abstract class GeocentricAffine e
/**
* Returns the parameters for creating a datum shift operation.
- * The operation method will be one of the {@code GeocentricAffine} subclasses.
+ * The operation method will be one of the {@code GeocentricAffine} subclasses,
+ * unless the specified {@code method} argument is {@link DatumShiftMethod#NONE}.
* If no single operation method can be used, then this method returns {@code null}.
*
* <p>This method does <strong>not</strong> change the coordinate system type.
@@ -235,19 +236,18 @@ public abstract class GeocentricAffine e
* will cause this method to return {@code null}. In such case, it is caller's responsibility to apply
* the datum shift itself in Cartesian geocentric coordinates.</p>
*
- * @param sourceCS the source coordinate system. Only the type and number of dimensions is checked.
- * @param targetCS the target coordinate system. Only the type and number of dimensions is checked.
- * @param datumShift the datum shift as a matrix.
- * @param useMolodensky {@code true} for allowing the use of Molodensky approximation, or {@code false}
- * for using the transformation in geocentric space (which should be more accurate).
+ * @param sourceCS the source coordinate system. Only the type and number of dimensions is checked.
+ * @param targetCS the target coordinate system. Only the type and number of dimensions is checked.
+ * @param datumShift the datum shift as a matrix, or {@code null} if there is no datum shift information.
+ * @param method the preferred datum shift method. Note that {@code createParameters(…)} may overwrite.
* @return the parameter values, or {@code null} if no single operation method can be found.
*/
public static ParameterValueGroup createParameters(final CoordinateSystem sourceCS,
- final CoordinateSystem targetCS, final Matrix datumShift, boolean useMolodensky)
+ final CoordinateSystem targetCS, final Matrix datumShift, DatumShiftMethod method)
{
final boolean isEllipsoidal = (sourceCS instanceof EllipsoidalCS);
- if (!(isEllipsoidal ? targetCS instanceof EllipsoidalCS
- : targetCS instanceof CartesianCS && sourceCS instanceof CartesianCS))
+ if (!(isEllipsoidal ? (targetCS instanceof EllipsoidalCS)
+ : (targetCS instanceof CartesianCS && sourceCS instanceof CartesianCS)))
{
return null; // Coordinate systems are not two EllipsoidalCS or two CartesianCS.
}
@@ -256,43 +256,93 @@ public abstract class GeocentricAffine e
if (dimension != targetCS.getDimension()) {
dimension = 4; // Any value greater than 3 means "mismatched dimensions" for this method.
}
+ if (method == DatumShiftMethod.NONE) {
+ if (dimension <= 3) {
+ return Affine.identity(dimension);
+ } else if (isEllipsoidal) {
+ final ParameterDescriptorGroup descriptor;
+ switch (sourceCS.getDimension()) {
+ case 2: descriptor = Geographic2Dto3D.PARAMETERS; break;
+ case 3: descriptor = Geographic3Dto2D.PARAMETERS; break;
+ default: return null;
+ }
+ return descriptor.createValue();
+ } else {
+ return null;
+ }
+ }
/*
* Try to convert the matrix into (tX, tY, tZ, rX, rY, rZ, dS) parameters.
- * The matrix may not be convertible, in which case we will let the callers
+ * The matrix may not be convertible, in which case we will let the caller
* uses the matrix directly in Cartesian geocentric coordinates.
*/
final BursaWolfParameters parameters = new BursaWolfParameters(null, null);
- try {
+ if (datumShift != null) try {
parameters.setPositionVectorTransformation(datumShift, BURSAWOLF_TOLERANCE);
} catch (IllegalArgumentException e) {
log(Loggers.COORDINATE_OPERATION, "createParameters", e);
return null;
+ } else {
+ /*
+ * If there is no datum shift parameters (not to be confused with identity), then those parameters
+ * are assumed unknown. Using the most accurate methods would give a false impression of accuracy,
+ * so we use the fastest method instead. Since all parameter values are zero, Apache SIS should use
+ * the AbridgedMolodenskyTransform2D optimization.
+ */
+ method = DatumShiftMethod.ABRIDGED_MOLODENSKY;
}
final boolean isTranslation = parameters.isTranslation();
final ParameterDescriptorGroup descriptor;
/*
- * Following "if" blocks are ordered from more accurate to less accurate datum shift method
- * supported by GeocentricAffine subclasses.
+ * Following "if" blocks are ordered from most accurate to less accurate datum shift method
+ * supported by GeocentricAffine subclasses (except NONE which has already been handled).
+ * Special cases:
+ *
+ * - If the datum shift is applied between geocentric CRS, then the Molodensky approximations do not apply
+ * as they are designed for transformations between geographic CRS only. User preference is then ignored.
+ *
+ * - Molodensky methods are approximations for datum shifts having only translation terms in their Bursa-Wolf
+ * parameters. If there is also a scale or rotation terms, then we can not use Molodensky methods. The user
+ * preference is then ignored.
*/
if (!isEllipsoidal) {
- useMolodensky = false;
+ method = DatumShiftMethod.GEOCENTRIC_DOMAIN;
descriptor = isTranslation ? GeocentricTranslation.PARAMETERS
: PositionVector7Param .PARAMETERS;
- } else {
- if (!isTranslation) {
- useMolodensky = false;
- descriptor = (dimension >= 3) ? PositionVector7Param3D.PARAMETERS
- : PositionVector7Param2D.PARAMETERS;
- } else if (!useMolodensky) {
+ } else if (!isTranslation) {
+ method = DatumShiftMethod.GEOCENTRIC_DOMAIN;
+ descriptor = (dimension >= 3) ? PositionVector7Param3D.PARAMETERS
+ : PositionVector7Param2D.PARAMETERS;
+ } else switch (method) {
+ case GEOCENTRIC_DOMAIN: {
descriptor = (dimension >= 3) ? GeocentricTranslation3D.PARAMETERS
: GeocentricTranslation2D.PARAMETERS;
- } else {
+ break;
+ }
+ case MOLODENSKY: {
descriptor = Molodensky.PARAMETERS;
+ break;
+ }
+ case ABRIDGED_MOLODENSKY: {
+ descriptor = AbridgedMolodensky.PARAMETERS;
+ break;
}
+ default: throw new AssertionError(method);
}
+ /*
+ * Following lines will set all Bursa-Wolf parameter values (scale, translation
+ * and rotation terms). In the particular case of Molodensky method, we have an
+ * additional parameter for the number of source and target dimensions (2 or 3).
+ */
final Parameters values = createParameters(descriptor, parameters, isTranslation);
- if (useMolodensky && dimension <= 3) {
- values.getOrCreate(Molodensky.DIMENSION).setValue(dimension);
+ switch (method) {
+ case MOLODENSKY:
+ case ABRIDGED_MOLODENSKY: {
+ if (dimension <= 3) {
+ values.getOrCreate(Molodensky.DIMENSION).setValue(dimension);
+ }
+ break;
+ }
}
return values;
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -17,9 +17,12 @@
package org.apache.sis.referencing.operation;
import java.util.Map;
+import java.util.Set;
import java.util.Objects;
import java.util.Collection;
import java.util.Collections;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlSeeAlso;
@@ -55,6 +58,7 @@ import org.apache.sis.referencing.Abstra
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.operation.transform.PassThroughTransform;
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.Resources;
import org.apache.sis.internal.referencing.WKTUtilities;
@@ -96,7 +100,7 @@ import static org.apache.sis.util.Utilit
* synchronization.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
* @since 0.6
* @module
*/
@@ -207,6 +211,16 @@ public class AbstractCoordinateOperation
MathTransform transform;
/**
+ * Indices of target dimensions where "wrap around" may happen as a result of this coordinate operation.
+ * This is usually the longitude axis when the source CRS uses the [-180 … +180]° range and the target
+ * CRS uses the [0 … 360]° range, or the converse. If there is no change, then this is an empty set.
+ *
+ * @see #getWrapAroundChanges()
+ * @see #computeTransientFields()
+ */
+ private transient Set<Integer> wrapAroundChanges;
+
+ /**
* Creates a new coordinate operation initialized from the given properties.
* It is caller's responsibility to:
*
@@ -369,6 +383,30 @@ check: for (int isTarget=0; ; isTar
}
}
}
+ computeTransientFields();
+ }
+
+ /**
+ * Computes the {@link #wrapAroundChanges} field after we verified that the coordinate operation is valid.
+ */
+ final void computeTransientFields() {
+ if (sourceCRS != null && targetCRS != null) {
+ wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCRS.getCoordinateSystem());
+ } else {
+ wrapAroundChanges = Collections.emptySet();
+ }
+ }
+
+ /**
+ * Computes transient fields after deserialization.
+ *
+ * @param in the input stream from which to deserialize a coordinate operation.
+ * @throws IOException if an I/O error occurred while reading or if the stream contains invalid data.
+ * @throws ClassNotFoundException if the class serialized on the stream is not on the classpath.
+ */
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ computeTransientFields();
}
/**
@@ -392,6 +430,11 @@ check: for (int isTarget=0; ; isTar
domainOfValidity = operation.getDomainOfValidity();
scope = operation.getScope();
transform = operation.getMathTransform();
+ if (operation instanceof AbstractCoordinateOperation) {
+ wrapAroundChanges = ((AbstractCoordinateOperation) operation).wrapAroundChanges;
+ } else {
+ computeTransientFields();
+ }
}
/**
@@ -727,6 +770,40 @@ check: for (int isTarget=0; ; isTar
}
/**
+ * Returns the indices of target dimensions where "wrap around" may happen as a result of this coordinate operation.
+ * If such change exists, then this is usually the longitude axis when the source CRS uses the [-180 … +180]° range
+ * and the target CRS uses the [0 … 360]° range, or the converse. If there is no change, then this is an empty set.
+ *
+ * <div class="note"><b>Inverse relationship:</b>
+ * sometime the target dimensions returned by this method can be mapped directly to wraparound axes in source CRS,
+ * but this is not always the case. For example consider the following operation chain:
+ *
+ * <center>source projected CRS ⟶ base CRS ⟶ target geographic CRS</center>
+ *
+ * In this example, a wraparound axis in the target CRS (the longitude) can be mapped to a wraparound axis in
+ * the {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS#getBaseCRS() base CRS}. But there is no
+ * corresponding wraparound axis in the source CRS because the <em>easting</em> axis in projected CRS does not
+ * have a wraparound range meaning. We could argue that
+ * {@linkplain org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis#getDirection() axis directions} match,
+ * but such matching is not guaranteed to exist since {@code ProjectedCRS} is a special case of
+ * {@code GeneralDerivedCRS} and derived CRS can have rotations.</div>
+ *
+ * <p>The default implementation infers this set by inspecting the source and target coordinate system axes.
+ * It returns the indices of all target axes having {@link org.opengis.referencing.cs.RangeMeaning#WRAPAROUND}
+ * and for which the following condition holds: a colinear source axis exists with compatible unit of measurement,
+ * and the range (taking unit conversions in account) or range meaning of those source and target axes are not
+ * the same.</p>
+ *
+ * @return indices of target dimensions where "wrap around" may happen as a result of this coordinate operation.
+ *
+ * @since 0.8
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public Set<Integer> getWrapAroundChanges() {
+ return wrapAroundChanges;
+ }
+
+ /**
* Compares this coordinate operation with the specified object for equality. If the {@code mode} argument
* is {@link ComparisonMode#STRICT} or {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available
* properties are compared including the {@linkplain #getDomainOfValidity() domain of validity} and the
@@ -974,7 +1051,7 @@ check: for (int isTarget=0; ; isTar
private void setSource(final CoordinateReferenceSystem crs) {
if (sourceCRS == null) {
sourceCRS = crs;
- } else {
+ } else if (!sourceCRS.equals(crs)) { // Could be defined by ConcatenatedOperation.
MetadataUtilities.propertyAlreadySet(AbstractCoordinateOperation.class, "setSource", "sourceCRS");
}
}
@@ -993,7 +1070,7 @@ check: for (int isTarget=0; ; isTar
private void setTarget(final CoordinateReferenceSystem crs) {
if (targetCRS == null) {
targetCRS = crs;
- } else {
+ } else if (!targetCRS.equals(crs)) { // Could be defined by ConcatenatedOperation.
MetadataUtilities.propertyAlreadySet(AbstractCoordinateOperation.class, "setTarget", "targetCRS");
}
}
@@ -1057,4 +1134,11 @@ check: for (int isTarget=0; ; isTar
MetadataUtilities.propertyAlreadySet(AbstractCoordinateOperation.class, "setScope", "scope");
}
}
+
+ /**
+ * Invoked by JAXB after unmarshalling.
+ */
+ void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+ computeTransientFields();
+ }
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1813531&r1=1813530&r2=1813531&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] Fri Oct 27 12:58:39 2017
@@ -70,7 +70,7 @@ import static org.apache.sis.util.Utilit
* {@link DefaultPassThroughOperation}.</p>
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
* @since 0.6
* @module
*/
@@ -173,10 +173,8 @@ class AbstractSingleOperation extends Ab
* In the particular case of a {@linkplain PassThroughTransform pass through transform} with more dimensions
* than what we would expect from the given method, the check will rather be performed against the
* {@linkplain PassThroughTransform#getSubTransform() sub transform}.
- *
- * <p>The intend is to allow creation of a three-dimensional {@code ProjectedCRS} with a two-dimensional
- * {@code OperationMethod}, where the third-dimension just pass through. This is not a recommended approach
- * and we do not document that as a supported feature, but we do not prevent it neither.</p>
+ * The intend is to allow creation of a three-dimensional {@code ProjectedCRS} with a two-dimensional
+ * {@code OperationMethod}, where the third-dimension just pass through.
*
* <p>This method tries to locates what seems to be the "core" of the given math transform. The definition
* of "core" is imprecise and may be adjusted in future SIS versions. The current algorithm is as below:</p>
@@ -513,7 +511,9 @@ class AbstractSingleOperation extends Ab
*
* @see <a href="http://issues.apache.org/jira/browse/SIS-291">SIS-291</a>
*/
- private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+ @Override
+ final void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+ super.afterUnmarshal(unmarshaller, parent);
final CoordinateReferenceSystem sourceCRS = super.getSourceCRS();
final CoordinateReferenceSystem targetCRS = super.getTargetCRS();
if (transform == null && sourceCRS != null && targetCRS != null && parameters != null) try {