sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1807629 [1/5] - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/ core/sis-referen...
Date Thu, 07 Sep 2017 16:55:03 GMT
Author: desruisseaux
Date: Thu Sep  7 16:55:01 2017
New Revision: 1807629

URL: http://svn.apache.org/viewvc?rev=1807629&view=rev
Log:
Merge from JDK7 branch.

Added:
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/AngularVelocity.java
      - copied unchanged from r1807628, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngularVelocity.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Salinity.java
      - copied unchanged from r1807628, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Salinity.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/ScaleRateOfChange.java
      - copied unchanged from r1807628, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/ScaleRateOfChange.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractDataSet.java
      - copied, changed from r1807628, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractDataSet.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
      - copied unchanged from r1807628, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalOpenParameterException.java
      - copied unchanged from r1807628, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalOpenParameterException.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
      - copied unchanged from r1807628, sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/ReadOnlyStorageException.java
    sis/trunk/storage/sis-storage/src/test/resources/
      - copied from r1807628, sis/branches/JDK7/storage/sis-storage/src/test/resources/
Removed:
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/FeatureAccess.java
    sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/FeatureAccess.java
Modified:
    sis/trunk/   (props changed)
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/MergerTest.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.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/DefaultOperationMethod.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/NilReason.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/xml/ReferenceResolver.java
    sis/trunk/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames.properties
    sis/trunk/core/sis-utility/src/main/resources/org/apache/sis/measure/UnitNames_fr.properties
    sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
    sis/trunk/ide-project/NetBeans/build.xml
    sis/trunk/pom.xml
    sis/trunk/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
    sis/trunk/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStoreProvider.java
    sis/trunk/storage/sis-gdal/src/main/c/org_apache_sis_storage_gdal_PJ.c
    sis/trunk/storage/sis-gdal/src/main/java/org/apache/sis/storage/gdal/PJ.java
    sis/trunk/storage/sis-gdal/src/main/java/org/apache/sis/storage/gdal/Proj4.java
    sis/trunk/storage/sis-gdal/src/main/java/org/apache/sis/storage/gdal/Proj4Factory.java
    sis/trunk/storage/sis-gdal/src/main/resources/native/darwin/libproj-binding.so
    sis/trunk/storage/sis-gdal/src/main/resources/native/linux/libproj-binding.so
    sis/trunk/storage/sis-gdal/src/test/java/org/apache/sis/storage/gdal/PJTest.java
    sis/trunk/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
    sis/trunk/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStoreProvider.java
    sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
    sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/DocumentedStoreProvider.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/csv/StoreProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataOutput.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.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/internal/storage/wkt/StoreProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedStorageException.java
    sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreMock.java
    sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java
    sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Link.java
    sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Store.java
    sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java
    sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxStreamReader.java
    sis/trunk/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/ReaderTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Sep  7 16:55:01 2017
@@ -1,5 +1,5 @@
 /sis/branches/Android:1430670-1480699
 /sis/branches/JDK6:1394364-1758914
-/sis/branches/JDK7:1394913-1805566
-/sis/branches/JDK8:1584960-1804582
+/sis/branches/JDK7:1394913-1807628
+/sis/branches/JDK8:1584960-1807624
 /sis/branches/JDK9:1773327-1803064

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -47,19 +47,19 @@ import org.apache.sis.internal.jdk8.JDK8
  *   <li>Otherwise if the target value is a collection, then:
  *     <ul>
  *       <li>For each element of the source collection, a corresponding element of the target collection is searched.
