sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1548777 [3/6] - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/geometry/ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-me...
Date Sat, 07 Dec 2013 02:00:45 GMT
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -16,76 +16,550 @@
  */
 package org.apache.sis.referencing.cs;
 
-import java.util.Set;
-import java.util.Collection;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Locale;
 import javax.measure.unit.Unit;
+import javax.measure.unit.NonSI;
+import javax.measure.quantity.Angle;
+import javax.measure.converter.UnitConverter;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlAttribute;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.referencing.ReferenceIdentifier;
 import org.opengis.referencing.cs.RangeMeaning;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
-
-
-public class DefaultCoordinateSystemAxis implements CoordinateSystemAxis {
-    public DefaultCoordinateSystemAxis(final String        abbreviation,
+import org.apache.sis.internal.referencing.AxisDirections;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.measure.Longitude;
+import org.apache.sis.measure.Latitude;
+import org.apache.sis.measure.Units;
+import org.apache.sis.util.Immutable;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.io.wkt.Formatter;
+
+import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+import static org.apache.sis.util.CharSequences.trimWhitespaces;
+
+// Related to JDK7
+import org.apache.sis.internal.jdk7.Objects;
+
+
+/**
+ * Coordinate system axis name, direction, unit and range of values.
+ *
+ * {@section Axis names}
+ * In some case, the axis name is constrained by ISO 19111 depending on the
+ * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} type.
+ * This constraint works in two directions. For example the names "<cite>geodetic latitude</cite>" and
+ * "<cite>geodetic longitude</cite>" shall be used to designate the coordinate axis names associated
+ * with a {@link org.opengis.referencing.crs.GeographicCRS}. Conversely, these names shall not be used
+ * in any other context. See the GeoAPI {@link CoordinateSystemAxis} javadoc for more information.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.4 (derived from geotk-2.0)
+ * @version 0.4
+ * @module
+ *
+ * @see AbstractCS
+ * @see Unit
+ */
+@Immutable
+public class DefaultCoordinateSystemAxis extends AbstractIdentifiedObject implements CoordinateSystemAxis {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = -7883614853277827689L;
+
+    /**
+     * Some names to be treated as equivalent. This is needed because axis names are the primary way to
+     * distinguish between {@link CoordinateSystemAxis} instances. Those names are strictly defined by
+     * ISO 19111 as "Geodetic latitude" and "Geodetic longitude" among others, but the legacy WKT
+     * specifications from OGC 01-009 defined the names as "Lon" and "Lat" for the same axis.
+     *
+     * <p>Keys in this map are names <strong>in lower cases</strong>.
+     * Values are any object that allow us to differentiate latitude from longitude.</p>
+     *
+     * @see #isHeuristicMatchForName(String)
+     */
+    private static final Map<String,Object> ALIASES = new HashMap<String,Object>(12);
+    static {
+        final Boolean latitude  = Boolean.TRUE;
+        final Boolean longitude = Boolean.FALSE;
+        ALIASES.put("lat",                latitude);
+        ALIASES.put("latitude",           latitude);
+        ALIASES.put("geodetic latitude",  latitude);
+        ALIASES.put("lon",                longitude);
+        ALIASES.put("long",               longitude);
+        ALIASES.put("longitude",          longitude);
+        ALIASES.put("geodetic longitude", longitude);
+        /*
+         * Do not add aliases for "x" and "y" in this map. See ALIASES_XY for more information.
+         */
+    }
+
+    /**
+     * Aliases for the "x" and "y" abbreviations (special cases). "x" and "y" are sometime used (especially in WKT)
+     * for meaning "Easting" and "Northing". However we shall not add "x" and "y" as aliases in the {@link #ALIASES}
+     * map, because experience has shown that doing so cause a lot of undesirable side effects. The "x" abbreviation
+     * is used for too many things ("Easting", "Westing", "Geocentric X", "Display right", "Display left") and likewise
+     * for "y". Declaring them as aliases introduces confusion in many places. Instead, the "x" and "y" cases are
+     * handled in a special way by the {@code isHeuristicMatchForNameXY(…)} method.
+     *
+     * <p>Names at even index are for "x" and names at odd index are for "y".</p>
+     *
+     * @see #isHeuristicMatchForNameXY(String, String)
+     */
+    private static final String[] ALIASES_XY = {
+        "Easting", "Northing",
+        "Westing", "Southing"
+    };
+
+    /**
+     * The abbreviation used for this coordinate system axes.
+     * Examples are "<var>X</var>" and "<var>Y</var>".
+     */
+    @XmlElement(name = "axisAbbrev", required = true)
+    private final String abbreviation;
+
+    /**
+     * Direction of this coordinate system axis. In the case of Cartesian projected
+     * coordinates, this is the direction of this coordinate system axis locally.
+     */
+    @XmlElement(name = "axisDirection", required = true)
+    private final AxisDirection direction;
+
+    /**
+     * The unit of measure used for this coordinate system axis.
+     */
+    @XmlAttribute(name= "uom", required = true)
+    private final Unit<?> unit;
+
+    /**
+     * Minimal and maximal value for this axis.
+     */
+    private final double minimum, maximum;
+
+    /**
+     * The range meaning for this axis.
+     */
+    private final RangeMeaning rangeMeaning;
+
+    /**
+     * Constructs an axis from a set of properties and a given range.
+     * The properties map is given unchanged to the
+     * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
+     * The following table is a reminder of main (not all) properties:
+     *
+     * <table class="sis">
+     *   <tr>
+     *     <th>Property name</th>
+     *     <th>Value type</th>
+     *     <th>Returned by</th>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
+     *     <td>{@link ReferenceIdentifier} or {@link String}</td>
+     *     <td>{@link #getName()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td>
+     *     <td>{@link GenericName} or {@link CharSequence} (optionally as array)</td>
+     *     <td>{@link #getAlias()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
+     *     <td>{@link ReferenceIdentifier} (optionally as array)</td>
+     *     <td>{@link #getIdentifiers()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
+     *     <td>{@link #getRemarks()}</td>
+     *   </tr>
+     * </table>
+     *
+     * @param properties   The properties to be given to the identified object.
+     * @param abbreviation The {@linkplain #getAbbreviation() abbreviation} used for this coordinate system axis.
+     * @param direction    The {@linkplain #getDirection() direction} of this coordinate system axis.
+     * @param unit         The {@linkplain #getUnit() unit of measure} used for this coordinate system axis.
+     * @param minimum      The minimum value normally allowed for this axis.
+     * @param maximum      The maximum value normally allowed for this axis.
+     * @param rangeMeaning The meaning of axis value range specified by the minimum and maximum values.
+     */
+    public DefaultCoordinateSystemAxis(final Map<String,?> properties,
+                                       final String        abbreviation,
+                                       final AxisDirection direction,
+                                       final Unit<?>       unit,
+                                       final double        minimum,
+                                       final double        maximum,
+                                       final RangeMeaning  rangeMeaning)
+    {
+        super(properties);
+        this.abbreviation = abbreviation;
+        this.direction    = direction;
+        this.unit         = unit;
+        this.minimum      = minimum;
+        this.maximum      = maximum;
+        this.rangeMeaning = rangeMeaning;
+        ensureNonNull("abbreviation", abbreviation);
+        ensureNonNull("direction",    direction);
+        ensureNonNull("unit",         unit);
+        ensureNonNull("rangeMeaning", rangeMeaning);
+        if (!(minimum < maximum)) { // Use '!' for catching NaN
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, minimum, maximum));
+        }
+    }
+
+    /**
+     * Constructs an axis from a set of properties and a range inferred from the axis unit and direction.
+     * The properties map is the same than for the {@linkplain #DefaultCoordinateSystemAxis(Map, String,
+     * AxisDirection, Unit, double, double, RangeMeaning) above constructor}.
+     *
+     * @param properties   The properties to be given to the identified object.
+     * @param abbreviation The {@linkplain #getAbbreviation() abbreviation} used for this coordinate system axis.
+     * @param direction    The {@linkplain #getDirection() direction} of this coordinate system axis.
+     * @param unit         The {@linkplain #getUnit() unit of measure} used for this coordinate system axis.
+     */
+    public DefaultCoordinateSystemAxis(final Map<String,?> properties,
+                                       final String        abbreviation,
                                        final AxisDirection direction,
                                        final Unit<?>       unit)
     {
-    }
-
-    @Override
-    public ReferenceIdentifier getName() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public Collection<GenericName> getAlias() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
+        // NOTE: we would invoke this(properties, abbreviation, ...) instead if Oracle fixed
+        // RFE #4093999 ("Relax constraint on placement of this()/super() call in constructors").
+        super(properties);
+        this.abbreviation = abbreviation;
+        this.direction    = direction;
+        this.unit         = unit;
+        ensureNonNull("abbreviation", abbreviation);
+        ensureNonNull("direction",    direction);
+        ensureNonNull("unit",         unit);
+        double min = Double.NEGATIVE_INFINITY;
+        double max = Double.POSITIVE_INFINITY;
+        RangeMeaning r = RangeMeaning.EXACT;
+        if (Units.isAngular(unit)) {
+            final UnitConverter fromDegrees = NonSI.DEGREE_ANGLE.getConverterTo(unit.asType(Angle.class));
+            final AxisDirection dir = AxisDirections.absolute(direction);
+            if (dir.equals(AxisDirection.NORTH)) {
+                min = fromDegrees.convert(Latitude.MIN_VALUE);
+                max = fromDegrees.convert(Latitude.MAX_VALUE);
+            } else if (dir.equals(AxisDirection.EAST)) {
+                min = fromDegrees.convert(Longitude.MIN_VALUE);
+                max = fromDegrees.convert(Longitude.MAX_VALUE);
+                r = RangeMeaning.WRAPAROUND; // 180°E wraps to 180°W
+            }
+            if (min > max) {
+                final double t = min;
+                min = max;
+                max = t;
+            }
+        }
+        minimum = min;
+        maximum = max;
+        rangeMeaning = r;
+    }
+
+    /**
+     * Creates a new coordinate system axis with the same values than the specified one.
+     * This copy constructor provides a way to convert an arbitrary implementation into a SIS one
+     * or a user-defined one (as a subclass), usually in order to leverage some implementation-specific API.
+     *
+     * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p>
+     *
+     * @param axis The coordinate system axis to copy.
+     *
+     * @see #castOrCopy(CoordinateSystemAxis)
+     */
+    protected DefaultCoordinateSystemAxis(final CoordinateSystemAxis axis) {
+        super(axis);
+        abbreviation = axis.getAbbreviation();
+        direction    = axis.getDirection();
+        unit         = axis.getUnit();
+        minimum      = axis.getMinimumValue();
+        maximum      = axis.getMaximumValue();
+        rangeMeaning = axis.getRangeMeaning();
+    }
+
+    /**
+     * Returns a SIS axis implementation with the same values than the given arbitrary implementation.
+     * If the given object is {@code null}, then this method returns {@code null}. Otherwise if the
+     * given object is already a SIS implementation, then the given object is returned unchanged.
+     * Otherwise a new SIS implementation is created and initialized to the values of the given object.
+     *
+     * @param  object The object to get as a SIS implementation, or {@code null} if none.
+     * @return A SIS implementation containing the values of the given object (may be the
+     *         given object itself), or {@code null} if the argument was null.
+     */
+    public static DefaultCoordinateSystemAxis castOrCopy(final CoordinateSystemAxis object) {
+        return (object == null) || (object instanceof DefaultCoordinateSystemAxis)
+                ? (DefaultCoordinateSystemAxis) object : new DefaultCoordinateSystemAxis(object);
+    }
+
+    /**
+     * Returns the direction of this coordinate system axis.
+     * This direction is often approximate and intended to provide a human interpretable meaning to the axis.
+     * A {@linkplain AbstractCS coordinate system} can not contain two axes having the same direction or
+     * opposite directions.
+     *
+     * <p>Examples:
+     * {@linkplain AxisDirection#NORTH north} or {@linkplain AxisDirection#SOUTH south},
+     * {@linkplain AxisDirection#EAST  east}  or {@linkplain AxisDirection#WEST  west},
+     * {@linkplain AxisDirection#UP    up}    or {@linkplain AxisDirection#DOWN  down}.</p>
+     *
+     * @return The direction of this coordinate system axis.
+     */
     @Override
