sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1817597 [7/19] - in /sis/branches/ISO-19115-3: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/artifact/lib/ application/sis-console/src/main/artifact/lib/darwin/ applica...
Date Sat, 09 Dec 2017 10:57:47 GMT
Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -26,6 +26,7 @@ import java.sql.DatabaseMetaData;
 import java.sql.SQLException;
 import javax.sql.DataSource;
 import java.io.IOException;
+import java.io.FileNotFoundException;
 import org.opengis.util.NameFactory;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.crs.CRSFactory;
@@ -44,6 +45,7 @@ import org.apache.sis.internal.system.De
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.referencing.factory.ConcurrentAuthorityFactory;
 import org.apache.sis.referencing.factory.UnavailableFactoryException;
+import org.apache.sis.util.resources.Messages;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Classes;
@@ -269,7 +271,7 @@ public class EPSGFactory extends Concurr
                 throw new UnavailableFactoryException(Initializer.unspecified(locale));
             }
         } catch (Exception e) {
-            throw new UnavailableFactoryException(message(e), e);
+            throw new UnavailableFactoryException(canNotUse(e), e);
         }
         dataSource   = ds;
         nameFactory  = factory(NameFactory.class,                "nameFactory",  properties);
@@ -292,12 +294,19 @@ public class EPSGFactory extends Concurr
     /**
      * Returns the message to put in an {@link UnavailableFactoryException} having the given exception as its cause.
      */
-    private String message(final Exception e) {
+    private String canNotUse(final Exception e) {
         String message = Exceptions.getLocalizedMessage(e, locale);
         if (message == null) {
             message = Classes.getShortClassName(e);
         }
-        return Resources.forLocale(locale).getString(Resources.Keys.CanNotUseGeodeticParameters_2, Constants.EPSG, message);
+        return canNotUse(message);
+    }
+
+    /**
+     * Returns the message to put in an {@link UnavailableFactoryException} having the given cause.
+     */
+    private String canNotUse(final String cause) {
+        return Resources.forLocale(locale).getString(Resources.Keys.CanNotUseGeodeticParameters_2, Constants.EPSG, cause);
     }
 
     /**
@@ -366,13 +375,18 @@ public class EPSGFactory extends Concurr
      * See <a href="https://issues.apache.org/jira/browse/LEGAL-183">LEGAL-183</a> for more information.</p>
      *
      * @param  connection  connection to the database where to create the EPSG schema.
-     * @throws IOException if the SQL script can not be found or an I/O error occurred while reading them.
-     * @throws SQLException if an error occurred while writing to the database.
+     * @throws UnavailableFactoryException if installation failed. The exception will have a
+     *         {@link FileNotFoundException} cause if a SQL script has not been found
+     *         (typically because a required resource is not on the classpath), an
+     *         {@link IOException} if an I/O error occurred while reading a SQL script, or a
+     *         {@link SQLException} if an error occurred while writing to the database.
      *
      * @see InstallationScriptProvider
      */
-    public synchronized void install(final Connection connection) throws IOException, SQLException {
+    public synchronized void install(final Connection connection) throws UnavailableFactoryException {
         ArgumentChecks.ensureNonNull("connection", connection);
+        String    message = null;
+        Exception failure = null;
         try (EPSGInstaller installer = new EPSGInstaller(connection)) {
             final boolean ac = connection.getAutoCommit();
             if (ac) {
@@ -400,9 +414,21 @@ public class EPSGFactory extends Concurr
                     }
                 }
             } catch (IOException | SQLException e) {
-                installer.logFailure(locale, e);
-                throw e;
+                message = installer.failure(locale, e);
+                failure = e;
             }
+        } catch (SQLException e) {
+            message = Messages.getResources(locale).getString(Messages.Keys.CanNotCreateSchema_1, Constants.EPSG);
+            failure = e;
+        }
+        if (failure != null) {
+            /*
+             * Derby sometime wraps SQLException into another SQLException.  For making the stack strace a
+             * little bit simpler, keep only the root cause provided that the exception type is compatible.
+             */
+            UnavailableFactoryException exception = new UnavailableFactoryException(message, Exceptions.unwrap(failure));
+            exception.setUnavailableFactory(this);
+            throw exception;
         }
     }
 
@@ -452,7 +478,7 @@ public class EPSGFactory extends Concurr
                 return newDataAccess(connection, tr);
             } else {
                 connection.close();
-                exception = new UnavailableFactoryException(SQLTranslator.tableNotFound(locale));
+                exception = new UnavailableFactoryException(canNotUse(SQLTranslator.tableNotFound(locale)));
             }
         } catch (Exception e) {                     // Really want to catch all exceptions here.
             if (connection != null) try {
@@ -460,11 +486,14 @@ public class EPSGFactory extends Concurr
             } catch (SQLException e2) {
                 e.addSuppressed(e2);
             }
+            if (e instanceof FactoryException) {
+                throw (FactoryException) e;
+            }
             /*
              * Derby sometime wraps SQLException into another SQLException.  For making the stack strace a
              * little bit simpler, keep only the root cause provided that the exception type is compatible.
              */
-            exception = new UnavailableFactoryException(message(e), Exceptions.unwrap(e));
+            exception = new UnavailableFactoryException(canNotUse(e), Exceptions.unwrap(e));
         }
         exception.setUnavailableFactory(this);
         throw exception;

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -18,20 +18,20 @@ package org.apache.sis.referencing.facto
 
 import java.util.Locale;
 import java.io.IOException;
+import java.io.FileNotFoundException;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.SQLException;
 import java.util.StringTokenizer;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
-import java.util.logging.LogRecord;
 import java.io.BufferedReader;
 import org.apache.sis.util.StringBuilders;
 import org.apache.sis.internal.metadata.sql.ScriptRunner;
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.StandardDateFormat;
-import org.apache.sis.internal.util.Fallback;
+import org.apache.sis.internal.referencing.Fallback;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.resources.Messages;
 import org.apache.sis.util.logging.PerformanceLevel;
@@ -230,7 +230,9 @@ final class EPSGInstaller extends Script
      * Processes to the creation of the EPSG database using the SQL scripts from the given provider.
      *
      * @param  scriptProvider  user-provided scripts, or {@code null} for automatic lookup.
