sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1737072 [2/5] - in /sis/branches/JDK7: ./ application/sis-console/src/main/artifact/conf/ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/io/wkt/ core/sis-referencing/src/mai...
Date Tue, 29 Mar 2016 22:51:41 GMT
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -20,6 +20,8 @@ import java.util.Map;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
 import javax.measure.unit.Unit;
@@ -38,8 +40,10 @@ import org.opengis.referencing.crs.CRSAu
 import org.opengis.referencing.cs.TimeCS;
 import org.opengis.referencing.cs.VerticalCS;
 import org.opengis.referencing.cs.CartesianCS;
+import org.opengis.referencing.cs.SphericalCS;
 import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.AxisDirection;
+import org.opengis.referencing.cs.CSAuthorityFactory;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.datum.PrimeMeridian;
@@ -65,8 +69,10 @@ import org.apache.sis.internal.system.Sy
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Exceptions;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Units;
@@ -279,6 +285,11 @@ public enum CommonCRS {
     static final CommonCRS DEFAULT = WGS84;
 
     /**
+     * Properties to exclude when using an other object as a template.
+     */
+    private static final String[] EXCLUDE = new String[] {IdentifiedObject.IDENTIFIERS_KEY};
+
+    /**
      * The EPSG code of the two-dimensional geographic CRS.
      */
     final short geographic;
@@ -337,13 +348,20 @@ public enum CommonCRS {
     private transient volatile GeographicCRS cachedGeo3D;
 
     /**
-     * The geocentric CRS, created when first needed.
+     * The geocentric CRS using Cartesian coordinate system, created when first needed.
      *
      * @see #geocentric()
      */
     private transient volatile GeocentricCRS cachedGeocentric;
 
     /**
+     * The geocentric CRS using spherical coordinate system, created when first needed.
+     *
+     * @see #spherical()
+     */
+    private transient volatile GeocentricCRS cachedSpherical;
+
+    /**
      * The Universal Transverse Mercator projections, created when first needed.
      * All accesses to this map shall be synchronized on {@code cachedUTM}.
      *
@@ -505,7 +523,7 @@ public enum CommonCRS {
                         cached = object = factory.createGeographicCRS(String.valueOf(geographic));
                         return object;
                     } catch (FactoryException e) {
-                        failure(this, "geographic", e);
+                        failure(this, "geographic", e, geographic);
                     }
                     /*
                      * All constants defined in this enumeration use the same coordinate system, EPSG:6422.
@@ -561,7 +579,7 @@ public enum CommonCRS {
                             cachedGeo3D = object = factory.createGeographicCRS(String.valueOf(geo3D));
                             return object;
                         } catch (FactoryException e) {
-                            failure(this, "geographic3D", e);
+                            failure(this, "geographic3D", e, geo3D);
                         }
                     }
                     /*
@@ -619,7 +637,7 @@ public enum CommonCRS {
                             cachedGeocentric = object = factory.createGeocentricCRS(String.valueOf(geocentric));
                             return object;
                         } catch (FactoryException e) {
-                            failure(this, "geocentric", e);
+                            failure(this, "geocentric", e, geocentric);
                         }
                     }
                     /*
@@ -644,6 +662,56 @@ public enum CommonCRS {
     }
 
     /**
+     * Returns the geocentric CRS using a spherical coordinate system. Axes are:
+     *
+     * <ol>
+     *   <li>Spherical latitude in degrees oriented toward {@linkplain AxisDirection#NORTH north}.</li>
+     *   <li>Spherical longitude in degrees oriented toward {@linkplain AxisDirection#EAST east}.</li>
+     *   <li>Geocentric radius in metres oriented toward {@linkplain AxisDirection#UP up}.</li>
+     * </ol>
+     *
+     * @return The geocentric CRS associated to this enum.
+     *
+     * @see DefaultGeocentricCRS
+     *
+     * @since 0.7
+     */
+    public GeocentricCRS spherical() {
+        GeocentricCRS object = cachedSpherical;
+        if (object == null) {
+            synchronized (this) {
+                object = cachedSpherical;
+                if (object == null) {
+                    /*
+                     * All constants defined in this enumeration use the same coordinate system, EPSG:6404.
+                     * We will arbitrarily create this CS only for the most frequently created CRS,
+                     * and share that CS instance for all other constants.
+                     */
+                    SphericalCS cs = null;
+                    if (this == DEFAULT) {
+                        final CSAuthorityFactory factory = csFactory();
+                        if (factory != null) try {
+                            cs = factory.createSphericalCS("6404");
+                        } catch (FactoryException e) {
+                            failure(this, "spherical", e, (short) 6404);
+                        }
+                        if (cs == null) {
+                            cs = (SphericalCS) StandardDefinitions.createCoordinateSystem((short) 6404);
+                        }
+                    } else {
+                        cs = (SphericalCS) DEFAULT.spherical().getCoordinateSystem();
+                    }
+                    // Use same name and datum than the geographic CRS.
+                    final GeographicCRS base = geographic();
+                    object = new DefaultGeocentricCRS(IdentifiedObjects.getProperties(base, EXCLUDE), base.getDatum(), cs);
+                    cachedSpherical = object;
+                }
+            }
+        }
+        return object;
+    }
+
+    /**
      * Returns the geodetic datum associated to this geodetic object.
      * The following table summarizes the datums known to this class,
      * together with an enumeration value that can be used for fetching that datum:
@@ -675,7 +743,7 @@ public enum CommonCRS {
                         cached = object = factory.createGeodeticDatum(String.valueOf(datum));
                         return object;
                     } catch (FactoryException e) {
-                        failure(this, "datum", e);
+                        failure(this, "datum", e, datum);
                     }
                     object = StandardDefinitions.createGeodeticDatum(datum, ellipsoid(), primeMeridian());
                     cached = object;
@@ -719,7 +787,7 @@ public enum CommonCRS {
                             cached = object = factory.createEllipsoid(String.valueOf(ellipsoid));
                             return object;
                         } catch (FactoryException e) {
-                            failure(this, "ellipsoid", e);
+                            failure(this, "ellipsoid", e, ellipsoid);
                         }
                         object = StandardDefinitions.createEllipsoid(ellipsoid);
                     }
@@ -759,7 +827,7 @@ public enum CommonCRS {
                             cached = object = factory.createPrimeMeridian(StandardDefinitions.GREENWICH);
                             return object;
                         } catch (FactoryException e) {
-                            failure(this, "primeMeridian", e);
+                            failure(this, "primeMeridian", e, (short) 8901);
                         }
                         object = StandardDefinitions.primeMeridian();
                     }
@@ -881,7 +949,7 @@ public enum CommonCRS {
                     if (factory != null) try {
                         return factory.createProjectedCRS(String.valueOf(code));
                     } catch (FactoryException e) {
-                        failure(this, "UTM", e);
+                        failure(this, "UTM", e, code);
                     }
                 }
             }
@@ -1131,12 +1199,12 @@ public enum CommonCRS {
                                 cached = object = factory.createVerticalCRS(String.valueOf(crs));
                                 return object;
                             } catch (FactoryException e) {
-                                failure(this, "crs", e);
+                                failure(this, "crs", e, crs);
                             }
                             object = StandardDefinitions.createVerticalCRS(crs, datum());
                         } else {
                             final VerticalCS cs = cs();
-                            object = new DefaultVerticalCRS(IdentifiedObjects.getProperties(cs), datum(), cs);
+                            object = new DefaultVerticalCRS(IdentifiedObjects.getProperties(cs, EXCLUDE), datum(), cs);
                         }
                         cached = object;
                     }
@@ -1196,7 +1264,7 @@ public enum CommonCRS {
                                 cached = object = factory.createVerticalDatum(String.valueOf(datum));
                                 return object;
                             } catch (FactoryException e) {
-                                failure(this, "datum", e);
+                                failure(this, "datum", e, datum);
                             }
                             object = StandardDefinitions.createVerticalDatum(datum);
                         } else {
@@ -1374,7 +1442,7 @@ public enum CommonCRS {
                     object = crs(cached);
                     if (object == null) {
                         final TemporalDatum datum = datum();
-                        object = new DefaultTemporalCRS(IdentifiedObjects.getProperties(datum), datum, cs());
+                        object = new DefaultTemporalCRS(IdentifiedObjects.getProperties(datum, EXCLUDE), datum, cs());
                         cached = object;
                     }
                 }
@@ -1402,8 +1470,8 @@ public enum CommonCRS {
                 case UNIX: {
                     // Share the NamedIdentifier created for Java time.
                     final TimeCS share = JAVA.crs().getCoordinateSystem();
-                    cs   = IdentifiedObjects.getProperties(share);
-                    axis = IdentifiedObjects.getProperties(share.getAxis(0));
+                    cs   = IdentifiedObjects.getProperties(share, EXCLUDE);
+                    axis = IdentifiedObjects.getProperties(share.getAxis(0), EXCLUDE);
                     break;
                 }
                 case JAVA: {
@@ -1503,7 +1571,7 @@ public enum CommonCRS {
      * Returns the same properties than the given object, except for the identifier which is set to the given code.
      */
     private static Map<String,?> properties(final IdentifiedObject template, final short code) {
-        final Map<String,Object> properties = new HashMap<>(IdentifiedObjects.getProperties(template));
+        final Map<String,Object> properties = new HashMap<>(IdentifiedObjects.getProperties(template, EXCLUDE));
         properties.put(GeographicCRS.IDENTIFIERS_KEY, new NamedIdentifier(Citations.EPSG, String.valueOf(code)));
         return properties;
     }
@@ -1523,6 +1591,20 @@ public enum CommonCRS {
     }
 
     /**
+     * Returns the EPSG factory to use for creating coordinate systems, or {@code null} if none.
+     * If this method returns {@code null}, then the caller will silently fallback on hard-coded values.
+     */
+    static CSAuthorityFactory csFactory() {
+        if (!EPSGFactoryFallback.FORCE_HARDCODED) {
+            final AuthorityFactory factory = AuthorityFactories.EPSG();
+            if (!(factory instanceof EPSGFactoryFallback)) {
+                return (CSAuthorityFactory) factory;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the EPSG factory to use for creating datum, ellipsoids and prime meridians, or {@code null} if none.
      * If this method returns {@code null}, then the caller will silently fallback on hard-coded values.
      */
@@ -1540,10 +1622,15 @@ public enum CommonCRS {
      * Invoked when a factory failed to create an object.
      * After invoking this method, the caller will fallback on hard-coded values.
      */
-    static void failure(final Object caller, final String method, final FactoryException e) {
-        if (e instanceof UnavailableFactoryException) {
-            AuthorityFactories.failure((UnavailableFactoryException) e);
+    static void failure(final Object caller, final String method, final FactoryException e, final int code) {
+        String message = Errors.format(Errors.Keys.CanNotInstantiate_1, "EPSG:" + code);
+        message = Exceptions.formatChainedMessages(null, message, e);
+        final LogRecord record = new LogRecord(Level.WARNING, message);
+        if (!(e instanceof UnavailableFactoryException) || !AuthorityFactories.failure((UnavailableFactoryException) e)) {
+            // Append the stack trace only if the exception is the the one we expect when the factory is not available.
+            record.setThrown(e);
         }
-        Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY), caller.getClass(), method, e);
+        record.setLoggerName(Loggers.CRS_FACTORY);
+        Logging.log(caller.getClass(), method, record);
     }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -90,6 +90,10 @@ public final class IdentifiedObjects ext
      *       <td>{@link CoordinateOperation#getOperationVersion()}</td></tr>
      *   <tr><td>{@value org.opengis.referencing.operation.CoordinateOperation#COORDINATE_OPERATION_ACCURACY_KEY}</td>
      *       <td>{@link CoordinateOperation#getCoordinateOperationAccuracy()}</td></tr>
+     *   <tr><td>{@value org.opengis.referencing.operation.OperationMethod#FORMULA_KEY}</td>
+     *       <td>{@link org.opengis.referencing.operation.OperationMethod#getFormula()}</td></tr>
+     *   <tr><td>{@value org.apache.sis.referencing.AbstractIdentifiedObject#DEPRECATED_KEY}</td>
+     *       <td>{@link AbstractIdentifiedObject#isDeprecated()}</td></tr>
      * </table>
      *
      * <div class="note"><b>Note:</b>

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -26,8 +26,10 @@ import org.opengis.referencing.datum.Dat
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.metadata.quality.PositionalAccuracy;
 import org.apache.sis.internal.util.AbstractMap;
+import org.apache.sis.util.Deprecable;
 
 
 /**
@@ -40,7 +42,7 @@ import org.apache.sis.internal.util.Abst
  *
  * @author  Martin Desruisseaux (IRD)
  * @since   0.4
- * @version 0.4
+ * @version 0.7
  * @module
  */
 final class Properties extends AbstractMap<String,Object> implements Serializable {
@@ -55,14 +57,16 @@ final class Properties extends AbstractM
      * of {@link #INDICES}.
      */
     private static final String[] KEYS = {
-        /*[0]*/ IdentifiedObject    .NAME_KEY,
-        /*[1]*/ IdentifiedObject    .IDENTIFIERS_KEY,
-        /*[2]*/ IdentifiedObject    .ALIAS_KEY,
-        /*[3]*/ IdentifiedObject    .REMARKS_KEY,
-        /*[4]*/ CoordinateOperation .SCOPE_KEY,              // same in Datum and ReferenceSystem
-        /*[5]*/ CoordinateOperation .DOMAIN_OF_VALIDITY_KEY, // same in Datum and ReferenceSystem
-        /*[6]*/ CoordinateOperation .OPERATION_VERSION_KEY,
-        /*[7]*/ CoordinateOperation .COORDINATE_OPERATION_ACCURACY_KEY
+        /*[0]*/ IdentifiedObject        .NAME_KEY,
+        /*[1]*/ IdentifiedObject        .IDENTIFIERS_KEY,
+        /*[2]*/ IdentifiedObject        .ALIAS_KEY,
+        /*[3]*/ IdentifiedObject        .REMARKS_KEY,
+        /*[4]*/ CoordinateOperation     .SCOPE_KEY,                     // same in Datum and ReferenceSystem
+        /*[5]*/ CoordinateOperation     .DOMAIN_OF_VALIDITY_KEY,        // same in Datum and ReferenceSystem
+        /*[6]*/ CoordinateOperation     .OPERATION_VERSION_KEY,
+        /*[7]*/ CoordinateOperation     .COORDINATE_OPERATION_ACCURACY_KEY,
+        /*[8]*/ OperationMethod         .FORMULA_KEY,
+        /*[9]*/ AbstractIdentifiedObject.DEPRECATED_KEY
 
         /*
          * The current implementation does not look for minimum and maximum values in ParameterDescriptor
@@ -182,6 +186,18 @@ final class Properties extends AbstractM
                     }
                     break;
                 }
+                case 8: {   // FORMULA_KEY
+                    if (object instanceof OperationMethod) {
+                        return ((OperationMethod) object).getFormula();
+                    }
+                    break;
+                }
+                case 9: {   // DEPRECATED_KEY
+                    if (object instanceof Deprecable) {
+                        return ((Deprecable) object).isDeprecated();
+                    }
+                    break;
+                }
                 default: throw new AssertionError(key);
             }
         }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -51,6 +51,7 @@ import org.apache.sis.referencing.datum.
 import org.apache.sis.referencing.datum.DefaultVerticalDatum;
 import org.apache.sis.referencing.cs.DefaultVerticalCS;
 import org.apache.sis.referencing.cs.DefaultCartesianCS;
+import org.apache.sis.referencing.cs.DefaultSphericalCS;
 import org.apache.sis.referencing.cs.DefaultEllipsoidalCS;
 import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
@@ -311,12 +312,14 @@ final class StandardDefinitions {
     @SuppressWarnings("fallthrough")
     static CoordinateSystem createCoordinateSystem(final short code) {
         final String name;
-        final int dim;  // Number of dimension.
-        short axisCode; // Code of first axis + dim (or code after the last axis).
+        final int dim;                  // Number of dimension.
+        short axisCode;                 // Code of first axis + dim (or code after the last axis).
         boolean isCartesian = false;
+        boolean isSpherical = false;
         switch (code) {
             case 6422: name = "Ellipsoidal 2D"; dim = 2; axisCode = 108; break;
             case 6423: name = "Ellipsoidal 3D"; dim = 3; axisCode = 111; break;
+            case 6404: name = "Spherical";      dim = 3; axisCode =  63; isSpherical = true; break;
             case 6500: name = "Earth centred";  dim = 3; axisCode = 118; isCartesian = true; break;
             case 4400: name = "Cartesian 2D";   dim = 2; axisCode =   3; isCartesian = true; break;
             default:   throw new AssertionError(code);
@@ -336,6 +339,8 @@ final class StandardDefinitions {
             } else {
                 return new DefaultCartesianCS(properties, xAxis, yAxis);
             }
+        } else if (isSpherical) {
+            return new DefaultSphericalCS(properties, xAxis, yAxis, zAxis);
         } else {
             if (zAxis != null) {
                 return new DefaultEllipsoidalCS(properties, xAxis, yAxis, zAxis);
@@ -369,6 +374,29 @@ final class StandardDefinitions {
                        unit = SI.METRE;
                        dir  = AxisDirection.NORTH;
                        break;
+            case 60:   name = "Spherical latitude";
+                       abrv = "φ′";                         // See HardCodedAxes.SPHERICAL_LATITUDE in tests.
+                       unit = NonSI.DEGREE_ANGLE;
+                       dir  = AxisDirection.NORTH;
+                       min  = Latitude.MIN_VALUE;
+                       max  = Latitude.MAX_VALUE;
+                       rm   = RangeMeaning.EXACT;
+                       break;
+            case 61:   name = "Spherical longitude";
+                       abrv = "θ";                          // See HardCodedAxes.SPHERICAL_LONGITUDE in tests.
+                       unit = NonSI.DEGREE_ANGLE;
+                       dir  = AxisDirection.EAST;
+                       min  = Longitude.MIN_VALUE;
+                       max  = Longitude.MAX_VALUE;
+                       rm   = RangeMeaning.WRAPAROUND;
+                       break;
+            case 62:   name = "Geocentric radius";
+                       abrv = "R";                          // See HardCodedAxes.GEOCENTRIC_RADIUS in tests.
+                       unit = SI.METRE;
+                       dir  = AxisDirection.UP;
+                       rm   = RangeMeaning.EXACT;
+                       min  = 0;
+                       break;
             case 108:  // Used in Ellipsoidal 3D.
             case 106:  name = AxisNames.GEODETIC_LATITUDE;
                        abrv = "φ";

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -108,7 +108,7 @@ import org.apache.sis.measure.Units;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see AbstractCS#forConvention(AxesConvention)
@@ -153,7 +153,13 @@ public enum AxesConvention implements Ax
         }
 
         @Override
+        @Deprecated
         public Unit<?> getUnitReplacement(Unit<?> unit) {
+            return getUnitReplacement(null, unit);
+        }
+
+        @Override
+        public Unit<?> getUnitReplacement(final CoordinateSystemAxis axis, Unit<?> unit) {
             if (Units.isLinear(unit)) {
                 unit = SI.METRE;
             } else if (Units.isAngular(unit)) {
@@ -164,18 +170,35 @@ public enum AxesConvention implements Ax
             return unit;
         }
 
-        /*
-         * Same policy than AxesConvention.CONVENTIONALLY_ORIENTATED.
-         */
         @Override
+        @Deprecated
         public AxisDirection getDirectionReplacement(final AxisDirection direction) {
-            return AxisDirections.isIntercardinal(direction) ? direction : AxisDirections.absolute(direction);
+            return getDirectionReplacement(null, direction);
+        }
+
+        @Override
+        public AxisDirection getDirectionReplacement(final CoordinateSystemAxis axis, final AxisDirection direction) {
+            /*
+             * For now we do not touch to inter-cardinal directions (e.g. "North-East")
+             * because it is not clear which normalization policy would match common usage.
+             */
+            if (!AxisDirections.isIntercardinal(direction)) {
+                /*
+                 * Change the direction only if the axis allows negative values.
+                 * If the axis accepts only positive values, then the direction
+                 * is considered non-invertible.
+                 */
+                if (axis == null || axis.getMinimumValue() < 0) {
+                    return AxisDirections.absolute(direction);
+                }
+            }
+            return direction;
         }
     },
 
     /**
      * Axes are oriented toward conventional directions and ordered for a {@linkplain #RIGHT_HANDED right-handed}
-     * coordinate system. Ranges of ordinate values and units of measurement are unchanged.
+     * coordinate system. Units of measurement are unchanged.
      *
      * <p>More specifically, directions opposites to the following ones are replaced by their "forward" counterpart
      * (e.g. {@code SOUTH} → {@code NORTH}):</p>
@@ -227,17 +250,25 @@ public enum AxesConvention implements Ax
         }
 
         @Override
+        @Deprecated
         public Unit<?> getUnitReplacement(final Unit<?> unit) {
             return unit;
         }
 
-        /*
-         * For now we do not touch to inter-cardinal directions (e.g. "North-East")
-         * because it is not clear which normalization policy would match common usage.
-         */
         @Override
+        public Unit<?> getUnitReplacement(final CoordinateSystemAxis axis, final Unit<?> unit) {
+            return unit;
+        }
+
+        @Override
+        @Deprecated
         public AxisDirection getDirectionReplacement(final AxisDirection direction) {
-            return AxisDirections.isIntercardinal(direction) ? direction : AxisDirections.absolute(direction);
+            return getDirectionReplacement(null, direction);
+        }
+
+        @Override
+        public AxisDirection getDirectionReplacement(CoordinateSystemAxis axis, AxisDirection direction) {
+            return NORMALIZED.getDirectionReplacement(axis, direction);
         }
     },
 
@@ -275,14 +306,26 @@ public enum AxesConvention implements Ax
         }
 
         @Override
+        @Deprecated
         public Unit<?> getUnitReplacement(final Unit<?> unit) {
             return unit;
         }
 
         @Override
+        public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, final Unit<?> unit) {
+            return unit;
+        }
+
+        @Override
+        @Deprecated
         public AxisDirection getDirectionReplacement(final AxisDirection direction) {
             return direction;
         }
+
+        @Override
+        public AxisDirection getDirectionReplacement(CoordinateSystemAxis axis, final AxisDirection direction) {
+            return direction;
+        }
     },
 
     /**
@@ -315,13 +358,25 @@ public enum AxesConvention implements Ax
         }
 
         @Override
+        @Deprecated
         public Unit<?> getUnitReplacement(final Unit<?> unit) {
             return unit;
         }
 
         @Override
+        public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, final Unit<?> unit) {
+            return unit;
+        }
+
+        @Override
+        @Deprecated
         public AxisDirection getDirectionReplacement(final AxisDirection direction) {
             return direction;
         }
+
+        @Override
+        public AxisDirection getDirectionReplacement(CoordinateSystemAxis axis, final AxisDirection direction) {
+            return direction;
+        }
     }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxisFilter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxisFilter.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxisFilter.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxisFilter.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -42,7 +42,7 @@ import javax.measure.unit.Unit;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see CoordinateSystems#replaceAxes(CoordinateSystem, AxisFilter)
@@ -65,7 +65,7 @@ public interface AxisFilter {
      *
      * {@preformat java
      *     &#64;Override
-     *     public getDirectionReplacement(AxisDirection direction) {
+     *     public getDirectionReplacement(CoordinateSystemAxis axis, AxisDirection direction) {
      *         if (direction == AxisDirection.DOWN) {
      *             direction = AxisDirection.UP;
      *         }
@@ -74,9 +74,21 @@ public interface AxisFilter {
      * }
      * </div>
      *
+     * @param  axis The axis for which to change axis direction, if desired.
+     * @param  direction The original axis direction.
+     * @return The new axis direction, or {@code direction} if there is no change.
+     *
+     * @since 0.7
+     */
+    AxisDirection getDirectionReplacement(CoordinateSystemAxis axis, AxisDirection direction);
+
+    /**
+     * @deprecated Use {@link #getDirectionReplacement(CoordinateSystemAxis, AxisDirection)} instead.
+     *
      * @param  direction The original axis direction.
      * @return The new axis direction, or {@code direction} if there is no change.
      */
+    @Deprecated
     AxisDirection getDirectionReplacement(AxisDirection direction);
 
     /**
@@ -88,7 +100,7 @@ public interface AxisFilter {
      *
      * {@preformat java
      *     &#64;Override
-     *     public Unit<?> getUnitReplacement(Unit<?> unit) {
+     *     public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
      *         if (Units.isAngular(unit)) {
      *             unit = NonSI.DEGREE_ANGLE;
      *         }
@@ -97,8 +109,20 @@ public interface AxisFilter {
      * }
      * </div>
      *
+     * @param  axis The axis for which to change unit, if desired.
+     * @param  unit The original axis unit.
+     * @return The new axis unit, or {@code unit} if there is no change.
+     *
+     * @since 0.7
+     */
+    Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit);
+
+    /**
+     * @deprecated Use {@link #getUnitReplacement(CoordinateSystemAxis, Unit)} instead.
+     *
      * @param  unit The original axis unit.
      * @return The new axis unit, or {@code unit} if there is no change.
      */
+    @Deprecated
     Unit<?> getUnitReplacement(Unit<?> unit);
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -349,7 +349,7 @@ public final class CoordinateSystems ext
      *     CoordinateSystem cs = ...;
      *     cs = CoordinateSystems.replaceAxes(cs, new AxisFilter() {
      *         &#64;Override
-     *         public Unit<?> getUnitReplacement(Unit<?> unit) {
+     *         public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
      *             if (Units.isAngular(unit)) {
      *                 unit = NonSI.DEGREE_ANGLE;
      *             }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -323,7 +323,7 @@ public class DefaultCoordinateSystemAxis
                 } else if (dir.equals(AxisDirection.EAST)) {
                     min = fromDegrees.convert(Longitude.MIN_VALUE);
                     max = fromDegrees.convert(Longitude.MAX_VALUE);
-                    rm  = RangeMeaning.WRAPAROUND; // 180°E wraps to 180°W
+                    rm  = RangeMeaning.WRAPAROUND;                                  // 180°E wraps to 180°W
                 }
                 if (min > max) {
                     final double t = min;
@@ -697,10 +697,10 @@ public class DefaultCoordinateSystemAxis
      */
     private static CoordinateSystem getEnclosingCS(final Formatter formatter) {
         final FormattableObject e = formatter.getEnclosingElement(1);
-        if (e instanceof CoordinateReferenceSystem) {   // This is what we expect in standard WKT.
+        if (e instanceof CoordinateReferenceSystem) {           // This is what we expect in standard WKT.
             return ((CoordinateReferenceSystem) e).getCoordinateSystem();
         }
-        if (e instanceof CoordinateSystem) {    // Not standard WKT, but conceptually the right thing.
+        if (e instanceof CoordinateSystem) {                    // Not standard WKT, but conceptually the right thing.
             return (CoordinateSystem) e;
         }
         return null;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCylindricalCS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCylindricalCS.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCylindricalCS.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCylindricalCS.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -165,7 +165,7 @@ public class DefaultCylindricalCS extend
         if (!AxisDirections.isSpatialOrUserDefined(direction, false)) {
             return INVALID_DIRECTION;
         }
-        if (!Units.isLinear(unit)) {
+        if (!Units.isAngular(unit) && !Units.isLinear(unit)) {
             return INVALID_UNIT;
         }
         return VALID;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultLinearCS.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultLinearCS.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultLinearCS.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultLinearCS.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -157,7 +157,7 @@ public class DefaultLinearCS extends Abs
         if (!AxisDirections.isSpatialOrUserDefined(direction, false)) {
             return INVALID_DIRECTION;
         }
-        if (!Units.isLinear(unit)) {
+        if (!Units.isLinear(unit) && !Unit.ONE.equals(unit)) {
             return INVALID_UNIT;
         }
         return VALID;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -26,11 +26,16 @@ import org.opengis.referencing.cs.RangeM
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.cs.EllipsoidalCS;
+import org.opengis.referencing.cs.SphericalCS;
+import org.opengis.referencing.cs.CylindricalCS;
+import org.opengis.referencing.cs.PolarCS;
 import org.apache.sis.internal.metadata.AxisDirections;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArraysExt;
+import org.apache.sis.measure.Units;
 
 import static java.util.Collections.singletonMap;
 import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
@@ -50,12 +55,39 @@ import static org.apache.sis.internal.re
  * The main usage for this class is to reorder the axes in some fixed order like
  * (<var>x</var>, <var>y</var>, <var>z</var>) or (<var>longitude</var>, <var>latitude</var>).
  *
+ * <p>The normalization performed by this class shall be compatible with axis order expected by various
+ * {@code MathTransform} implementations in the {@link org.apache.sis.referencing.operation.transform} package.
+ * In particular:</p>
+ *
+ * <ul>
+ *   <li>{@code EllipsoidToCentricTransform} input:<ol>
+ *     <li>Geodetic longitude (λ) in degrees</li>
+ *     <li>Geodetic latitude (φ) in degrees</li>
+ *     <li>Height in units of semi-axes</li>
+ *   </ol></li>
+ *   <li>{@code SphericalToCartesian} input:<ol>
+ *     <li>Spherical longitude in degrees</li>
+ *     <li>Spherical latitude in degrees</li>
+ *     <li>Spherical radius (r) in any units</li>
+ *   </ol></li>
+ *   <li>{@code CartesianToSpherical} input:<ol>
+ *     <li>X in units of the above radius</li>
+ *     <li>Y in units of the above radius</li>
+ *     <li>Z in units of the above radius</li>
+ *   </ol></li>
+ *   <li>{@code CylindricalToCartesian} input:<ol>
+ *     <li>Radius (r) in any units</li>
+ *     <li>Angle (θ) in degrees</li>
+ *     <li>Height (z) in any units</li>
+ *   </ol></li>
+ * </ul>
+ *
  * <p>This class implements {@link Comparable} for opportunist reasons.
  * This should be considered as an implementation details.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.5
+ * @version 0.7
  * @module
  */
 final class Normalizer implements Comparable<Normalizer> {
@@ -67,6 +99,30 @@ final class Normalizer implements Compar
     };
 
     /**
+     * Number of bits by which to shift the {@link AxisDirection#ordinal()} value in order to make room for
+     * inserting intermediate values between them. A shift of 2 make room for {@literal 1 << 2} intermediate
+     * values. Those intermediate values are declared in the {@link #ORDER} map.
+     *
+     * @see #order(AxisDirection)
+     */
+    private static final int SHIFT = 2;
+
+    /**
+     * Custom code list values to handle as if the where defined between two GeoAPI values.
+     *
+     * @see #order(AxisDirection)
+     */
+    private static final Map<AxisDirection,Integer> ORDER = new HashMap<>();
+    static {
+        final Map<AxisDirection,Integer> m = ORDER;
+        // Get ordinal of last compass direction defined by GeoAPI. We will continue on the horizontal plane.
+        final int horizontal = (AxisDirection.NORTH.ordinal() + (AxisDirections.COMPASS_COUNT - 1)) << SHIFT;
+        m.put(AxisDirections.AWAY_FROM,         horizontal + 1);
+        m.put(AxisDirections.COUNTER_CLOCKWISE, horizontal + 2);
+        m.put(AxisDirections.CLOCKWISE,         horizontal + 3);
+    }
+
+    /**
      * The axis to be compared by {@link #compareTo(Normalizer)}.
      */
     private final CoordinateSystemAxis axis;
@@ -78,53 +134,78 @@ final class Normalizer implements Compar
     private final DirectionAlongMeridian meridian;
 
     /**
+     * Angular units order relative to other units.
+     * A value of -1 means that angular units should be first.
+     * A value of +1 means than angular units should be last.
+     * A value of 0 means to not use this criterion.
+     */
+    private final int unitOrder;
+
+    /**
      * For internal usage by {@link #sort(CoordinateSystemAxis[])} only.
      */
-    private Normalizer(final CoordinateSystemAxis axis) {
+    private Normalizer(final CoordinateSystemAxis axis, final int angularUnitOrder) {
         this.axis = axis;
+        unitOrder = Units.isAngular(axis.getUnit()) ? angularUnitOrder : 0;
         final AxisDirection dir = axis.getDirection();
         meridian = AxisDirections.isUserDefined(dir) ? DirectionAlongMeridian.parse(dir) : null;
     }
 
     /**
+     * Returns the order of the given axis direction.
+     */
+    private static int order(final AxisDirection dir) {
+        final Integer p = ORDER.get(dir);
+        return (p != null) ? p : (dir.ordinal() << SHIFT);
+    }
+
+    /**
      * Compares two axis for an order that try to favor right-handed coordinate systems.
      * Compass directions like North and East are first. Vertical directions like Up or Down are next.
      */
     @Override
     public int compareTo(final Normalizer that) {
-        final AxisDirection d1 = this.axis.getDirection();
-        final AxisDirection d2 = that.axis.getDirection();
-        final int compass = AxisDirections.angleForCompass(d2, d1);
-        if (compass != Integer.MIN_VALUE) {
-            return compass;
-        }
-        if (meridian != null) {
-            if (that.meridian != null) {
-                return meridian.compareTo(that.meridian);
+        int d = unitOrder - that.unitOrder;
+        if (d == 0) {
+            final AxisDirection d1 = this.axis.getDirection();
+            final AxisDirection d2 = that.axis.getDirection();
+            d = AxisDirections.angleForCompass(d2, d1);
+            if (d == Integer.MIN_VALUE) {
+                if (meridian != null) {
+                    if (that.meridian != null) {
+                        d = meridian.compareTo(that.meridian);
+                    } else {
+                        d = -1;
+                    }
+                } else if (that.meridian != null) {
+                    d = +1;
+                } else {
+                    d = order(d1) - order(d2);
+                }
             }
-            return -1;
-        } else if (that.meridian != null) {
-            return +1;
         }
-        return d1.ordinal() - d2.ordinal();
+        return d;
     }
 
     /**
      * Sorts the specified axis in an attempt to create a right-handed system.
      * The sorting is performed in place. This method returns {@code true} if
      * at least one axis moved as result of this method call.
+     *
+     * @param axes The axes to sort.
+     * @param angularUnitOrder -1 for sorting angular units first, +1 for sorting them last, or 0 if neutral.
      */
-    static boolean sort(final CoordinateSystemAxis[] axis) {
-        final Normalizer[] wrappers = new Normalizer[axis.length];
-        for (int i=0; i<axis.length; i++) {
-            wrappers[i] = new Normalizer(axis[i]);
+    static boolean sort(final CoordinateSystemAxis[] axes, final int angularUnitOrder) {
+        final Normalizer[] wrappers = new Normalizer[axes.length];
+        for (int i=0; i<axes.length; i++) {
+            wrappers[i] = new Normalizer(axes[i], angularUnitOrder);
         }
         Arrays.sort(wrappers);
         boolean changed = false;
-        for (int i=0; i<axis.length; i++) {
+        for (int i=0; i<axes.length; i++) {
             final CoordinateSystemAxis a = wrappers[i].axis;
-            changed |= (axis[i] != a);
-            axis[i] = a;
+            changed |= (axes[i] != a);
+            axes[i] = a;
         }
         return changed;
     }
@@ -140,8 +221,8 @@ final class Normalizer implements Compar
     static CoordinateSystemAxis normalize(final CoordinateSystemAxis axis, final AxisFilter changes) {
         final Unit<?>       unit      = axis.getUnit();
         final AxisDirection direction = axis.getDirection();
-        final Unit<?>       newUnit   = changes.getUnitReplacement(unit);
-        final AxisDirection newDir    = changes.getDirectionReplacement(direction);
+        final Unit<?>       newUnit   = changes.getUnitReplacement(axis, unit);
+        final AxisDirection newDir    = changes.getDirectionReplacement(axis, direction);
         /*
          * Reuse some properties (name, remarks, etc.) from the existing axis. If the direction changed,
          * then the axis name may need change too (e.g. "Westing" → "Easting"). The new axis name may be
@@ -153,22 +234,8 @@ final class Normalizer implements Compar
             return axis;
         }
         final String abbreviation = axis.getAbbreviation();
-        String newAbbr = abbreviation;
-        if (!sameDirection) {
-            if (AxisDirections.isCompass(direction)) {
-                if (CharSequences.isAcronymForWords(abbreviation, direction.name())) {
-                    if (newDir.equals(AxisDirection.EAST)) {
-                        newAbbr = "E";
-                    } else if (newDir.equals(AxisDirection.NORTH)) {
-                        newAbbr = "N";
-                    }
-                }
-            } else if (newDir.equals(AxisDirection.UP)) {
-                newAbbr = "z";
-            } else if (newDir.equals(AxisDirection.FUTURE)) {
-                newAbbr = "t";
-            }
-        }
+        final String newAbbr = sameDirection ? abbreviation :
+                AxisDirections.suggestAbbreviation(axis.getName().getCode(), newDir, newUnit);
         final Map<String,Object> properties = new HashMap<>();
         if (newAbbr.equals(abbreviation)) {
             properties.putAll(IdentifiedObjects.getProperties(axis, EXCLUDES));
@@ -176,18 +243,30 @@ final class Normalizer implements Compar
             properties.put(NAME_KEY, UNNAMED);
         }
         /*
-         * Converts the axis range and build the new axis.
+         * Convert the axis range and build the new axis. The axis range will be converted only if
+         * the axis direction is the same or the opposite, otherwise we do not know what should be
+         * the new values. In the particular case of opposite axis direction, we need to reverse the
+         * sign of minimum and maximum values.
          */
-        final UnitConverter c;
-        try {
-            c = unit.getConverterToAny(newUnit);
-        } catch (ConversionException e) {
-            // Use IllegalStateException because the public API is an AbstractCS member method.
-            throw new IllegalStateException(Errors.format(Errors.Keys.IllegalUnitFor_2, "axis", unit), e);
-        }
-        properties.put(DefaultCoordinateSystemAxis.MINIMUM_VALUE_KEY, c.convert(axis.getMinimumValue()));
-        properties.put(DefaultCoordinateSystemAxis.MAXIMUM_VALUE_KEY, c.convert(axis.getMaximumValue()));
-        properties.put(DefaultCoordinateSystemAxis.RANGE_MEANING_KEY, axis.getRangeMeaning());
+        if (sameDirection || newDir.equals(AxisDirections.opposite(direction))) {
+            final UnitConverter c;
+            try {
+                c = unit.getConverterToAny(newUnit);
+            } catch (ConversionException e) {
+                // Use IllegalStateException because the public API is an AbstractCS member method.
+                throw new IllegalStateException(Errors.format(Errors.Keys.IllegalUnitFor_2, "axis", unit), e);
+            }
+            double minimum = c.convert(axis.getMinimumValue());
+            double maximum = c.convert(axis.getMaximumValue());
+            if (!sameDirection) {
+                final double tmp = minimum;
+                minimum = -maximum;
+                maximum = -tmp;
+            }
+            properties.put(DefaultCoordinateSystemAxis.MINIMUM_VALUE_KEY, minimum);
+            properties.put(DefaultCoordinateSystemAxis.MAXIMUM_VALUE_KEY, maximum);
+            properties.put(DefaultCoordinateSystemAxis.RANGE_MEANING_KEY, axis.getRangeMeaning());
+        }
         return new DefaultCoordinateSystemAxis(properties, newAbbr, newDir, newUnit);
     }
 
@@ -221,7 +300,26 @@ final class Normalizer implements Compar
          * If nothing changed, return the given Coordinate System as-is.
          */
         if (reorder) {
-            changed |= sort(axes);
+            int angularUnitOrder = 0;
+            if  (cs instanceof EllipsoidalCS || cs instanceof SphericalCS) angularUnitOrder = -1;      // (λ,φ,h) order
+            else if (cs instanceof CylindricalCS || cs instanceof PolarCS) angularUnitOrder = +1;      // (r,θ) order
+            changed |= sort(axes, angularUnitOrder);
+            if (angularUnitOrder == 1) {                            // Cylindrical or polar
+                /*
+                 * Change (r,z,θ) to (r,θ,z) order in CylindricalCS. The check on unit of
+                 * measurements should be always true, but we verify as a paranoiac check.
+                 */
+                if (axes.length == 3 && isLengthAndAngle(axes, 1)) {
+                    ArraysExt.swap(axes, 1, 2);
+                }
+                /*
+                 * If we were not allowed to normalize the axis direction, we may have a
+                 * left-handed coordinate system here. If so, make it right-handed.
+                 */
+                if (AxisDirections.CLOCKWISE.equals(axes[1].getDirection()) && isLengthAndAngle(axes, 0)) {
+                    ArraysExt.swap(axes, 0, 1);
+                }
+            }
         }
         if (!changed && n == dimension) {
             return null;
@@ -236,6 +334,14 @@ final class Normalizer implements Compar
     }
 
     /**
+     * Returns {@code true} if the units of measurement at the given position is a linear unit,
+     * followed by an angular unit on the next axis.
+     */
+    private static boolean isLengthAndAngle(final CoordinateSystemAxis[] axes, final int p) {
+        return Units.isLinear(axes[p].getUnit()) && Units.isAngular(axes[p+1].getUnit());
+    }
+
+    /**
      * Returns a coordinate system with the same axes than the given CS, except that the wrapround axes
      * are shifted to a range of positive values. This method can be used in order to shift between the
      * [-180 … +180]° and [0 … 360]° ranges of longitude values.

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -21,11 +21,13 @@ import java.util.Arrays;
 import java.io.Serializable;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.referencing.datum.GeodeticDatum;
+import org.opengis.referencing.datum.PrimeMeridian;
 import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.referencing.operation.matrix.Matrix4;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.io.wkt.FormattableObject;
 import org.apache.sis.io.wkt.Formatter;
+import org.apache.sis.util.Utilities;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.metadata.WKTKeywords;
@@ -123,7 +125,7 @@ import java.util.Objects;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.6
+ * @version 0.7
  * @module
  *
  * @see DefaultGeodeticDatum#getBursaWolfParameters()
@@ -226,15 +228,37 @@ public class BursaWolfParameters extends
 
     /**
      * Verifies parameters validity after initialization of {@link DefaultGeodeticDatum}.
-     */
-    void verify() {
+     * This method requires that the prime meridian of the target datum is either the same
+     * than the enclosing {@code GeodeticDatum}, or Greenwich. We put this restriction for
+     * avoiding ambiguity about whether the longitude rotation should be applied before or
+     * after the datum shift.
+     *
+     * <p>If the target prime meridian is Greenwich, then SIS will assume that the datum shift
+     * needs to be applied in a coordinate system having Greenwich as the prime meridian.</p>
+     *
+     * <p><b>Maintenance note:</b>
+     * if the above policy regarding prime meridians is modified, then some {@code createOperationStep(…)} method
+     * implementations in {@link org.apache.sis.referencing.operation.CoordinateOperationInference} may need to be
+     * revisited. See especially the methods creating a transformation between a pair of {@code GeocentricCRS} or
+     * between a pair of {@code GeographicCRS} (tip: search for {@code DefaultGeodeticDatum}).</p>
+     *
+     * @param pm The prime meridian of the enclosing {@code GeodeticDatum}.
+     */
+    void verify(final PrimeMeridian pm) throws IllegalArgumentException {
+        if (targetDatum != null) {
+            final PrimeMeridian actual = targetDatum.getPrimeMeridian();
+            if (actual.getGreenwichLongitude() != 0 && !Utilities.equalsIgnoreMetadata(pm, actual)) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedPrimeMeridian_2,
+                        IdentifiedObjects.getName(pm, null), IdentifiedObjects.getName(actual, null)));
+            }
+        }
         ensureFinite("tX", tX);
         ensureFinite("tY", tY);
         ensureFinite("tZ", tZ);
         ensureFinite("rX", rX);
         ensureFinite("rY", rY);
         ensureFinite("rZ", rZ);
-        ensureBetween("dS", -PPM, PPM, dS); // For preventing zero or negative value on the matrix diagonal.
+        ensureBetween("dS", -PPM, PPM, dS);     // For preventing zero or negative value on the matrix diagonal.
     }
 
     /**
@@ -276,7 +300,7 @@ public class BursaWolfParameters extends
     public double[] getValues() {
         final double[] elements = new double[(dS != 0) ? 7 : (rZ != 0 || rY != 0 || rX != 0) ? 6 : 3];
         switch (elements.length) {
-            default: elements[6] = dS;  // Fallthrough everywhere.
+            default: elements[6] = dS;      // Fallthrough everywhere.
             case 6:  elements[5] = rZ;
                      elements[4] = rY;
                      elements[3] = rX;
@@ -307,7 +331,7 @@ public class BursaWolfParameters extends
     @SuppressWarnings("fallthrough")
     public void setValues(final double... elements) {
         switch (elements.length) {
-            default: dS = elements[6];  // Fallthrough everywhere.
+            default: dS = elements[6];      // Fallthrough everywhere.
             case 6:  rZ = elements[5];
             case 5:  rY = elements[4];
             case 4:  rX = elements[3];
@@ -510,10 +534,14 @@ public class BursaWolfParameters extends
         }
         /*
          * Translation terms, taken "as-is".
+         * If the matrix contains only translation terms (which is often the case), we are done.
          */
         tX = matrix.getElement(0,3);
         tY = matrix.getElement(1,3);
         tZ = matrix.getElement(2,3);
+        if (Matrices.isTranslation(matrix)) {   // Optimization for a common case.
+            return;
+        }
         /*
          * Scale factor: take the average of elements on the diagonal. All those
          * elements should have the same value, but we tolerate slight deviation
@@ -543,17 +571,17 @@ public class BursaWolfParameters extends
             }
             for (int i = j+1; i < SIZE-1; i++) {
                 S.setFrom(RS);
-                S.inverseDivide(getNumber(matrix, j,i)); // Negative rotation term.
+                S.inverseDivide(getNumber(matrix, j,i));        // Negative rotation term.
                 double value = S.value;
                 double error = S.error;
                 S.setFrom(RS);
-                S.inverseDivide(getNumber(matrix, i,j)); // Positive rotation term.
-                if (!(abs(value + S.value) <= tolerance)) { // We expect r1 ≈ -r2
+                S.inverseDivide(getNumber(matrix, i,j));        // Positive rotation term.
+                if (!(abs(value + S.value) <= tolerance)) {     // We expect r1 ≈ -r2
                     throw new IllegalArgumentException(Errors.format(Errors.Keys.NotASkewSymmetricMatrix));
                 }
                 S.subtract(value, error);
                 S.multiply(0.5, 0);
-                value = S.value; // Average of the two rotation terms.
+                value = S.value;                                // Average of the two rotation terms.
                 switch (j*SIZE + i) {
                     case 1: rZ =  value; break;
                     case 2: rY = -value; break;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -243,6 +243,13 @@ public class DefaultGeodeticDatum extend
      *   </tr>
      * </table>
      *
+     * If Bursa-Wolf parameters are specified, then the prime meridian of their
+     * {@linkplain BursaWolfParameters#getTargetDatum() target datum} shall be either the same than the
+     * {@code primeMeridian} given to this constructor, or Greenwich. This restriction is for avoiding
+     * ambiguity about whether the longitude rotation shall be applied before or after the datum shift.
+     * If the target prime meridian is Greenwich, then the datum shift will be applied in a coordinate
+     * system having Greenwich as the prime meridian.
+     *
      * @param properties    The properties to be given to the identified object.
      * @param ellipsoid     The ellipsoid.
      * @param primeMeridian The prime meridian.
@@ -265,7 +272,7 @@ public class DefaultGeodeticDatum extend
                 BursaWolfParameters param = bursaWolf[i];
                 ensureNonNullElement("bursaWolf", i, param);
                 param = param.clone();
-                param.verify();
+                param.verify(primeMeridian);
                 bursaWolf[i] = param;
             }
         }
@@ -368,12 +375,21 @@ public class DefaultGeodeticDatum extend
      * 1033 – <cite>Position Vector transformation (geocentric domain)</cite>, or
      * 1053 – <cite>Time-dependent Position Vector transformation</cite>.
      *
-     * <p>If this datum and the given {@code targetDatum} do not use the same
-     * {@linkplain #getPrimeMeridian() prime meridian}, then it is caller's responsibility
-     * to apply longitude rotation before to use the matrix returned by this method.</p>
-     *
-     * <div class="section">Search order</div>
-     * This method performs the search in the following order:
+     * <p>If this datum and the given {@code targetDatum} do not use the same {@linkplain #getPrimeMeridian() prime meridian},
+     * then it is caller's responsibility to to apply longitude rotation before to use the matrix returned by this method.
+     * The target prime meridian should be Greenwich (see {@linkplain #DefaultGeodeticDatum(Map, Ellipsoid, PrimeMeridian)
+     * constructor javadoc}), in which case the datum shift should be applied in a geocentric coordinate system having
+     * Greenwich as the prime meridian.</p>
+     *
+     * <div class="note"><b>Note:</b>
+     * in EPSG dataset version 8.9, all datum shifts that can be represented by this method use Greenwich as the
+     * prime meridian, both in source and target datum.</div>
+     *
+     * <div class="section">Search criterion</div>
+     * If the given {@code areaOfInterest} is non-null and contains at least one geographic bounding box, then this
+     * method ignores any Bursa-Wolf parameters having a {@linkplain BursaWolfParameters#getDomainOfValidity() domain
+     * of validity} that does not intersect the given geographic extent.
+     * This method performs the search among the remaining parameters in the following order:
      * <ol>
      *   <li>If this {@code GeodeticDatum} contains {@code BursaWolfParameters} having the given
      *       {@linkplain BursaWolfParameters#getTargetDatum() target datum} (ignoring metadata),
@@ -449,7 +465,7 @@ public class DefaultGeodeticDatum extend
          * not a subclass of BursaWolfParameters. This optimisation covers the vast majority of cases.
          */
         return bursaWolf.getPositionVectorTransformation(bursaWolf.getClass() != BursaWolfParameters.class ?
-                Extents.getDate(areaOfInterest, 0.5) : null); // 0.5 is for choosing midway instant.
+                Extents.getDate(areaOfInterest, 0.5) : null);       // 0.5 is for choosing midway instant.
     }
 
     /**
@@ -530,7 +546,7 @@ public class DefaultGeodeticDatum extend
     @Override
     public boolean equals(final Object object, final ComparisonMode mode) {
         if (object == this) {
-            return true; // Slight optimization.
+            return true;                                // Slight optimization.
         }
         if (!super.equals(object, mode)) {
             return false;
@@ -547,14 +563,10 @@ public class DefaultGeodeticDatum extend
                 return deepEquals(getEllipsoid(),     that.getEllipsoid(),     mode) &&
                        deepEquals(getPrimeMeridian(), that.getPrimeMeridian(), mode);
                 /*
-                 * HACK: We do not consider Bursa-Wolf parameters as a non-metadata field.
-                 *       This is needed in order to get equalsIgnoreMetadata(...) to returns
-                 *       'true' when comparing the WGS84 constant in this class with a WKT
-                 *       DATUM element with a TOWGS84[0,0,0,0,0,0,0] element. Furthermore,
-                 *       the Bursa-Wolf parameters are not part of ISO 19111 specification.
-                 *       We don't want two CRS to be considered as different because one has
-                 *       more of those transformation informations (which is nice, but doesn't
-                 *       change the CRS itself).
+                 * Bursa-Wolf parameters are considered ignorable metadata. This is needed in order to get
+                 * equalsIgnoreMetadata(…) to return true when comparing WGS84 datums with and without the
+                 * WKT 1 "TOWGS84[0,0,0,0,0,0,0]" element. Furthermore those Bursa-Wolf parameters are not
+                 * part of ISO 19111 specification.
                  */
             }
         }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/TimeDependentBWP.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.datum
 import java.util.Date;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.referencing.datum.GeodeticDatum;
+import org.opengis.referencing.datum.PrimeMeridian;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.DoubleDouble;
 
@@ -132,8 +133,8 @@ public class TimeDependentBWP extends Bu
      * Verifies parameters validity after initialization.
      */
     @Override
-    void verify() {
-        super.verify();
+    void verify(final PrimeMeridian pm) throws IllegalArgumentException {
+        super.verify(pm);
         ensureFinite("dtX", dtX);
         ensureFinite("dtY", dtY);
         ensureFinite("dtZ", dtZ);

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/package-info.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -40,6 +40,18 @@
  *       DefaultGeodeticDatum.getPositionVectorTransformation(GeodeticDatum, Extent)}</li>
  * </ul>
  *
+ * <div class="section">Datum shifts</div>
+ * Three classes are provided in support of coordinate transformations between different datums:
+ * <ul>
+ *   <li>{@link org.apache.sis.referencing.datum.BursaWolfParameters} performs an approximation
+ *       based on a translation, rotation and scale of geocentric coordinates.</li>
+ *   <li>{@link org.apache.sis.referencing.datum.TimeDependentBWP} is like {@code BursaWolfParameters},
+ *       but varies with time for taking in account the motion of plate tectonic.</li>
+ *   <li>{@link org.apache.sis.referencing.datum.DatumShiftGrid} is used for more accurate transformations
+ *       than what {@code BursaWolfParameters} allows, by interpolating the geographic or geocentric translations
+ *       in a grid (e.g. NADCON or NTv2) instead than apply the same transformation for every points.</li>
+ * </ul>
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Cédric Briançon (Geomatys)
  * @since   0.4

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/CommonAuthorityFactory.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -640,6 +640,11 @@ public class CommonAuthorityFactory exte
                         return unit;
                     }
 
+                    @Override public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> ignored) {
+                        assert SI.METRE.equals(ignored) : ignored;
+                        return unit;
+                    }
+
                     @Override public boolean accept(CoordinateSystemAxis axis) {
                         return true;
                     }
@@ -647,6 +652,10 @@ public class CommonAuthorityFactory exte
                     @Override public AxisDirection getDirectionReplacement(AxisDirection direction) {
                         return direction;
                     }
+
+                    @Override public AxisDirection getDirectionReplacement(CoordinateSystemAxis axis, AxisDirection direction) {
+                        return direction;
+                    }
                 });
             }
             /*

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -751,7 +751,7 @@ public class MultiAuthoritiesFactory ext
                 if (code == null) {
                     message = Errors.format(Errors.Keys.MissingComponentInElement_2, s, "code");
                 } else {
-                    message = Errors.format(Errors.Keys.CanNotCreateObjectOfType_2, type, uri.type);
+                    message = Errors.format(Errors.Keys.CanNotCreateObjectAsInstanceOf_2, type, uri.type);
                 }
                 throw new NoSuchAuthorityCodeException(message, authority, code, s);
             }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -17,14 +17,21 @@
 package org.apache.sis.referencing.factory.sql;
 
 import java.util.Locale;
+import java.util.List;
+import java.util.Map;
+import java.util.LinkedHashMap;
 import java.sql.ResultSet;
 import javax.measure.unit.Unit;
 import javax.measure.unit.NonSI;
 import javax.measure.unit.SI;
 import javax.measure.converter.ConversionException;
+import org.opengis.util.FactoryException;
+import org.opengis.metadata.extent.Extent;
+import org.apache.sis.internal.referencing.ExtentSelector;
 import org.apache.sis.referencing.datum.BursaWolfParameters;
 import org.apache.sis.referencing.datum.TimeDependentBWP;
 import org.apache.sis.referencing.factory.FactoryDataException;
+import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.measure.Units;
 
@@ -123,11 +130,22 @@ final class BursaWolfInfo {
     final int target;
 
     /**
+     * The value of {@code AREA_OF_USE_CODE}.
+     */
+    private final int domainOfValidity;
+
+    /**
+     * The domain of validity as an {@code Extent} object.
+     */
+    private Extent extent;
+
+    /**
      * Fills a structure with the specified values.
      */
-    BursaWolfInfo(final int operation, final int method, final int targetCRS) {
-        this.operation = operation;
-        this.method    = method;
+    BursaWolfInfo(final int operation, final int method, final int targetCRS, final int domainOfValidity) {
+        this.operation        = operation;
+        this.method           = method;
+        this.domainOfValidity = domainOfValidity;
         switch (targetCRS) {
             case TARGET_CRS: target = TARGET_DATUM; break;
             // More codes may be added in future SIS version.
@@ -155,4 +173,44 @@ final class BursaWolfInfo {
     public String toString() {
         return String.valueOf(operation);
     }
+
+    /**
+     * Gets the domain of validity. The result is cached.
+     *
+     * @param factory The factory to use for creating {@code Extent} instances.
+     */
+    Extent getDomainOfValidity(final GeodeticAuthorityFactory factory) throws FactoryException {
+        if (extent == null && domainOfValidity != 0) {
+            extent = factory.createExtent(String.valueOf(domainOfValidity));
+        }
+        return extent;
+    }
+
+    /**
+     * Given an array of {@code BursaWolfInfo} instances, retains only the instances having the largest
+     * domain of validity for each target datum. If two instances have the same domain of validity, the
+     * first one is retained. This presume that the instances have already been sorted for preference order
+     * before to invoke this method.
+     *
+     * @param factory     The factory to use for creating {@code Extent} instances.
+     * @param candidates  The Bursa-Wolf parameters candidates.
+     * @param addTo       Where to add the instances retained by this method.
+     */
+    static void filter(final GeodeticAuthorityFactory factory, final BursaWolfInfo[] candidates,
+            final List<BursaWolfInfo> addTo) throws FactoryException
+    {
+        final Map<Integer,ExtentSelector<BursaWolfInfo>> added = new LinkedHashMap<>();
+        for (BursaWolfInfo candidate : candidates) {
+            final Integer target = candidate.target;
+            ExtentSelector<BursaWolfInfo> selector = added.get(target);
+            if (selector == null) {
+                selector = new ExtentSelector<>(null);
+                added.put(target, selector);
+            }
+            selector.evaluate(candidate.getDomainOfValidity(factory), candidate);
+        }
+        for (final ExtentSelector<BursaWolfInfo> select : added.values()) {
+            addTo.add(select.best());
+        }
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java?rev=1737072&r1=1737071&r2=1737072&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] Tue Mar 29 22:51:40 2016
@@ -19,7 +19,6 @@ package org.apache.sis.referencing.facto
 import java.util.Set;
 import java.util.Map;
 import java.util.List;
-import java.util.HashSet;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashMap;
@@ -71,6 +70,7 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
 import org.apache.sis.internal.referencing.DeprecatedCode;
 import org.apache.sis.internal.referencing.EPSGParameterDomain;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.system.Semaphores;
@@ -107,11 +107,13 @@ import org.apache.sis.util.CharSequences
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.Version;
 import org.apache.sis.util.Workaround;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Units;
 
+import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
 import static org.apache.sis.internal.referencing.ServicesForMetadata.CONNECTION;
 
 // Branch-dependent imports
@@ -1620,7 +1622,7 @@ addURIs:    for (int i=0; ; i++) {
                         properties = new HashMap<>(properties);         // Protect from changes
                         final Ellipsoid ellipsoid    = owner.createEllipsoid    (getString(code, result, 10));
                         final PrimeMeridian meridian = owner.createPrimeMeridian(getString(code, result, 11));
-                        final BursaWolfParameters[] param = createBursaWolfParameters(epsg);
+                        final BursaWolfParameters[] param = createBursaWolfParameters(meridian, epsg);
                         if (param != null) {
                             properties.put(DefaultGeodeticDatum.BURSA_WOLF_KEY, param);
                         }
@@ -1691,10 +1693,13 @@ addURIs:    for (int i=0; ; i++) {
      * That legacy format had a {@code TOWGS84} element which needs the information provided by this method.
      * Note that {@code TOWGS84} is a deprecated element as of WKT 2 (ISO 19162).</p>
      *
-     * @param  code The EPSG code of the {@link GeodeticDatum}.
+     * @param  meridian The source datum prime meridian, used for discarding any target datum using a different meridian.
+     * @param  code The EPSG code of the source {@link GeodeticDatum}.
      * @return an array of Bursa-Wolf parameters, or {@code null}.
      */
-    private BursaWolfParameters[] createBursaWolfParameters(final Integer code) throws SQLException, FactoryException {
+    private BursaWolfParameters[] createBursaWolfParameters(final PrimeMeridian meridian, final Integer code)
+            throws SQLException, FactoryException
+    {
         /*
          * We do not provide TOWGS84 information for WGS84 itself or for any other datum on our list of target datum,
          * in order to avoid infinite recursivity. The 'ensureNonRecursive' call is an extra safety check which should
@@ -1707,7 +1712,8 @@ addURIs:    for (int i=0; ; i++) {
         try (ResultSet result = executeQuery("BursaWolfParametersSet",
                 "SELECT COORD_OP_CODE," +
                       " COORD_OP_METHOD_CODE," +
-                      " TARGET_CRS_CODE" +
+                      " TARGET_CRS_CODE," +
+                      " AREA_OF_USE_CODE"+
                 " FROM [Coordinate_Operation]" +
                " WHERE DEPRECATED=0" +           // Do not put spaces around "=" - SQLTranslator searches for this exact match.
                  " AND TARGET_CRS_CODE = "       + BursaWolfInfo.TARGET_CRS +
@@ -1721,7 +1727,8 @@ addURIs:    for (int i=0; ; i++) {
                 final BursaWolfInfo info = new BursaWolfInfo(
                         getInteger(code, result, 1),                // Operation
                         getInteger(code, result, 2),                // Method
-                        getInteger(code, result, 3));               // Target datum
+                        getInteger(code, result, 3),                // Target datum
+                        getInteger(code, result, 4));               // Domain of validity
                 if (info.target != code) {                          // Paranoiac check.
                     bwInfos.add(info);
                 }
@@ -1740,12 +1747,7 @@ addURIs:    for (int i=0; ; i++) {
             final BursaWolfInfo[] codes = bwInfos.toArray(new BursaWolfInfo[size]);
             sort("Coordinate_Operation", codes);
             bwInfos.clear();
-            final Set<Integer> added = new HashSet<>();
-            for (BursaWolfInfo candidate : codes) {
-                if (added.add(candidate.target)) {
-                    bwInfos.add(candidate);
-                }
-            }
+            BursaWolfInfo.filter(owner, codes, bwInfos);
             size = bwInfos.size();
         }
         /*
@@ -1753,6 +1755,7 @@ addURIs:    for (int i=0; ; i++) {
          */
         final BursaWolfParameters[] parameters = new BursaWolfParameters[size];
         final Locale locale = getLocale();
+        int count = 0;
         for (int i=0; i<size; i++) {
             final BursaWolfInfo info = bwInfos.get(i);
             final GeodeticDatum datum;
@@ -1762,7 +1765,17 @@ addURIs:    for (int i=0; ; i++) {
             } finally {
                 endOfRecursivity(BursaWolfParameters.class, code);
             }
-            final BursaWolfParameters bwp = new BursaWolfParameters(datum, null);
+            /*
+             * Accept only Bursa-Wolf parameters between datum that use the same prime meridian.
+             * This is for avoiding ambiguity about whether longitude rotation should be applied
+             * before or after the datum change. This check is useless for EPSG dataset 8.9 since
+             * all datum seen by this method use Greenwich. But we nevertheless perform this check
+             * as a safety for future evolution or customized EPSG dataset.
+             */
+            if (!equalsIgnoreMetadata(meridian, datum.getPrimeMeridian())) {
+                continue;
+            }
+            final BursaWolfParameters bwp = new BursaWolfParameters(datum, info.getDomainOfValidity(owner));
             try (ResultSet result = executeQuery("BursaWolfParameters",
                 "SELECT PARAMETER_CODE," +
                       " PARAMETER_VALUE," +
@@ -1783,9 +1796,9 @@ addURIs:    for (int i=0; ; i++) {
                 // except for the sign of rotation parameters.
                 bwp.reverseRotation();
             }
-            parameters[i] = bwp;
+            parameters[count++] = bwp;
         }
-        return parameters;
+        return ArraysExt.resize(parameters, count);
     }
 
     /**
@@ -2260,7 +2273,8 @@ addURIs:    for (int i=0; ; i++) {
                     throw new FactoryDataException(exception.getLocalizedMessage(), exception);
                 }
                 final AxisName an = getAxisName(nameCode);
-                final CoordinateSystemAxis axis = owner.csFactory.createCoordinateSystemAxis(createProperties("Coordinate Axis", an.name, epsg, an.description, false),
+                final CoordinateSystemAxis axis = owner.csFactory.createCoordinateSystemAxis(
+                        createProperties("Coordinate Axis", an.name, epsg, an.description, false),
                         abbreviation, direction, owner.createUnit(unit));
                 returnValue = ensureSingleton(axis, returnValue, code);
             }
@@ -2737,7 +2751,7 @@ next:               while (r.next()) {
                         sourceDimensions = sourceCRS.getCoordinateSystem().getDimension();
                     } else {
                         sourceCRS = null;
-                        sourceDimensions = 2;       // Acceptable default for projections only.
+                        sourceDimensions = 2;           // Acceptable default for projections only.
                         isDimensionKnown = false;
                     }
                     if (targetCode != null) {
@@ -2745,7 +2759,7 @@ next:               while (r.next()) {
                         targetDimensions = targetCRS.getCoordinateSystem().getDimension();
                     } else {
                         targetCRS = null;
-                        targetDimensions = 2;       // Acceptable default for projections only.
+                        targetDimensions = 2;           // Acceptable default for projections only.
                         isDimensionKnown = false;
                     }
                     /*
@@ -2797,7 +2811,7 @@ next:               while (r.next()) {
                          * we are going to invoke this method recursively in the following lines.
                          */
                         result.close();
-                        opProperties = new HashMap<>(opProperties);     // Because this class uses a shared map.
+                        opProperties = new HashMap<>(opProperties);         // Because this class uses a shared map.
                         final List<String> codes = new ArrayList<>();
                         try (ResultSet cr = executeQuery("Coordinate_Operation Path",
                                 "SELECT SINGLE_OPERATION_CODE" +
@@ -2833,10 +2847,8 @@ next:               while (r.next()) {
                         final MathTransform mt;
                         final MathTransformFactory mtFactory = owner.mtFactory;
                         if (mtFactory instanceof DefaultMathTransformFactory) {
-                            DefaultMathTransformFactory.Context context = new DefaultMathTransformFactory.Context();
-                            context.setSource(sourceCRS);
-                            context.setTarget(targetCRS);
-                            mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters, context);
+                            mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(parameters,
+                                    ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
                         } else {
                             // Fallback for non-SIS implementations. Work for map projections but not for Molodensky.
                             mt = mtFactory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());



Mime
View raw message