Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -21,14 +21,17 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.logging.LogRecord;
+import javax.measure.Unit;
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.cs.CSFactory;
 import org.opengis.referencing.crs.CRSFactory;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.CompoundCRS;
@@ -41,6 +44,8 @@ import org.opengis.referencing.crs.Proje
 import org.opengis.referencing.crs.TemporalCRS;
 import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.crs.EngineeringCRS;
+import org.opengis.referencing.operation.Conversion;
+import org.opengis.referencing.operation.CoordinateOperationFactory;
 import org.opengis.referencing.operation.OperationNotFoundException;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.extent.Extent;
@@ -53,6 +58,7 @@ import org.apache.sis.measure.Units;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.internal.metadata.AxisDirections;
+import org.apache.sis.internal.metadata.EllipsoidalHeightCombiner;
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
 import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -60,14 +66,19 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.referencing.cs.AxisFilter;
+import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.cs.DefaultVerticalCS;
-import org.apache.sis.referencing.cs.DefaultEllipsoidalCS;
 import org.apache.sis.referencing.crs.DefaultGeographicCRS;
+import org.apache.sis.referencing.crs.DefaultProjectedCRS;
 import org.apache.sis.referencing.crs.DefaultVerticalCRS;
 import org.apache.sis.referencing.crs.DefaultCompoundCRS;
+import org.apache.sis.referencing.crs.DefaultEngineeringCRS;
 import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.operation.CoordinateOperationContext;
 import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
+import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.factory.GeodeticObjectFactory;
 import org.apache.sis.referencing.factory.UnavailableFactoryException;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.metadata.iso.extent.Extents;
@@ -78,8 +89,6 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.Static;
 
-import static java.util.Collections.singletonMap;
-
 // Branch-dependent imports
 import org.opengis.geometry.Geometry;
 
@@ -176,7 +185,7 @@ public final class CRS extends Static {
      *   <tr><td>EPSG:5714</td> <td>{@link CommonCRS.Vertical#MEAN_SEA_LEVEL 
MEAN_SEA_LEVEL}</td> <td>Vertical</td> <td>Mean Sea Level height</td></tr>
      * </table></blockquote>
      *
-     * This method accepts also the URN and URL syntax.
+     * This method accepts also the URN and URL syntaxes.
      * For example the following codes are considered equivalent to {@code 
"EPSG:4326"}:
      * <ul>
      *   <li>{@code "EPSG::4326"}</li>
@@ -185,6 +194,26 @@ public final class CRS extends Static {
      *   <li>{@code "http://www.opengis.net/gml/srs/epsg.xml#4326"}</li>
      * </ul>
      *
+     * URIs can be combined for creating larger objects. For example the 
following URIs combine a
+     * two-dimensional WGS84 reference system (EPSG:4326) with a Mean Sea 
Level height (EPSG:5714).
+     * The result is a three-dimensional {@linkplain 
org.apache.sis.referencing.crs.DefaultCompoundCRS
+     * compound coordinate reference system}:
+     *
+     * <ul>
+     *   <li>{@code "urn:ogc:def:crs,crs:EPSG::4326,crs:EPSG::5714"}</li>
+     *   <li><code>"http://www.opengis.net/def/crs-compound?<br>
+     *            1=http://www.opengis.net/def/crs/epsg/0/4326&amp;<br>
+     *            2=http://www.opengis.net/def/crs/epsg/0/5714";</code></li>
+     * </ul>
+     *
+     * <p>URNs (but not URLs) can also combine a
+     * {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum 
geodetic datum} with an
+     * {@linkplain org.apache.sis.referencing.cs.DefaultEllipsoidalCS 
ellipsoidal coordinate system} for creating a new
+     * {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS 
geographic CRS}, or a base geographic CRS with a
+     * {@linkplain org.apache.sis.referencing.operation.DefaultConversion 
conversion} and a
+     * {@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian 
coordinate system} for creating a new
+     * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS 
projected coordinate reference system}.</p>
+     *
      * Note that the {@link IdentifiedObjects#lookupURN(IdentifiedObject, 
Citation)}
      * method can be seen as a converse of this method.
      * More codes may also be supported depending on which extension modules 