-     * @throws IOException  if an error occurred while reading an input.
+     * @param  locale          the locale for information or warning messages, if any.
+     * @throws FileNotFoundException if a SQL script has not been found.
+     * @throws IOException  if another error occurred while reading an input.
      * @throws SQLException if an error occurred while executing a SQL statement.
      */
     public void run(InstallationResources scriptProvider, final Locale locale) throws SQLException, IOException {
@@ -255,12 +257,14 @@ final class EPSGInstaller extends Script
 
     /**
      * Searches for a SQL script provider on the classpath before to fallback on the default provider.
+     *
+     * @param  locale  the locale for information or warning messages, if any.
      */
     private static InstallationResources lookupProvider(final Locale locale) throws IOException {
         InstallationResources fallback = null;
         for (final InstallationResources provider : DefaultFactories.createServiceLoader(InstallationResources.class)) {
             if (provider.getAuthorities().contains(EPSG)) {
-                if (provider.getClass().isAnnotationPresent(Fallback.class)) {
+                if (!provider.getClass().isAnnotationPresent(Fallback.class)) {
                     return provider;
                 }
                 fallback = provider;
@@ -270,18 +274,16 @@ final class EPSGInstaller extends Script
     }
 
     /**
-     * Logs a message reporting the failure to create EPSG database. This method is invoked when {@link EPSGFactory}
-     * caught an exception. This log completes rather than replaces the exception message since {@code EPSGFactory}
-     * lets the exception propagate. Another code (for example {@link org.apache.sis.referencing.CRS#forCode(String)})
-     * may catch that exception and log another record with the exception message.
+     * Creates a message reporting the failure to create EPSG database. This method is invoked when {@link EPSGFactory}
+     * caught an exception. This method completes the exception message with the file name and line number where the
+     * error occurred, if such information is available.
      */
-    final void logFailure(final Locale locale, final Exception cause) {
+    final String failure(final Locale locale, final Exception cause) {
         String message = Messages.getResources(locale).getString(Messages.Keys.CanNotCreateSchema_1, EPSG);
         String status = status(locale);
         if (status != null) {
             message = message + ' ' + status;
         }
-        message = Exceptions.formatChainedMessages(locale, message, cause);
-        InstallationScriptProvider.log(new LogRecord(Level.WARNING, message));
+        return Exceptions.formatChainedMessages(locale, message, cause);
     }
 }

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -38,6 +38,7 @@ import org.apache.sis.util.logging.Loggi
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.setup.InstallationResources;
 import org.apache.sis.internal.referencing.Resources;
+import org.apache.sis.internal.referencing.Fallback;
 import org.apache.sis.internal.system.DataDirectory;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.CollectionsExt;
@@ -205,6 +206,7 @@ public abstract class InstallationScript
      * @return a reader for the content of SQL script to execute.
      * @throws IllegalArgumentException if the given {@code authority} argument is not the expected value.
      * @throws IndexOutOfBoundsException if the given {@code resource} argument is out of bounds.
+     * @throws FileNotFoundException if the SQL script of the given name has not been found.
      * @throws IOException if an error occurred while creating the reader.
      */
     @Override
@@ -294,6 +296,7 @@ public abstract class InstallationScript
      * @since   0.7
      * @module
      */
+    @Fallback
     static final class Default extends InstallationScriptProvider {
         /**
          * The directory containing the scripts, or {@code null} if it does not exist.
@@ -308,6 +311,8 @@ public abstract class InstallationScript
 
         /**
          * Creates a default provider.
+         *
+         * @param locale  the locale for warning messages, if any.
          */
         Default(final Locale locale) throws IOException {
             super(Constants.EPSG,

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/package-info.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -35,6 +35,8 @@
  *       a JDBC connection for the {@code "jdbc:derby:$SIS_DATA/Databases/SpatialMetadata"} URL.</li>
  *   <li>If the {@code "derby.system.home"} {@linkplain java.lang.System#getProperty(String) property} is defined,
  *       a JDBC connection for the {@code "jdbc:derby:SpatialMetadata"} URL.</li>
+ *   <li>If the {@code org.apache.sis.non-free:sis-embedded-data} module is present on the classpath,
+ *       a read-only connection to the database in the JAR file.</li>
  * </ol>
  *
  * In choice 1, the JDBC driver must be provided by the application container (e.g. Apache Tomcat).

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Sat Dec  9 10:57:44 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;
@@ -50,16 +53,19 @@ import org.apache.sis.util.collection.Co
 import org.apache.sis.util.UnsupportedImplementationException;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.parameter.Parameterized;
+import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 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;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.metadata.MetadataUtilities;
+import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.internal.system.Semaphores;
@@ -96,7 +102,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 +213,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 +385,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 +432,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 +772,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
@@ -886,10 +965,27 @@ check:      for (int isTarget=0; ; isTar
                 parameters = null;
             }
             if (parameters != null) {
+                /*
+                 * Format the parameter values. Apache SIS uses the EPSG geodetic dataset as the main source of
+                 * parameter definitions. When a parameter is defined by both OGC and EPSG with different names,
+                 * the Formatter class is responsible for choosing an appropriate name. But when the difference
+                 * is more fundamental, we may have duplication. For example in the "Molodensky" operation, OGC
+                 * uses source and target axis lengths while EPSG uses only difference between those lengths.
+                 * In this case, OGC and EPSG parameters are defined separately and are redundant. To simplify
+                 * the CoordinateOperation WKT, we omit non-EPSG parameters when we have determined that we are
+                 * about to describe an EPSG operation. We could generalize this filtering to any authority, but
+                 * we don't because few authorities are as complete as EPSG, so other authorities are more likely
+                 * to mix EPSG or someone else components with their own. Note also that we don't apply filtering
+                 * on MathTransform WKT neither for more reliable debugging.
+                 */
+                final boolean filter = WKTUtilities.isEPSG(parameters.getDescriptor(), false) &&   // NOT method.getName()
+                        Constants.EPSG.equalsIgnoreCase(Citations.getCodeSpace(formatter.getNameAuthority()));
                 formatter.newLine();
                 formatter.indent(+1);
                 for (final GeneralParameterValue param : parameters.values()) {
-                    WKTUtilities.append(param, formatter);
+                    if (!filter || WKTUtilities.isEPSG(param.getDescriptor(), true)) {
+                        WKTUtilities.append(param, formatter);
+                    }
                 }
                 formatter.indent(-1);
             }
@@ -974,7 +1070,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 +1089,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 +1153,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/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] Sat Dec  9 10:57:44 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 {

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -122,10 +122,10 @@ final class CRSPair {
     }
 
     /**
-     * Return a string representation of this key.
+     * Returns a string representation of this key.
      */
     @Override
     public String toString() {
-        return label(sourceCRS) + " → " + label(targetCRS);
+        return label(sourceCRS) + " ⟶ " + label(targetCRS);
     }
 }

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -57,6 +57,9 @@ import java.util.function.Predicate;
  * @version 0.7
  * @since   0.7
  * @module
+ *
+ * @todo Should also take the country of a {@link java.util.Locale}. The EPSG database contains ISO2 and ISO3
+ *       identifiers that we can use.
  */
 public class CoordinateOperationContext implements Serializable {
     /**

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -33,9 +33,10 @@ import org.opengis.metadata.Identifier;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.internal.metadata.AxisDirections;
-import org.apache.sis.internal.metadata.VerticalDatumTypes;
-import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.internal.referencing.provider.Affine;
+import org.apache.sis.internal.referencing.provider.DatumShiftMethod;
 import org.apache.sis.internal.referencing.provider.Geographic2Dto3D;
 import org.apache.sis.internal.referencing.provider.Geographic3Dto2D;
 import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
@@ -116,17 +117,6 @@ import static org.apache.sis.util.Utilit
  */
 public class CoordinateOperationFinder extends CoordinateOperationRegistry {
     /**
-     * The accuracy threshold (in metres) for allowing the use of Molodensky approximation instead than the
-     * Geocentric Translation method. The accuracy of datum shifts with Molodensky approximation is about 5
-     * or 10 metres. However for this constant, we are not interested in absolute accuracy but rather in the
-     * difference between Molodensky and Geocentric Translation methods, which is much lower (less than 1 m).
-     * We nevertheless use a relatively high threshold as a conservative approach.
-     *
-     * @see #desiredAccuracy
-     */
-    private static final double MOLODENSKY_ACCURACY = 5;
-
-    /**
      * Identifiers used as the basis for identifier of CRS used as an intermediate step.
      * The values can be of two kinds:
      *
@@ -442,7 +432,7 @@ public class CoordinateOperationFinder e
          * Actually we do not know if the longitude rotation should be before or after datum shift. But this ambiguity
          * can usually be ignored because Bursa-Wolf parameters are always used with source and target prime meridians
          * set to Greenwich in EPSG dataset 8.9.  For safety, the SIS's DefaultGeodeticDatum class ensures that if the
-         * prime meridian are not the same, then the target meridian must be Greenwich.
+         * prime meridians are not the same, then the target meridian must be Greenwich.
          */
         final DefaultMathTransformFactory.Context context = ReferencingUtilities.createTransformContext(
                 sourceCRS, targetCRS, new MathTransformContext(sourceDatum, targetDatum));
@@ -498,20 +488,34 @@ public class CoordinateOperationFinder e
         final DefaultMathTransformFactory mtFactory = factorySIS.getDefaultMathTransformFactory();
         MathTransform before = null, after = null;
         ParameterValueGroup parameters;
-        if (datumShift != null) {
+        if (identifier == DATUM_SHIFT || identifier == ELLIPSOID_CHANGE) {
             /*
              * If the transform can be represented by a single coordinate operation, returns that operation.
              * Possible operations are:
              *
-             *    - Geocentric translation         (in geocentric, geographic-2D or geographic-3D domains)
              *    - Position Vector transformation (in geocentric, geographic-2D or geographic-3D domains)
+             *    - Geocentric translation         (in geocentric, geographic-2D or geographic-3D domains)
+             *    - [Abridged] Molodensky          (as an approximation of geocentric translation)
+             *    - Identity                       (if the desired accuracy is so large than we can skip datum shift)
              *
-             * Otherwise, maybe we failed to create the operation because the coordinate system type were not the same.
-             * Convert unconditionally to XYZ geocentric coordinates and apply the datum shift in that coordinate space.
+             * TODO: if both CS are ellipsoidal but with different number of dimensions, then we should use
+             * an intermediate 3D geographic CRS in order to enable the use of Molodensky method if desired.
              */
-            parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift, desiredAccuracy >= MOLODENSKY_ACCURACY);
+            final DatumShiftMethod preferredMethod = DatumShiftMethod.forAccuracy(desiredAccuracy);
+            parameters = GeocentricAffine.createParameters(sourceCS, targetCS, datumShift, preferredMethod);
             if (parameters == null) {
-                parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE), datumShift);
+                /*
+                 * Failed to select a coordinate operation. Maybe because the coordinate system types are not the same.
+                 * Convert unconditionally to XYZ geocentric coordinates and apply the datum shift in that CS space.
+                 *
+                 * TODO: operation name should not be "Affine" if 'before' or 'after' transforms are not identity.
+                 *       Reminder: the parameter group name here determines the OperationMethod later in this method.
+                 */
+                if (datumShift != null) {
+                    parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE), datumShift);
+                } else {
+                    parameters = Affine.identity(3);                        // Dimension of geocentric CRS.
+                }
                 final CoordinateSystem normalized = CommonCRS.WGS84.geocentric().getCoordinateSystem();
                 before = mtFactory.createCoordinateSystemChange(sourceCS, normalized, sourceDatum.getEllipsoid());
                 after  = mtFactory.createCoordinateSystemChange(normalized, targetCS, targetDatum.getEllipsoid());
@@ -522,18 +526,26 @@ public class CoordinateOperationFinder e
             parameters = (isGeographicToGeocentric ? GeographicToGeocentric.PARAMETERS
                                                    : GeocentricToGeographic.PARAMETERS).createValue();
         } else {
+            /*
+             * Coordinate system change (including change in the number of dimensions) without datum shift.
+             */
             final int sourceDim = sourceCS.getDimension();
             final int targetDim = targetCS.getDimension();
-            if ((sourceDim & ~1) == 2 && (sourceDim ^ targetDim) == 1    // sourceDim == 2 or 3 and difference with targetDim is 1.
+            if ((sourceDim & ~1) == 2                           // sourceDim == 2 or 3.
+                    && (sourceDim ^ targetDim) == 1             // abs(sourceDim - targetDim) == 1.
                     && (sourceCS instanceof EllipsoidalCS)
                     && (targetCS instanceof EllipsoidalCS))
             {
                 parameters = (sourceDim == 2 ? Geographic2Dto3D.PARAMETERS
                                              : Geographic3Dto2D.PARAMETERS).createValue();
             } else {
-                parameters = TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE));      // Initialized to identity.
-                parameters.parameter(Constants.NUM_COL).setValue(targetDim + 1);
-                parameters.parameter(Constants.NUM_ROW).setValue(targetDim + 1);
+                /*
+                 * TODO: instead than creating parameters for an identity operation, we should create the
+                 *       CoordinateOperation directly from the MathTransform created by mtFactory below.
+                 *       The intend if to get the correct OperationMethod, which should not be "Affine"
+                 *       if there is a CS type change.
+                 */
+                parameters = Affine.identity(targetDim);
                 /*
                  * createCoordinateSystemChange(…) needs the ellipsoid associated to the ellipsoidal coordinate system,
                  * if any. If none or both coordinate systems are ellipsoidal, then the ellipsoid will be ignored (see
@@ -629,7 +641,7 @@ public class CoordinateOperationFinder e
         VerticalCRS heightCRS = targetCRS;      // First candidate, will be replaced if it doesn't fit.
         VerticalCS  heightCS  = heightCRS.getCoordinateSystem();
         if (equalsIgnoreMetadata(heightCS.getAxis(0), expectedAxis)) {
-            isEllipsoidalHeight = VerticalDatumTypes.ELLIPSOIDAL.equals(heightCRS.getDatum().getVerticalDatumType());
+            isEllipsoidalHeight = ReferencingUtilities.isEllipsoidalHeight(heightCRS.getDatum());
         } else {
             heightCRS = CommonCRS.Vertical.ELLIPSOIDAL.crs();
             heightCS  = heightCRS.getCoordinateSystem();
@@ -840,8 +852,8 @@ public class CoordinateOperationFinder e
             } else if (stepComponents.length == 1) {
                 stepTargetCRS = target;                 // Slight optimization of the next block.
             } else {
-                stepTargetCRS = toAuthorityDefinition(CoordinateReferenceSystem.class, ReferencingServices.getInstance()
-                        .createCompoundCRS(factorySIS.getCRSFactory(), factorySIS.getCSFactory(), derivedFrom(target), stepComponents));
+                stepTargetCRS = toAuthorityDefinition(CoordinateReferenceSystem.class,
+                        new CompoundCRSBuilder(factory, factorySIS).createCompoundCRS(derivedFrom(target), stepComponents));
             }
             int delta = source.getCoordinateSystem().getDimension();
             final int startAtDimension = endAtDimension;
@@ -949,10 +961,13 @@ public class CoordinateOperationFinder e
          * trivial operations.
          */
         CoordinateOperation main = null;
-        final boolean isAxisChange1 = (step1.getName() == AXIS_CHANGES);
-        final boolean isAxisChange2 = (step2.getName() == AXIS_CHANGES);
+        final boolean isAxisChange1 = canHide(step1.getName());
+        final boolean isAxisChange2 = canHide(step2.getName());
         if (isAxisChange1 && isAxisChange2 && isAffine(step1) && isAffine(step2)) {
             main = step2;                                           // Arbitrarily take the last step.
+            if (main.getName() == IDENTITY && step1.getName() != IDENTITY) {
+                main = step1;
+            }
         } else {
             if (isAxisChange1 && mt1.getSourceDimensions() == mt1.getTargetDimensions()) main = step2;
             if (isAxisChange2 && mt2.getSourceDimensions() == mt2.getTargetDimensions()) main = step1;
@@ -1005,8 +1020,8 @@ public class CoordinateOperationFinder e
         if (isIdentity(step1)) return concatenate(step2, step3);
         if (isIdentity(step2)) return concatenate(step1, step3);
         if (isIdentity(step3)) return concatenate(step1, step2);
-        if (step1.getName() == AXIS_CHANGES) return concatenate(concatenate(step1, step2), step3);
-        if (step3.getName() == AXIS_CHANGES) return concatenate(step1, concatenate(step2, step3));
+        if (canHide(step1.getName())) return concatenate(concatenate(step1, step2), step3);
+        if (canHide(step3.getName())) return concatenate(step1, concatenate(step2, step3));
         final Map<String,?> properties = defaultName(step1.getSourceCRS(), step3.getTargetCRS());
         return factory.createConcatenatedOperation(properties, step1, step2, step3);
     }
@@ -1030,7 +1045,22 @@ public class CoordinateOperationFinder e
      * are usually datum shift and must be visible.
      */
     private static boolean isIdentity(final CoordinateOperation operation) {
-        return (operation == null) || ((operation instanceof Conversion) && operation.getMathTransform().isIdentity());
+        if (operation == null) {
+            return true;
+        }
+        if ((operation instanceof Conversion) && operation.getMathTransform().isIdentity()) {
+            return CoordinateOperations.wrapAroundChanges(operation).isEmpty();
+        }
+        return false;
+    }
+
+    /**
+     * Returns {@code true} if a coordinate operation of the given name can be hidden
+     * in the list of operations. Note that the {@code MathTransform} will still take
+     * the operation in account however.
+     */
+    private static boolean canHide(final Identifier id) {
+        return (id == AXIS_CHANGES) || (id == IDENTITY);
     }
 
     /**

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -112,7 +112,7 @@ class CoordinateOperationRegistry {
     /**
      * The identifier for an identity operation.
      */
-    private static final Identifier IDENTITY = createIdentifier(Vocabulary.Keys.Identity);
+    static final Identifier IDENTITY = createIdentifier(Vocabulary.Keys.Identity);
 
     /**
      * The identifier for conversion using an affine transform for axis swapping and/or unit conversions.
@@ -257,19 +257,27 @@ class CoordinateOperationRegistry {
     }
 
     /**
-     * Finds the authority code for the given coordinate reference system.
+     * Finds the authority codes for the given coordinate reference system.
      * This method does not trust the code given by the user in its CRS - we verify it.
-     * This method may return a code even if the axis order does not match;
+     * This method may return codes even if the axis order does not match;
      * it will be caller's responsibility to make necessary adjustments.
      */
-    private String findCode(final CoordinateReferenceSystem crs) throws FactoryException {
+    private List<String> findCode(final CoordinateReferenceSystem crs) throws FactoryException {
+        final List<String> codes = new ArrayList<>();
         if (codeFinder != null) {
-            final Identifier identifier = IdentifiedObjects.getIdentifier(codeFinder.findSingleton(crs), null);
-            if (identifier != null) {
-                return identifier.getCode();
+            for (final IdentifiedObject candidate : codeFinder.find(crs)) {
+                final Identifier identifier = IdentifiedObjects.getIdentifier(candidate, registry.getAuthority());
+                if (identifier != null) {
+                    final String code = identifier.getCode();
+                    if (Utilities.deepEquals(candidate, crs, ComparisonMode.APPROXIMATIVE)) {
+                        codes.add(0, code);     // If axis order match, give precedence to that CRS.
+                    } else {
+                        codes.add(code);
+                    }
+                }
             }
         }
-        return null;
+        return codes;
     }
 
     /**
@@ -366,56 +374,58 @@ class CoordinateOperationRegistry {
                                        final CoordinateReferenceSystem targetCRS)
             throws IllegalArgumentException, IncommensurableException, FactoryException
     {
-        final String sourceID = findCode(sourceCRS);
-        if (sourceID == null) {
-            return null;
-        }
-        final String targetID = findCode(targetCRS);
-        if (targetID == null) {
-            return null;
-        }
-        if (sourceID.equals(targetID)) {
-            /*
-             * Above check is necessary because this method may be invoked in some situations where the code
-             * are equal while the CRS are not. Such situation should be illegal, but unfortunately it still
-             * happen because many softwares are not compliant with EPSG definition of axis order.   In such
-             * cases we will need to compute a transform from sourceCRS to targetCRS ignoring the source and
-             * target codes. The CoordinateOperationFinder class can do that, providing that we prevent this
-             * CoordinateOperationRegistry to (legitimately) claims that the operation from sourceCode to
-             * targetCode is the identity transform.
-             */
-            return null;
-        }
-        final boolean inverse;
-        Collection<CoordinateOperation> operations;
-        boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);    // See comment for the same call inside the loop.
-        try {
-            try {
-                operations = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
-                inverse = Containers.isNullOrEmpty(operations);
-                if (inverse) {
+        final List<String> sources = findCode(sourceCRS); if (sources.isEmpty()) return null;
+        final List<String> targets = findCode(targetCRS); if (targets.isEmpty()) return null;
+        Collection<CoordinateOperation> operations = null;
+        boolean inverse = false;
+        for (final String sourceID : sources) {
+            for (final String targetID : targets) {
+                if (sourceID.equals(targetID)) {
                     /*
-                     * No operation from 'source' to 'target' available. But maybe there is an inverse operation.
-                     * This is typically the case when the user wants to convert from a projected to a geographic CRS.
-                     * The EPSG database usually contains transformation paths for geographic to projected CRS only.
+                     * Above check is necessary because this method may be invoked in some situations where the code
+                     * are equal while the CRS are not. Such situation should be illegal, but unfortunately it still
+                     * happen because many software products are not compliant with EPSG definition of axis order.
+                     * In such cases we will need to compute a transform from sourceCRS to targetCRS ignoring the
+                     * source and target codes. The CoordinateOperationFinder class can do that, providing that we
+                     * prevent this CoordinateOperationRegistry to (legitimately) claims that the operation from
+                     * sourceCode to targetCode is the identity transform.
                      */
-                    operations = registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
-                    if (Containers.isNullOrEmpty(operations)) {
-                        return null;
-                    }
+                    return null;
                 }
-            } finally {
-                if (!mdOnly) {
-                    Semaphores.clear(Semaphores.METADATA_ONLY);
+                final boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
+                try {
+                    try {
+                        operations = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
+                        inverse = Containers.isNullOrEmpty(operations);
+                        if (inverse) {
+                            /*
+                             * No operation from 'source' to 'target' available. But maybe there is an inverse operation.
+                             * This is typically the case when the user wants to convert from a projected to a geographic CRS.
+                             * The EPSG database usually contains transformation paths for geographic to projected CRS only.
+                             */
+                            operations = registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
+                            if (Containers.isNullOrEmpty(operations)) {
+                                continue;
+                            }
+                        }
+                    } finally {
+                        if (!mdOnly) {
+                            Semaphores.clear(Semaphores.METADATA_ONLY);
+                        }
+                    }
+                } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException e) {
+                    /*
+                     * sourceCode or targetCode is unknown to the underlying authority factory.
+                     * Ignores the exception and fallback on the generic algorithm provided by
+                     * CoordinateOperationFinder.
+                     */
+                    log(null, e);
+                    continue;
                 }
+                break;          // Stop on the first non-empty set of operations that we find.
             }
-        } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException e) {
-            /*
-             * sourceCode or targetCode is unknown to the underlying authority factory.
-             * Ignores the exception and fallback on the generic algorithm provided by
-             * CoordinateOperationFinder.
-             */
-            log(null, e);
+        }
+        if (operations == null) {
             return null;
         }
         /*
@@ -443,7 +453,7 @@ class CoordinateOperationRegistry {
                  * The non-public Semaphores.METADATA_ONLY mechanism instructs EPSGDataAccess to
                  * instantiate DeferredCoordinateOperation instead of full coordinate operations.
                  */
-                mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
+                final boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
                 try {
                     try {
                         if (!it.hasNext()) break;
@@ -1034,10 +1044,7 @@ class CoordinateOperationRegistry {
             }
         }
         return toAuthorityDefinition(CoordinateReferenceSystem.class,
-                ReferencingServices.getInstance().createCompoundCRS(
-                        factorySIS.getCRSFactory(),
-                        factorySIS.getCSFactory(),
-                        derivedFrom(crs), crs, CommonCRS.Vertical.ELLIPSOIDAL.crs()));
+                new CompoundCRSBuilder(factory, factorySIS).createCompoundCRS(derivedFrom(crs), crs, CommonCRS.Vertical.ELLIPSOIDAL.crs()));
     }
 
     /**

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -52,7 +52,7 @@ import static org.apache.sis.util.Utilit
  * reference system associated with the concatenated operation.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.6
  * @module
  */
@@ -113,18 +113,36 @@ final class DefaultConcatenatedOperation
             throw new IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class));
         }
+        initialize(properties, operations, mtFactory);
+        checkDimensions(properties);
+    }
+
+    /**
+     * Initializes the {@link #sourceCRS}, {@link #targetCRS} and {@link #operations} fields.
+     * If the source or target CRS is already non-null (which may happen on JAXB unmarshalling),
+     * leaves that CRS unchanged.
+     *
+     * @param  properties   the properties specified at construction time, or {@code null} if unknown.
+     * @param  operations   the operations to concatenate.
+     * @param  mtFactory    the math transform factory to use, or {@code null} for not performing concatenation.
+     * @throws FactoryException if the factory can not concatenate the math transforms.
+     */
+    private void initialize(final Map<String,?>         properties,
+                            final CoordinateOperation[] operations,
+                            final MathTransformFactory  mtFactory)
+            throws FactoryException
+    {
         final List<CoordinateOperation> flattened = new ArrayList<>(operations.length);
-        initialize(properties, operations, flattened, mtFactory,
-                (coordinateOperationAccuracy == null), (domainOfValidity == null));
+        final CoordinateReferenceSystem crs = initialize(properties, operations, flattened, mtFactory,
+                (sourceCRS == null), (coordinateOperationAccuracy == null), (domainOfValidity == null));
+        if (targetCRS == null) {
+            targetCRS = crs;
+        }
         /*
          * At this point we should have flattened.size() >= 2, except if some operations
          * were omitted because their associated math transform were identity operation.
          */
-        operations      = flattened.toArray(new CoordinateOperation[flattened.size()]);
-        this.operations = UnmodifiableArrayList.wrap(operations);
-        this.sourceCRS  = operations[0].getSourceCRS();
-        this.targetCRS  = operations[operations.length - 1].getTargetCRS();
-        checkDimensions(properties);
+        this.operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()]));
     }
 
     /**
@@ -162,17 +180,20 @@ final class DefaultConcatenatedOperation
      * @param  operations   the operations to concatenate.
      * @param  flattened    the destination list in which to add the {@code SingleOperation} instances.
      * @param  mtFactory    the math transform factory to use, or {@code null} for not performing concatenation.
+     * @param  setSource    {@code true} for setting the {@link #sourceCRS} on the very first CRS (regardless if null or not).
      * @param  setAccuracy  {@code true} for setting the {@link #coordinateOperationAccuracy} field.
      * @param  setDomain    {@code true} for setting the {@link #domainOfValidity} field.
+     * @return the last target CRS, regardless if null or not.
      * @throws FactoryException if the factory can not concatenate the math transforms.
      */
-    private void initialize(final Map<String,?>             properties,
-                            final CoordinateOperation[]     operations,
-                            final List<CoordinateOperation> flattened,
-                            final MathTransformFactory      mtFactory,
-                            boolean                         setAccuracy,
-                            boolean                         setDomain)
-            throws FactoryException
+    private CoordinateReferenceSystem initialize(
+            final Map<String,?>             properties,
+            final CoordinateOperation[]     operations,
+            final List<CoordinateOperation> flattened,
+            final MathTransformFactory      mtFactory,
+            boolean                         setSource,
+            boolean                         setAccuracy,
+            boolean                         setDomain) throws FactoryException
     {
         CoordinateReferenceSystem previous = null;
         for (int i=0; i<operations.length; i++) {
@@ -182,17 +203,19 @@ final class DefaultConcatenatedOperation
              * Verify consistency of user argument: for each coordinate operation, the number of dimensions of the
              * source CRS shall be equals to the number of dimensions of the target CRS in the previous operation.
              */
-            if (previous != null) {
-                final CoordinateReferenceSystem next = op.getSourceCRS();
-                if (next != null) {
-                    final int dim1 = previous.getCoordinateSystem().getDimension();
-                    final int dim2 = next.getCoordinateSystem().getDimension();
-                    if (dim1 != dim2) {
-                        throw new IllegalArgumentException(Errors.getResources(properties).getString(
-                                Errors.Keys.MismatchedDimension_3, "operations[" + i + "].sourceCRS", dim1, dim2));
-                    }
+            final CoordinateReferenceSystem next = op.getSourceCRS();
+            if (previous != null && next != null) {
+                final int dim1 = previous.getCoordinateSystem().getDimension();
+                final int dim2 = next.getCoordinateSystem().getDimension();
+                if (dim1 != dim2) {
+                    throw new IllegalArgumentException(Errors.getResources(properties).getString(
+                            Errors.Keys.MismatchedDimension_3, "operations[" + i + "].sourceCRS", dim1, dim2));
                 }
             }
+            if (setSource) {
+                setSource = false;
+                sourceCRS = next;                                               // Take even if null.
+            }
             previous = op.getTargetCRS();                                       // For next iteration cycle.
             /*
              * Now that we have verified the CRS dimensions, we should be able to concatenate the transforms.
@@ -207,11 +230,11 @@ final class DefaultConcatenatedOperation
                 final List<? extends CoordinateOperation> children = ((ConcatenatedOperation) op).getOperations();
                 @SuppressWarnings("SuspiciousToArrayCall")
                 final CoordinateOperation[] asArray = children.toArray(new CoordinateOperation[children.size()]);
-                initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, setAccuracy, setDomain);
+                initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, false, setAccuracy, setDomain);
             } else if (!step.isIdentity()) {
                 flattened.add(op);
             }
-            if (mtFactory != null) {
+            if (mtFactory != null && step != null) {
                 transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step;
             }
             /*
@@ -248,6 +271,7 @@ final class DefaultConcatenatedOperation
                 }
             }
         }
+        return previous;
     }
 
     /**
@@ -388,7 +412,6 @@ final class DefaultConcatenatedOperation
      * Returns the operations to marshal. We use this private methods instead than annotating
      * {@link #getOperations()} in order to force JAXB to invoke the setter method on unmarshalling.
      */
-    @SuppressWarnings("SuspiciousToArrayCall")
     @XmlElement(name = "coordOperation", required = true)
     private CoordinateOperation[] getSteps() {
         final List<? extends CoordinateOperation> operations = getOperations();
@@ -399,9 +422,6 @@ final class DefaultConcatenatedOperation
      * Invoked by JAXB for setting the operations.
      */
     private void setSteps(final CoordinateOperation[] steps) throws FactoryException {
-        final List<CoordinateOperation> flattened = new ArrayList<>(steps.length);
-        initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class),
-                (coordinateOperationAccuracy == null), (domainOfValidity == null));
-        operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()]));
+        initialize(null, steps, DefaultFactories.forBuildin(MathTransformFactory.class));
     }
 }

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.ServiceLoader;
 import org.opengis.util.FactoryException;
 import org.opengis.util.NoSuchIdentifierException;
