sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1675207 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/internal/referencing/ sis-referencing/src/main/java/org/apache/sis/parameter/ sis-referencing/src/main/java/org/apache/sis/referencing/ sis-referencing/src/...
Date Tue, 21 Apr 2015 23:03:17 GMT
Author: desruisseaux
Date: Tue Apr 21 23:03:17 2015
New Revision: 1675207

URL: http://svn.apache.org/r1675207
Log:
Referencing: initial port of AbstractCoordinateOperation, together with an opportunist improvement of WKT2 formatting of axes.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/OperationMethods.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/SubTypes.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultCompoundCRSTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultGeographicCRSTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxisTest.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/OperationMethods.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/OperationMethods.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/OperationMethods.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/OperationMethods.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -17,11 +17,17 @@
 package org.apache.sis.internal.referencing;
 
 import java.util.Map;
+import java.util.Collection;
+import org.opengis.util.Record;
+import javax.measure.unit.SI;
+import javax.measure.unit.Unit;
+import javax.measure.quantity.Length;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
-import org.opengis.referencing.operation.Matrix;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.metadata.quality.Result;
+import org.opengis.metadata.quality.PositionalAccuracy;
+import org.opengis.metadata.quality.QuantitativeResult;
+import org.opengis.referencing.operation.*;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.PassThroughTransform;
@@ -30,6 +36,7 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.Static;
+import org.apache.sis.measure.Units;
 
 
 /**
@@ -38,17 +45,60 @@ import org.apache.sis.util.Static;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.6
  * @module
  */
 public final class OperationMethods extends Static {
     /**
+     * The key for specifying explicitely the value to be returned by
+     * {@link org.apache.sis.referencing.operation.DefaultSingleOperation#getParameterValues()}.
+     * It is usually not necessary to specify those parameters because they are inferred either from
+     * the {@link MathTransform}, or specified explicitely in a {@code DefiningConversion}. However
+     * there is a few cases, for example the Molodenski transform, where none of the above can apply,
+     * because SIS implements those operations as a concatenation of math transforms, and such
+     * concatenations do not have {@link org.opengis.parameter.ParameterValueGroup}.
+     */
+    public static final String PARAMETERS_KEY = "parameters";
+
+    /**
      * Do not allow instantiation of this class.
      */
     private OperationMethods() {
     }
 
     /**
+     * Returns the most specific {@link CoordinateOperation} interface implemented by the specified operation.
+     * Special cases:
+     *
+     * <ul>
+     *   <li>If the operation implements the {@link Transformation} interface,
+     *       then this method returns {@code Transformation.class}. Transformation
+     *       has precedence over any other interface implemented by the operation.</li>
+     *   <li>Otherwise if the operation implements the {@link Conversion} interface,
+     *       then this method returns the most specific {@code Conversion} sub-interface.</li>
+     *   <li>Otherwise if the operation implements the {@link SingleOperation} interface,
+     *       then this method returns {@code SingleOperation.class}.</li>
+     *   <li>Otherwise if the operation implements the {@link ConcatenatedOperation} interface,
+     *       then this method returns {@code ConcatenatedOperation.class}.</li>
+     *   <li>Otherwise this method returns {@code CoordinateOperation.class}.</li>
+     * </ul>
+     *
+     * @param  operation A coordinate operation.
+     * @return The most specific GeoAPI interface implemented by the given operation.
+     */
+    public static Class<? extends CoordinateOperation> getType(final CoordinateOperation operation) {
+        if (operation instanceof        Transformation) return        Transformation.class;
+        if (operation instanceof       ConicProjection) return       ConicProjection.class;
+        if (operation instanceof CylindricalProjection) return CylindricalProjection.class;
+        if (operation instanceof      PlanarProjection) return      PlanarProjection.class;
+        if (operation instanceof            Projection) return            Projection.class;
+        if (operation instanceof            Conversion) return            Conversion.class;
+        if (operation instanceof       SingleOperation) return       SingleOperation.class;
+        if (operation instanceof ConcatenatedOperation) return ConcatenatedOperation.class;
+        return CoordinateOperation.class;
+    }
+
+    /**
      * Determines whether a match or mismatch is found between the two given collections of identifiers.
      * If any of the given collections is {@code null} or empty, this method returns {@code null}.
      *
@@ -207,4 +257,100 @@ public final class OperationMethods exte
         }
         return false;
     }
+
+    /**
+     * Convenience method returning the accuracy in meters for the specified operation.
+     * This method tries each of the following procedures and returns the first successful one:
+     *
+     * <ul>
+     *   <li>If a {@link QuantitativeResult} is found with a linear unit, then this accuracy estimate
+     *       is converted to {@linkplain SI#METRE metres} and returned.</li>
+     *
+     *   <li>Otherwise, if the operation is a {@link Conversion}, then returns 0 since a conversion
+     *       is by definition accurate up to rounding errors.</li>
+     *
+     *   <li>Otherwise, if the operation is a {@link Transformation}, then checks if the datum shift
+     *       were applied with the help of Bursa-Wolf parameters. This procedure looks for SIS-specific
+     *       {@link PositionalAccuracyConstant#DATUM_SHIFT_APPLIED} and
+     *       {@link PositionalAccuracyConstant#DATUM_SHIFT_OMITTED DATUM_SHIFT_OMITTED} constants.
+     *       If a datum shift has been applied, returns 25 meters.
+     *       If a datum shift should have been applied but has been omitted, returns 1000 meters.
+     *       The 1000 meters value is higher than the highest value (999 meters) found in the EPSG
+     *       database version 6.7. The 25 meters value is the next highest value found in the EPSG
+     *       database for a significant number of transformations.
+     *
+     *   <li>Otherwise, if the operation is a {@link ConcatenatedOperation}, returns the sum of the accuracy
+     *       of all components. This is a conservative scenario where we assume that errors cumulate linearly.
+     *       Note that this is not necessarily the "worst case" scenario since the accuracy could be worst
+     *       if the math transforms are highly non-linear.</li>
+     * </ul>
+     *
+     * @param  operation The operation to inspect for accuracy.
+     * @return The accuracy estimate (always in meters), or NaN if unknown.
+     */
+    public static double getLinearAccuracy(final CoordinateOperation operation) {
+        final Collection<PositionalAccuracy> accuracies = operation.getCoordinateOperationAccuracy();
+        for (final PositionalAccuracy accuracy : accuracies) {
+            for (final Result result : accuracy.getResults()) {
+                if (result instanceof QuantitativeResult) {
+                    final QuantitativeResult quantity = (QuantitativeResult) result;
+                    final Collection<? extends Record> records = quantity.getValues();
+                    if (records != null) {
+                        final Unit<?> unit = quantity.getValueUnit();
+                        if (Units.isLinear(unit)) {
+                            final Unit<Length> unitOfLength = unit.asType(Length.class);
+                            for (final Record record : records) {
+                                for (final Object value : record.getAttributes().values()) {
+                                    if (value instanceof Number) {
+                                        double v = ((Number) value).doubleValue();
+                                        v = unitOfLength.getConverterTo(SI.METRE).convert(v);
+                                        return v;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        /*
+         * No quantitative (linear) accuracy were found. If the coordinate operation is actually
+         * a conversion, the accuracy is up to rounding error (i.e. conceptually 0) by definition.
+         */
+        if (operation instanceof Conversion) {
+            return 0;
+        }
+        /*
+         * If the coordinate operation is actually a transformation, checks if Bursa-Wolf parameters
+         * were available for the datum shift. This is SIS-specific. See method javadoc for a rational
+         * about the return values chosen.
+         */
+        if (operation instanceof Transformation) {
+            if (!accuracies.contains(PositionalAccuracyConstant.DATUM_SHIFT_OMITTED)) {
+                if (accuracies.contains(PositionalAccuracyConstant.DATUM_SHIFT_APPLIED)) {
+                    return 25;
+                }
+            }
+            return 1000;
+        }
+        /*
+         * If the coordinate operation is a compound of other coordinate operations, returns the sum of their accuracy,
+         * skipping unknown ones. Making the sum is a conservative approach (not exactly the "worst case" scenario,
+         * since it could be worst if the transforms are highly non-linear).
+         */
+        double accuracy = Double.NaN;
+        if (operation instanceof ConcatenatedOperation) {
+            for (final SingleOperation op : ((ConcatenatedOperation) operation).getOperations()) {
+                final double candidate = Math.abs(getLinearAccuracy(op));
+                if (!Double.isNaN(candidate)) {
+                    if (Double.isNaN(accuracy)) {
+                        accuracy = candidate;
+                    } else {
+                        accuracy += candidate;
+                    }
+                }
+            }
+        }
+        return accuracy;
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -848,6 +848,12 @@ public class DefaultParameterValue<T> ex
             }
             formatter.append(convertedValue);
         } else {
+            if (!isWKT1 && (unit == null) && (value instanceof URI || value instanceof URL
+                    || value instanceof File || value instanceof Path))
+            {
+                formatter.append(value.toString(), null);
+                return "ParameterFile";
+            }
             formatter.appendAny(value);
         }
         /*

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -431,6 +431,7 @@ public class AbstractIdentifiedObject ex
      *       {@link org.opengis.referencing.datum.Datum},
      *       {@link org.opengis.referencing.datum.Ellipsoid},
      *       {@link org.opengis.referencing.datum.PrimeMeridian},
+     *       {@link org.opengis.referencing.operation.OperationMethod},
      *       {@link org.opengis.parameter.ParameterDescriptor} or
      *       {@link org.opengis.parameter.ParameterDescriptorGroup},
      *       then this method delegates to the {@code castOrCopy(…)} method of the corresponding SIS subclass.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/SubTypes.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/SubTypes.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/SubTypes.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/SubTypes.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -23,6 +23,7 @@ import org.opengis.referencing.crs.Coord
 import org.opengis.referencing.datum.Datum;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.PrimeMeridian;
+import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.apache.sis.referencing.crs.AbstractCRS;
@@ -31,6 +32,7 @@ import org.apache.sis.referencing.cs.Def
 import org.apache.sis.referencing.datum.AbstractDatum;
 import org.apache.sis.referencing.datum.DefaultEllipsoid;
 import org.apache.sis.referencing.datum.DefaultPrimeMeridian;
+import org.apache.sis.referencing.operation.DefaultOperationMethod;
 import org.apache.sis.parameter.DefaultParameterDescriptor;
 import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
 
@@ -47,7 +49,7 @@ import org.apache.sis.parameter.DefaultP
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.4
+ * @version 0.6
  * @module
  */
 final class SubTypes {
@@ -81,6 +83,9 @@ final class SubTypes {
         if (object instanceof PrimeMeridian) {
             return DefaultPrimeMeridian.castOrCopy((PrimeMeridian) object);
         }
+        if (object instanceof OperationMethod) {
+            return DefaultOperationMethod.castOrCopy((OperationMethod) object);
+        }
         if (object instanceof ParameterDescriptor<?>) {
             return DefaultParameterDescriptor.castOrCopy((ParameterDescriptor<?>) object);
         }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxis.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -33,6 +33,8 @@ import org.opengis.metadata.Identifier;
 import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.cs.RangeMeaning;
 import org.opengis.referencing.cs.AxisDirection;
+import org.opengis.referencing.cs.PolarCS;
+import org.opengis.referencing.cs.SphericalCS;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.apache.sis.internal.referencing.AxisDirections;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
@@ -79,7 +81,7 @@ import java.util.Objects;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4
- * @version 0.4
+ * @version 0.6
  * @module
  *
  * @see AbstractCS
@@ -743,8 +745,13 @@ public class DefaultCoordinateSystemAxis
      * The only actions (derived from ISO 19162 rules) taken by this method are:
      *
      * <ul>
-     *   <li>Replace “<cite>Geodetic latitude</cite>” and “<cite>Geodetic longitude</cite>” names (case insensitive)
-     *       by “<cite>Latitude</cite>” and “<cite>Longitude</cite>” respectively.</li>
+     *   <li>Replace <cite>“Geodetic latitude”</cite> and <cite>“Geodetic longitude”</cite> names (case insensitive)
+     *       by <cite>“latitude”</cite> and <cite>“longitude”</cite> respectively.</li>
+     *   <li>For latitude and longitude axes, replace “φ” and “λ” abbreviations by <var>“B”</var> and <var>“L”</var>
+     *       respectively (from German “Breite” and “Länge”, used in academic texts worldwide).
+     *       Note that <var>“L”</var> is also the transliteration of Greek letter “lambda” (λ).</li>
+     *   <li>In {@link SphericalCS}, replace “φ” and “θ” abbreviations by <var>“U”</var> and <var>“V”</var> respectively.</li>
+     *   <li>In {@link PolarCS}, replace “θ” abbreviation by <var>“U”</var>.</li>
      * </ul>
      *
      * @return {@code "Axis"}.
@@ -760,9 +767,9 @@ public class DefaultCoordinateSystemAxis
             if (name == null) {
                 name = IdentifiedObjects.getName(this, null);
             }
-            if (!isInternal && name != null) {
+            if (name != null && !isInternal) {
                 if (name.equalsIgnoreCase("Geodetic latitude")) {
-                    name = "Latitude"; // ISO 19162 §7.5.3(ii)
+                    name = "Latitude";    // ISO 19162 §7.5.3(ii)
                 } else if (name.equalsIgnoreCase("Geodetic longitude")) {
                     name = "Longitude";
                 }
@@ -770,13 +777,52 @@ public class DefaultCoordinateSystemAxis
         }
         /*
          * ISO 19162 §7.5.3 suggests to put abbreviation in parentheses, e.g. "Easting (x)".
+         * The specification also suggests to write only the abbreviation (e.g. "(X)") in the
+         * special case of Geocentric axis, and disallows Greek letters.
          */
-        if (!isWKT1 && (name == null || !name.equals(abbreviation))) {
+        String a = abbreviation;
+        if (!isWKT1 && (a != null) && !a.equals(name)) {
+            if (!isInternal && a.length() == 1) {
+                switch (a.charAt(0)) {
+                    /*
+                     * ISO 19162 §7.5.3 recommendations:
+                     *
+                     *   a) For PolarCS using Greek letter θ for direction, the letter ‘U’ should be used in WKT.
+                     *   b) For SphericalCS using φ and θ, the letter ‘U’ and ‘V’ respectively should be used in WKT.
+                     */
+                    case 'θ': {
+                        final Object e = formatter.getEnclosingElement(0);
+                        if  (e instanceof SphericalCS) a ="V";
+                        else if (e instanceof PolarCS) a ="U";
+                        break;
+                    }
+                    /*
+                     * ISO 19162 §7.5.3 requirement (ii) and recommendation (b):
+                     *
+                     *  ii) Greek letters φ and λ for geodetic latitude and longitude must be replaced by Latin char.
+                     *   b) For SphericalCS using φ and θ, the letter ‘U’ and ‘V’ respectively should be used in WKT.
+                     */
+                    case 'φ': {
+                        if (formatter.getEnclosingElement(0) instanceof SphericalCS) {
+                            a = "U";
+                        } else if ("Latitude".equalsIgnoreCase(name)) {
+                            a = "B";    // From German "Breite", used in academic texts worldwide.
+                        }
+                        break;
+                    }
+                    case 'λ': {
+                        if ("Longitude".equalsIgnoreCase(name)) {
+                            a = "L";    // From German "Länge", used in academic texts worldwide.
+                        }
+                        break;
+                    }
+                }
+            }
             final StringBuilder buffer = new StringBuilder();
             if (name != null) {
                 buffer.append(name).append(' ');
             }
-            name = buffer.append('(').append(abbreviation).append(')').toString();
+            name = buffer.append('(').append(a).append(')').toString();
         }
         formatter.append(name, ElementKind.AXIS);
         /*

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1675207&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -0,0 +1,551 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation;
+
+import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
+import org.opengis.util.InternationalString;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.quality.PositionalAccuracy;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.*;     // We really use most of this package content.
+import org.opengis.metadata.Identifier;
+import org.apache.sis.parameter.Parameterized;
+import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.io.wkt.Formatter;
+import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.UnsupportedImplementationException;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
+import org.apache.sis.referencing.operation.transform.PassThroughTransform;
+import org.apache.sis.internal.referencing.OperationMethods;
+import org.apache.sis.internal.referencing.WKTUtilities;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.internal.system.Semaphores;
+
+import static org.apache.sis.util.Utilities.deepEquals;
+
+// Branch-dependent imports
+import java.util.Objects;
+
+
+/**
+ * Establishes an association between a source and a target {@linkplain CoordinateReferenceSystem CRS}, and provides a
+ * {@linkplain MathTransform transform} for transforming coordinates in the source CRS to coordinates in the target CRS.
+ *
+ * <div class="section">Instantiation</div>
+ * This class is conceptually <cite>abstract</cite>, even if it is technically possible to instantiate it.
+ * Typical applications should create instances of the most specific subclass prefixed by {@code Default} instead.
+ * An exception to this rule may occur when it is not possible to identify the exact CRS type.
+ *
+ * <div class="section">Immutability and thread safety</div>
+ * This base class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
+ * given to the constructor are also immutable. Most SIS subclasses and related classes are immutable under similar
+ * conditions. This means that unless otherwise noted in the javadoc, {@code CoordinateOperation} instances created
+ * using only SIS factories and static constants can be shared by many objects and passed between threads without
+ * synchronization.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public class AbstractCoordinateOperation extends AbstractIdentifiedObject implements CoordinateOperation {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 1237358357729193885L;
+
+    /**
+     * The source CRS, or {@code null} if not available.
+     *
+     * @see #getSourceCRS()
+     */
+    private final CoordinateReferenceSystem sourceCRS;
+
+    /**
+     * The target CRS, or {@code null} if not available.
+     *
+     * @see #getTargetCRS()
+     */
+    private final CoordinateReferenceSystem targetCRS;
+
+    /**
+     * Version of the coordinate transformation
+     * (i.e., instantiation due to the stochastic nature of the parameters).
+     */
+    private final String operationVersion;
+
+    /**
+     * Estimate(s) of the impact of this operation on point accuracy, or {@code null} if none.
+     */
+    private final Collection<PositionalAccuracy> coordinateOperationAccuracy;
+
+    /**
+     * Area in which this operation is valid, or {@code null} if not available.
+     */
+    private final Extent domainOfValidity;
+
+    /**
+     * Description of domain of usage, or limitations of usage, for which this operation is valid.
+     */
+    private final InternationalString scope;
+
+    /**
+     * Transform from positions in the {@linkplain #getSourceCRS source coordinate reference system}
+     * to positions in the {@linkplain #getTargetCRS target coordinate reference system}.
+     */
+    private final MathTransform transform;
+
+    /**
+     * Constructs a new coordinate operation with the same values than the specified
+     * defining conversion, together with the specified source and target CRS.
+     * This constructor is used by {@link DefaultConversion} only.
+     */
+    AbstractCoordinateOperation(final Conversion               definition,
+                                final CoordinateReferenceSystem sourceCRS,
+                                final CoordinateReferenceSystem targetCRS,
+                                final MathTransform             transform)
+    {
+        super(definition);
+        this.sourceCRS                   = sourceCRS;
+        this.targetCRS                   = targetCRS;
+        this.operationVersion            = definition.getOperationVersion();
+        this.coordinateOperationAccuracy = definition.getCoordinateOperationAccuracy();
+        this.domainOfValidity            = definition.getDomainOfValidity();
+        this.scope                       = definition.getScope();
+        this.transform                   = transform;
+    }
+
+    /**
+     * Creates a coordinate operation from the given properties.
+     * The properties given in argument follow the same rules than for the
+     * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
+     * Additionally, the following properties are understood by this constructor:
+     *
+     * <table class="sis">
+     *   <caption>Recognized properties (non exhaustive list)</caption>
+     *   <tr>
+     *     <th>Property name</th>
+     *     <th>Value type</th>
+     *     <th>Returned by</th>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.operation.CoordinateOperation#OPERATION_VERSION_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link #getOperationVersion()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.operation.CoordinateOperation#COORDINATE_OPERATION_ACCURACY_KEY}</td>
+     *     <td>{@link PositionalAccuracy} (optionally as array)</td>
+     *     <td>{@link #getCoordinateOperationAccuracy()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY}</td>
+     *     <td>{@link Extent}</td>
+     *     <td>{@link #getDomainOfValidity()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.operation.CoordinateOperation#SCOPE_KEY}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
+     *     <td>{@link #getScope()}</td>
+     *   </tr>
+     *   <tr>
+     *     <th colspan="3" class="hsep">Defined in parent class (reminder)</th>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
+     *     <td>{@link Identifier} 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 Identifier} (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 sourceCRS  The source CRS, or {@code null} if unspecified.
+     * @param targetCRS  The target CRS, or {@code null} if unspecified.
+     * @param transform  Transform from positions in the source CRS to positions in the target CRS,
+     *                   or {@code null} if unspecified.
+     */
+    public AbstractCoordinateOperation(final Map<String,?>             properties,
+                                       final CoordinateReferenceSystem sourceCRS,
+                                       final CoordinateReferenceSystem targetCRS,
+                                       final MathTransform             transform)
+    {
+        super(properties);
+        Object positionalAccuracy;
+        domainOfValidity   = Containers.property(properties, DOMAIN_OF_VALIDITY_KEY, Extent.class);
+        scope              = Types.toInternationalString(properties, SCOPE_KEY);
+        operationVersion   = Containers.property(properties, OPERATION_VERSION_KEY, String.class);
+        positionalAccuracy = properties.get(COORDINATE_OPERATION_ACCURACY_KEY);
+        if (positionalAccuracy instanceof PositionalAccuracy[]) {
+            coordinateOperationAccuracy = CollectionsExt.nonEmptySet((PositionalAccuracy[]) positionalAccuracy);
+        } else {
+            coordinateOperationAccuracy = (positionalAccuracy == null) ? null :
+                    Collections.singleton((PositionalAccuracy) positionalAccuracy);
+        }
+        this.sourceCRS = sourceCRS;
+        this.targetCRS = targetCRS;
+        this.transform = transform;
+        if (transform != null) {
+            ArgumentChecks.ensureDimensionMatches("sourceCRS", transform.getSourceDimensions(), sourceCRS);
+            ArgumentChecks.ensureDimensionMatches("targetCRS", transform.getTargetDimensions(), targetCRS);
+        }
+    }
+
+    /**
+     * Returns the GeoAPI interface implemented by this class.
+     * The default implementation returns {@code CoordinateOperation.class}.
+     * Subclasses implementing a more specific GeoAPI interface shall override this method.
+     *
+     * @return The coordinate operation interface implemented by this class.
+     */
+    @Override
+    public Class<? extends CoordinateOperation> getInterface() {
+        return CoordinateOperation.class;
+    }
+
+    /**
+     * Returns the source CRS, or {@code null} if unspecified.
+     * The source CRS is mandatory for {@linkplain DefaultTransformation transformations} only.
+     * This information is optional for {@linkplain DefaultConversion conversions} according
+     * the ISO 19111 standard, but Apache SIS tries to provide that CRS in most cases anyway.
+     *
+     * @return The source CRS, or {@code null} if not available.
+     */
+    @Override
+    public CoordinateReferenceSystem getSourceCRS() {
+        return sourceCRS;
+    }
+
+    /**
+     * Returns the target CRS, or {@code null} if unspecified.
+     * The target CRS is mandatory for {@linkplain DefaultTransformation transformations} only.
+     * This information is optional for {@linkplain DefaultConversion conversions} according
+     * the ISO 19111 standard, but Apache SIS tries to provide that CRS in most cases anyway.
+     *
+     * @return The target CRS, or {@code null} if not available.
+     */
+    @Override
+    public CoordinateReferenceSystem getTargetCRS() {
+        return targetCRS;
+    }
+
+    /**
+     * Returns the CRS which is neither the {@linkplain #getSourceCRS() source CRS} or
+     * {@linkplain #getTargetCRS() target CRS} but still required for performing the operation.
+     *
+     * <div class="note"><b>Example:</b>
+     * some transformations of vertical coordinates (<var>h</var>) require the horizontal coordinates (φ,λ)
+     * in order to interpolate in a grid. This method returns the CRS of the grid where such interpolations
+     * are performed.</div>
+     *
+     * @return The CRS (neither source or target CRS) required for interpolating the values, or {@code null} if none.
+     */
+    public CoordinateReferenceSystem getInterpolationCRS() {
+        return null;
+    }
+
+    /**
+     * Returns the version of the coordinate operation. Different versions of a coordinate
+     * {@linkplain DefaultTransformation transformation} may exist because of the stochastic
+     * nature of the parameters. In principle this property is irrelevant to coordinate
+     * {@linkplain DefaultConversion conversions}, but Apache SIS accepts it anyway.
+     *
+     * @return The coordinate operation version, or {@code null} in none.
+     */
+    @Override
+    public String getOperationVersion() {
+        return operationVersion;
+    }
+
+    /**
+     * Returns an estimation of the impact of this operation on point accuracy.
+     * The positional accuracy gives position error estimates for target coordinates
+     * of this coordinate operation, assuming no errors in source coordinates.
+     *
+     * @return The position error estimations, or an empty collection if not available.
+     *
+     * @see #getLinearAccuracy()
+     */
+    @Override
+    public Collection<PositionalAccuracy> getCoordinateOperationAccuracy() {
+        return (coordinateOperationAccuracy != null) ? coordinateOperationAccuracy : Collections.emptySet();
+    }
+
+    /**
+     * Returns an estimation of positional accuracy in metres, or {@code NaN} if unknown.
+     * The default implementation tries to infer a value from the metadata returned by
+     * {@link #getCoordinateOperationAccuracy()} using SIS-specific heuristics.
+     *
+     * @return The accuracy estimation (always in meters), or NaN if unknown.
+     */
+    public double getLinearAccuracy() {
+        return OperationMethods.getLinearAccuracy(this);
+    }
+
+    /**
+     * Returns the area or region or timeframe in which this coordinate operation is valid.
+     *
+     * @return The coordinate operation valid domain, or {@code null} if not available.
+     */
+    @Override
+    public Extent getDomainOfValidity() {
+        return domainOfValidity;
+    }
+
+    /**
+     * Returns a description of domain of usage, or limitations of usage, for which this operation is valid.
+     *
+     * @return A description of domain of usage, or {@code null} if none.
+     */
+    @Override
+    public InternationalString getScope() {
+        return scope;
+    }
+
+    /**
+     * Returns the object for transforming coordinates in the {@linkplain #getSourceCRS() source CRS}
+     * to coordinates in the {@linkplain #getTargetCRS() target CRS}.
+     *
+     * @return The transform from source to target CRS, or {@code null} if not applicable.
+     */
+    @Override
+    public MathTransform getMathTransform() {
+        return transform;
+    }
+
+    /**
+     * Returns the operation method. {@link DefaultConversion} and {@link DefaultTransformation} need to override
+     * this method as a final method because {@code equals(Object, ComparisonMode.STRICT)} assumes that the field
+     * value is returned directly.
+     */
+    OperationMethod getMethod() {
+        return null;
+    }
+
+    /**
+     * Returns the parameter values. The default implementation infers the parameter
+     * values from the {@linkplain #transform transform}, if possible.
+     *
+     * @throws UnsupportedOperationException if the parameter values can't be determined
+     *         for the current math transform implementation.
+     *
+     * @see DefaultMathTransformFactory#createParameterizedTransform(ParameterValueGroup)
+     * @see Parameterized#getParameterValues()
+     */
+    ParameterValueGroup getParameterValues() throws UnsupportedOperationException {
+        MathTransform mt = transform;
+        while (mt != null) {
+            if (mt instanceof Parameterized) {
+                final ParameterValueGroup param;
+                if (Semaphores.queryAndSet(Semaphores.PROJCS)) {
+                    throw new AssertionError(); // Should never happen.
+                }
+                try {
+                    param = ((Parameterized) mt).getParameterValues();
+                } finally {
+                    Semaphores.clear(Semaphores.PROJCS);
+                }
+                if (param != null) {
+                    return param;
+                }
+            }
+            if (mt instanceof PassThroughTransform) {
+                mt = ((PassThroughTransform) mt).getSubTransform();
+            } else {
+                break;
+            }
+        }
+        throw new UnsupportedImplementationException(Classes.getClass(mt));
+    }
+
+
+    /**
+     * Compares this coordinate operation with the specified object for equality. If the {@code mode} argument
+     * is {@link ComparisonMode#STRICT} or {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available
+     * properties are compared including the {@linkplain #getDomainOfValidity() domain of validity} and the
+     * {@linkplain #getScope() scope}.
+     *
+     * @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.
+     * @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 (!super.equals(object, mode)) {
+            return false;
+        }
+        if (mode == ComparisonMode.STRICT) {
+            final AbstractCoordinateOperation that = (AbstractCoordinateOperation) object;
+            if (!Objects.equals(getMethod(),                 that.getMethod())      ||
+                !Objects.equals(sourceCRS,                   that.sourceCRS)        ||
+                !Objects.equals(transform,                   that.transform)        ||
+                !Objects.equals(scope,                       that.scope)            ||
+                !Objects.equals(domainOfValidity,            that.domainOfValidity) ||
+                !Objects.equals(coordinateOperationAccuracy, that.coordinateOperationAccuracy))
+            {
+                return false;
+            }
+            // See comment at the end of this method.
+            if (Semaphores.queryAndSet(Semaphores.COMPARING)) {
+                return true;
+            } else try {
+                return Objects.equals(targetCRS, that.targetCRS);
+            } finally {
+                Semaphores.clear(Semaphores.COMPARING);
+            }
+        }
+        final CoordinateOperation that = (CoordinateOperation) object;
+        if (mode == ComparisonMode.BY_CONTRACT) {
+            if (!deepEquals(getMethod(), (that instanceof SingleOperation) ? ((SingleOperation) that).getMethod() : null, mode) ||
+                !deepEquals(getScope(),                       that.getScope(), mode) ||
+                !deepEquals(getDomainOfValidity(),            that.getDomainOfValidity(), mode) ||
+                !deepEquals(getCoordinateOperationAccuracy(), that.getCoordinateOperationAccuracy(), mode))
+            {
+                return false;
+            }
+            // SourceCRS, targetCRS and transform to be tested below.
+        }
+        /*
+         * We consider the operation method as metadata. One could argue that OperationMethod's 'sourceDimension' and
+         * 'targetDimension' are not metadata, but their values should be identical to the 'sourceCRS' and 'targetCRS'
+         * dimensions, already checked below. We could also argue that 'OperationMethod.parameters' are not metadata,
+         * but their values should have been taken in account for the MathTransform creation, compared below.
+         *
+         * Comparing the MathTransforms instead of parameters avoid the problem of implicit parameters. For example in
+         * a ProjectedCRS, the "semiMajor" and "semiMinor" axis lengths are sometime provided as explicit parameters,
+         * and sometime inferred from the geodetic datum. The two cases would be different set of parameters from the
+         * OperationMethod's point of view, but still result in the creation of identical MathTransforms.
+         *
+         * An other rational for treating OperationMethod as metadata is that SIS's MathTransform providers extend
+         * DefaultOperationMethod. Consequently there is a wide range of subclasses, which make the comparisons more
+         * difficult. For example Mercator1SP and Mercator2SP providers are two different ways to describe the same
+         * projection. The SQL-backed EPSG factory uses yet an other implementation.
+         *
+         * NOTE: A previous Geotk implementation made this final check:
+         *
+         *     return nameMatches(this.method, that.method);
+         *
+         * but it was not strictly necessary since it was redundant with the comparisons of MathTransforms.
+         * Actually it was preventing to detect that two CRS were equivalent despite different method names
+         * (e.g. "Mercator (1SP)" and "Mercator (2SP)" when the parameters are properly chosen).
+         */
+        if (!deepEquals(getMathTransform(), that.getMathTransform(), mode)
+                || !deepEquals(getSourceCRS(), that.getSourceCRS(), mode))
+        {
+            return false;
+        }
+        /*
+         * Avoid never-ending recursivity: AbstractDerivedCRS has a 'conversionFromBase'
+         * field that is set to this AbstractCoordinateOperation.
+         */
+        if (Semaphores.queryAndSet(Semaphores.COMPARING)) {
+            return true;
+        } else try {
+            return deepEquals(getTargetCRS(), that.getTargetCRS(), mode);
+        } finally {
+            Semaphores.clear(Semaphores.COMPARING);
+        }
+    }
+
+    /**
+     * Invoked by {@code hashCode()} for computing the hash code when first needed.
+     * See {@link #computeHashCode()} for more information.
+     *
+     * @return The hash code value. This value may change in any future Apache SIS version.
+     */
+    @Override
+    protected long computeHashCode() {
+        /*
+         * Do NOT take 'getMethod()' in account in hash code calculation. See the comment
+         * inside the above 'equals(Object, ComparisonMode)' method for more information.
+         * Note that we use the 'transform' hash code, which should be sufficient.
+         */
+        return super.computeHashCode() + Objects.hash(sourceCRS, targetCRS, transform);
+    }
+
+    /**
+     * Formats this coordinate operation in Well Known Text (WKT) version 2 format.
+     *
+     * @param  formatter The formatter to use.
+     * @return {@code "CoordinateOperation"}.
+     */
+    @Override
+    protected String formatTo(final Formatter formatter) {
+        super.formatTo(formatter);
+        append(formatter, getSourceCRS(), "SourceCRS");
+        append(formatter, getTargetCRS(), "TargetCRS");
+        formatter.append(DefaultOperationMethod.castOrCopy(getMethod()));
+        append(formatter, getInterpolationCRS(), "InterpolationCRS");
+        final double accuracy = getLinearAccuracy();
+        if (accuracy > 0) {
+            formatter.append(new FormattableObject() {
+                @Override protected String formatTo(final Formatter formatter) {
+                    formatter.append(accuracy);
+                    return "OperationAccuracy";
+                }
+            });
+        }
+        if (formatter.getConvention().majorVersion() == 1) {
+            formatter.setInvalidWKT(this, null);
+        }
+        return "CoordinateOperation";
+    }
+
+    /**
+     * Appends the given CRS (if non-null) wrapped in an element of the given name.
+     *
+     * @param formatter The formatter where to append the object name.
+     * @param crs       The object to append, or {@code null} if none.
+     * @param type      The keyword to write before the object.
+     */
+    private static void append(final Formatter formatter, final CoordinateReferenceSystem crs, final String type) {
+        if (crs != null) {
+            formatter.append(new FormattableObject() {
+                @Override protected String formatTo(final Formatter formatter) {
+                    formatter.append(WKTUtilities.toFormattable(crs));
+                    return type;
+                }
+            });
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultCompoundCRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultCompoundCRSTest.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultCompoundCRSTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultCompoundCRSTest.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -181,8 +181,8 @@ public final strictfp class DefaultCompo
                 "      Ellipsoid[“WGS84”, 6378137.0, 298.257223563, LengthUnit[“metre”, 1]]],\n" +
                 "      PrimeMeridian[“Greenwich”, 0.0, AngleUnit[“degree”, 0.017453292519943295]],\n" +
                 "    CS[“ellipsoidal”, 2],\n" +
-                "      Axis[“Longitude (λ)”, east],\n" +
-                "      Axis[“Latitude (φ)”, north],\n" +
+                "      Axis[“Longitude (L)”, east],\n" +
+                "      Axis[“Latitude (B)”, north],\n" +
                 "      AngleUnit[“degree”, 0.017453292519943295]],\n" +
                 "  VerticalCRS[“Gravity-related height”,\n" +
                 "    VerticalDatum[“Mean Sea Level”],\n" +

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultGeographicCRSTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultGeographicCRSTest.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultGeographicCRSTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultGeographicCRSTest.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -162,8 +162,8 @@ public final strictfp class DefaultGeogr
                 "    Ellipsoid[“WGS84”, 6378137.0, 298.257223563, LengthUnit[“metre”, 1]]],\n" +
                 "    PrimeMeridian[“Greenwich”, 0.0, AngleUnit[“degree”, 0.017453292519943295]],\n" +
                 "  CS[“ellipsoidal”, 2],\n" +
-                "    Axis[“Longitude (λ)”, east],\n" +
-                "    Axis[“Latitude (φ)”, north],\n" +
+                "    Axis[“Longitude (L)”, east],\n" +
+                "    Axis[“Latitude (B)”, north],\n" +
                 "    AngleUnit[“degree”, 0.017453292519943295],\n" +
                 "  Area[“World”],\n" +
                 "  BBox[-90.00, -180.00, 90.00, 180.00]]",
@@ -175,8 +175,8 @@ public final strictfp class DefaultGeogr
                 "    Ellipsoid[“WGS84”, 6378137.0, 298.257223563]],\n" +
                 "    PrimeMeridian[“Greenwich”, 0.0],\n" +
                 "  CS[“ellipsoidal”, 2],\n" +
-                "    Axis[“Longitude (λ)”, east],\n" +
-                "    Axis[“Latitude (φ)”, north],\n" +
+                "    Axis[“Longitude (L)”, east],\n" +
+                "    Axis[“Latitude (B)”, north],\n" +
                 "    Unit[“degree”, 0.017453292519943295],\n" +
                 "  Area[“World”],\n" +
                 "  BBox[-90.00, -180.00, 90.00, 180.00]]",

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxisTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxisTest.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxisTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxisTest.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -89,13 +89,13 @@ public final strictfp class DefaultCoord
         assertWktEquals("Axis[“x”, east, LengthUnit[“metre”, 1]]",  X);
         assertWktEquals("Axis[“y”, north, LengthUnit[“metre”, 1]]", Y);
         assertWktEquals("Axis[“z”, up, LengthUnit[“metre”, 1]]",    Z);
-        assertWktEquals("Axis[“Longitude (λ)”, east, AngleUnit[“grade”, 0.015707963267948967]]",            LONGITUDE_gon);
-        assertWktEquals("Axis[“Latitude (φ)”, north, AngleUnit[“grade”, 0.015707963267948967]]",            LATITUDE_gon);
+        assertWktEquals("Axis[“Longitude (L)”, east, AngleUnit[“grade”, 0.015707963267948967]]",            LONGITUDE_gon);
+        assertWktEquals("Axis[“Latitude (B)”, north, AngleUnit[“grade”, 0.015707963267948967]]",            LATITUDE_gon);
         assertWktEquals("Axis[“Altitude (h)”, up, LengthUnit[“metre”, 1]]",                                 ALTITUDE);
         assertWktEquals("Axis[“Time (t)”, future, TimeUnit[“day”, 86400]]",                                 TIME);
-        assertWktEquals("Axis[“Longitude (λ)”, east, AngleUnit[“degree”, 0.017453292519943295]]",           GEODETIC_LONGITUDE);
+        assertWktEquals("Axis[“Longitude (L)”, east, AngleUnit[“degree”, 0.017453292519943295]]",           GEODETIC_LONGITUDE);
         assertWktEquals("Axis[“Spherical longitude (Ω)”, east, AngleUnit[“degree”, 0.017453292519943295]]", SPHERICAL_LONGITUDE);
-        assertWktEquals("Axis[“Latitude (φ)”, north, AngleUnit[“degree”, 0.017453292519943295]]",           GEODETIC_LATITUDE);
+        assertWktEquals("Axis[“Latitude (B)”, north, AngleUnit[“degree”, 0.017453292519943295]]",           GEODETIC_LATITUDE);
         assertWktEquals("Axis[“Spherical latitude (Θ)”, north, AngleUnit[“degree”, 0.017453292519943295]]", SPHERICAL_LATITUDE);
 
         assertWktEquals(Convention.WKT1,     "AXIS[“x”, EAST]",  X);

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1675207&r1=1675206&r2=1675207&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Tue Apr 21 23:03:17 2015
@@ -104,6 +104,22 @@ public final class CollectionsExt extend
     }
 
     /**
+     * Returns a copy of the given array as a non-empty immutable set.
+     * If the given array is empty, then this method returns {@code null}.
+     *
+     * @param  <T> The type of elements.
+     * @param  elements The elements to copy in a set.
+     * @return An unmodifiable set which contains all the given elements, or {@code null}.
+     *
+     * @since 0.6
+     */
+    @SafeVarargs
+    public static <T> Set<T> nonEmptySet(final T... elements) {
+        final Set<T> asSet = immutableSet(true, elements);
+        return (asSet != null && asSet.isEmpty()) ? null : asSet;
+    }
+
+    /**
      * Returns the given array if non-empty, or {@code null} if the given array is null or empty.
      * This method is generally not recommended, since public API should prefer empty array instead of null.
      * However this method is occasionally useful for managing private fields.



Mime
View raw message