-    public Set<ReferenceIdentifier> getIdentifiers() {
-        throw new UnsupportedOperationException("Not supported yet.");
+    public AxisDirection getDirection() {
+        return direction;
     }
 
+    /**
+     * Returns the abbreviation used for this coordinate system axes.
+     * Examples are "<var>X</var>" and "<var>Y</var>".
+     *
+     * @return The coordinate system axis abbreviation.
+     */
     @Override
     public String getAbbreviation() {
-        throw new UnsupportedOperationException("Not supported yet.");
+        return abbreviation;
     }
 
+    /**
+     * Returns the unit of measure used for this coordinate system axis. If this {@code CoordinateSystemAxis}
+     * was given by <code>{@link AbstractCS#getAxis(int) CoordinateSystem.getAxis}(i)</code>, then all ordinate
+     * values at dimension <var>i</var> in a coordinate tuple shall be recorded using this unit of measure.
+     *
+     * @return The unit of measure used for ordinate values along this coordinate system axis.
+     */
     @Override
-    public AxisDirection getDirection() {
-        throw new UnsupportedOperationException("Not supported yet.");
+    public Unit<?> getUnit() {
+        return unit;
     }
 
+    /**
+     * Returns the minimum value normally allowed for this axis, in the {@linkplain #getUnit()
+     * unit of measure for the axis}. If there is no minimum value, then this method returns
+     * {@linkplain Double#NEGATIVE_INFINITY negative infinity}.
+     *
+     * @return The minimum value normally allowed for this axis.
+     */
     @Override
     public double getMinimumValue() {
-        throw new UnsupportedOperationException("Not supported yet.");
+        return minimum;
     }
 
+    /**
+     * Returns the maximum value normally allowed for this axis, in the {@linkplain #getUnit()
+     * unit of measure for the axis}. If there is no maximum value, then this method returns
+     * {@linkplain Double#POSITIVE_INFINITY negative infinity}.
+     *
+     * @return The maximum value normally allowed for this axis.
+     */
     @Override
     public double getMaximumValue() {
-        throw new UnsupportedOperationException("Not supported yet.");
+        return maximum;
     }
 
+    /**
+     * Returns the meaning of axis value range specified by the {@linkplain #getMinimumValue() minimum}
+     * and {@linkplain #getMaximumValue() maximum} values.
+     *
+     * @return The meaning of axis value range.
+     */
     @Override
     public RangeMeaning getRangeMeaning() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public Unit<?> getUnit() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public InternationalString getRemarks() {
-        throw new UnsupportedOperationException("Not supported yet.");
+        return rangeMeaning;
     }
 
-    @Override
-    public String toWKT() throws UnsupportedOperationException {
-        throw new UnsupportedOperationException("Not supported yet.");
+    /**
+     * Returns {@code true} if either the {@linkplain #getName() primary name} or at least
+     * one {@linkplain #getAlias() alias} matches the given string according heuristic rules.
+     * This method performs the comparison documented in the
+     * {@link AbstractIdentifiedObject#isHeuristicMatchForName(String) super-class},
+     * with an additional flexibility for latitudes and longitudes:
+     *
+     * <ul>
+     *   <li>{@code "Lat"}, {@code "Latitude"}  and {@code "Geodetic latitude"}  are considered equivalent.</li>
+     *   <li>{@code "Lon"}, {@code "Longitude"} and {@code "Geodetic longitude"} are considered equivalent.</li>
+     * </ul>
+     *
+     * The above special cases are needed in order to workaround a conflict in specifications:
+     * ISO 19111 states explicitly that the latitude and longitude axis names shall be
+     * "<cite>Geodetic latitude</cite>" and "<cite>Geodetic longitude</cite>", while the legacy
+     * OGC 01-009 (where version 1 of the WKT format is defined) said that the default values shall be
+     * "<cite>Lat</cite>" and "<cite>Lon</cite>".
+     *
+     * {@section Future evolutions}
+     * This method implements heuristic rules learned from experience while trying to provide inter-operability
+     * with different data producers. Those rules may be adjusted in any future SIS version according experience
+     * gained while working with more data producers.
+     *
+     * @param  name The name to compare.
+     * @return {@code true} if the primary name of at least one alias matches the specified {@code name}.
+     */
+    @Override
+    public boolean isHeuristicMatchForName(final String name) {
+        if (super.isHeuristicMatchForName(name)) {
+            return true;
+        }
+        /*
+         * The standard comparisons didn't worked. Check for the aliases. Note: we don't test
+         * for  'isHeuristicMatchForNameXY(...)'  here because the "x" and "y" axis names are
+         * too generic.  We test them only in the 'equals' method, which has the extra-safety
+         * of units comparison (so less risk to treat incompatible axes as equivalent).
+         */
+        final Object type = ALIASES.get(trimWhitespaces(name).toLowerCase(Locale.US)); // Our ALIASES are in English.
+        return (type != null) && (type == ALIASES.get(trimWhitespaces(getName().getCode()).toLowerCase(Locale.US)));
+    }
+
+    /**
+     * Special cases for "x" and "y" names. "x" is considered equivalent to "Easting" or "Westing",
+     * but the converse is not true. Note: by avoiding to put "x" in the {@link #ALIASES} map, we
+     * avoid undesirable side effects like considering "Easting" as equivalent to "Westing".
+     *
+     * @param  xy   The name which may be "x" or "y".
+     * @param  name The second name to compare with.
+     * @return {@code true} if the second name is equivalent to "x" or "y"
+     *         (depending on the {@code xy} value), or {@code false} otherwise.
+     */
+    private static boolean isHeuristicMatchForNameXY(String xy, String name) {
+        xy = trimWhitespaces(xy);
+        if (xy.length() == 1) {
+            int i = Character.toLowerCase(xy.charAt(0)) - 'x';
+            if (i >= 0 && i <= 1) {
+                name = trimWhitespaces(name);
+                if (!name.isEmpty()) do {
+                    if (name.regionMatches(true, 0, ALIASES_XY[i], 0, name.length())) {
+                        return true;
+                    }
+                } while ((i += 2) < ALIASES_XY.length);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Compares the specified object with this axis for equality.
+     *
+     * @param  object The object to compare to {@code this}.
+     * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
+     *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
+     *         relevant to coordinate transformations.
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object object, final ComparisonMode mode) {
+        if (object == this) {
+            return true; // Slight optimization.
+        }
+        if (!(object instanceof CoordinateSystemAxis && super.equals(object, mode))) {
+            return false;
+        }
+        final DefaultCoordinateSystemAxis that = castOrCopy((CoordinateSystemAxis) object);
+        return equals(that, mode.ordinal() < ComparisonMode.IGNORE_METADATA.ordinal(), true);
+    }
+
+    /**
+     * Compares the specified object with this axis for equality, with optional comparison of units.
+     * Units shall always be compared (they are not just metadata), except in the particular case of
+     * {@link CoordinateSystems#axisColinearWith}, which is used as a first step toward units conversions
+     * through {@link CoordinateSystems#swapAndScaleAxes}.
+     */
+    final boolean equals(final DefaultCoordinateSystemAxis that,
+                         final boolean compareMetadata, final boolean compareUnit)
+    {
+        /*
+         * It is important to NOT compare the minimum and maximum values when we are in
+         * "ignore metadata" mode,  because we want CRS with a [-180 … +180]° longitude
+         * range to be considered equivalent, from a coordinate transformation point of
+         * view, to a CRS with a [0 … 360]° longitude range.
+         */
+        if (compareMetadata) {
+            if (!Objects.equals(this.abbreviation, that.abbreviation) ||
+                !Objects.equals(this.rangeMeaning, that.rangeMeaning) ||
+                Double.doubleToLongBits(minimum) != Double.doubleToLongBits(that.minimum) ||
+                Double.doubleToLongBits(maximum) != Double.doubleToLongBits(that.maximum))
+            {
+                return false;
+            }
+        } else {
+            /*
+             * Checking the abbreviation is not sufficient. For example the polar angle and the
+             * spherical latitude have the same abbreviation (θ). SIS names like "Longitude"
+             * (in addition to ISO 19111 "Geodetic longitude") bring more potential confusion.
+             * Furthermore, not all implementors use the greek letters. For example most CRS in
+             * WKT format use the "Lat" abbreviation instead of the greek letter φ.
+             * For comparisons without metadata, we ignore the unreliable abbreviation and check
+             * the axis name instead. These names are constrained by ISO 19111 specification
+             * (see class javadoc), so they should be reliable enough.
+             *
+             * Note: there is no need to execute this block if 'compareMetadata' is true,
+             *       because in this case a stricter check has already been performed by
+             *       the 'equals' method in the superclass.
+             */
+            final String thatName = that.getName().getCode();
+            if (!isHeuristicMatchForName(thatName)) {
+                /*
+                 * The above test checked for special cases ("Lat" / "Lon" aliases, etc.).
+                 * The next line may repeat the same check, so we may have a partial waste
+                 * of CPU.   But we do it anyway for checking the 'that' aliases, and also
+                 * because the user may have overridden 'that.isHeuristicMatchForName(…)'.
+                 */
+                final String thisName = getName().getCode();
+                if (!IdentifiedObjects.isHeuristicMatchForName(that, thisName)) {
+                    /*
+                     * For the needs of CoordinateSystems.axisColinearWith(...), we must stop here.
+                     * In addition it may be safer to not test 'isHeuristicMatchForNameXY' when we
+                     * do not have the extra-safety of units comparison, because "x" and "y" names
+                     * are too generic.
+                     */
+                    if (!compareUnit) {
+                        return false;
+                    }
+                    // Last chance: check for the special case of "x" and "y" axis names.
+                    if (!isHeuristicMatchForNameXY(thatName, thisName) &&
+                        !isHeuristicMatchForNameXY(thisName, thatName))
+                    {
+                        return false;
+                    }
+                }
+            }
+        }
+        return Objects.equals(direction, that.direction) && (!compareUnit || Objects.equals(unit, that.unit));
+    }
+
+    /**
+     * Computes a hash value consistent with the given comparison mode.
+     *
+     * @return The hash code value for the given comparison mode.
+     */
+    @Override
+    public int hashCode(final ComparisonMode mode) throws IllegalArgumentException {
+        int code = super.hashCode(mode);
+        if (unit      != null) code = 31*code + unit     .hashCode();
+        if (direction != null) code = 31*code + direction.hashCode();
+        if (mode == ComparisonMode.STRICT) {
+            code = Numerics.hash(minimum, code);
+            code = Numerics.hash(maximum, code);
+        }
+        return code;
+    }
+
+    /**
+     * Formats the inner part of a <cite>Well Known Text</cite> (WKT) element.
+     *
+     * @param  formatter The formatter to use.
+     * @return The WKT element name, which is {@code "AXIS"}.
+     */
+    @Override
+    protected String formatTo(final Formatter formatter) {
+        formatter.append(direction);
+        return "AXIS";
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/AbstractDatum.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.datum
 import java.util.Date;
 import java.util.Map;
 import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.opengis.util.GenericName;
@@ -64,6 +65,9 @@ import org.apache.sis.internal.jdk7.Obje
  */
 @Immutable
 @XmlType(name="AbstractDatumType")
+@XmlSeeAlso(
+    DefaultGeodeticDatum.class
+)
 public class AbstractDatum extends AbstractIdentifiedObject implements Datum {
     /**
      * Serial number for inter-operability with different versions.
@@ -71,6 +75,11 @@ public class AbstractDatum extends Abstr
     private static final long serialVersionUID = -4894180465652474930L;
 
     /**
+     * The prefix used by ESRI at the beginning of datum names.
+     */
+    private static final String ESRI_PREFIX = "D_";
+
+    /**
      * Description, possibly including coordinates, of the point or points used to anchor the datum
      * to the Earth. Also known as the "origin", especially for Engineering and Image Datums.
      */
@@ -99,6 +108,19 @@ public class AbstractDatum extends Abstr
     private final InternationalString scope;
 
     /**
+     * Constructs a new object in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    AbstractDatum() {
+        super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE);
+        anchorPoint      = null;
+        realizationEpoch = Long.MIN_VALUE;
+        domainOfValidity = null;
+        scope            = null;
+    }
+
+    /**
      * Creates a datum from the given properties.
      * The properties given in argument follow the same rules than for the
      * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
@@ -287,6 +309,40 @@ public class AbstractDatum extends Abstr
     }
 
     /**
+     * Returns {@code true} if either the {@linkplain #getName() primary name} or at least
+     * one {@linkplain #getAlias() alias} matches the given string according heuristic rules.
+     * This method performs the comparison documented in the
+     * {@link AbstractIdentifiedObject#isHeuristicMatchForName(String) super-class},
+     * with the following additional flexibility:
+     *
+     * <ul>
+     *   <li>The {@code "D_"} prefix (used in ESRI datum names), if presents in the given name or in this datum name,
+     *       is ignored.</li>
+     * </ul>
+     *
+     * {@section Future evolutions}
+     * This method implements heuristic rules learned from experience while trying to provide inter-operability
+     * with different data producers. Those rules may be adjusted in any future SIS version according experience
+     * gained while working with more data producers.
+     *
+     * @param  name The name to compare.
+     * @return {@code true} if the primary name of at least one alias matches the specified {@code name}.
+     */
+    @Override
+    public boolean isHeuristicMatchForName(final String name) {
+        if (name.startsWith((ESRI_PREFIX))) {
+            if (super.isHeuristicMatchForName(name.substring(ESRI_PREFIX.length()))) {
+                return true;
+            }
+        } else if (getName().getCode().startsWith(ESRI_PREFIX)) {
+            if (super.isHeuristicMatchForName(ESRI_PREFIX.concat(name))) {
+                return true;
+            }
+        }
+        return super.isHeuristicMatchForName(name);
+    }
+
+    /**
      * Compares the specified object with this datum for equality.
      * If the {@code mode} argument value is {@link ComparisonMode#STRICT STRICT} or
      * {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available properties are compared including the
@@ -296,43 +352,41 @@ public class AbstractDatum extends Abstr
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
     public boolean equals(final Object object, final ComparisonMode mode) {
-        if (super.equals(object, mode)) {
-            switch (mode) {
-                case STRICT: {
-                    final AbstractDatum that = (AbstractDatum) object;
-                    return this.realizationEpoch == that.realizationEpoch &&
-                           Objects.equals(this.domainOfValidity, that.domainOfValidity) &&
-                           Objects.equals(this.anchorPoint,      that.anchorPoint) &&
-                           Objects.equals(this.scope,            that.scope);
-                }
-                case BY_CONTRACT: {
-                    if (!(object instanceof Datum)) break;
-                    final Datum that = (Datum) object;
-                    return deepEquals(getRealizationEpoch(), that.getRealizationEpoch(), mode) &&
-                           deepEquals(getDomainOfValidity(), that.getDomainOfValidity(), mode) &&
-                           deepEquals(getAnchorPoint(),      that.getAnchorPoint(),      mode) &&
-                           deepEquals(getScope(),            that.getScope(),            mode);
-                }
-                default: {
-                    /*
-                     * Tests for name, since datum with different name have completely
-                     * different meaning. We don't perform this comparison if the user
-                     * asked for metadata comparison, because in such case the names
-                     * have already been compared by the subclass.
-                     */
-                    if (!(object instanceof Datum)) break;
-                    final Datum that = (Datum) object;
-                    return nameMatches(that. getName().getCode()) ||
-                           IdentifiedObjects.nameMatches(that, getName().getCode());
-                }
+        if (!(object instanceof Datum && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
+                final AbstractDatum that = (AbstractDatum) object;
+                return this.realizationEpoch == that.realizationEpoch &&
+                       Objects.equals(this.domainOfValidity, that.domainOfValidity) &&
+                       Objects.equals(this.anchorPoint,      that.anchorPoint) &&
+                       Objects.equals(this.scope,            that.scope);
+            }
+            case BY_CONTRACT: {
+                final Datum that = (Datum) object;
+                return deepEquals(getRealizationEpoch(), that.getRealizationEpoch(), mode) &&
+                       deepEquals(getDomainOfValidity(), that.getDomainOfValidity(), mode) &&
+                       deepEquals(getAnchorPoint(),      that.getAnchorPoint(),      mode) &&
+                       deepEquals(getScope(),            that.getScope(),            mode);
+            }
+            default: {
+                /*
+                 * Tests for name, since datum with different name have completely
+                 * different meaning. We don't perform this comparison if the user
+                 * asked for metadata comparison, because in such case the names
+                 * have already been compared by the subclass.
+                 */
+                final Datum that = (Datum) object;
+                return isHeuristicMatchForName(that. getName().getCode()) ||
+                       IdentifiedObjects.isHeuristicMatchForName(that, getName().getCode());
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -298,8 +298,8 @@ public class BursaWolfParameters extends
      * @return {@code true} if the given datum is equal to WGS84 for computational purpose.
      */
     final boolean isToWGS84() {
-        return IdentifiedObjects.nameMatches(targetDatum, "WGS 84") ||
-               IdentifiedObjects.nameMatches(targetDatum, "WGS84");
+        return IdentifiedObjects.isHeuristicMatchForName(targetDatum, "WGS 84") ||
+               IdentifiedObjects.isHeuristicMatchForName(targetDatum, "WGS84");
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -180,6 +180,17 @@ public class DefaultEllipsoid extends Ab
     private Unit<Length> unit;
 
     /**
+     * Constructs a new object in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    private DefaultEllipsoid() {
+        super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE);
+        // We need to let the DefaultEllipsoid fields unitialized
+        // because afterUnmarshal(…) will check for zero values.
+    }
+
+    /**
      * Creates a new ellipsoid using the specified axis length.
      * The properties map is given unchanged to the
      * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
@@ -390,15 +401,20 @@ public class DefaultEllipsoid extends Ab
      * Sets the semi-major axis value.
      * This method is invoked by JAXB at unmarshalling time only.
      *
+     * @throws ConversionException If semi-major and semi-minor axes use inconsistent units
+     *         and we can not convert from one to the other.
+     *
      * @see #setSecondDefiningParameter(SecondDefiningParameter)
      * @see #afterUnmarshal(Object, Object)
      */
-    private void setSemiMajorAxisMeasure(final Measure uom) {
+    private void setSemiMajorAxisMeasure(final Measure measure) throws ConversionException {
         if (semiMajorAxis != 0) {
             warnDuplicated("semiMajorAxis");
         } else {
-            semiMajorAxis = uom.value;
-            unit = uom.unit.asType(Length.class);
+            final Unit<Length> uom = unit; // In case semi-minor were defined before semi-major.
+            semiMajorAxis = measure.value;
+            unit = measure.getUnit(Length.class);
+            harmonizeAxisUnits(uom);
         }
     }
 
@@ -485,6 +501,9 @@ public class DefaultEllipsoid extends Ab
      * value or the semi minor axis value, according to what have been defined in the
      * second defining parameter given. This is for JAXB unmarshalling process only.
      *
+     * @throws ConversionException If semi-major and semi-minor axes use inconsistent units
+     *         and we can not convert from one to the other.
+     *
      * @see #setSemiMajorAxisMeasure(Measure)
      * @see #afterUnmarshal(Object, Object)
      */
@@ -501,26 +520,33 @@ public class DefaultEllipsoid extends Ab
                     ivfDefinitive = true;
                     return;
                 }
-            } else {
-                final Unit<?> uom = measure.unit;
-                if (uom != null) {
-                    if (unit != null) {
-                        value = uom.getConverterToAny(unit).convert(value);
-                    } else {
-                        unit = uom.asType(Length.class);
-                    }
-                }
-                if (semiMinorAxis == 0) {
-                    semiMinorAxis = value;
-                    ivfDefinitive = false;
-                    return;
+            } else if (semiMinorAxis == 0) {
+                semiMinorAxis = value;
+                ivfDefinitive = false;
+                if (unit == null) {
+                    unit = measure.getUnit(Length.class);
+                } else {
+                    harmonizeAxisUnits(measure.unit);
                 }
+                return;
             }
             warnDuplicated("secondDefiningParameter");
         }
     }
 
     /**
+     * Ensures that the semi-minor axis uses the same unit than the semi-major one.
+     *
+     * @throws ConversionException If semi-major and semi-minor axes use inconsistent units
+     *         and we can not convert from one to the other.
+     */
+    private void harmonizeAxisUnits(final Unit<?> uom) throws ConversionException {
+        if (uom != null && uom != unit) {
+            semiMinorAxis = uom.getConverterToAny(unit).convert(semiMinorAxis);
+        }
+    }
+
+    /**
      * Emits a warning telling that the given element is repeated twice.
      */
     private void warnDuplicated(final String element) {
@@ -645,42 +671,45 @@ public class DefaultEllipsoid extends Ab
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
+    @SuppressWarnings("fallthrough")
     public boolean equals(final Object object, final ComparisonMode mode) {
         if (object == this) {
             return true; // Slight optimization.
         }
-        if (super.equals(object, mode)) {
-            if (mode == ComparisonMode.STRICT) {
+        if (!(object instanceof Ellipsoid && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
                 final DefaultEllipsoid that = (DefaultEllipsoid) object;
                 return ivfDefinitive == that.ivfDefinitive &&
                        Numerics.equals(this.semiMajorAxis,     that.semiMajorAxis)     &&
                        Numerics.equals(this.semiMinorAxis,     that.semiMinorAxis)     &&
                        Numerics.equals(this.inverseFlattening, that.inverseFlattening) &&
                         Objects.equals(this.unit,              that.unit);
-            } else {
-                if (object instanceof Ellipsoid) {
-                    final Ellipsoid that = (Ellipsoid) object;
-                    if (mode == ComparisonMode.BY_CONTRACT) {
-                        /*
-                         * isIvfDefinitive has no incidence on calculation using ellipsoid parameters,
-                         * so we consider it as metadata that can be ignored in IGNORE_METADATA mode.
-                         */
-                        if (isIvfDefinitive() != that.isIvfDefinitive()) {
-                            return false;
-                        }
-                    }
-                    return epsilonEqual(getSemiMajorAxis(),     that.getSemiMajorAxis(),     mode) &&
-                           epsilonEqual(getSemiMinorAxis(),     that.getSemiMinorAxis(),     mode) &&
-                           epsilonEqual(getInverseFlattening(), that.getInverseFlattening(), mode) &&
-                           Objects.equals(getAxisUnit(),        that.getAxisUnit());
+            }
+            case BY_CONTRACT: {
+                /*
+                 * isIvfDefinitive has no incidence on calculation using ellipsoid parameters,
+                 * so we consider it as metadata that can be ignored in IGNORE_METADATA mode.
+                 */
+                if (isIvfDefinitive() != ((Ellipsoid) object).isIvfDefinitive()) {
+                    return false;
                 }
+                // Fall through
+            }
+            default: {
+                final Ellipsoid that = (Ellipsoid) object;
+                return epsilonEqual(getSemiMajorAxis(),     that.getSemiMajorAxis(),     mode) &&
+                       epsilonEqual(getSemiMinorAxis(),     that.getSemiMinorAxis(),     mode) &&
+                       epsilonEqual(getInverseFlattening(), that.getInverseFlattening(), mode) &&
+                       Objects.equals(getAxisUnit(),        that.getAxisUnit());
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -49,6 +49,14 @@ public class DefaultEngineeringDatum ext
     private static final long serialVersionUID = 1498304918725248637L;
 
     /**
+     * Constructs a new datum in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    private DefaultEngineeringDatum() {
+    }
+
+    /**
      * Creates an engineering datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      * The following table is a reminder of main (not all) properties:
@@ -86,12 +94,12 @@ public class DefaultEngineeringDatum ext
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td>
-     *     <td>{@link Date}</td>
+     *     <td>{@link java.util.Date}</td>
      *     <td>{@link #getRealizationEpoch()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#DOMAIN_OF_VALIDITY_KEY}</td>
-     *     <td>{@link Extent}</td>
+     *     <td>{@link org.opengis.metadata.extent.Extent}</td>
      *     <td>{@link #getDomainOfValidity()}</td>
      *   </tr>
      *   <tr>
@@ -143,7 +151,7 @@ public class DefaultEngineeringDatum ext
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
@@ -151,7 +159,7 @@ public class DefaultEngineeringDatum ext
         if (object == this) {
             return true; // Slight optimization.
         }
-        return  (object instanceof EngineeringDatum) && super.equals(object, mode);
+        return (object instanceof EngineeringDatum) && super.equals(object, mode);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -154,6 +154,17 @@ public class DefaultGeodeticDatum extend
     private final BursaWolfParameters[] bursaWolf;
 
     /**
+     * Constructs a new datum in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    private DefaultGeodeticDatum() {
+        ellipsoid     = null;
+        primeMeridian = null;
+        bursaWolf     = null;
+    }
+
+    /**
      * Creates a geodetic datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      * In addition to the properties documented in the parent constructor,
@@ -418,7 +429,7 @@ public class DefaultGeodeticDatum extend
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
@@ -426,33 +437,32 @@ public class DefaultGeodeticDatum extend
         if (object == this) {
             return true; // Slight optimization.
         }
-        if (super.equals(object, mode)) {
-            switch (mode) {
-                case STRICT: {
-                    final DefaultGeodeticDatum that = (DefaultGeodeticDatum) object;
-                    return Objects.equals(this.ellipsoid,     that.ellipsoid)     &&
-                           Objects.equals(this.primeMeridian, that.primeMeridian) &&
-                            Arrays.equals(this.bursaWolf,     that.bursaWolf);
-                }
-                default: {
-                    if (!(object instanceof GeodeticDatum)) break;
-                    final GeodeticDatum that = (GeodeticDatum) object;
-                    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).
-                     */
-                }
+        if (!(object instanceof GeodeticDatum && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
+                final DefaultGeodeticDatum that = (DefaultGeodeticDatum) object;
+                return Objects.equals(this.ellipsoid,     that.ellipsoid)     &&
+                       Objects.equals(this.primeMeridian, that.primeMeridian) &&
+                        Arrays.equals(this.bursaWolf,     that.bursaWolf);
+            }
+            default: {
+                final GeodeticDatum that = (GeodeticDatum) object;
+                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).
+                 */
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -59,6 +59,15 @@ public class DefaultImageDatum extends A
     private final PixelInCell pixelInCell;
 
     /**
+     * Constructs a new datum in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    private DefaultImageDatum() {
+        pixelInCell = null;
+    }
+
+    /**
      * Creates an image datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      * The following table is a reminder of main (not all) properties:
@@ -96,12 +105,12 @@ public class DefaultImageDatum extends A
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td>
-     *     <td>{@link Date}</td>
+     *     <td>{@link java.util.Date}</td>
      *     <td>{@link #getRealizationEpoch()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#DOMAIN_OF_VALIDITY_KEY}</td>
-     *     <td>{@link Extent}</td>
+     *     <td>{@link org.opengis.metadata.extent.Extent}</td>
      *     <td>{@link #getDomainOfValidity()}</td>
      *   </tr>
      *   <tr>
@@ -167,7 +176,7 @@ public class DefaultImageDatum extends A
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
@@ -175,20 +184,17 @@ public class DefaultImageDatum extends A
         if (object == this) {
             return true; // Slight optimization.
         }
-        if (super.equals(object, mode)) {
-            switch (mode) {
-                case STRICT: {
-                    final DefaultImageDatum that = (DefaultImageDatum) object;
-                    return Objects.equals(this.pixelInCell, that.pixelInCell);
-                }
-                default: {
-                    if (!(object instanceof ImageDatum)) break;
-                    final ImageDatum that = (ImageDatum) object;
-                    return Objects.equals(getPixelInCell(), that.getPixelInCell());
-                }
+        if (!(object instanceof ImageDatum && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
+                return Objects.equals(pixelInCell, ((DefaultImageDatum) object).pixelInCell);
+            }
+            default: {
+                return Objects.equals(getPixelInCell(), ((ImageDatum) object).getPixelInCell());
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -28,6 +28,7 @@ import org.opengis.util.InternationalStr
 import org.opengis.referencing.ReferenceIdentifier;
 import org.opengis.referencing.datum.PrimeMeridian;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
+import org.apache.sis.internal.jaxb.gco.Measure;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.util.ComparisonMode;
@@ -89,14 +90,26 @@ public class DefaultPrimeMeridian extend
 
     /**
      * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward.
+     *
+     * <p>Consider this field as final. It is declared non-final only for JAXB unmarshalling.</p>
      */
-    @XmlElement(required = true)
-    private final double greenwichLongitude;
+    private double greenwichLongitude;
 
     /**
      * The angular unit of the {@linkplain #getGreenwichLongitude() Greenwich longitude}.
+     *
+     * <p>Consider this field as final. It is declared non-final only for JAXB unmarshalling.</p>
+     */
+    private Unit<Angle> angularUnit;
+
+    /**
+     * Constructs a new object in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
      */
-    private final Unit<Angle> angularUnit;
+    private DefaultPrimeMeridian() {
+        super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE);
+    }
 
     /**
      * Creates a prime meridian from the given properties. The properties map is given unchanged to the
@@ -214,12 +227,28 @@ public class DefaultPrimeMeridian extend
     }
 
     /**
+     * Invoked by JAXB for obtaining the Greenwich longitude to marshall together with its {@code "uom"} attribute.
+     */
+    @XmlElement(name = "greenwichLongitude", required = true)
+    private Measure getGreenwichMeasure() {
+        return new Measure(greenwichLongitude, angularUnit);
+    }
+
+    /**
+     * Invoked by JAXB for setting the Greenwich longitude and its unit of measurement.
+     */
+    private void setGreenwichMeasure(final Measure measure) {
+        greenwichLongitude = measure.value;
+        angularUnit = measure.getUnit(Angle.class);
+    }
+
+    /**
      * Compares this prime meridian with the specified object for equality.
      *
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
@@ -227,33 +256,31 @@ public class DefaultPrimeMeridian extend
         if (object == this) {
             return true; // Slight optimization.
         }
-        if (super.equals(object, mode)) {
-            switch (mode) {
-                case STRICT: {
-                    final DefaultPrimeMeridian that = (DefaultPrimeMeridian) object;
-                    return Numerics.equals(this.greenwichLongitude, that.greenwichLongitude) &&
-                            Objects.equals(this.angularUnit,        that.angularUnit);
-                }
-                case BY_CONTRACT: {
-                    if (!(object instanceof PrimeMeridian)) break;
-                    final PrimeMeridian that = (PrimeMeridian) object;
-                    return Numerics.equals(getGreenwichLongitude(), that.getGreenwichLongitude()) &&
-                            Objects.equals(getAngularUnit(),        that.getAngularUnit());
-                }
-                default: {
-                    if (!(object instanceof PrimeMeridian)) break;
-                    final DefaultPrimeMeridian that = castOrCopy((PrimeMeridian) object);
-                    return Numerics.epsilonEqual(this.getGreenwichLongitude(NonSI.DEGREE_ANGLE),
-                                                 that.getGreenwichLongitude(NonSI.DEGREE_ANGLE), mode);
-                    /*
-                     * Note: if mode==IGNORE_METADATA, we relax the unit check because EPSG uses
-                     *       sexagesimal degrees for the Greenwich meridian. Requirying the same
-                     *       unit prevent Geodetic.isWGS84(...) method to recognize EPSG's WGS84.
-                     */
-                }
+        if (!(object instanceof PrimeMeridian && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
+                final DefaultPrimeMeridian that = (DefaultPrimeMeridian) object;
+                return Numerics.equals(this.greenwichLongitude, that.greenwichLongitude) &&
+                        Objects.equals(this.angularUnit,        that.angularUnit);
+            }
+            case BY_CONTRACT: {
+                final PrimeMeridian that = (PrimeMeridian) object;
+                return Numerics.equals(getGreenwichLongitude(), that.getGreenwichLongitude()) &&
+                        Objects.equals(getAngularUnit(),        that.getAngularUnit());
+            }
+            default: {
+                final DefaultPrimeMeridian that = castOrCopy((PrimeMeridian) object);
+                return Numerics.epsilonEqual(this.getGreenwichLongitude(NonSI.DEGREE_ANGLE),
+                                             that.getGreenwichLongitude(NonSI.DEGREE_ANGLE), mode);
+                /*
+                 * Note: if mode==IGNORE_METADATA, we relax the unit check because EPSG uses
+                 *       sexagesimal degrees for the Greenwich meridian. Requirying the same
+                 *       unit prevent Geodetic.isWGS84(...) method to recognize EPSG's WGS84.
+                 */
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultTemporalDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultTemporalDatum.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultTemporalDatum.java (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultTemporalDatum.java Sat Dec  7 02:00:42 2013
@@ -24,6 +24,7 @@ import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.referencing.ReferenceIdentifier;
 import org.opengis.referencing.datum.TemporalDatum;
+import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Immutable;
 
@@ -77,11 +78,22 @@ public class DefaultTemporalDatum extend
     private static final long serialVersionUID = 3357241732140076884L;
 
     /**
-     * The date and time origin of this temporal datum.
+     * The date and time origin of this temporal datum, or {@link Long#MIN_VALUE} if none.
+     * This information is mandatory, but SIS is tolerant to missing value is case a XML
+     * fragment was incomplete.
      */
     private final long origin;
 
     /**
+     * Constructs a new datum in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    private DefaultTemporalDatum() {
+        origin = Long.MIN_VALUE;
+    }
+
+    /**
      * Creates a temporal datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      * The following table is a reminder of main (not all) properties:
@@ -124,7 +136,7 @@ public class DefaultTemporalDatum extend
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#DOMAIN_OF_VALIDITY_KEY}</td>
-     *     <td>{@link Extent}</td>
+     *     <td>{@link org.opengis.metadata.extent.Extent}</td>
      *     <td>{@link #getDomainOfValidity()}</td>
      *   </tr>
      *   <tr>
@@ -156,7 +168,7 @@ public class DefaultTemporalDatum extend
      */
     protected DefaultTemporalDatum(final TemporalDatum datum) {
         super(datum);
-        origin = datum.getOrigin().getTime();
+        origin = MetadataUtilities.toMilliseconds(datum.getOrigin());
     }
 
     /**
@@ -181,7 +193,7 @@ public class DefaultTemporalDatum extend
      */
     @Override
     public Date getOrigin() {
-        return new Date(origin);
+        return MetadataUtilities.toDate(origin);
     }
 
     /**
@@ -190,7 +202,7 @@ public class DefaultTemporalDatum extend
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
@@ -198,20 +210,17 @@ public class DefaultTemporalDatum extend
         if (object == this) {
             return true; // Slight optimization.
         }
-        if (super.equals(object, mode)) {
-            switch (mode) {
-                case STRICT: {
-                    final DefaultTemporalDatum that = (DefaultTemporalDatum) object;
-                    return this.origin == that.origin;
-                }
-                default: {
-                    if (!(object instanceof TemporalDatum)) break;
-                    final TemporalDatum that = (TemporalDatum) object;
-                    return Objects.equals(getOrigin(), that.getOrigin());
-                }
+        if (!(object instanceof TemporalDatum && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
+                return origin == ((DefaultTemporalDatum) object).origin;
+            }
+            default: {
+                return Objects.equals(getOrigin(), ((TemporalDatum) object).getOrigin());
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -29,7 +29,7 @@ import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.util.Immutable;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.internal.jaxb.Context;
-import org.apache.sis.internal.jaxb.gml.GMLAdapter;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
 import org.apache.sis.internal.referencing.VerticalDatumTypes;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -90,6 +90,14 @@ public class DefaultVerticalDatum extend
     private VerticalDatumType type;
 
     /**
+     * Constructs a new datum in which every attributes are set to a null value.
+     * <strong>This is not a valid object.</strong> This constructor is strictly
+     * reserved to JAXB, which will assign values to the fields using reflexion.
+     */
+    private DefaultVerticalDatum() {
+    }
+
+    /**
      * Creates a vertical datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      * The following table is a reminder of main (not all) properties:
@@ -127,12 +135,12 @@ public class DefaultVerticalDatum extend
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td>
-     *     <td>{@link Date}</td>
+     *     <td>{@link java.util.Date}</td>
      *     <td>{@link #getRealizationEpoch()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.referencing.datum.Datum#DOMAIN_OF_VALIDITY_KEY}</td>
-     *     <td>{@link Extent}</td>
+     *     <td>{@link org.opengis.metadata.extent.Extent}</td>
      *     <td>{@link #getDomainOfValidity()}</td>
      *   </tr>
      *   <tr>
@@ -226,14 +234,14 @@ public class DefaultVerticalDatum extend
      * This element was present in GML 3.0 and 3.1, but has been removed from GML 3.2.
      */
     @XmlElement(name = "verticalDatumType")
-    private VerticalDatumType getMarshalled() {
-        return (Context.isGMLVersion(Context.current(), GMLAdapter.GML_3_2)) ? null : getVerticalDatumType();
+    private VerticalDatumType getTypeElement() {
+        return Context.isGMLVersion(Context.current(), LegacyNamespaces.VERSION_3_2) ? null : getVerticalDatumType();
     }
 
     /**
      * Invoked by JAXB only. The vertical datum type is set only if it has not already been specified.
      */
-    private void setMarshalled(final VerticalDatumType t) {
+    private void setTypeElement(final VerticalDatumType t) {
         if (type != null) {
             throw new IllegalStateException();
         }
@@ -246,7 +254,7 @@ public class DefaultVerticalDatum extend
      * @param  object The object to compare to {@code this}.
      * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
      *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
-     *         relevant to transformations.
+     *         relevant to coordinate transformations.
      * @return {@code true} if both objects are equal.
      */
     @Override
@@ -254,20 +262,17 @@ public class DefaultVerticalDatum extend
         if (object == this) {
             return true; // Slight optimization.
         }
-        if (super.equals(object, mode)) {
-            switch (mode) {
-                case STRICT: {
-                    final DefaultVerticalDatum that = (DefaultVerticalDatum) object;
-                    return Objects.equals(this.type(), that.type());
-                }
-                default: {
-                    if (!(object instanceof VerticalDatum)) break;
-                    final VerticalDatum that = (VerticalDatum) object;
-                    return Objects.equals(getVerticalDatumType(), that.getVerticalDatumType());
-                }
+        if (!(object instanceof VerticalDatum && super.equals(object, mode))) {
+            return false;
+        }
+        switch (mode) {
+            case STRICT: {
+                return Objects.equals(type(), ((DefaultVerticalDatum) object).type());
+            }
+            default: {
+                return Objects.equals(getVerticalDatumType(), ((VerticalDatum) object).getVerticalDatumType());
             }
         }
-        return false;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.opera
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.cs.AxisDirection;
+import org.opengis.referencing.cs.CoordinateSystem; // For javadoc
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.geometry.MismatchedDimensionException;
@@ -322,6 +323,7 @@ public final class Matrices extends Stat
      *
      * @see #createTransform(AxisDirection[], AxisDirection[])
      * @see #createTransform(Envelope, AxisDirection[], Envelope, AxisDirection[])
+     * @see org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes(CoordinateSystem, CoordinateSystem)
      */
     public static MatrixSIS createTransform(final Envelope srcEnvelope, final Envelope dstEnvelope) {
         ArgumentChecks.ensureNonNull("srcEnvelope", srcEnvelope);
@@ -399,6 +401,7 @@ public final class Matrices extends Stat
      *
      * @see #createTransform(Envelope, Envelope)
      * @see #createTransform(Envelope, AxisDirection[], Envelope, AxisDirection[])
+     * @see org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes(CoordinateSystem, CoordinateSystem)
      */
     public static MatrixSIS createTransform(final AxisDirection[] srcAxes, final AxisDirection[] dstAxes) {
         ArgumentChecks.ensureNonNull("srcAxes", srcAxes);
@@ -462,6 +465,7 @@ public final class Matrices extends Stat
      *
      * @see #createTransform(Envelope, Envelope)
      * @see #createTransform(AxisDirection[], AxisDirection[])
+     * @see org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes(CoordinateSystem, CoordinateSystem)
      */
     public static MatrixSIS createTransform(final Envelope srcEnvelope, final AxisDirection[] srcAxes,
                                             final Envelope dstEnvelope, final AxisDirection[] dstAxes)
@@ -920,7 +924,7 @@ public final class Matrices extends Stat
                      */
                     int s = element.lastIndexOf('.');
                     if (s < 0) {
-                        element = element.replace("Infinity", "∞");
+                        element = CharSequences.replace(element, "Infinity", "∞").toString();
                         width = spacing + element.length();
                         widthBeforeFraction[i] = (byte) Math.max(widthBeforeFraction[i], width);
                     } else {

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java Sat Dec  7 02:00:42 2013
@@ -16,7 +16,7 @@
  */
 
 /**
- * {@linkplain org.opengis.referencing.operation.Matrix} implementations for spatio-temporal referencing.
+ * Matrix implementations for spatio-temporal referencing.
  * Matrices can be of arbitrary size, but the most common ones in the context of geospatial coordinate operations
  * are not greater than 5×5 (number of spatio-temporal dimensions + 1).
  * This package differs from other matrix packages by:

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -16,13 +16,14 @@
  */
 
 /**
- * {@linkplain org.apache.sis.referencing.AbstractReferenceSystem Reference system} implementations.
+ * Base classes for reference systems used for general positioning.
  * An explanation for this package is provided in the {@linkplain org.opengis.referencing OpenGIS® javadoc}.
  * The remaining discussion on this page is specific to the SIS implementation.
  *
- * <p>This package provides implementations for general positioning, coordinate reference systems (CRS),
- * and coordinate transformations. Coordinates can have any number of dimensions. So this implementation
- * can handle 2D and 3D coordinates, as well as 4D, 5D, <i>etc.</i></p>
+ * <p>The most commonly used kinds of Reference Systems in Apache SIS are the <cite>Coordinate Reference Systems</cite>
+ * (CRS), which handle coordinates of arbitrary dimensions. The SIS implementations can handle 2D and 3D coordinates,
+ * as well as 4D, 5D, <i>etc</i>. An other less-frequently used kind of Reference System uses labels instead, as in
+ * postal address. This package is the root for both kinds, with an emphasis on the one for coordinates.</p>
  *
  * {@section Fetching geodetic object instances}
  * Geodetic objects can be instantiated either directly by specifying all information to a factory method
@@ -69,4 +70,28 @@
  * @version 0.4
  * @module
  */
+@XmlSchema(elementFormDefault = XmlNsForm.QUALIFIED, namespace = Namespaces.GML, xmlns = {
+    @XmlNs(prefix = "gml", namespaceURI = Namespaces.GML),
+    @XmlNs(prefix = "gmd", namespaceURI = Namespaces.GMD)
+})
+@XmlAccessorType(XmlAccessType.NONE)
+@XmlJavaTypeAdapters({
+    @XmlJavaTypeAdapter(CI_Citation.class),
+    @XmlJavaTypeAdapter(RS_Identifier.class),
+    @XmlJavaTypeAdapter(StringAdapter.class),
+    @XmlJavaTypeAdapter(InternationalStringConverter.class)
+})
 package org.apache.sis.referencing;
+
+import javax.xml.bind.annotation.XmlNs;
+import javax.xml.bind.annotation.XmlNsForm;
+import javax.xml.bind.annotation.XmlSchema;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
+
+import org.apache.sis.xml.Namespaces;
+import org.apache.sis.internal.jaxb.gco.*;
+import org.apache.sis.internal.jaxb.metadata.*;
+import org.apache.sis.internal.jaxb.referencing.RS_Identifier;

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -37,7 +37,7 @@ import static org.apache.sis.metadata.is
  * @version 0.4
  * @module
  */
-@DependsOn(NamedIdentifierTest.class)
+@DependsOn({IdentifiedObjectsTest.class, NamedIdentifierTest.class})
 public final strictfp class AbstractIdentifiedObjectTest extends TestCase {
     /**
      * Tests the {@link AbstractIdentifiedObject#AbstractIdentifiedObject(Map)} constructor.
@@ -57,7 +57,6 @@ public final strictfp class AbstractIden
         assertNull  ("version",                           object.getName().getVersion());
         assertTrue  ("aliases",                           object.getAlias().isEmpty());
         assertTrue  ("identifiers",                       object.getIdentifiers().isEmpty());
-        assertNull  ("identifier",                        object.getIdentifier());
         assertEquals("ID",         "Thisisaname",         object.getID());
         assertEquals("remarks",    "There is remarks",    object.getRemarks().toString(Locale.ENGLISH));
         assertEquals("remarks_fr", "Voici des remarques", object.getRemarks().toString(Locale.FRENCH));
@@ -90,7 +89,6 @@ public final strictfp class AbstractIden
 
         assertEquals("name",        "WGS 84",                     object.getName().getCode());
         assertEquals("identifiers", "[EPSG:4326, EPSG:IgnoreMe]", object.getIdentifiers().toString());
-        assertEquals("identifier",  "EPSG:4326",                  object.getIdentifier().toString());
         assertEquals("ID",          "EPSG4326",                   object.getID());
     }
 

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectsTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectsTest.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectsTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticObjectsTest.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -18,6 +18,9 @@ package org.apache.sis.referencing;
 
 import java.util.Date;
 import org.opengis.referencing.datum.TemporalDatum;
+import org.opengis.referencing.datum.VerticalDatum;
+import org.opengis.referencing.datum.VerticalDatumType;
+import org.apache.sis.internal.referencing.VerticalDatumTypes;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.opengis.test.Validators;
@@ -47,6 +50,26 @@ public final strictfp class GeodeticObje
     private static final double DAY_LENGTH = 24 * 60 * 60 * 1000;
 
     /**
+     * Verifies the vertical datum enumeration.
+     */
+    @Test
+    public void testVertical() {
+        assertEquals(VerticalDatumType. BAROMETRIC,    type(GeodeticObjects.Vertical.BAROMETRIC));
+        assertEquals(VerticalDatumType. GEOIDAL,       type(GeodeticObjects.Vertical.GEOIDAL));
+        assertEquals(VerticalDatumTypes.ELLIPSOIDAL,   type(GeodeticObjects.Vertical.ELLIPSOIDAL));
+        assertEquals(VerticalDatumType. OTHER_SURFACE, type(GeodeticObjects.Vertical.OTHER_SURFACE));
+    }
+
+    /**
+     * Validates the datum of the given enumeration, then returns its datum type.
+     */
+    private static VerticalDatumType type(final GeodeticObjects.Vertical e) {
+        final VerticalDatum datum = e.datum();
+        Validators.validate(datum);
+        return datum.getVerticalDatumType();
+    }
+
+    /**
      * Verifies the epoch values of temporal enumeration compared to the Julian epoch.
      *
      * @see <a href="http://en.wikipedia.org/wiki/Julian_day">Wikipedia: Julian day</a>

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -21,7 +21,6 @@ import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.test.mock.GeodeticDatumMock;
-import org.apache.sis.test.TestCase;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.DependsOnMethod;
@@ -40,7 +39,7 @@ import static org.apache.sis.referencing
  * @module
  */
 @DependsOn(org.apache.sis.internal.referencing.FormulasTest.class)
-public final strictfp class DefaultEllipsoidTest extends TestCase {
+public final strictfp class DefaultEllipsoidTest extends DatumTestCase {
     /**
      * Half of a minute of angle, in degrees.
      */

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -29,7 +29,6 @@ import org.apache.sis.metadata.iso.exten
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
-import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.apache.sis.referencing.Assert.*;
@@ -49,7 +48,7 @@ import static org.apache.sis.test.mock.G
   DefaultEllipsoidTest.class,
   BursaWolfParametersTest.class
 })
-public final strictfp class DefaultGeodeticDatumTest extends TestCase {
+public final strictfp class DefaultGeodeticDatumTest extends DatumTestCase {
     /**
      * Tests the creation and serialization of a {@link DefaultGeodeticDatum}.
      */
@@ -91,6 +90,20 @@ public final strictfp class DefaultGeode
     }
 
     /**
+     * Tests {@link DefaultGeodeticDatum#isHeuristicMatchForName(String)}.
+     */
+    @Test
+    public void testIsHeuristicMatchForName() {
+        final DefaultGeodeticDatum datum = new DefaultGeodeticDatum(WGS84);
+        assertFalse(datum.isHeuristicMatchForName("WGS72"));
+        assertTrue (datum.isHeuristicMatchForName("WGS84"));
+        assertTrue (datum.isHeuristicMatchForName("WGS 84"));
+        assertTrue (datum.isHeuristicMatchForName("WGS_84"));
+        assertTrue (datum.isHeuristicMatchForName("D_WGS_84"));
+        assertFalse(datum.isHeuristicMatchForName("E_WGS_84"));
+    }
+
+    /**
      * Tests {@link DefaultGeodeticDatum#getPositionVectorTransformation(GeodeticDatum, Extent)}.
      */
     @Test



Mime
View raw message