@@ -42,8 +43,10 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.Constants;
-import org.apache.sis.internal.util.LazySet;
+import org.apache.sis.internal.referencing.LazySet;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
@@ -676,6 +679,13 @@ next:   for (int i=components.size(); --
     public CoordinateOperation createConcatenatedOperation(final Map<String,?> properties,
             final CoordinateOperation... operations) throws FactoryException
     {
+        /*
+         * If the user specified a single operation, there is no need to create a ConcatenatedOperation;
+         * the operation to return will be the specified one. The metadata given in argument are ignored
+         * on the assumption that the single operation has more complete metadata (in particular an EPSG
+         * code, in which case we do not want to modify any other metadata in order to stay compliant
+         * with EPSG definition).
+         */
         if (operations != null && operations.length == 1) {
             return operations[0];
         }
@@ -688,14 +698,39 @@ next:   for (int i=components.size(); --
         /*
          * Verifies again the number of single operations.  We may have a singleton if some operations
          * were omitted because their associated math transform were identity. This happen for example
-         * if a "Geographic 3D to 2D conversion" as been redimensioned to a "3D to 3D" operation.
+         * if a "Geographic 3D to 2D conversion" has been redimensioned to a "3D to 3D" operation.
          */
         final List<? extends CoordinateOperation> co = op.getOperations();
-        if (co.size() == 1) {
-            assert op.getMathTransform().equals(co.get(0).getMathTransform()) : op;
-            return co.get(0);
+        if (co.size() != 1) {
+            return pool.unique(op);
         }
-        return pool.unique(op);
+        final CoordinateOperation single = co.get(0);
+        assert op.getMathTransform().equals(single.getMathTransform()) : op;
+        if (!Objects.equals(single.getSourceCRS(), op.getSourceCRS()) ||
+            !Objects.equals(single.getTargetCRS(), op.getTargetCRS()))
+        {
+            /*
+             * The CRS of the single operation may be different than the CRS of the concatenated operation
+             * if the first or the last operation was an identity operation. It happens for example if the
+             * sole purpose of an operation step was to change the longitude range from [-180 … +180]° to
+             * [0 … 360]°: the MathTransform is identity (because Apache SIS does not handle those changes
+             * in MathTransform; we handle that elsewhere, for example in the Envelopes utility class),
+             * but omitting the transform should not cause the lost of the CRS with desired longitude range.
+             */
+            if (single instanceof SingleOperation) {
+                final Map<String,Object> merge = new HashMap<>(
+                        IdentifiedObjects.getProperties(single, CoordinateOperation.IDENTIFIERS_KEY));
+                merge.put(ReferencingServices.PARAMETERS_KEY, ((SingleOperation) single).getParameterValues());
+                if (single instanceof AbstractIdentifiedObject) {
+                    merge.put(ReferencingServices.OPERATION_TYPE_KEY, ((AbstractIdentifiedObject) single).getInterface());
+                }
+                merge.putAll(properties);
+                return createSingleOperation(merge, op.getSourceCRS(), op.getTargetCRS(),
+                        AbstractCoordinateOperation.getInterpolationCRS(op),
+                        ((SingleOperation) single).getMethod(), single.getMathTransform());
+            }
+        }
+        return single;
     }
 
     /**

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -86,7 +86,7 @@ import static org.apache.sis.util.Argume
  * recommended to provide also {@linkplain #getIdentifiers() identifiers} (e.g. “EPSG:9804” in the above example)
  * since names can sometime be ambiguous or be spelled in different ways.
  *
- * <div class="note"><b>Departure from the ISO 19111 standard</b><br>
+ * <div class="section">Departure from the ISO 19111 standard</div>
  * The following properties are mandatory according ISO 19111,
  * but may be missing under some conditions in Apache SIS:
  * <ul>
@@ -95,7 +95,7 @@ import static org.apache.sis.util.Argume
  *     can not be {@linkplain #DefaultOperationMethod(MathTransform) inferred from the given math transform}.</li>
  *   <li>The {@linkplain #getParameters() parameters} if the {@link #DefaultOperationMethod(MathTransform)}
  *     constructor can not infer them.</li>
- * </ul></div>
+ * </ul>
  *
  * <div class="section">Relationship with other classes or interfaces</div>
  * {@code OperationMethod} describes parameters without providing any value (except sometime default values).

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/MathTransformContext.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -18,6 +18,8 @@ package org.apache.sis.referencing.opera
 
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.cs.CartesianCS;
+import org.opengis.referencing.cs.SphericalCS;
+import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.operation.Matrix;
@@ -102,7 +104,7 @@ final class MathTransformContext extends
                 } else {
                     matrix = cm.multiply(rot);                  // Apply the rotation before normalization.
                 }
-            } else {
+            } else if (cs == null || cs instanceof EllipsoidalCS || cs instanceof SphericalCS) {
                 final Double value = rotation;
                 if (inverse) {
                     cm.convertBefore(0, null, value);           // Longitude is the first axis in normalized CS.
@@ -110,6 +112,8 @@ final class MathTransformContext extends
                     cm.convertAfter(0, null, value);
                 }
                 matrix = cm;
+            } else {
+                throw new FactoryException(Errors.format(Errors.Keys.UnsupportedCoordinateSystem_1, cs.getName()));
             }
         }
         return matrix;

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -110,7 +110,7 @@ public class LinearTransformBuilder exte
     private int numPoints;
 
     /**
-     * The transform created by the last call to {@link #create()}.
+     * The transform created by the last call to {@link #create(MathTransformFactory)}.
      * This is reset to {@code null} when coordinates are modified.
      */
     private transient LinearTransform transform;
@@ -373,8 +373,6 @@ search: for (int j=0; j<numPoints; j++)
             throws MismatchedDimensionException
     {
         ArgumentChecks.ensureNonNull("sourceToTarget", sourceToTarget);
-        pendingSources = null;
-        pendingTargets = null;
         transform   = null;
         correlation = null;
         sources     = null;
@@ -513,7 +511,6 @@ search: for (int j=0; j<numPoints; j++)
      * @since 0.8
      */
     public double[] getControlPoint(final int[] source) {
-        processPendings();
         ArgumentChecks.ensureNonNull("source", source);
         verifySourceDimension(source.length);
         if (targets == null) {
@@ -551,97 +548,6 @@ search: for (int j=0; j<numPoints; j++)
     }
 
     /**
-     * Sets the source points, overwriting any previous setting. The number of source points will need to be the same
-     * than the number of {@linkplain #setTargetPoints target points} when the {@link #create()} method will be invoked.
-     * In current Apache SIS implementation, the source points must be one or two-dimensional.
-     *
-     * <p>If this builder has been created with the {@link #LinearTransformBuilder(int...)} constructor,
-     * then all given points must be two-dimensional and all ordinate values must be integers in the
-     * [0 … <var>width</var>-1] or [0 … <var>height</var>-1] range for the first and second dimension
-     * respectively. This constraint does not apply if this builder has been created with the
-     * {@link #LinearTransformBuilder()} constructor.</p>
-     *
-     * <p>It is caller's responsibility to ensure that no source point is duplicated.
-     * If the same source point is repeated twice, then {@code LinearTransformBuilder} behavior is undefined.</p>
-     *
-     * @param  points  the source points, assumed precise.
-     * @throws MismatchedDimensionException if at least one point does not have the expected number of dimensions.
-     *
-     * @deprecated Replaced by {@link #setControlPoints(Map)}.
-     */
-    @Deprecated
-    public void setSourcePoints(final DirectPosition... points) throws MismatchedDimensionException {
-        ArgumentChecks.ensureNonNull("points", points);
-        transform   = null;
-        correlation = null;
-        sources     = null;
-        targets     = null;
-        numPoints   = 0;
-        pendingSources = points.clone();
-    }
-
-    /**
-     * Sets the target points, overwriting any previous setting. The number of target points will need to be the same
-     * than the number of {@linkplain #setSourcePoints source points} when the {@link #create()} method will be invoked.
-     * Target points can have any number of dimensions (not necessarily 2), but all points shall have
-     * the same number of dimensions.
-     *
-     * @param  points  the target points, assumed uncertain.
-     * @throws MismatchedDimensionException if not all points have the same number of dimensions.
-     *
-     * @deprecated Replaced by {@link #setControlPoints(Map)}.
-     */
-    @Deprecated
-    public void setTargetPoints(final DirectPosition... points) throws MismatchedDimensionException {
-        ArgumentChecks.ensureNonNull("points", points);
-        transform   = null;
-        correlation = null;
-        sources     = null;
-        targets     = null;
-        numPoints   = 0;
-        pendingTargets = points.clone();
-    }
-
-    @Deprecated
-    private transient DirectPosition[] pendingSources, pendingTargets;
-
-    @Deprecated
-    private void processPendings() {
-        if (pendingSources != null || pendingTargets != null) {
-            if (pendingSources == null || pendingTargets == null) {
-                throw new IllegalStateException(Errors.format(
-                        Errors.Keys.MissingValueForProperty_1, (pendingSources == null) ? "sources" : "targets"));
-            }
-            final int length = pendingSources.length;
-            if (pendingTargets.length != length) {
-                throw new IllegalStateException(Errors.format(Errors.Keys.MismatchedArrayLengths));
-            }
-            final Map<DirectPosition,DirectPosition> sourceToTarget = new java.util.HashMap<>(length);
-            for (int i=0; i<length; i++) {
-                sourceToTarget.put(pendingSources[i], pendingTargets[i]);
-            }
-            setControlPoints(sourceToTarget);
-        }
-    }
-
-    /**
-     * Creates a linear transform approximation from the source positions to the target positions.
-     * This method assumes that source positions are precise and that all uncertainty is in the target positions.
-     *
-     * @return the fitted linear transform.
-     *
-     * @deprecated Replaced by {@link #create(MathTransformFactory)}.
-     */
-    @Deprecated
-    public LinearTransform create() {
-        try {
-            return create(null);
-        } catch (FactoryException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
      * Creates a linear transform approximation from the source positions to the target positions.
      * This method assumes that source positions are precise and that all uncertainty is in the target positions.
      *
@@ -658,7 +564,6 @@ search: for (int j=0; j<numPoints; j++)
     @SuppressWarnings("serial")
     public LinearTransform create(final MathTransformFactory factory) throws FactoryException {
         if (transform == null) {
-            processPendings();
             final double[][] sources = this.sources;                    // Protect from changes.
             final double[][] targets = this.targets;
             if (targets == null) {
@@ -731,7 +636,7 @@ search: for (int j=0; j<numPoints; j++)
     }
 
     /**
-     * Returns the correlation coefficients of the last transform created by {@link #create()},
+     * Returns the correlation coefficients of the last transform created by {@link #create create(…)},
      * or {@code null} if none. If non-null, the array length is equals to the number of target
      * dimensions.
      *

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -41,7 +41,7 @@
  *
  * <div class="section">Apache SIS specific behavior</div>
  * The following operations have a behavior in Apache SIS which may be different
- * than the behavior found in other softwares. Those particularities apply only when the math transform is
+ * than the behavior found in other software products. Those particularities apply only when the math transform is
  * {@linkplain org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#createParameterizedTransform
  * created directly}. Users do not need to care about them when the coordinate operation is
  * {@linkplain org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory#createOperation

Modified: sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -63,11 +63,11 @@ public class AlbersEqualArea extends Equ
      *
      * <p>In Apache SIS implementation, we use modified formulas in which the (1 - ℯ²) factor is omitted in
      * {@link #qm(double)} calculation. Consequently what we get is a modified value <var>nm</var> which is
-     * related to Synder's <var>n</var> value by {@literal n = nm / (1 - ℯ²)}.  The omitted (1 - ℯ²) factor
+     * related to Snyder's <var>n</var> value by {@literal n = nm / (1 - ℯ²)}.  The omitted (1 - ℯ²) factor
      * is either taken in account by the (de)normalization matrix, or cancels with other (1 - ℯ²) factors
      * when we develop the formulas.</p>
      *
-     * <p>Note that in the spherical case, <var>nm</var> = Synder's <var>n</var>.</p>
+     * <p>Note that in the spherical case, <var>nm</var> = Snyder's <var>n</var>.</p>
      */
     final double nm;
 
@@ -136,7 +136,7 @@ public class AlbersEqualArea extends Equ
         }
         C = m1*m1 + nm*α1;                  // Omitted (1-ℯ²) term in nm cancels with omitted (1-ℯ²) term in α₁.
         /*
-         * Compute rn = (1-ℯ²)/nm, which is the reciprocal of the "real" n used in Synder and EPSG guidance note.
+         * Compute rn = (1-ℯ²)/nm, which is the reciprocal of the "real" n used in Snyder and EPSG guidance note.
          * We opportunistically use double-double arithmetic since the MatrixSIS operations use them anyway, but
          * we do not really have that accuracy because of the limited precision of 'nm'. The intend is rather to
          * increase the chances term cancellations happen during concatenation of coordinate operations.
@@ -260,14 +260,14 @@ public class AlbersEqualArea extends Equ
         final double x = srcPts[srcOff  ];
         final double y = srcPts[srcOff+1];
         /*
-         * Note: Synder suggests to reverse the sign of x, y and ρ₀ if n is negative. It should not done in Apache SIS
+         * Note: Snyder suggests to reverse the sign of x, y and ρ₀ if n is negative. It should not done in Apache SIS
          * implementation because (x,y) are premultiplied by n (by the normalization affine transform) before to enter
          * in this method, so if n was negative those values have already their sign reverted.
          */
         dstPts[dstOff  ] = atan2(x, y);
         dstPts[dstOff+1] = φ((C - (x*x + y*y)) / nm);
         /*
-         * Note: Synder 14-19 gives  q = (C - ρ²n²/a²)/n  where  ρ = √(x² + (ρ₀ - y)²).
+         * Note: Snyder 14-19 gives  q = (C - ρ²n²/a²)/n  where  ρ = √(x² + (ρ₀ - y)²).
          * But in Apache SIS implementation, ρ₀ has already been subtracted by the matrix before we reach this point.
          * So we can simplify by ρ² = x² + y². Furthermore the matrix also divided x and y by a (the semi-major axis
          * length) before this method, and multiplied by n. so what we have is actually (ρ⋅n/a)² = x² + y².
@@ -318,10 +318,10 @@ public class AlbersEqualArea extends Equ
             final double cosθ = cos(θ);
             final double sinθ = sin(θ);
             final double sinφ = sin(φ);
-            final double ρ = sqrt(C - 2*nm*sinφ);           // Synder 14-3 with radius and division by n omitted.
+            final double ρ = sqrt(C - 2*nm*sinφ);           // Snyder 14-3 with radius and division by n omitted.
             if (dstPts != null) {
-                dstPts[dstOff  ] = ρ * sinθ;                // Synder 14-1
-                dstPts[dstOff+1] = ρ * cosθ;                // Synder 14-2
+                dstPts[dstOff  ] = ρ * sinθ;                // Snyder 14-1
+                dstPts[dstOff+1] = ρ * cosθ;                // Snyder 14-2
             }
             if (!derivate) {
                 return null;
@@ -341,8 +341,8 @@ public class AlbersEqualArea extends Equ
         {
             final double x = srcPts[srcOff];
             final double y = srcPts[srcOff + 1];
-            dstPts[dstOff  ] = atan2(x, y);                         // Part of Synder 14-11
-            dstPts[dstOff+1] = asin((C - (x*x + y*y)) / (nm*2));    // Synder 14-8 modified
+            dstPts[dstOff  ] = atan2(x, y);                         // Part of Snyder 14-11
+            dstPts[dstOff+1] = asin((C - (x*x + y*y)) / (nm*2));    // Snyder 14-8 modified
         }
     }
 }



Mime
View raw message