are available.
@@ -197,6 +226,7 @@ public final class CRS extends Static {
      *
      * @see #getAuthorityFactory(String)
      * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory
+     * @see <a href="http://epsg-registry.org/";>EPSG Geodetic Registry</a>
      *
      * @category factory
      */
@@ -805,6 +835,61 @@ public final class CRS extends Static {
     }
 
     /**
+     * Creates a compound coordinate reference system from an ordered list of 
CRS components.
+     * A CRS is inferred from the given components and the domain of validity 
is set to the
+     * {@linkplain org.apache.sis.metadata.iso.extent.DefaultExtent#intersect 
intersection}
+     * of the domain of validity of all components.
+     *
+     * <div class="section">Ellipsoidal height</div>
+     * If a two-dimensional geographic or projected CRS if followed or 
preceded by a vertical CRS with ellipsoidal
+     * {@linkplain 
org.apache.sis.referencing.datum.DefaultVerticalDatum#getVerticalDatumType() 
datum type}, then
+     * this method combines them in a single three-dimensional geographic or 
projected CRS.  Note that standalone
+     * ellipsoidal heights are not allowed according ISO 19111. But if such 
situation is nevertheless found, then
+     * the action described here fixes the issue. This is the reverse of 
<code>{@linkplain #getVerticalComponent
+     * getVerticalComponent}(crs, true)</code>.
+     *
+     * <div class="section">Components order</div>
+     * Apache SIS is permissive on the order of components that can be used in 
a compound CRS.
+     * However for better inter-operability, users are encouraged to follow 
the order mandated by ISO 19162:
+     *
+     * <ol>
+     *   <li>A mandatory horizontal CRS (only one of two-dimensional {@code 
GeographicCRS} or {@code ProjectedCRS} or {@code EngineeringCRS}).</li>
+     *   <li>Optionally followed by a {@code VerticalCRS} or a {@code 
ParametricCRS} (but not both).</li>
+     *   <li>Optionally followed by a {@code TemporalCRS}.</li>
+     * </ol>
+     *
+     * @param  components  the sequence of coordinate reference systems making 
the compound CRS.
+     * @return the compound CRS, or {@code components[0]} if the given array 
contains only one component.
+     * @throws IllegalArgumentException if the given array is empty or if the 
array contains incompatible components.
+     * @throws FactoryException if the geodetic factory failed to create the 
compound CRS.
+     *
+     * @since 0.8
+     *
+     * @see GeodeticObjectFactory#createCompoundCRS(Map, 
CoordinateReferenceSystem...)
+     */
+    public static CoordinateReferenceSystem compound(final 
CoordinateReferenceSystem... components) throws FactoryException {
+        ArgumentChecks.ensureNonNull("components", components);
+        switch (components.length) {
+            case 0: {
+                throw new 
IllegalArgumentException(Errors.format(Errors.Keys.EmptyArgument_1, 
"components"));
+            }
+            case 1: {
+                final CoordinateReferenceSystem crs = components[0];
+                if (crs != null) return crs;
+                break;
+            }
+        }
+        final Map<String,?> properties = 
EllipsoidalHeightCombiner.properties(components);
+        return new EllipsoidalHeightCombiner() {
+            @Override public void initialize(final int factoryTypes) {
+                if ((factoryTypes & CRS)       != 0) crsFactory = 
DefaultFactories.forBuildin(CRSFactory.class);
+                if ((factoryTypes & CS)        != 0) csFactory  = 
DefaultFactories.forBuildin(CSFactory.class);
+                if ((factoryTypes & OPERATION) != 0) opFactory  = 
DefaultFactories.forBuildin(CoordinateOperationFactory.class);
+            }
+        }.createCompoundCRS(properties, components);
+    }
+
+    /**
      * Returns {@code true} if the given CRS is horizontal. The current 
implementation considers a
      * CRS as horizontal if it is two-dimensional and comply with one of the 
following conditions:
      *
@@ -831,20 +916,38 @@ public final class CRS extends Static {
      * @category information
      */
     public static boolean isHorizontalCRS(final CoordinateReferenceSystem crs) 
{
+        return horizontalCode(crs) == 2;
+    }
+
+    /**
+     * If the given CRS would quality as horizontal except for its number of 
dimensions, returns that number.
+     * Otherwise returns 0. The number of dimensions can only be 2 or 3.
+     */
+    private static int horizontalCode(final CoordinateReferenceSystem crs) {
         /*
          * In order to determine if the CRS is geographic, checking the 
CoordinateSystem type is more reliable
          * then checking if the CRS implements the GeographicCRS interface.  
This is because the GeographicCRS
-         * interface is GeoAPI-specific, so a CRS may be OGC-compliant without 
implementing that interface.
+         * type did not existed in ISO 19111:2007, so a CRS could be 
standard-compliant without implementing
+         * the GeographicCRS interface.
          */
+        boolean isEngineering = false;
         final boolean isGeodetic = (crs instanceof GeodeticCRS);
-        if (isGeodetic || crs instanceof ProjectedCRS || crs instanceof 
EngineeringCRS) {
-            @SuppressWarnings("null")
+        if (isGeodetic || crs instanceof ProjectedCRS || (isEngineering = (crs 
instanceof EngineeringCRS))) {
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            if (cs.getDimension() == 2) {
-                return !isGeodetic || (cs instanceof EllipsoidalCS);
+            final int dim = cs.getDimension();
+            if ((dim & ~1) == 2 && (!isGeodetic || (cs instanceof 
EllipsoidalCS))) {
+                if (isEngineering) {
+                    int n = 0;
+                    for (int i=0; i<dim; i++) {
+                        if 
(AxisDirections.isCompass(cs.getAxis(i).getDirection())) n++;
+                    }
+                    // If we don't have exactly 2 east, north, etc. 
directions, consider as non-horizontal.
+                    if (n != 2) return 0;
+                }
+                return dim;
             }
         }
-        return false;
+        return 0;
     }
 
     /**
@@ -854,8 +957,8 @@ public final class CRS extends Static {
      * first horizontal component in the order of the {@linkplain 
#getSingleComponents(CoordinateReferenceSystem)
      * single components list}.
      *
-     * <p>In the special case where a three-dimensional geographic CRS is 
found, this method will create a
-     * two-dimensional geographic CRS without the vertical axis.</p>
+     * <p>In the special case where a three-dimensional geographic or 
projected CRS is found, this method
+     * will create a two-dimensional geographic or projected CRS without the 
vertical axis.</p>
      *
      * @param  crs  the coordinate reference system, or {@code null}.
      * @return the first horizontal CRS, or {@code null} if none.
@@ -863,26 +966,63 @@ public final class CRS extends Static {
      * @category information
      */
     public static SingleCRS getHorizontalComponent(final 
CoordinateReferenceSystem crs) {
-        if (crs instanceof GeodeticCRS) {
-            CoordinateSystem cs = crs.getCoordinateSystem();
-            if (cs instanceof EllipsoidalCS) {                          // See 
comment in isHorizontalCRS(…) method.
-                final int i = AxisDirections.indexOfColinear(cs, 
AxisDirection.UP);
-                if (i < 0) {
-                    return (SingleCRS) crs;
-                }
-                final CoordinateSystemAxis xAxis = cs.getAxis(i > 0 ? 0 : 1);
-                final CoordinateSystemAxis yAxis = cs.getAxis(i > 1 ? 1 : 2);
-                cs = CommonCRS.DEFAULT.geographic().getCoordinateSystem();
-                if (!Utilities.equalsIgnoreMetadata(cs.getAxis(0), xAxis) ||
-                    !Utilities.equalsIgnoreMetadata(cs.getAxis(1), yAxis))
-                {
-                    // We can not reuse the name of the existing CS, because 
it typically
-                    // contains text about axes including the axis that we 
just dropped.
-                    cs = new 
DefaultEllipsoidalCS(singletonMap(EllipsoidalCS.NAME_KEY, "Ellipsoidal 2D"), 
xAxis, yAxis);
-                }
-                return new DefaultGeographicCRS(
-                        ReferencingUtilities.getPropertiesForModifiedCRS(crs, 
CoordinateReferenceSystem.IDENTIFIERS_KEY),
-                        ((GeodeticCRS) crs).getDatum(), (EllipsoidalCS) cs);
+        switch (horizontalCode(crs)) {
+            /*
+             * If the CRS is already two-dimensional and horizontal, return 
as-is.
+             * We don't need to check if crs is an instance of SingleCRS since 
all
+             * CRS accepted by horizontalCode(…) are SingleCRS.
+             */
+            case 2: {
+                return (SingleCRS) crs;
+            }
+            case 3: {
+                /*
+                 * The CRS would be horizontal if we can remove the vertical 
axis. CoordinateSystems.replaceAxes(…)
+                 * will do this task for us. We can verify if the operation 
has been successful by checking that
+                 * the number of dimensions has been reduced by 1 (from 3 to 
2).
+                 */
+                final CoordinateSystem cs = 
CoordinateSystems.replaceAxes(crs.getCoordinateSystem(), new AxisFilter() {
+                    @Override public boolean accept(final CoordinateSystemAxis 
axis) {
+                        return !AxisDirections.isVertical(axis.getDirection());
+                    }
+
+                    @Override
+                    public AxisDirection 
getDirectionReplacement(CoordinateSystemAxis axis, AxisDirection direction) {
+                        return direction;
+                    }
+
+                    @Override
+                    public Unit<?> getUnitReplacement(CoordinateSystemAxis 
axis, Unit<?> unit) {
+                        return unit;
+                    }
+                });
+                if (cs.getDimension() != 2) break;
+                /*
+                 * Most of the time, the CRS to rebuild will be geodetic. In 
such case we known that the
+                 * coordinate system is ellipsoidal because (i.e. the CRS is 
geographic) because it was
+                 * a condition verified by horizontalCode(…). A 
ClassCastException would be a bug.
+                 */
+                final Map<String, ?> properties = 
ReferencingUtilities.getPropertiesForModifiedCRS(crs);
+                if (crs instanceof GeodeticCRS) {
+                    return new DefaultGeographicCRS(properties, ((GeodeticCRS) 
crs).getDatum(), (EllipsoidalCS) cs);
+                }
+                /*
+                 * In Apache SIS implementation, the Conversion contains the 
source and target CRS together with
+                 * a MathTransform.   We need to recreate the same conversion, 
but without CRS and MathTransform
+                 * for letting SIS create or associate new ones, which will be 
two-dimensional now.
+                 */
+                if (crs instanceof ProjectedCRS) {
+                    final ProjectedCRS  proj = (ProjectedCRS) crs;
+                    final GeographicCRS base = (GeographicCRS) 
getHorizontalComponent(proj.getBaseCRS());
+                    Conversion fromBase = proj.getConversionFromBase();
+                    fromBase = new 
DefaultConversion(IdentifiedObjects.getProperties(fromBase),
+                            fromBase.getMethod(), null, 
fromBase.getParameterValues());
+                    return new DefaultProjectedCRS(properties, base, fromBase, 
(CartesianCS) cs);
+                }
+                /*
+                 * If the CRS is neither geographic or projected, then it is 
engineering.
+                 */
+                return new DefaultEngineeringCRS(properties, ((EngineeringCRS) 
crs).getDatum(), cs);
             }
         }
         if (crs instanceof CompoundCRS) {
@@ -894,7 +1034,7 @@ public final class CRS extends Static {
                 }
             }
         }
-        return isHorizontalCRS(crs) ? (SingleCRS) crs : null;
+        return null;
     }
 
     /**
@@ -925,6 +1065,8 @@ public final class CRS extends Static {
      *         The recommended value is {@code false}.
      * @return the first vertical CRS, or {@code null} if none.
      *
+     * @see #compound(CoordinateReferenceSystem...)
+     *
      * @category information
      */
     public static VerticalCRS getVerticalComponent(final 
CoordinateReferenceSystem crs,
@@ -945,19 +1087,17 @@ public final class CRS extends Static {
                 }
             } while ((a = !a) == allowCreateEllipsoidal);
         }
-        if (allowCreateEllipsoidal && crs instanceof GeodeticCRS) {
+        if (allowCreateEllipsoidal && horizontalCode(crs) == 3) {
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            if (cs instanceof EllipsoidalCS) {                          // See 
comment in isHorizontalCRS(…) method.
-                final int i = AxisDirections.indexOfColinear(cs, 
AxisDirection.UP);
-                if (i >= 0) {
-                    final CoordinateSystemAxis axis = cs.getAxis(i);
-                    VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
-                    if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
-                        final Map<String,?> properties = 
IdentifiedObjects.getProperties(c);
-                        c = new DefaultVerticalCRS(properties, c.getDatum(), 
new DefaultVerticalCS(properties, axis));
-                    }
-                    return c;
+            final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
+            if (i >= 0) {
+                final CoordinateSystemAxis axis = cs.getAxis(i);
+                VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
+                if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
+                    final Map<String,?> properties = 
IdentifiedObjects.getProperties(c);
+                    c = new DefaultVerticalCRS(properties, c.getDatum(), new 
DefaultVerticalCS(properties, axis));
                 }
+                return c;
             }
         }
         return null;

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -18,6 +18,7 @@ package org.apache.sis.referencing;
 
 import java.util.Map;
 import java.util.Set;
+import java.util.List;
 import java.util.LinkedHashSet;
 import java.util.Iterator;
 import java.util.Collection;
@@ -28,7 +29,9 @@ import org.opengis.util.FactoryException
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.crs.CompoundCRS;
 import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.ConcatenatedOperation;
 
 import org.apache.sis.util.Static;
 import org.apache.sis.util.CharSequences;
@@ -36,6 +39,7 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.DefinitionURI;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.metadata.NameMeaning;
 import org.apache.sis.internal.metadata.NameToIdentifier;
@@ -53,7 +57,7 @@ import static org.apache.sis.internal.ut
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Guilhem Legal (Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see CRS
  * @see org.apache.sis.geometry.Envelopes
@@ -379,11 +383,17 @@ public final class IdentifiedObjects ext
     }
 
     /**
-     * Looks up a URN, such as {@code "urn:ogc:def:crs:EPSG:8.2:4326"}, of the 
specified object.
+     * Looks up a URN, such as {@code "urn:ogc:def:crs:EPSG:9.1:4326"}, of the 
specified object.
      * This method searches in all {@linkplain 
org.apache.sis.referencing.factory.GeodeticAuthorityFactory geodetic
      * authority factories} known to SIS for an object {@linkplain 
org.apache.sis.util.ComparisonMode#APPROXIMATIVE
-     * approximatively equals} to the specified object. If such an object is 
found, then the URN for the given
-     * authority is returned. Otherwise or if there is ambiguity, this method 
returns {@code null}.
+     * approximatively equals} to the specified object. Then there is a choice:
+     *
+     * <ul>
+     *   <li>If a single matching object is found in the specified authority 
factory, then its URN is returned.</li>
+     *   <li>Otherwise if the given object is a {@link CompoundCRS} or {@link 
ConcatenatedOperation}
+     *       and all components have an URN, then this method returns a 
combined URN.</li>
+     *   <li>Otherwise this method returns {@code null}.</li>
+     * </ul>
      *
      * <p><strong>Note that this method checks the identifier 
validity.</strong>
      * If the given object declares explicitly an identifier, then this method 
will instantiate an object from the
@@ -411,9 +421,59 @@ public final class IdentifiedObjects ext
      * @since 0.7
      */
     public static String lookupURN(final IdentifiedObject object, final 
Citation authority) throws FactoryException {
+        if (object == null) {
+            return null;
+        }
+        IdentifiedObjectFinder finder;
+        try {
+            finder = newFinder(Citations.getCodeSpace(authority));
+        } catch (NoSuchAuthorityFactoryException e) {
+            warning("lookupURN", e);
+            finder = newFinder(null);
+        }
+        String urn = lookupURN(object, authority, finder);
+        if (urn != null) {
+            return urn;
+        }
+        /*
+         * If we didn't found a URN but the given object is made of smaller 
components, build a combined URN.
+         * Example: "urn:ogc:def:crs, crs:EPSG::27700, crs:EPSG::5701" 
(without spaces actually).
+         */
+        final List<? extends IdentifiedObject> components;
+        if (object instanceof CompoundCRS) {
+            components = CRS.getSingleComponents((CompoundCRS) object);
+        } else if (object instanceof ConcatenatedOperation) {
+            components = ((ConcatenatedOperation) object).getOperations();
+        } else {
+            return null;
+        }
+        StringBuilder buffer = null;
+        for (final IdentifiedObject component : components) {
+            urn = lookupURN(component, authority, finder);
+            if (urn == null) {
+                return null;
+            }
+            assert urn.startsWith(DefinitionURI.PREFIX) : urn;
+            if (buffer == null) {
+                buffer = new 
StringBuilder(40).append(DefinitionURI.PREFIX).append(DefinitionURI.SEPARATOR)
+                                              
.append(NameMeaning.toObjectType(object.getClass()));
+            }
+            buffer.append(DefinitionURI.COMPONENT_SEPARATOR)
+                  .append(urn, DefinitionURI.PREFIX.length() + 1, 
urn.length());
+        }
+        return (buffer != null) ? buffer.toString() : null;
+    }
+
+    /**
+     * Implementation of {@link #lookupURN(IdentifiedObject, Citation)}, 
possibly invoked many times
+     * if the identified object is a {@link CompoundCRS} or {@link 
ConcatenatedOperation}.
+     */
+    private static String lookupURN(final IdentifiedObject object, final 
Citation authority,
+                                    final IdentifiedObjectFinder finder) 
throws FactoryException
+    {
         String urn = null;
         if (object != null) {
-            for (final IdentifiedObject candidate : 
newFinder(null).find(object)) {
+            for (final IdentifiedObject candidate : finder.find(object)) {
                 String c = toURN(candidate.getClass(), 
getIdentifier(candidate, authority));
                 if (c == null && authority == null) {
                     /*
@@ -426,6 +486,9 @@ public final class IdentifiedObjects ext
                         if (c != null) break;
                     }
                 }
+                /*
+                 * We should find at most one URN. But if we find many, verify 
that all of them are consistent.
+                 */
                 if (c != null) {
                     if (urn != null && !urn.equals(c)) {
                         return null;
@@ -477,7 +540,7 @@ public final class IdentifiedObjects ext
                         return null;
                     }
                 } catch (NumberFormatException e) {
-                    
Logging.recoverableException(Logging.getLogger(Modules.REFERENCING), 
IdentifiedObjects.class, "lookupEPSG", e);
+                    warning("lookupEPSG", e);
                 }
             }
         }
@@ -485,6 +548,13 @@ public final class IdentifiedObjects ext
     }
 
     /**
+     * Logs a warning for a non-critical error. The callers should have a 
fallback.
+     */
+    private static void warning(final String method, final Exception e) {
+        Logging.recoverableException(Logging.getLogger(Modules.REFERENCING), 
IdentifiedObjects.class, method, e);
+    }
+
+    /**
      * Creates a finder which can be used for looking up unidentified objects.
      * This method is an alternative to {@code lookup(…)} methods when more 
control are desired.
      *

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -569,8 +569,9 @@ abstract class AuthorityFactoryProxy<T>
      */
     private static final Map<String, AuthorityFactoryProxy<?>> BY_URN_TYPE;
     static {
-        final Map<String, AuthorityFactoryProxy<?>> map = new HashMap<>(14);
+        final Map<String, AuthorityFactoryProxy<?>> map = new HashMap<>(16);
         map.put("crs",                  CRS);
+        map.put("crs-compound",         CRS);
         map.put("datum",                DATUM);
         map.put("ellipsoid",            ELLIPSOID);
         map.put("meridian",             PRIME_MERIDIAN);
@@ -610,7 +611,7 @@ abstract class AuthorityFactoryProxy<T>
      * The proxy to use for a given type declared in a URN.
      * For example in the {@code "urn:ogc:def:crs:EPSG::4326"} URN, the proxy 
to use is {@link #CRS}.
      *
-     * @param  typeName  the URN type.
+     * @param  typeName  the name of URN type.
      * @return the proxy for the given type, or {@code null} if the given type 
is illegal.
      */
     @SuppressWarnings("unchecked")

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -1128,8 +1128,8 @@ public class GeodeticObjectFactory exten
     }
 
     /**
-     * Creates a compound coordinate reference system from an ordered list of 
{@code CoordinateReferenceSystem} objects.
-     * Apache SIS puts no restriction on the components that can be used in a 
compound CRS.
+     * Creates a compound coordinate reference system from an ordered list of 
CRS components.
+     * Apache SIS is permissive on the order of components that can be used in 
a compound CRS.
      * However for better inter-operability, users are encouraged to follow 
the order mandated by ISO 19162:
      *
      * <ol>
@@ -1141,19 +1141,20 @@ public class GeodeticObjectFactory exten
      * The default implementation creates a {@link DefaultCompoundCRS} 
instance.
      *
      * @param  properties  name and other properties to give to the new object.
-     * @param  elements   Ordered array of {@code CoordinateReferenceSystem} 
objects.
+     * @param  components  the sequence of coordinate reference systems making 
the compound CRS.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultCompoundCRS#DefaultCompoundCRS(Map, 
CoordinateReferenceSystem...)
      * @see GeodeticAuthorityFactory#createCompoundCRS(String)
+     * @see 
org.apache.sis.referencing.CRS#compound(CoordinateReferenceSystem...)
      */
     @Override
     public CompoundCRS createCompoundCRS(final Map<String,?> properties,
-            final CoordinateReferenceSystem... elements) throws 
FactoryException
+            final CoordinateReferenceSystem... components) throws 
FactoryException
     {
         final DefaultCompoundCRS crs;
         try {
-            crs = new DefaultCompoundCRS(complete(properties), elements);
+            crs = new DefaultCompoundCRS(complete(properties), components);
         } catch (IllegalArgumentException exception) {
             throw new InvalidGeodeticParameterException(exception);
         }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -20,6 +20,7 @@ import java.util.ServiceLoader;
 import java.util.Collections;
 import java.util.Collection;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Set;
 import java.util.Map;
@@ -49,9 +50,13 @@ import org.apache.sis.internal.util.Abst
 import org.apache.sis.internal.util.DefinitionURI;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.SetOfUnknownSize;
+import org.apache.sis.internal.metadata.NameMeaning;
 import org.apache.sis.internal.referencing.LazySet;
 import org.apache.sis.internal.referencing.Resources;
+import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
@@ -77,7 +82,7 @@ import org.apache.sis.internal.jdk8.JDK8
  * then the work is delegated to that factory. Otherwise a {@link 
NoSuchAuthorityFactoryException} is thrown.</p>
  *
  * <div class="section">URI syntax</div>
- * This factory can also parse URNs of the following forms:
+ * This factory can also parse URNs or URLs of the following forms:
  *
  * <ul>
  *   <li>{@code "urn:ogc:def:}<var>type</var>{@code 
:}<var>authority</var>{@code :}<var>version</var>{@code :}<var>code</var>{@code 
"}</li>
@@ -97,6 +102,30 @@ import org.apache.sis.internal.jdk8.JDK8
  * instead of {@link 
org.apache.sis.referencing.factory.sql.EPSGDataAccess#createObject(String)} 
because of the
  * {@code "crs"} part in the URN. The more specific method gives better 
performances and avoid ambiguities.</div>
  *
+ * This class accepts also combined URIs of the following forms
+ * (only two components shown, but arbitrary number of components is allowed):
+ *
+ * <ul>
+ *   <li>{@code "urn:ogc:def:}<var>type</var>{@code ,}
+ *       <var>type₁</var>{@code :}<var>authority₁</var>{@code 
:}<var>version₁</var>{@code :}<var>code₁</var>{@code ,}
+ *       <var>type₂</var>{@code :}<var>authority₂</var>{@code 
:}<var>version₂</var>{@code :}<var>code₂</var>{@code "}</li>
+ *   <li>{@code  "http://www.opengis.net/def/crs-compound?}<br>
+ *       {@code 1=http://www.opengis.net/def/crs/}<var>authority₁</var>{@code 
/}<var>version₁</var>{@code /}<var>code₁</var>{@code &}<br>
+ *       {@code 2=http://www.opengis.net/def/crs/}<var>authority₂</var>{@code 
/}<var>version₂</var>{@code /}<var>code₂</var>{@code "}</li>
+ * </ul>
+ *
+ * Given such URIs, {@code MultiAuthoritiesFactory} invokes {@link 
#createObject(String)} for each component
+ * and combines the result as described by the {@link 
CRS#compound(CoordinateReferenceSystem...)} method.
+ * URNs (but not URLs) can also combine a
+ * {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum geodetic 
datum} with an
+ * {@linkplain org.apache.sis.referencing.cs.DefaultEllipsoidalCS ellipsoidal 
coordinate system} for creating a new
+ * {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS geographic 
CRS}, or a base geographic CRS with a
+ * {@linkplain org.apache.sis.referencing.operation.DefaultConversion 
conversion} and a
+ * {@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian 
coordinate system} for creating a new
+ * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected 
coordinate reference system}, or
+ * {@linkplain 
org.apache.sis.referencing.operation.AbstractCoordinateOperation coordinate 
operations}
+ * for creating a concatenated operation.
+ *
  * <div class="section">Multiple versions for the same authority</div>
  * {@code MultiAuthoritiesFactory} accepts an arbitrary amount of factories 
for the same authority, provided that
  * those factories have different version numbers. If a {@code 
createFoo(String)} method is invoked with a URN
@@ -120,7 +149,7 @@ import org.apache.sis.internal.jdk8.JDK8
  * do not need to be thread-safe. See constructor Javadoc for more information.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @see org.apache.sis.referencing.CRS#getAuthorityFactory(String)
  *
@@ -730,21 +759,47 @@ public class MultiAuthoritiesFactory ext
      * @return the object from one of the authority factory specified at 
construction time.
      * @throws FactoryException if an error occurred while creating the object.
      */
-    final <T> T create(AuthorityFactoryProxy<? extends T> proxy, String code) 
throws FactoryException {
+    private <T> T create(AuthorityFactoryProxy<? extends T> proxy, String 
code) throws FactoryException {
         ArgumentChecks.ensureNonNull("code", code);
         final String authority, version;
         final String[] parameters;
         final DefinitionURI uri = DefinitionURI.parse(code);
         if (uri != null) {
+            Class<? extends T> type = proxy.type;
+            proxy = proxy.specialize(uri.type);
+            /*
+             * If the URN or URL contains combined references for compound 
coordinate reference systems,
+             * create the components. First we verify that all component 
references have been parsed
+             * before to start creating any object.
+             */
+            if (uri.code == null) {
+                final DefinitionURI[] components = uri.components;
+                if (components != null) {
+                    for (int i=0; i < components.length; i++) {
+                        if (components[i] == null) {
+                            throw new 
NoSuchAuthorityCodeException(Resources.format(
+                                    
Resources.Keys.CanNotParseCombinedReference_2, i+1, uri.isHTTP ? 1 : 0),
+                                    uri.authority, null, uri.toString());
+                        }
+                    }
+                    if (proxy != null) type = proxy.type;       // Use the 
more specific type declared in the URN.
+                    return combine(type, components, uri.isHTTP);
+                }
+            }
+            /*
+             * At this point we determined that the URN or URL references a 
single instance (not combined references).
+             * Example: "urn:ogc:def:crs:EPSG:9.1:4326". Verifies that the 
object type is recognized and that a code
+             * is present. The remainder steps are the same as if the user 
gave a simple code (e.g. "EPSG:4326").
+             */
             if (uri.authority == null) {
-                throw new 
NoSuchAuthorityCodeException(Resources.format(Resources.Keys.MissingAuthority_1,
 code), null, uri.code, code);
+                // We want this check before the 'code' value is modified 
below.
+                throw new NoSuchAuthorityCodeException(
+                        Resources.format(Resources.Keys.MissingAuthority_1, 
code), null, uri.code, code);
             }
-            final Class<? extends T> type = proxy.type;
             authority  = uri.authority;
             version    = uri.version;
             code       = uri.code;
             parameters = uri.parameters;
-            proxy      = proxy.specialize(uri.type);
             if (code == null || proxy == null) {
                 final String s = uri.toString();
                 final String message;
@@ -1489,6 +1544,195 @@ public class MultiAuthoritiesFactory ext
     }
 
     /**
+     * Invoked when a {@code createFoo(…)} method is given a combined URI.
+     * A combined URI is a URN or URL referencing other components. For 
example if the given URI
+     * is {@code "urn:ogc:def:crs, crs:EPSG::27700, crs:EPSG::5701"}, then the 
components are:
+     * <ol>
+     *   <li>{@code "urn:ogc:def:crs:EPSG:9.1:27700"}</li>
+     *   <li>{@code "urn:ogc:def:crs:EPSG:9.1:5701"}</li>
+     * </ol>
+     *
+     * We do not require the components to be instance of CRS, since the 
"Definition identifier URNs in
+     * OGC namespace" best practice paper allows other kinds of combination 
(e.g. of coordinate operations).
+     *
+     * @param  <T>         compile-time value of {@code type} argument.
+     * @param  type        type of object to create.
+     * @param  references  parsed URI of the components.
+     * @param  isHTTP      whether the user URI is an URL (i.e. {@code 
"http://something"}) instead than a URN.
+     * @return the combined object.
+     * @throws FactoryException if an error occurred while creating the 
combined object.
+     */
+    private <T> T combine(final Class<T> type, final DefinitionURI[] 
references, final boolean isHTTP) throws FactoryException {
+        /*
+         * Identify the type requested by the user and create all components 
with the assumption that they will
+         * be of that type. This is the most common case. If during iteration 
we find an object of another kind,
+         * then the array type will be downgraded to IdentifiedObject[]. The 
'componentType' variable will keep
+         * its non-null value only if the array stay of the expected sub-type.
+         */
+        final byte requestedType;
+        IdentifiedObject[] components;
+        Class<? extends IdentifiedObject> componentType;
+        if (CoordinateReferenceSystem.class.isAssignableFrom(type)) {
+            requestedType = AuthorityFactoryIdentifier.CRS;
+            componentType = CoordinateReferenceSystem.class;
+            components    = new CoordinateReferenceSystem[references.length];  
     // Intentional covariance.
+        } else if (CoordinateOperation.class.isAssignableFrom(type)) {
+            requestedType = AuthorityFactoryIdentifier.OPERATION;
+            componentType = CoordinateOperation.class;
+            components    = new CoordinateOperation[references.length];        
     // Intentional covariance.
+        } else {
+            throw new 
FactoryException(Resources.format(Resources.Keys.CanNotCombineUriAsType_1, 
type));
+        }
+        final String expected = NameMeaning.toObjectType(componentType);    // 
Note: "compound-crs" ⟶ "crs".
+        for (int i=0; i<references.length; i++) {
+            final DefinitionURI ref = references[i];
+            final IdentifiedObject component = createObject(ref.toString());
+            if (componentType != null && (!componentType.isInstance(component) 
|| !expected.equalsIgnoreCase(ref.type))) {
+                componentType = null;
+                components = Arrays.copyOf(components, components.length, 
IdentifiedObject[].class);
+            }
+            components[i] = component;
+        }
+        /*
+         * At this point we have successfully created all components. The way 
to interpret those components
+         * depends mostly on the type of object requested by the user. For a 
given requested type, different
+         * rules apply depending on the type of components. Those rules are 
described in OGC 07-092r1 (2007):
+         * "Definition identifier URNs in OGC namespace".
+         */
+        IdentifiedObject combined = null;
+        switch (requestedType) {
+            case AuthorityFactoryIdentifier.OPERATION: {
+                if (componentType != null) {
+                    /*
+                     * URN combined references for concatenated operations. We 
build an operation name from
+                     * the operation identifiers (rather than CRS identifiers) 
because this is what the user
+                     * gave to us, and because source/target CRS are not 
guaranteed to be defined. We do not
+                     * yet support swapping roles of source and target CRS if 
an implied-reverse coordinate
+                     * operation is included.
+                     */
+                    final CoordinateOperation[] ops = (CoordinateOperation[]) 
components;
+                    String name = 
IdentifiedObjects.getIdentifierOrName(ops[0]) + " ⟶ "
+                                + 
IdentifiedObjects.getIdentifierOrName(ops[ops.length - 1]);
+                    combined = 
DefaultFactories.forBuildin(CoordinateOperationFactory.class)
+                            
.createConcatenatedOperation(Collections.singletonMap(CoordinateOperation.NAME_KEY,
 name), ops);
+                }
+                break;
+            }
+            case AuthorityFactoryIdentifier.CRS: {
+                if (componentType != null) {
+                    /*
+                     * URN combined references for compound coordinate 
reference systems.
+                     * The URNs of the individual well-known CRSs are listed 
in the same order in which the
+                     * individual coordinate tuples are combined to form the 
CompoundCRS coordinate tuple.
+                     */
+                    combined = CRS.compound((CoordinateReferenceSystem[]) 
components);
+                } else if (!isHTTP) {
+                    final CoordinateSystem cs = remove(references, components, 
CoordinateSystem.class);
+                    if (cs != null) {
+                        final Datum datum = remove(references, components, 
Datum.class);
+                        if (datum != null) {
+                            /*
+                             * URN combined references for datum and 
coordinate system. In this case, the URN shall
+                             * concatenate the URNs of one well-known datum 
and one well-known coordinate system.
+                             */
+                            if (ArraysExt.allEquals(references, null)) {
+                                combined = combine((GeodeticDatum) datum, cs);
+                            }
+                        } else {
+                            /*
+                             * URN combined references for projected or 
derived CRSs. In this case, the URN shall
+                             * concatenate the URNs of the one well-known CRS, 
one well-known Conversion, and one
+                             * well-known CartesianCS. Similar action can be 
taken for derived CRS.
+                             */
+                            CoordinateReferenceSystem baseCRS = 
remove(references, components, CoordinateReferenceSystem.class);
+                            CoordinateOperation op = remove(references, 
components, CoordinateOperation.class);
+                            if (ArraysExt.allEquals(references, null) && op 
instanceof Conversion) {
+                                combined = combine(baseCRS, (Conversion) op, 
cs);
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+        /*
+         * At this point the combined object has been created if we know how 
to create it.
+         * Maybe the result matches the definition of an existing object in 
the database,
+         * in which case we will use the existing definition for better 
metadata.
+         */
+        if (combined == null) {
+            throw new 
FactoryException(Resources.format(Resources.Keys.UnexpectedComponentInURI));
+        }
+        final IdentifiedObject existing = 
newIdentifiedObjectFinder().findSingleton(combined);
+        return type.cast(existing != null ? existing : combined);
+    }
+
+    /**
+     * If the given {@code type} is found in the given {@code references}, 
sets that reference element to {@code null}
+     * and returns the corresponding {@code components} element. Otherwise 
returns {@code null}. This is equivalent to
+     * {@link Map#remove(Object, Object)} where {@code references} are the 
keys and {@code components} are the values.
+     * We do not bother building that map because the arrays are very short (2 
or 3 elements).
+     */
+    private static <T> T remove(final DefinitionURI[] references, final 
IdentifiedObject[] components, final Class<T> type) {
+        final String expected = NameMeaning.toObjectType(type);
+        for (int i=0; i<references.length; i++) {
+            final DefinitionURI ref = references[i];
+            if (ref != null && expected.equalsIgnoreCase(ref.type)) {
+                references[i] = null;
+                return type.cast(components[i]);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked when a {@code createFoo(…)} method is given a combined URI 
containing a datum and a coordinate system.
+     * If the given information are not sufficient or not applicable, then 
this method returns {@code null}.
+     *
+     * @param  datum  the datum, or {@code null} if missing.
+     * @param  cs     the coordinate system (never null).
+     * @return the combined CRS, or {@code null} if the given information are 
not sufficient.
+     * @throws FactoryException if an error occurred while creating the 
combined CRS.
+     */
+    private static GeodeticCRS combine(final GeodeticDatum datum, final 
CoordinateSystem cs) throws FactoryException {
+        final Map<String,?> properties = 
IdentifiedObjects.getProperties(datum, Datum.IDENTIFIERS_KEY);
+        final CRSFactory factory = 
DefaultFactories.forBuildin(CRSFactory.class);
+        if (datum instanceof GeodeticDatum) {
+            if (cs instanceof EllipsoidalCS) {
+                return factory.createGeographicCRS(properties, datum, 
(EllipsoidalCS) cs);
+            } else if (cs instanceof SphericalCS) {
+                return factory.createGeocentricCRS(properties, datum, 
(SphericalCS) cs);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked when a {@code createFoo(…)} method is given a combined URI 
containing a conversion and a coordinate
+     * system. If the given information are not sufficient or not applicable, 
then this method returns {@code null}.
+     *
+     * @param  baseCRS   the CRS on which the derived CRS will be based on, or 
{@code null} if missing.
+     * @param  fromBase  the conversion from {@code baseCRS} to the CRS to be 
created by this method.
+     * @param  cs        the coordinate system (never null).
+     * @return the combined CRS, or {@code null} if the given information are 
not sufficient.
+     * @throws FactoryException if an error occurred while creating the 
combined CRS.
+     */
+    private static GeneralDerivedCRS combine(final CoordinateReferenceSystem 
baseCRS, final Conversion fromBase,
+            final CoordinateSystem cs) throws FactoryException
+    {
+        if (baseCRS != null && fromBase.getSourceCRS() == null && 
fromBase.getTargetCRS() == null) {
+            final Map<String,?> properties = 
IdentifiedObjects.getProperties(fromBase, Datum.IDENTIFIERS_KEY);
+            final CRSFactory factory = 
DefaultFactories.forBuildin(CRSFactory.class);
+            if (baseCRS instanceof GeographicCRS && cs instanceof CartesianCS) 
{
+                return factory.createProjectedCRS(properties, (GeographicCRS) 
baseCRS, fromBase, (CartesianCS) cs);
+            } else {
+                return factory.createDerivedCRS(properties, baseCRS, fromBase, 
cs);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Creates a finder which can be used for looking up unidentified objects.
      * The default implementation delegates the lookups to the underlying 
factories.
      *

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -122,10 +122,10 @@ final class CRSPair {
     }
 
     /**
-     * Return a string representation of this key.
+     * Returns a string representation of this key.
      */
     @Override
     public String toString() {
-        return label(sourceCRS) + " → " + label(targetCRS);
+        return label(sourceCRS) + " ⟶ " + label(targetCRS);
     }
 }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ProjectionException.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -32,7 +32,7 @@ import org.opengis.referencing.operation
  *   <li>If the expected mathematical value is infinite (for example the 
Mercator projection at ±90° of latitude),
  *       then the map projection should return a {@link 
Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY},
  *       depending on the sign of the correct mathematical answer.</li>
- *   <li>If no real number is expected to exist for the input coordinate (for 
example the root of a negative value),
+ *   <li>If no real number is expected to exist for the input coordinate (for 
example at a latitude greater than 90°),
  *       then the map projection should return {@link Double#NaN}.</li>
  *   <li>If a real number is expected to exist but the map projection fails to 
compute it (for example because an
  *       iterative algorithm does not converge), then the projection should 
throw {@code ProjectionException}.</li>

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/EllipsoidalHeightCombinerTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/EllipsoidalHeightCombinerTest.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/EllipsoidalHeightCombinerTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/EllipsoidalHeightCombinerTest.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -32,6 +32,7 @@ import org.apache.sis.referencing.crs.Ha
 import org.apache.sis.referencing.factory.GeodeticObjectFactory;
 import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
 import org.apache.sis.referencing.operation.HardCodedConversions;
+import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -81,11 +82,11 @@ public final strictfp class EllipsoidalH
     public void testGeographicCRS() throws FactoryException {
         final EllipsoidalHeightCombiner services = create();
         final Map<String,String> properties = 
Collections.singletonMap(CoordinateReferenceSystem.NAME_KEY, "WGS 84 (4D)");
-        final GeographicCRS horizontal   = HardCodedCRS.WGS84;
-        final GeographicCRS horizontal3D = HardCodedCRS.WGS84_3D;
-        final VerticalCRS   vertical     = HardCodedCRS.ELLIPSOIDAL_HEIGHT;
-        final TemporalCRS   temporal     = HardCodedCRS.TIME;
-        final VerticalCRS   geoidal      = HardCodedCRS.GRAVITY_RELATED_HEIGHT;
+        final GeographicCRS horizontal = HardCodedCRS.WGS84;
+        final GeographicCRS volumetric = HardCodedCRS.WGS84_3D;
+        final VerticalCRS   vertical   = HardCodedCRS.ELLIPSOIDAL_HEIGHT;
+        final TemporalCRS   temporal   = HardCodedCRS.TIME;
+        final VerticalCRS   geoidal    = HardCodedCRS.GRAVITY_RELATED_HEIGHT;
         /*
          * createCompoundCRS(…) should not combine GeographicCRS with 
non-ellipsoidal height.
          */
@@ -95,12 +96,12 @@ public final strictfp class EllipsoidalH
          * createCompoundCRS(…) should combine GeographicCRS with ellipsoidal 
height.
          */
         compound = services.createCompoundCRS(properties, horizontal, 
vertical);
-        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {horizontal3D}, 
CRS.getSingleComponents(compound).toArray());
+        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {volumetric}, 
CRS.getSingleComponents(compound).toArray());
         /*
          * createCompoundCRS(…) should combine GeographicCRS with ellipsoidal 
height and keep time.
          */
         compound = services.createCompoundCRS(properties, horizontal, 
vertical, temporal);
-        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {horizontal3D, 
temporal}, CRS.getSingleComponents(compound).toArray());
+        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {volumetric, 
temporal}, CRS.getSingleComponents(compound).toArray());
         /*
          * Non-standard feature: accept (VerticalCRS + GeodeticCRS) order.
          * The test below use the reverse order for all axes compared to the 
previous test.
@@ -127,11 +128,11 @@ public final strictfp class EllipsoidalH
         final EllipsoidalHeightCombiner services = create();
         final GeodeticObjectFactory factory = new GeodeticObjectFactory();
         final Map<String,String> properties = 
Collections.singletonMap(CoordinateReferenceSystem.NAME_KEY, "World Mercator 
(4D)");
-        final ProjectedCRS horizontal   = 
factory.createProjectedCRS(properties, HardCodedCRS.WGS84,    
HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED);
-        final ProjectedCRS horizontal3D = 
factory.createProjectedCRS(properties, HardCodedCRS.WGS84_3D, 
HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED_3D);
-        final VerticalCRS  vertical     = HardCodedCRS.ELLIPSOIDAL_HEIGHT;
-        final TemporalCRS  temporal     = HardCodedCRS.TIME;
-        final VerticalCRS  geoidal      = HardCodedCRS.GRAVITY_RELATED_HEIGHT;
+        final ProjectedCRS horizontal = factory.createProjectedCRS(properties, 
HardCodedCRS.WGS84,    HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED);
+        final ProjectedCRS volumetric = factory.createProjectedCRS(properties, 
HardCodedCRS.WGS84_3D, HardCodedConversions.MERCATOR, HardCodedCS.PROJECTED_3D);
+        final VerticalCRS  vertical   = HardCodedCRS.ELLIPSOIDAL_HEIGHT;
+        final TemporalCRS  temporal   = HardCodedCRS.TIME;
+        final VerticalCRS  geoidal    = HardCodedCRS.GRAVITY_RELATED_HEIGHT;
         /*
          * createCompoundCRS(…) should not combine ProjectedCRS with 
non-ellipsoidal height.
          */
@@ -141,12 +142,12 @@ public final strictfp class EllipsoidalH
          * createCompoundCRS(…) should combine ProjectedCRS with ellipsoidal 
height.
          */
         compound = services.createCompoundCRS(properties, horizontal, 
vertical);
-        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {horizontal3D}, 
CRS.getSingleComponents(compound).toArray());
+        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {volumetric}, 
CRS.getSingleComponents(compound).toArray());
         /*
          * createCompoundCRS(…) should combine ProjectedCRS with ellipsoidal 
height and keep time.
          */
         compound = services.createCompoundCRS(properties, horizontal, 
vertical, temporal);
-        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {horizontal3D, 
temporal}, CRS.getSingleComponents(compound).toArray());
+        assertArrayEqualsIgnoreMetadata(new SingleCRS[] {volumetric, 
temporal}, CRS.getSingleComponents(compound).toArray());
         /*
          * Non-standard feature: accept (VerticalCRS + ProjectedCRS) order.
          */
@@ -159,4 +160,15 @@ public final strictfp class EllipsoidalH
                 ((CoordinateReferenceSystem) 
components[1]).getCoordinateSystem(),
                 AxisDirection.UP, AxisDirection.EAST, AxisDirection.NORTH);
     }
+
+    /**
+     * Tests {@link 
EllipsoidalHeightCombiner#properties(CoordinateReferenceSystem...)}.
+     */
+    @Test
+    public void testProperties() {
+        final Map<String,?> properties = 
EllipsoidalHeightCombiner.properties(HardCodedCRS.WGS84, 
HardCodedCRS.GRAVITY_RELATED_HEIGHT, HardCodedCRS.TIME);
+        assertEquals("WGS 84 + MSL height + Time", properties.remove("name"));
+        assertEquals(Extents.WORLD, properties.remove("domainOfValidity"));
+        assertTrue("No other property expected.", properties.isEmpty());
+    }
 }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -93,7 +93,7 @@ public final strictfp class ReferencingU
     }
 
     /**
-     * Tests {@link 
ReferencingUtilities#getPropertiesForModifiedCRS(IdentifiedObject, String...)}.
+     * Tests {@link 
ReferencingUtilities#getPropertiesForModifiedCRS(IdentifiedObject)}.
      *
      * @since 0.7
      */

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ServicesForMetadataTest.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.referencing;
 
+import java.util.Date;
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.metadata.extent.VerticalExtent;
@@ -23,6 +24,7 @@ import org.opengis.referencing.crs.Coord
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
+import org.apache.sis.metadata.iso.extent.DefaultTemporalExtent;
 import org.apache.sis.metadata.iso.extent.DefaultSpatialTemporalExtent;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.referencing.CommonCRS;
@@ -30,6 +32,8 @@ import org.apache.sis.referencing.crs.Ha
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
+import org.apache.sis.test.TestUtilities;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
@@ -162,4 +166,42 @@ public final strictfp class ServicesForM
         verifySpatialExtent((GeographicBoundingBox) 
getSingleton(extent.getSpatialExtent()));
         verifyVerticalExtent(CommonCRS.Vertical.MEAN_SEA_LEVEL, 
extent.getVerticalExtent());
     }
+
+    /**
+     * Tests {@link DefaultVerticalExtent#intersect(VerticalExtent)}.
+     *
+     * @throws TransformException if the transformation failed.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testVerticalIntersection() throws TransformException {
+        final DefaultVerticalExtent e1 = new DefaultVerticalExtent(1000, 2000, 
HardCodedCRS.ELLIPSOIDAL_HEIGHT_cm);
+        final DefaultVerticalExtent e2 = new DefaultVerticalExtent(15,   25,   
HardCodedCRS.ELLIPSOIDAL_HEIGHT);
+        e1.intersect(e2);
+        assertEquals(new DefaultVerticalExtent(1500, 2000, 
HardCodedCRS.ELLIPSOIDAL_HEIGHT_cm), e1);
+    }
+
+    /**
+     * Tests {@link DefaultTemporalExtent#intersect(TemporalExtent)}.
+     *
+     * @throws TransformException if the transformation failed.
+     *
+     * @since 0.8
+     */
+    @Test
+    @Ignore("This operation requires the sis-temporal module.")
+    public void testTemporalIntersection() throws TransformException {
+        final DefaultTemporalExtent e1 = new DefaultTemporalExtent();
+        final DefaultTemporalExtent e2 = new DefaultTemporalExtent();
+        final Date t1 = TestUtilities.date("2016-12-05 19:45:20");
+        final Date t2 = TestUtilities.date("2017-02-18 02:12:50");
+        final Date t3 = TestUtilities.date("2017-11-30 23:50:00");
+        final Date t4 = TestUtilities.date("2018-05-20 12:30:45");
+        e1.setBounds(t1, t3);
+        e2.setBounds(t2, t4);
+        e1.intersect(e2);
+        assertEquals("startTime", t2, e1.getStartTime());
+        assertEquals("endTime",   t3, e1.getEndTime());
+    }
 }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/DatumShiftTestCase.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/DatumShiftTestCase.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/DatumShiftTestCase.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/DatumShiftTestCase.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -24,7 +24,7 @@ import java.nio.file.FileSystemNotFoundE
 import org.apache.sis.test.TestCase;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.*;
+import static org.junit.Assume.assumeFalse;
 
 
 /**

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -19,6 +19,7 @@ package org.apache.sis.referencing;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Arrays;
+import java.util.Collections;
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -38,6 +39,7 @@ import org.apache.sis.util.Utilities;
 // Test imports
 import org.apache.sis.referencing.operation.HardCodedConversions;
 import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.referencing.cs.HardCodedCS;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -280,6 +282,26 @@ public final strictfp class CRSTest exte
     }
 
     /**
+     * Tests getting the horizontal and vertical components of a 
three-dimensional projected CRS.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testComponentsOfProjectedCRS() {
+        final ProjectedCRS volumetric = new 
DefaultProjectedCRS(Collections.singletonMap(ProjectedCRS.NAME_KEY, "3D"),
+                HardCodedCRS.WGS84_3D, HardCodedConversions.MERCATOR, 
HardCodedCS.PROJECTED_3D);
+
+        assertFalse("isHorizontalCRS", CRS.isHorizontalCRS(volumetric));
+        assertNull("getTemporalComponent", 
CRS.getTemporalComponent(volumetric));
+        assertNull("getVerticalComponent", 
CRS.getVerticalComponent(volumetric, false));
+        assertEqualsIgnoreMetadata(HardCodedCRS.ELLIPSOIDAL_HEIGHT, 
CRS.getVerticalComponent(volumetric, true));
+        final SingleCRS horizontal = CRS.getHorizontalComponent(volumetric);
+        assertInstanceOf("getHorizontalComponent", ProjectedCRS.class, 
horizontal);
+        assertEquals("dimension", 2, 
horizontal.getCoordinateSystem().getDimension());
+        assertTrue("isHorizontalCRS", CRS.isHorizontalCRS(horizontal));
+    }
+
+    /**
      * Tests {@link CRS#getComponentAt(CoordinateReferenceSystem, int, int)}.
      *
      * @since 0.5
@@ -310,6 +332,26 @@ public final strictfp class CRSTest exte
     }
 
     /**
+     * Tests {@link CRS#compound(CoordinateReferenceSystem...)}.
+     *
+     * @throws FactoryException if an error occurred while creating a compound 
CRS.
+     *
+     * @since 0.8
+     */
+    @Test
+    public void testCompound() throws FactoryException {
+        try {
+            CRS.compound();
+            fail("Should not accept empty array.");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("components"));
+        }
+        assertSame(HardCodedCRS.WGS84, CRS.compound(HardCodedCRS.WGS84));
+        assertEqualsIgnoreMetadata(HardCodedCRS.WGS84_3D, 
CRS.compound(HardCodedCRS.WGS84, HardCodedCRS.ELLIPSOIDAL_HEIGHT));
+    }
+
+    /**
      * Tests {@link CRS#getComponentAt(CoordinateReferenceSystem, int, int)} 
on a (x,y,z,t)
      * coordinate reference system having 4 dimensions. All arguments given to 
this method
      * except the last one are the expected components, which may be {@code 
null}.

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/CodesTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/CodesTest.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/CodesTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/CodesTest.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -19,19 +19,14 @@ package org.apache.sis.referencing.cs;
 import java.util.Map;
 import javax.measure.Unit;
 import java.lang.reflect.Field;
-import org.opengis.util.FactoryException;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CSAuthorityFactory;
-import org.opengis.referencing.crs.CRSAuthorityFactory;
-import org.apache.sis.referencing.factory.UnavailableFactoryException;
-import org.apache.sis.referencing.factory.sql.EPSGFactory;
-import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.factory.TestFactorySource;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
-import static org.junit.Assume.*;
 
 
 /**
@@ -44,20 +39,6 @@ import static org.junit.Assume.*;
  */
 public final strictfp class CodesTest extends TestCase {
     /**
-     * Returns the EPSG factory, or skips the test if the factory is not 
available.
-     */
-    private static CSAuthorityFactory factory() throws FactoryException {
-        final CRSAuthorityFactory factory = CRS.getAuthorityFactory("EPSG");
-        assumeTrue("No connection to EPSG dataset.", factory instanceof 
EPSGFactory);
-        try {
-            assertNotNull(factory.createGeographicCRS("4326"));
-        } catch (UnavailableFactoryException e) {
-            assumeTrue("No connection to EPSG dataset.", false);
-        }
-        return (EPSGFactory) factory;
-    }
-
-    /**
      * Compares the axis directions and units with EPSG definitions.
      *
      * @throws Exception if an error occurred while fetching the codes or 
querying the database.
@@ -65,7 +46,7 @@ public final strictfp class CodesTest ex
     @Test
     @SuppressWarnings("unchecked")
     public void verify() throws Exception {
-        final CSAuthorityFactory factory = factory();
+        final CSAuthorityFactory factory = 
TestFactorySource.getSharedFactory();
         final Field field = Codes.class.getDeclaredField("EPSG");
         field.setAccessible(true);
         for (final Codes c : ((Map<Codes,?>) field.get(null)).keySet()) {

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/AuthorityFactoryMock.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/AuthorityFactoryMock.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/AuthorityFactoryMock.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/AuthorityFactoryMock.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -27,6 +27,7 @@ import org.opengis.referencing.crs.CRSAu
 import org.opengis.referencing.crs.GeocentricCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.VerticalCRS;
+import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.cs.CSAuthorityFactory;
 import org.opengis.referencing.datum.DatumAuthorityFactory;
 import org.opengis.referencing.datum.GeodeticDatum;
@@ -40,6 +41,7 @@ import org.apache.sis.measure.Units;
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.referencing.datum.HardCodedDatum;
 import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.referencing.cs.HardCodedCS;
 
 import static org.junit.Assert.*;
 
@@ -48,7 +50,7 @@ import static org.junit.Assert.*;
  * A pseudo-authority factory with hard-coded objects.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -114,6 +116,7 @@ public final strictfp class AuthorityFac
         if (type.isAssignableFrom(GeodeticDatum.class)) add(codes, 6326, 6322, 
6807, 6301, 6612, 6047);
         if (type.isAssignableFrom(VerticalDatum.class)) add(codes, 5100);
         if (type.isAssignableFrom(VerticalCRS.class))   add(codes, 5714, 9905);
+        if (type.isAssignableFrom(EllipsoidalCS.class)) add(codes, 6422, 6424);
         return codes;
     }
 
@@ -147,6 +150,8 @@ public final strictfp class AuthorityFac
             case 6612: return HardCodedDatum.JGD2000;
             case 6047: return HardCodedDatum.SPHERE;
             case 5100: return HardCodedDatum.MEAN_SEA_LEVEL;
+            case 6422: return HardCodedCS.GEODETIC_φλ;
+            case 6424: return HardCodedCS.GEODETIC_2D;
             default: throw new NoSuchAuthorityCodeException(code, 
authority.getTitle().toString(), code);
         }
     }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2001.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2001.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2001.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2001.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -16,13 +16,7 @@
  */
 package org.apache.sis.referencing.factory;
 
-import java.util.Map;
-import java.util.HashMap;
 import org.opengis.util.FactoryException;
-import org.apache.sis.util.logging.Logging;
-import org.apache.sis.internal.system.Loggers;
-import org.apache.sis.internal.util.Constants;
-import org.apache.sis.referencing.factory.sql.EPSGFactory;
 
 // Test imports
 import org.junit.AfterClass;
@@ -32,7 +26,6 @@ import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.junit.runners.MethodSorters;
 
-import static org.opengis.test.Assert.*;
 
 
 /**
@@ -52,76 +45,29 @@ import static org.opengis.test.Assert.*;
 @FixMethodOrder(MethodSorters.JVM)      // Intentionally want some randomness
 public final strictfp class GIGS2001 extends 
org.opengis.test.referencing.gigs.GIGS2001 {
     /**
-     * The factory instance to use for the tests, or {@code null} if not 
available.
-     * This field is set by {@link #createFactory()} and cleared by {@link 
#close()}.
-     */
-    public static EPSGFactory factory;
-
-    /**
-     * {@code true} if we failed to create the {@link #factory}.
-     */
-    private static boolean isUnavailable;
-
-    /**
      * Creates a new test using the default authority factory.
      */
     public GIGS2001() {
-        super(factory);
+        super(TestFactorySource.factory);
     }
 
     /**
      * Creates the factory to use for all tests in this class.
-     * If this method fails to create the factory, then {@link #factory} is 
left to {@code null} value.
      *
      * @throws FactoryException if an error occurred while creating the 
factory.
      */
     @BeforeClass
-    @SuppressWarnings("null")
     public static void createFactory() throws FactoryException {
-        if (!isUnavailable) {
-            EPSGFactory af = factory;
-            if (af == null) {
-                final GeodeticObjectFactory f = new GeodeticObjectFactory();
-                final Map<String,Object> properties = new HashMap<>(6);
-                assertNull(properties.put("datumFactory", f));
-                assertNull(properties.put("csFactory", f));
-                assertNull(properties.put("crsFactory", f));
-                try {
-                    af = new EPSGFactory(properties);
-                    assertEquals("Expected no Data Access Object (DAO) before 
the first test is run.",
-                                 0, ((ConcurrentAuthorityFactory) 
af).countAvailableDataAccess());
-                    /*
-                     * Above method call may fail if no data source has been 
specified.
-                     * Following method call may fail if a data source has 
been specified,
-                     * but the database does not contain the required tables.
-                     */
-                    
assertNotNull(af.createUnit(String.valueOf(Constants.EPSG_METRE)));
-                    factory = af;                                              
             // Must be last.
-                } catch (UnavailableFactoryException e) {
-                    isUnavailable = true;
-                    
Logging.getLogger(Loggers.CRS_FACTORY).warning(e.toString());
-                } finally {
-                    if (factory != af) {
-                        af.close();
-                    }
-                }
-            }
-        }
+        TestFactorySource.createFactory();
     }
 
     /**
-     * Force releases of JDBC connections after the tests in this class.
+     * Forces release of JDBC connections after the tests in this class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterClass
     public static void close() throws FactoryException {
-        final EPSGFactory af = factory;
-        if (af != null) {
-            factory = null;
-            final int n = ((ConcurrentAuthorityFactory) 
af).countAvailableDataAccess();
-            af.close();
-            assertBetween("Since we ran all tests sequantially, should have no 
more than 1 Data Access Object (DAO).", 0, 1, n);
-        }
+        TestFactorySource.close();
     }
 }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2002.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2002.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2002.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2002.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -64,7 +64,7 @@ public final strictfp class GIGS2002 ext
      * Creates a new test using the default authority factory.
      */
     public GIGS2002() {
-        super(GIGS2001.factory);
+        super(TestFactorySource.factory);
     }
 
     /**
@@ -74,17 +74,17 @@ public final strictfp class GIGS2002 ext
      */
     @BeforeClass
     public static void createFactory() throws FactoryException {
-        GIGS2001.createFactory();
+        TestFactorySource.createFactory();
     }
 
     /**
-     * Force releases of JDBC connections after the tests in this class.
+     * Forces release of JDBC connections after the tests in this class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterClass
     public static void close() throws FactoryException {
-        GIGS2001.close();
+        TestFactorySource.close();
     }
 
     /**

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2003.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2003.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2003.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2003.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -52,7 +52,7 @@ public final strictfp class GIGS2003 ext
      * Creates a new test using the default authority factory.
      */
     public GIGS2003() {
-        super(GIGS2001.factory);
+        super(TestFactorySource.factory);
     }
 
     /**
@@ -62,16 +62,16 @@ public final strictfp class GIGS2003 ext
      */
     @BeforeClass
     public static void createFactory() throws FactoryException {
-        GIGS2001.createFactory();
+        TestFactorySource.createFactory();
     }
 
     /**
-     * Force releases of JDBC connections after the tests in this class.
+     * Forces release of JDBC connections after the tests in this class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterClass
     public static void close() throws FactoryException {
-        GIGS2001.close();
+        TestFactorySource.close();
     }
 }

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2004.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2004.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2004.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2004.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -68,7 +68,7 @@ public final strictfp class GIGS2004 ext
      * Creates a new test using the default authority factory.
      */
     public GIGS2004() {
-        super(GIGS2001.factory, GIGS2001.factory);
+        super(TestFactorySource.factory, TestFactorySource.factory);
     }
 
     /**
@@ -78,17 +78,17 @@ public final strictfp class GIGS2004 ext
      */
     @BeforeClass
     public static void createFactory() throws FactoryException {
-        GIGS2001.createFactory();
+        TestFactorySource.createFactory();
     }
 
     /**
-     * Force releases of JDBC connections after the tests in this class.
+     * Forces release of JDBC connections after the tests in this class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterClass
     public static void close() throws FactoryException {
-        GIGS2001.close();
+        TestFactorySource.close();
     }
 
     /**

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2005.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2005.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2005.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2005.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -65,7 +65,7 @@ public final strictfp class GIGS2005 ext
      * Creates a new test using the default authority factory.
      */
     public GIGS2005() {
-        super(GIGS2001.factory);
+        super(TestFactorySource.factory);
     }
 
     /**
@@ -75,17 +75,17 @@ public final strictfp class GIGS2005 ext
      */
     @BeforeClass
     public static void createFactory() throws FactoryException {
-        GIGS2001.createFactory();
+        TestFactorySource.createFactory();
     }
 
     /**
-     * Force releases of JDBC connections after the tests in this class.
+     * Forces release of JDBC connections after the tests in this class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterClass
     public static void close() throws FactoryException {
-        GIGS2001.close();
+        TestFactorySource.close();
     }
 
     /**

Modified: 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2006.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2006.java?rev=1812269&r1=1812268&r2=1812269&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2006.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS2006.java
 [UTF-8] Mon Oct 16 10:01:46 2017
@@ -65,7 +65,7 @@ public final strictfp class GIGS2006 ext
      * Creates a new test using the default authority factory.
      */
     public GIGS2006() {
-        super(GIGS2001.factory);
+        super(TestFactorySource.factory);
     }
 
     /**
@@ -75,17 +75,17 @@ public final strictfp class GIGS2006 ext
      */
     @BeforeClass
     public static void createFactory() throws FactoryException {
-        GIGS2001.createFactory();
+        TestFactorySource.createFactory();
     }
 
     /**
-     * Force releases of JDBC connections after the tests in this class.
+     * Forces release of JDBC connections after the tests in this class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterClass
     public static void close() throws FactoryException {
-        GIGS2001.close();
+        TestFactorySource.close();
     }
 
     /**


Reply via email to