- *         A pair of source and target elements is established if the pair meet all of the following conditions:
+ *         A pair of source and target elements is established if the pair meets all of the following conditions:
  *         <ul>
  *           <li>The {@linkplain MetadataStandard#getInterface(Class) standard type} of the source element
  *               is assignable to the type of the target element.</li>
- *           <li>There is no conflict, i.e. no property value that are not collection and not equal
- *               (note: this condition is disabled if {@link #avoidConflicts} is {@code false}).</li>
+ *           <li>There is no conflict, i.e. no property value that are not collection and not equal.
+ *               This condition can be modified by overriding {@link #resolve(Object, ModifiableMetadata)}.</li>
  *         </ul>
  *         If such pair is found, then the merge operation if performed recursively
  *         for that pair of source and target elements.</li>
  *       <li>All other source elements will be added as new elements in the target collection.</li>
  *     </ul>
  *   </li>
- *   <li>Otherwise the {@link #unmerged unmerged(…)} method is invoked.</li>
+ *   <li>Otherwise the {@link #copy(Object, ModifiableMetadata) copy(…)} method is invoked.</li>
  * </ul>
  *
  * @author  Johann Sorel (Geomatys)
@@ -82,14 +82,6 @@ public class Merger {
     protected final Locale locale;
 
     /**
-     * {@code true} for performing a greater effort of avoiding merge conflicts.
-     * The default value is {@code false}, which may cause {@link #unmerged unmerged(…)} to be invoked in
-     * situation where it could have been avoided. Setting this value to {@code true} increase the chances
-     * of merge success at the expense of more computations.
-     */
-    public boolean avoidConflicts;
-
-    /**
      * Creates a new merger.
      *
      * @param  locale  the locale to use for formatting error messages, or {@code null} for the default locale.
@@ -125,15 +117,15 @@ public class Merger {
      *         for example because the source class is a more specialized type than the target class.
      * @throws IllegalArgumentException if this method detects a cross-reference between source and target metadata.
      */
-    public final void merge(final Object source, final ModifiableMetadata target) {
-        if (!merge(source, target, false)) {
+    public final void copy(final Object source, final ModifiableMetadata target) {
+        if (!copy(source, target, false)) {
             throw new InvalidMetadataException(errors().getString(Errors.Keys.IllegalArgumentClass_3, "target",
                     target.getStandard().getInterface(source.getClass()), Classes.getClass(target)));
         }
     }
 
     /**
-     * Implementation of {@link #merge(Object, ModifiableMetadata)} method,
+     * Implementation of {@link #copy(Object, ModifiableMetadata)} method,
      * to be invoked recursively for all child properties to merge.
      *
      * @param  dryRun  {@code true} for executing the merge operation in "dry run" mode instead than performing the
@@ -142,7 +134,8 @@ public class Merger {
      * @return {@code true} if the merge operation is valid, or {@code false} if the given arguments are valid
      *         metadata but the merge operation can nevertheless not be executed because it could cause data lost.
      */
-    private boolean merge(final Object source, final ModifiableMetadata target, final boolean dryRun) {
+    @SuppressWarnings("fallthrough")
+    private boolean copy(final Object source, final ModifiableMetadata target, final boolean dryRun) {
         /*
          * Verify if the given source can be merged with the target. If this is not the case, action
          * taken will depend on the caller: it may either skips the value or throws an exception.
@@ -194,7 +187,7 @@ public class Merger {
                                                : JDK8.putIfAbsentConservative(targetMap, propertyName, sourceValue);
             if (targetValue != null) {
                 if (targetValue instanceof ModifiableMetadata) {
-                    success = merge(sourceValue, (ModifiableMetadata) targetValue, dryRun);
+                    success = copy(sourceValue, (ModifiableMetadata) targetValue, dryRun);
                     if (!success) {
                         /*
                          * This exception may happen if the source is a subclass of the target. This is the converse
@@ -226,17 +219,22 @@ public class Merger {
                     for (final Object element : targetList) {
                         if (element instanceof ModifiableMetadata) {
                             final Iterator<?> it = sourceList.iterator();
-                            while (it.hasNext()) {
+distribute:                 while (it.hasNext()) {
                                 final Object value = it.next();
-                                if (!avoidConflicts || merge(value, (ModifiableMetadata) element, true)) {
-                                    /*
-                                     * If enabled, above 'merge' call verified that the merge can be done, including
-                                     * by recursive checks in all children. The intend is to have a "all or nothing"
-                                     * behavior, before the 'merge' call below starts to modify the values.
-                                     */
-                                    if (merge(value, (ModifiableMetadata) element, false)) {
+                                switch (resolve(value, (ModifiableMetadata) element)) {
+                                //  case SEPARATE: do nothing.
+                                    case MERGE: {
+                                        /*
+                                         * If enabled, copy(…, true) call verified that the merge can be done, including
+                                         * by recursive checks in all children. The intend is to have a "all or nothing"
+                                         * behavior, before the copy(…, false) call below starts to modify the values.
+                                         */
+                                        if (!copy(value, (ModifiableMetadata) element, false)) break;
+                                        // Fall through
+                                    }
+                                    case IGNORE: {
                                         it.remove();
-                                        break;          // Merge at most one source element to each target element.
+                                        break distribute;   // Merge at most one source element to each target element.
                                     }
                                 }
                             }
@@ -266,7 +264,8 @@ public class Merger {
                     success = targetValue.equals(sourceValue);
                     if (!success) {
                         if (dryRun) break;
-                        unmerged(target, propertyName, sourceValue, targetValue);
+                        merge(target, propertyName, sourceValue, targetValue);
+                        success = true;  // If no exception has been thrown by 'merged', assume the conflict solved.
                     }
                 }
             }
@@ -280,19 +279,74 @@ public class Merger {
     }
 
     /**
-     * Invoked when a metadata value can not be merged.
+     * The action to perform when a <var>source</var> metadata element is about to be written in an existing
+     * <var>target</var> element. Many metadata elements defined by ISO 19115 allows multi-occurrence, i.e.
+     * are stored in {@link Collection}. When a value <var>A</var> is about to be added in an existing collection
+     * which already contains values <var>B</var> and <var>C</var>, then different scenarios are possible.
+     *
+     * <p>For <var>A</var> ⟶ {<var>B</var>, <var>C</var>}:</p>
+     * <ul>
+     *   <li>Value <var>A</var> may overwrite some values of <var>B</var>. This action is executed if
+     *       <code>{@linkplain Merger#resolve Merger.resolve}(A, B)</code> returns {@link #MERGE}.</li>
+     *   <li>Value <var>A</var> may overwrite some values of <var>C</var>. This action is executed if
+     *       <code>{@linkplain Merger#resolve Merger.resolve}(A, B)</code> returns {@link #SEPARATE},
+     *       then {@code Merger.resolve(A, C)} returns {@link #MERGE}.</li>
+     *   <li>Value <var>A</var> may be added as a new value after <var>B</var> and <var>C</var>.
+     *       This action is executed if <code>{@linkplain Merger#resolve Merger.resolve}(A, B)</code>
+     *       <strong>and</strong> {@code Merger.resolve(A, C)} return {@link #SEPARATE}.</li>
+     *   <li>Value <var>A</var> may be discarded. This action is executed if
+     *       <code>{@linkplain Merger#resolve Merger.resolve}(A, B)</code>
+     *       <strong>or</strong> {@code Merger.resolve(A, C)} return {@link #IGNORE}.</li>
+     * </ul>
+     *
+     * @see Merger#resolve(Object, ModifiableMetadata)
+     */
+    public enum Resolution {
+        /**
+         * Indicates that <var>source</var> values should be written in <var>target</var> attributes of existing
+         * metadata element. No new metadata object is created. If a value already exists in the target metadata,
+         * then the {@link Merger#merge(ModifiableMetadata, String, Object, Object) merge(…)} method will be invoked.
+         */
+        MERGE,
+
+        /**
+         * Indicates that <var>source</var> values should be written in another metadata element.
+         */
+        SEPARATE,
+
+        /**
+         * Indicates that <var>source</var> values should be discarded.
+         */
+        IGNORE
+    }
+
+    /**
+     * Invoked when a source metadata element is about to be written in an existing target element.
+     * The default implementation returns {@link Resolution#MERGE} if writing in the given target
+     * would only fill holes, without overwriting any existing value. Otherwise this method returns
+     * {@code Resolution#SEPARATE}.
+     *
+     * @param  source  the source metadata to copy.
+     * @param  target  where the source metadata would be copied if this method returns {@link Resolution#MERGE}.
+     * @return {@link Resolution#MERGE} for writing {@code source} into {@code target}, or
+     *         {@link Resolution#SEPARATE} for writing {@code source} in a separated metadata element, or
+     *         {@link Resolution#IGNORE} for discarding {@code source}.
+     */
+    protected Resolution resolve(Object source, ModifiableMetadata target) {
+        return copy(source, target, true) ? Resolution.MERGE : Resolution.SEPARATE;
+    }
+
+    /**
+     * Invoked when {@code Merger} can not merge a metadata value by itself.
      * The default implementation throws an {@link InvalidMetadataException}.
      * Subclasses can override this method if they want to perform a different processing.
      *
-     * <p><b>Tip:</b> to reduce the risks that this method is invoked, consider setting the
-     * {@link #avoidConflicts} flag to {@code true} before invoking {@link #merge merge(…)}.</p>
-     *
      * @param target        the metadata instance in which the value should have been written.
      * @param propertyName  the name of the property to write.
      * @param sourceValue   the value to write.
      * @param targetValue   the value that already exist in the target metadata.
      */
-    protected void unmerged(ModifiableMetadata target, String propertyName, Object sourceValue, Object targetValue) {
+    protected void merge(ModifiableMetadata target, String propertyName, Object sourceValue, Object targetValue) {
         throw new InvalidMetadataException(errors().getString(Errors.Keys.ValueAlreadyDefined_1, name(target, propertyName)));
     }
 }

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -47,8 +47,8 @@ import org.apache.sis.util.collection.Co
  * <p>This class is not thread-safe.
  * In multi-threads environment, each thread should use its own {@code MetadataCopier} instance.</p>
  *
- * <div class="section">Recommended alternative</div>
- * Deep metadata copies are sometime useful when using an existing metadata as a template.
+ * <div class="note"><b>Recommended alternative:</b>
+ * deep metadata copies are sometime useful when using an existing metadata as a template.
  * But the {@link ModifiableMetadata#unmodifiable()} method may provide a better way to use a metadata as a template,
  * as it returns a snapshot and allows the caller to continue to modify the original metadata object and create new
  * snapshots. Example:
@@ -77,6 +77,7 @@ import org.apache.sis.util.collection.Co
  * result of a call to {@link org.apache.sis.metadata.sql.MetadataSource#lookup(Class, String)}) into instances of the
  * public {@link AbstractMetadata} subclasses. But note that shallow copies as provided by the {@code castOrCopy(…)}
  * static methods in each {@code AbstractMetadata} subclass are sometime sufficient.</p>
+ * </div>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8

Modified: sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/MergerTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/MergerTest.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/MergerTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/MergerTest.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -99,8 +99,7 @@ public final strictfp class MergerTest e
         final DefaultMetadata source = createSample1();
         final DefaultMetadata target = createSample2();
         final Merger merger = new Merger(null);
-        merger.avoidConflicts = true;
-        merger.merge(source, target);
+        merger.copy(source, target);
 
         assertSetEquals(Arrays.asList(Locale.JAPANESE, Locale.FRENCH),  target.getLanguages());
         assertSetEquals(Collections.singleton(StandardCharsets.UTF_16), target.getCharacterSets());

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -473,7 +473,7 @@ public class DefaultParameterValue<T> ex
      * {@link URI}, {@link URL}, {@link Path}, {@link File}.
      *
      * @return the reference to a file containing parameter values.
-     * @throws InvalidParameterTypeException if the value is not a reference to a file or an URI.
+     * @throws InvalidParameterTypeException if the value is not a reference to a file or a URI.
      * @throws IllegalStateException if the value is not defined and there is no default value.
      *
      * @see #getValue()

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -549,6 +549,9 @@ public final class CRS extends Static {
      * Finds a mathematical operation that transforms or converts coordinates from the given source to the
      * given target coordinate reference system. If an estimation of the geographic area containing the points
      * to transform is known, it can be specified for helping this method to find a better suited operation.
+     * If no area of interest is specified, then the current default is the widest
+     * {@linkplain AbstractCoordinateOperation#getDomainOfValidity() domain of validity}.
+     * A future Apache SIS version may also take the country of current locale in account.
      *
      * <div class="note"><b>Note:</b>
      * the area of interest is just one aspect that may affect the coordinate operation.
@@ -662,6 +665,10 @@ public final class CRS extends Static {
      * associated with the given operation. If more than one geographic bounding box is found, then this method
      * computes their {@linkplain DefaultGeographicBoundingBox#add(GeographicBoundingBox) union}.
      *
+     * <p><b>Fallback:</b> if the given operation does not declare explicitely a domain of validity, then this
+     * method computes the intersection of the domain of validity declared by source and target CRS. If no CRS
+     * declare a domain of validity, then this method returns {@code null}.</p>
+     *
      * @param  operation  the coordinate operation for which to get the domain of validity, or {@code null}.
      * @return the geographic area where the operation is valid, or {@code null} if unspecified.
      *
@@ -673,7 +680,15 @@ public final class CRS extends Static {
      * @since 0.7
      */
     public static GeographicBoundingBox getGeographicBoundingBox(final CoordinateOperation operation) {
-        return (operation != null) ? Extents.getGeographicBoundingBox(operation.getDomainOfValidity()) : null;
+        if (operation == null) {
+            return null;
+        }
+        GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(operation.getDomainOfValidity());
+        if (bbox == null) {
+            bbox = Extents.intersection(getGeographicBoundingBox(operation.getSourceCRS()),
+                                        getGeographicBoundingBox(operation.getTargetCRS()));
+        }
+        return bbox;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -58,7 +58,7 @@ import org.apache.sis.util.Utilities;
  * is thread-safe. If concurrent searches are desired, then a new instance should be created for each thread.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see GeodeticAuthorityFactory#newIdentifiedObjectFinder()
  * @see IdentifiedObjects#newFinder(String)
@@ -302,7 +302,7 @@ public class IdentifiedObjectFinder {
             final AuthorityFactoryProxy<?> previous = proxy;
             proxy = AuthorityFactoryProxy.getInstance(object.getClass());
             try {
-                if (!ignoreIdentifiers) {
+                if (!ignoreIdentifiers && !ignoreAxes) {
                     /*
                      * First check if one of the identifiers can be used to find directly an identified object.
                      * Verify that the object that we found is actually equal to given one; we do not blindly

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -57,6 +57,9 @@ import org.apache.sis.internal.jdk8.Pred
  * @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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -258,19 +258,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;
     }
 
     /**
@@ -367,56 +375,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 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.
                      */
-                    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;
         }
         /*
@@ -444,7 +454,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;

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Thu Sep  7 16:55:01 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/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalEqualArea.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -228,26 +228,26 @@ public class CylindricalEqualArea extend
     }
 
     /**
-     * Converts a list of coordinate point ordinal values.
-     *
-     * <div class="note"><b>Note:</b>
-     * We override the super-class method only as an optimization in the special case where the target coordinates
-     * are written at the same locations than the source coordinates. In such case, we can take advantage of the
-     * fact that the λ values are not modified by the normalized Cylindrical Equal Area projection.</div>
+     * Converts a list of coordinate points. This method performs the same calculation than above
+     * {@link #transform(double[], int, double[], int, boolean)} method, but is overridden for efficiency.
      *
      * @throws TransformException if a point can not be converted.
      */
     @Override
     public void transform(final double[] srcPts, int srcOff,
-                          final double[] dstPts, int dstOff, int numPts)
-            throws TransformException
+                          final double[] dstPts, int dstOff, int numPts) throws TransformException
     {
-        if (srcPts != dstPts || srcOff != dstOff) {
+        if (srcPts != dstPts || srcOff != dstOff || getClass() != CylindricalEqualArea.class) {
             super.transform(srcPts, srcOff, dstPts, dstOff, numPts);
         } else {
+            /*
+             * Override the super-class method only as an optimization in the special case where the target coordinates
+             * are written at the same locations than the source coordinates. In such case, we can take advantage of
+             * the fact that the λ values are not modified by the normalized Cylindrical Equal Area projection.
+             */
             dstOff--;
             while (--numPts >= 0) {
-                final double φ = dstPts[dstOff += 2];                   // Same as srcPts[srcOff + 1].
+                final double φ = dstPts[dstOff += DIMENSION];           // Same as srcPts[srcOff + 1].
                 dstPts[dstOff] = qm_ellipsoid(sin(φ));                  // Part of Synder equation (10-15)
             }
         }
@@ -299,7 +299,7 @@ public class CylindricalEqualArea extend
          */
         Spherical(final CylindricalEqualArea other) {
             super(other);
-            context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).convertAfter(1, 2, null);
+            context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION).convertAfter(1, DIMENSION, null);
         }
 
         /**
@@ -322,8 +322,8 @@ public class CylindricalEqualArea extend
          * {@inheritDoc}
          *
          * <div class="note"><b>Note:</b>
-         * This method must be overridden because the {@link Mercator} class overrides the {@link NormalizedProjection}
-         * default implementation.</div>
+         * This method must be overridden because the {@link CylindricalEqualArea} class
+         * overrides the {@link NormalizedProjection} default implementation.</div>
          */
         @Override
         public void transform(final double[] srcPts, int srcOff,
@@ -335,7 +335,7 @@ public class CylindricalEqualArea extend
             } else {
                 dstOff--;
                 while (--numPts >= 0) {
-                    final double φ = dstPts[dstOff += 2];           // Same as srcPts[srcOff + 1].
+                    final double φ = dstPts[dstOff += DIMENSION];       // Same as srcPts[srcOff + 1].
                     dstPts[dstOff] = sin(φ);
                 }
             }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/EqualAreaProjection.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -54,13 +54,15 @@ abstract class EqualAreaProjection exten
      *
      *
      * <p><b>BENCHMARK AND ANALYSIS:</b>
-     * as of July 2016, benchmarking shows no benefit in using trigonometric identities for {@code EqualAreaProjection}
-     * (contrarily to {@link ConformalProjection} where we did measured a benefit). This may be because in this class,
+     * as of July 2016, benchmarking shows small benefit in using trigonometric identities for {@code EqualAreaProjection}
+     * (contrarily to {@link ConformalProjection} where we measured a greater benefit). This may be because in this class,
      * the series expansion is unconditionally followed by iterative method in order to reach the centimetric precision.
      * We observe that the original series expansion allows convergence in only one iteration, while the formulas using
-     * trigonometric identifies often requires two iterations. Consequently we disallow those modifications for now.</p>
+     * trigonometric identifies often requires two iterations.</p>
+     *
+     * @todo Redo the benchmark with JDK9, since {@code sin} seems much faster.
      */
-    private static final boolean ALLOW_TRIGONOMETRIC_IDENTITIES = false;
+    private static final boolean ALLOW_TRIGONOMETRIC_IDENTITIES = true;
 
     /**
      * Coefficients in the series expansion of the inverse projection,

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -58,7 +58,7 @@ import static org.apache.sis.internal.ut
  * @author  André Gosselin (MPO)
  * @author  Rueben Schulz (UBC)
  * @author  Rémi Maréchal (Geomatys)
- * @version 0.6
+ * @version 0.8
  * @since   0.6
  * @module
  */

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -32,6 +32,7 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.util.Workaround;
@@ -69,7 +70,7 @@ import static org.apache.sis.internal.ut
  * @author  Rueben Schulz (UBC)
  * @author  Simon Reynard (Geomatys)
  * @author  Rémi Maréchal (Geomatys)
- * @version 0.6
+ * @version 0.8
  *
  * @see TransverseMercator
  *
@@ -372,12 +373,8 @@ public class Mercator extends ConformalP
     }
 
     /**
-     * Converts a list of coordinate point ordinal values.
-     *
-     * <div class="note"><b>Note:</b>
-     * We override the super-class method only as an optimization in the special case where the target coordinates
-     * are written at the same locations than the source coordinates. In such case, we can take advantage of the
-     * fact that the λ values are not modified by the normalized Mercator projection.</div>
+     * Converts a list of coordinate points. This method performs the same calculation than above
+     * {@link #transform(double[], int, double[], int, boolean)} method, but is overridden for efficiency.
      *
      * @throws TransformException if a point can not be converted.
      */
@@ -386,12 +383,17 @@ public class Mercator extends ConformalP
                           final double[] dstPts, int dstOff, int numPts)
             throws TransformException
     {
-        if (srcPts != dstPts || srcOff != dstOff) {
+        if (srcPts != dstPts || srcOff != dstOff || getClass() != Mercator.class) {
             super.transform(srcPts, srcOff, dstPts, dstOff, numPts);
         } else {
+            /*
+             * Override the super-class method only as an optimization in the special case where the target coordinates
+             * are written at the same locations than the source coordinates. In such case, we can take advantage of
+             * the fact that the λ values are not modified by the normalized Mercator projection.
+             */
             dstOff--;
             while (--numPts >= 0) {
-                final double φ = dstPts[dstOff += 2];                           // Same as srcPts[srcOff + 1].
+                final double φ = dstPts[dstOff += DIMENSION];                   // Same as srcPts[srcOff + 1].
                 if (φ != 0) {
                     /*
                      * See the javadoc of the Spherical inner class for a note
@@ -521,7 +523,7 @@ public class Mercator extends ConformalP
             } else {
                 dstOff--;
                 while (--numPts >= 0) {
-                    final double φ = dstPts[dstOff += 2];                       // Same as srcPts[srcOff + 1].
+                    final double φ = dstPts[dstOff += DIMENSION];               // Same as srcPts[srcOff + 1].
                     if (φ != 0) {
                         // See class javadoc for a note about explicit check for poles.
                         final double a = abs(φ);
@@ -552,4 +554,27 @@ public class Mercator extends ConformalP
             dstPts[dstOff+1] = PI/2 - 2*atan(exp(-y));              // Part of Snyder (7-4);
         }
     }
+
+    /**
+     * Invoked when {@link #tryConcatenate(boolean, MathTransform, MathTransformFactory)} detected a
+     * (inverse) → (affine) → (this) transforms sequence.
+     */
+    @Override
+    final MathTransform tryConcatenate(boolean projectedSpace, Matrix affine, MathTransformFactory factory)
+            throws FactoryException
+    {
+        /*
+         * Verify that the latitude row is an identity conversion except for the sign which is allowed to change
+         * (but no scale and no translation are allowed).  Ignore the longitude row because it just pass through
+         * this Mercator projection with no impact on any calculation.
+         */
+        if (affine.getElement(1,0) == 0 && affine.getElement(1, DIMENSION) == 0 && Math.abs(affine.getElement(1,1)) == 1) {
+            if (factory != null) {
+                return factory.createAffineTransform(affine);
+            } else {
+                return MathTransforms.linear(affine);
+            }
+        }
+        return super.tryConcatenate(projectedSpace, affine, factory);
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.referencing.operation.projection;
 
+import java.util.List;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Objects;
@@ -31,6 +32,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.opengis.util.FactoryException;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.CharSequences;
@@ -40,6 +42,8 @@ import org.apache.sis.parameter.Paramete
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
 import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
@@ -48,9 +52,11 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.metadata.ReferencingServices;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.Logging;
 
 import static java.lang.Math.*;
 
@@ -117,7 +123,7 @@ import org.opengis.referencing.Reference
  * @author  André Gosselin (MPO)
  * @author  Rueben Schulz (UBC)
  * @author  Rémi Maréchal (Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see ContextualParameters
  * @see <a href="http://mathworld.wolfram.com/MapProjection.html">Map projections on MathWorld</a>
@@ -132,6 +138,15 @@ public abstract class NormalizedProjecti
     private static final long serialVersionUID = -4010883312927645853L;
 
     /**
+     * Number of input and output dimensions of all {@code NormalizedProjection}.
+     * We define this constant for clarity only; its value shall not be modified.
+     *
+     * @see #getSourceDimensions()
+     * @see #getTargetDimensions()
+     */
+    static final int DIMENSION = 2;
+
+    /**
      * Maximum difference allowed when comparing longitudes or latitudes in radians.
      * The current value takes the system-wide angular tolerance value (equivalent to
      * about 1 cm on Earth) converted to radians.
@@ -710,7 +725,7 @@ public abstract class NormalizedProjecti
      * Inverse of a normalized map projection.
      *
      * @author  Martin Desruisseaux (Geomatys)
-     * @version 0.6
+     * @version 0.8
      * @since   0.6
      * @module
      */
@@ -741,13 +756,131 @@ public abstract class NormalizedProjecti
                 return null;
             } else {
                 if (dstPts == null) {
-                    dstPts = new double[2];
+                    dstPts = new double[DIMENSION];
                     dstOff = 0;
                 }
                 inverseTransform(srcPts, srcOff, dstPts, dstOff);
                 return Matrices.inverse(NormalizedProjection.this.transform(dstPts, dstOff, null, 0, true));
             }
         }
+
+        /**
+         * Inverse transforms an arbitrary amount of coordinates. This method optimizes the
+         * case where conversions can be applied by a loop with indices in increasing order.
+         */
+        @Override
+        public void transform(final double[] srcPts, int srcOff,
+                              final double[] dstPts, int dstOff, int numPts) throws TransformException
+        {
+            if (srcPts == dstPts && srcOff < dstOff) {
+                super.transform(srcPts, srcOff, dstPts, dstOff, numPts);
+            } else while (--numPts >= 0) {
+                inverseTransform(srcPts, srcOff, dstPts, dstOff);
+                srcOff += DIMENSION;
+                dstOff += DIMENSION;
+            }
+        }
+
+        /**
+         * Concatenates or pre-concatenates in an optimized way this projection with the given transform, if possible.
+         * If no optimization is available, returns {@code null}.
+         */
+        @Override
+        protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
+                final MathTransformFactory factory) throws FactoryException
+        {
+            final Matrix m = getMiddleMatrix(this, other, applyOtherFirst);
+            if (m != null) {
+                /*
+                 * 'projectedSpace' values:
+                 *   - false if applyOtherFirst == false since we have (inverse projection) → (affine) → (projection).
+                 *   - true  if applyOtherFirst == true  since we have (projection) → (affine) → (inverse projection).
+                 */
+                return NormalizedProjection.this.tryConcatenate(applyOtherFirst, m, factory);
+            }
+            return super.tryConcatenate(applyOtherFirst, other, factory);
+        }
+    }
+
+    /**
+     * Concatenates or pre-concatenates in an optimized way this projection with the given transform, if possible.
+     * If transforms are concatenated in an (inverse projection) → (affine) → (projection) sequence where the
+     * (projection) and (inverse projection) steps are the {@linkplain #inverse() inverse} of each other,
+     * then in some particular case the sequence can be replaced by a single affine transform.
+     * If no such simplification is possible, this method returns {@code null}.
+     *
+     * @return the simplified (usually affine) transform, or {@code null} if no such optimization is available.
+     * @throws FactoryException if an error occurred while combining the transforms.
+     *
+     * @since 0.8
+     */
+    @Override
+    protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
+            final MathTransformFactory factory) throws FactoryException
+    {
+        final Matrix m = getMiddleMatrix(this, other, applyOtherFirst);
+        if (m != null) {
+            /*
+             * 'projectedSpace' values:
+             *   - false if applyOtherFirst == true  since we have (inverse projection) → (affine) → (projection).
+             *   - true  if applyOtherFirst == false since we have (projection) → (affine) → (inverse projection).
+             */
+            return tryConcatenate(!applyOtherFirst, m, factory);
+        }
+        return super.tryConcatenate(applyOtherFirst, other, factory);
+    }
+
+    /**
+     * Returns the concatenation of (inverse) → (affine) → (this) transforms, or {@code null} if none.
+     * The affine transform is applied in the geographic space with angular values in radians.
+     *
+     * <p>Above description is for the usual case where {@code projectedSpace} is {@code false}.
+     * If {@code true} (should be unusual), then the affine transform is rather applied in the
+     * projected space and the sequence to concatenate is (this) → (affine) → (inverse).</p>
+     *
+     * <p>Default implementation returns {@code null}. Subclasses should override if applicable.</p>
+     *
+     * @param  projectedSpace  {@code true} if affine transform is applied in projected instead than geographic space.
+     * @param  affine          the affine transform in the middle of (inverse) → (affine) → (this) transform sequence.
+     * @param  factory         the factory to use for creating new transform, or {@code null}.
+     * @return the optimized concatenation, or {@code null} if none.
+     */
+    MathTransform tryConcatenate(boolean projectedSpace, Matrix affine, MathTransformFactory factory)
+            throws FactoryException
+    {
+        return null;
+    }
+
+    /**
+     * If a sequence of 3 transforms are (inverse projection) → (affine) → (projection) where
+     * the (projection) and (inverse projection) steps are the inverse of each other, returns
+     * the matrix of the affine transform step. Otherwise returns {@code null}. This method
+     * accepts also (projection) → (affine) → (inverse projection) sequence, but such sequences
+     * should be much more unusual.
+     *
+     * @param  projection       either {@link NormalizedProjection} or {@link Inverse}.
+     * @param  other            the arbitrary transforms to be concatenated with the given projection.
+     * @param  applyOtherFirst  whether {@code other} is concatenated before {@code projection} or the converse.
+     * @return the 3×3 matrix of the affine transform step, or {@code null} if none.
+     */
+    static Matrix getMiddleMatrix(final AbstractMathTransform projection, final MathTransform other,
+            final boolean applyOtherFirst)
+    {
+        final List<MathTransform> steps = MathTransforms.getSteps(other);
+        if (steps.size() == 2) try {
+            final int oi = applyOtherFirst ? 0 : 1;
+            if (projection.equals(steps.get(oi).inverse(), ComparisonMode.IGNORE_METADATA)) {
+                final Matrix m = MathTransforms.getMatrix(steps.get(oi ^ 1));
+                if (Matrices.isAffine(m) && m.getNumRow() == DIMENSION+1 && m.getNumCol() == DIMENSION+1) {
+                    return m;
+                }
+            }
+        } catch (NoninvertibleTransformException e) {
+            Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
+                    (projection instanceof NormalizedProjection) ? NormalizedProjection.class : projection.getClass(),
+                    "tryConcatenate", e);
+        }
+        return null;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -263,11 +263,14 @@ public class ObliqueStereographic extend
         /*
          * Convert the geodetic coordinates (φ,λ) to conformal coordinates (χ,Λ) before to apply the
          * actual stereographic projection.  The geodetic and conformal coordinates will be the same
-         * if the ellipsoid is already a sphere.
+         * if the ellipsoid is already a sphere.  The original formulas were:
+         *
+         *    χ = asin((w - 1) / (w + 1))
+         *
+         * But since the projection needs only sin(χ) and cos(χ), we avoid the costly asin(…) function.
          */
-        final double χ    = asin((w - 1) / (w + 1));
-        final double cosχ = cos(χ);
-        final double sinχ = sin(χ);
+        final double sinχ = (w - 1) / (w + 1);
+        final double cosχ = sqrt(1 - sinχ*sinχ);
         /*
          * The conformal longitude is  Λ = n⋅(λ - λ₀) + Λ₀  where λ is the geodetic longitude.
          * But in Apache SIS implementation, the multiplication by  n  has been merged in the

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -357,8 +357,8 @@ public abstract class AbstractMathTransf
             throws TransformException;
 
     /**
-     * Transforms a list of coordinate point ordinal values. This method is provided for efficiently
-     * transforming many points. The supplied array of ordinal values will contain packed ordinal values.
+     * Transforms a list of coordinate points. This method is provided for efficiently transforming many points.
+     * The supplied array of coordinate values will contain packed coordinate values.
      *
      * <div class="note"><b>Example:</b> if the source dimension is 3, then the ordinates will be packed in this order:
      * (<var>x₀</var>,<var>y₀</var>,<var>z₀</var>,
@@ -443,8 +443,8 @@ public abstract class AbstractMathTransf
                     throw failure;
                 }
                 /*
-                 * Otherwise fills the ordinate values to NaN and count the number of exceptions,
-                 * so we known when to give up if there is too much of them. The first exception
+                 * Otherwise set the ordinate values to NaN and count the number of exceptions,
+                 * so we know when to give up if there is too much of them. The first exception
                  * will be propagated at the end of this method.
                  */
                 Arrays.fill(dstPts, dstOff, dstOff + Math.abs(dstInc), Double.NaN);
@@ -477,7 +477,7 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Transforms a list of coordinate point ordinal values. The default implementation delegates
+     * Transforms a list of coordinate points. The default implementation delegates
      * to {@link #transform(double[], int, double[], int, int)} using a temporary array of doubles.
      *
      * <div class="note"><b>Implementation note:</b> see {@link IterationStrategy} javadoc for a method skeleton.</div>
@@ -607,7 +607,7 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Transforms a list of coordinate point ordinal values. The default implementation delegates
+     * Transforms a list of coordinate points. The default implementation delegates
      * to {@link #transform(double[], int, double[], int, int)} using a temporary array of doubles.
      *
      * @param  srcPts  the array containing the source point coordinates.
@@ -670,7 +670,7 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Transforms a list of coordinate point ordinal values. The default implementation delegates
+     * Transforms a list of coordinate points. The default implementation delegates
      * to {@link #transform(double[], int, double[], int, int)} using a temporary array of doubles
      * if necessary.
      *
@@ -792,7 +792,7 @@ public abstract class AbstractMathTransf
     }
 
     /**
-     * Concatenates or pre-concatenates in an optimized way this math transform with the given one.
+     * Concatenates or pre-concatenates in an optimized way this math transform with the given one, if possible.
      * A new math transform is created to perform the combined transformation.
      * The {@code applyOtherFirst} value determines the transformation order as bellow:
      *
@@ -807,7 +807,7 @@ public abstract class AbstractMathTransf
      *
      * If no special optimization is available for the combined transform, then this method returns {@code null}.
      * In the later case, the concatenation will be prepared by {@link DefaultMathTransformFactory} using a generic
-     * {@link ConcatenatedTransform}.
+     * implementation.
      *
      * <p>The default implementation always returns {@code null}. This method is ought to be overridden
      * by subclasses capable of concatenating some combination of transforms in a special way.</p>
@@ -816,7 +816,7 @@ public abstract class AbstractMathTransf
      *                          {@code false} if the transformation order is {@code this} followed by {@code other}.
      * @param  other            the other math transform to (pre-)concatenate with this transform.
      * @param  factory          the factory which is (indirectly) invoking this method, or {@code null} if none.
-     * @return the combined math transform, or {@code null} if no optimized combined transform is available.
+     * @return the math transforms combined in an optimized way, or {@code null} if no such optimization is available.
      * @throws FactoryException if an error occurred while combining the transforms.
      *
      * @see DefaultMathTransformFactory#createConcatenatedTransform(MathTransform, MathTransform)

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -57,12 +57,16 @@ import static org.junit.Assume.*;
  * <ul>
  *   <li><cite>"NTF (Paris) to WGS 84 (1)"</cite> operation (EPSG:8094), which implies a longitude rotation
  *       followed by a geocentric translation in the geographic domain.</li>
+ *   <li><cite>"Martinique 1938 to RGAF09 (1)"</cite> operation (EPSG:5491), which implies a datum shift
+ *       that does not go through WGS84. Furthermore since the EPSG database defines (λ,φ) axis order in
+ *       addition to the usual (φ,λ) order for the target CRS, this tests allows us to verify we can find
+ *       this operation despite different axis order.</li>
  * </ul>
  *
  * The operations are tested with various axis order and dimension in source and target CRS.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -94,6 +98,11 @@ public final strictfp class CoordinateOp
     private static WKTFormat parser;
 
     /**
+     * The EPSG authority factory for CRS objects. Can be used as an alternative to {@link #parser}.
+     */
+    private final CRSAuthorityFactory crsFactory;
+
+    /**
      * The instance on which to execute the tests.
      */
     private final CoordinateOperationRegistry registry;
@@ -104,7 +113,7 @@ public final strictfp class CoordinateOp
      * @throws FactoryException if an error occurred while creating the factory to be tested.
      */
     public CoordinateOperationRegistryTest() throws FactoryException {
-        final CRSAuthorityFactory crsFactory = CRS.getAuthorityFactory("EPSG");
+        crsFactory = CRS.getAuthorityFactory("EPSG");
         assumeTrue(crsFactory instanceof CoordinateOperationAuthorityFactory);
         registry = new CoordinateOperationRegistry((CoordinateOperationAuthorityFactory) crsFactory, factory, null);
     }
@@ -365,4 +374,39 @@ public final strictfp class CoordinateOp
             assertFalse("EPSG identifier not allowed for modified objects.", "EPSG".equalsIgnoreCase(id.getCodeSpace()));
         }
     }
+
+    /**
+     * Tests <cite>"Martinique 1938 to RGAF09 (1)"</cite> operation with a target CRS fixed to EPSG:7086
+     * instead of EPSG:5489. Both are <cite>"RGAF09"</cite>, but the former use (longitude, latitude) axis
+     * order instead than the usual (latitude, longitude) order. The source CRS stay fixed to EPSG:4625.
+     *
+     * @throws FactoryException if an error occurred while creating a CRS or operation.
+     */
+    @Test
+    public void testFindDespiteDifferentAxisOrder() throws FactoryException {
+        CoordinateReferenceSystem sourceCRS = crsFactory.createGeographicCRS("EPSG:4625");
+        CoordinateReferenceSystem targetCRS = crsFactory.createGeographicCRS("EPSG:5489");
+        CoordinateOperation operation = registry.createOperation(sourceCRS, targetCRS);
+        assertEpsgNameAndIdentifierEqual("Martinique 1938 to RGAF09 (1)", 5491, operation);
+        /*
+         * Above was only a verification using the source and target CRS expected by EPSG dataset.
+         * Now the interesting test: use a target CRS with different axis order.
+         */
+        targetCRS = crsFactory.createGeographicCRS("EPSG:7086");
+        operation = registry.createOperation(sourceCRS, targetCRS);
+        assertEpsgNameWithoutIdentifierEqual("Martinique 1938 to RGAF09 (1)", operation);
+        final ParameterValueGroup p = ((SingleOperation) operation).getParameterValues();
+        /*
+         * Values below are copied from EPSG geodetic dataset 9.1. They may need
+         * to be adjusted if a future version of EPSG dataset modify those values.
+         */
+        assertEquals("X-axis translation", 127.744,  p.parameter("X-axis translation").doubleValue(), STRICT);
+        assertEquals("Y-axis translation", 547.069,  p.parameter("Y-axis translation").doubleValue(), STRICT);
+        assertEquals("Z-axis translation", 118.359,  p.parameter("Z-axis translation").doubleValue(), STRICT);
+        assertEquals("X-axis rotation",     -3.1116, p.parameter("X-axis rotation")   .doubleValue(), STRICT);
+        assertEquals("Y-axis rotation",      4.9509, p.parameter("Y-axis rotation")   .doubleValue(), STRICT);
+        assertEquals("Z-axis rotation",     -0.8837, p.parameter("Z-axis rotation")   .doubleValue(), STRICT);
+        assertEquals("Scale difference",    14.1012, p.parameter("Scale difference")  .doubleValue(), STRICT);
+        assertEquals("linearAccuracy",       0.1,    CRS.getLinearAccuracy(operation),                STRICT);
+    }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/UnitAdapter.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -66,7 +66,7 @@ public class UnitAdapter extends XmlAdap
     }
 
     /**
-     * A variant of {@link UnitAdapter} which marshal units as an URN for Coordinate System (CS) axes.
+     * A variant of {@link UnitAdapter} which marshal units as a URN for Coordinate System (CS) axes.
      * Example: {@code "urn:ogc:def:uom:EPSG::9001"}.
      *
      * The difference between coordinate system axis and other uses (prime meridian, etc.) is in the choice of EPSG

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gml/CodeListUID.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -32,7 +32,7 @@ import org.apache.sis.util.iso.Types;
  */
 public final class CodeListUID {
     /**
-     * The code space of the {@link #value} as an URI, or {@code null}.
+     * The code space of the {@link #value} as a URI, or {@code null}.
      */
     @XmlAttribute
     String codeSpace;

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -557,7 +557,7 @@ loop:   for (int p=0; ; p++) {
 
     /**
      * Returns a string representation of this URI. If the URI were originally a GML's URL, then this method formats
-     * the URI in the {@code "http://www.opengis.net/gml/srs/"} namespace. Otherwise the URI were originally an URL,
+     * the URI in the {@code "http://www.opengis.net/gml/srs/"} namespace. Otherwise the URI were originally a URL,
      * then this method formats the URI in the {@code "http://www.opengis.net/"} namespace.
      * Otherwise this method formats the URI as a URN.
      *

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java?rev=1807629&r1=1807628&r2=1807629&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java [UTF-8] Thu Sep  7 16:55:01 2017
@@ -121,22 +121,34 @@ final class UnitRegistry implements Syst
      */
     static <Q extends Quantity<Q>> SystemUnit<Q> init(final SystemUnit<Q> unit) {
         assert !Units.initialized : unit;        // This assertion happens during Units initialization, but it is okay.
-        boolean existed;
-        existed  = HARD_CODED.put(unit.dimension,   unit) != null;
-        existed |= HARD_CODED.put(unit.quantity,    unit) != null;
-        existed |= HARD_CODED.put(unit.getSymbol(), unit) != null;
+        int existed;
+        existed  = (HARD_CODED.put(unit.dimension,   unit) == null) ? 0 : 1;
+        existed |= (HARD_CODED.put(unit.quantity,    unit) == null) ? 0 : 2;
+        existed |= (HARD_CODED.put(unit.getSymbol(), unit) == null) ? 0 : 4;
         if (unit.epsg != 0) {
-            existed |= HARD_CODED.put(unit.epsg, unit) != null;
+            existed |= (HARD_CODED.put(unit.epsg, unit) == null) ? 0 : 8;
         }
         /*
          * Key collision on dimension and quantity tolerated for dimensionless units only, with an
          * an exception for "candela" because "lumen" is candela divided by a dimensionless unit.
+         * Another exception is "Hz" because it come after rad/s, which has the same dimension.
          */
-        assert !existed || unit.dimension.isDimensionless() || "cd".equals(unit.getSymbol()) : unit;
+        assert filter(existed, unit) == 0 : unit;
         return unit;
     }
 
     /**
+     * Clears the {@code existed} bits for the cases where we allow dimension or quantity type collisions.
+     * This method is invoked for assertions only.
+     */
+    private static int filter(int existed, final SystemUnit<?> unit) {
+        final String s = unit.getSymbol();
+        if (unit.dimension.isDimensionless()) existed &= ~(1 | 2);      // Accepts dimension and quantity collisions.
+        if (s.equals("cd") || s.equals("Hz")) existed &= ~(1    );      // Accepts dimension collisions only;
+        return s.isEmpty() ? 0 : existed;
+    }
+
+    /**
      * Invoked by {@link Units} static class initializer for registering SI conventional units.
      * This method shall be invoked in a single thread by the {@code Units} class initializer only.
      */



Mime
View raw message