sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1685555 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/internal/metadata/ sis-metadata/src/main/java/org/apache/sis/io/wkt/ sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/ sis-referencing/src/main/ja...
Date Mon, 15 Jun 2015 11:34:43 GMT
Author: desruisseaux
Date: Mon Jun 15 11:34:42 2015
New Revision: 1685555

URL: http://svn.apache.org/r1685555
Log:
WKT 2: complete the support of VerticalExtent element.

Added:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java?rev=1685555&r1=1685554&r2=1685555&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -25,6 +25,7 @@ import org.opengis.parameter.ParameterDe
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.DerivedCRS;
+import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CartesianCS;
 import org.opengis.referencing.cs.CoordinateSystem;
@@ -306,6 +307,17 @@ public class ReferencingServices extends
     ///////////////////////////////////////////////////////////////////////////////////////
 
     /**
+     * Returns a coordinate reference system for heights above the mean seal level.
+     *
+     * @return The "Mean Seal Level (MSL) height" coordinate reference system, or {@code
null}.
+     *
+     * @since 0.6
+     */
+    public VerticalCRS getMSLH() {
+        throw moduleNotFound();
+    }
+
+    /**
      * Returns the Greenwich prime meridian.
      *
      * @return The Greenwich prime meridian.

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java?rev=1685555&r1=1685554&r2=1685555&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -28,6 +28,7 @@ import java.text.ParsePosition;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import org.opengis.util.FactoryException;
+import org.opengis.util.InternationalString;
 import org.apache.sis.util.Workaround;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -95,6 +96,8 @@ abstract class AbstractParser implements
      *   <li><b>Values</b>: keywords of all elements containing an element
identified by the above-cited key.
      *       This list is used for helping the users to locate the ignored elements.</li>
      * </ul>
+     *
+     * @see #getAndClearWarnings(Object)
      */
     final Map<String, List<String>> ignoredElements;
 
@@ -227,7 +230,7 @@ abstract class AbstractParser implements
      * Reports a non-fatal warning that occurred while parsing a WKT.
      *
      * @param parent  The parent element.
-     * @param keyword The element that we can not parse.
+     * @param element The element that we can not parse.
      * @param ex      The non-fatal exception that occurred while parsing the element.
      */
     final void warning(final Element parent, final Element element, final Exception ex) {
@@ -238,11 +241,24 @@ abstract class AbstractParser implements
     }
 
     /**
+     * Reports a non-fatal warning that occurred while parsing a WKT.
+     *
+     * @param message The message. Can not be {@code null}.
+     * @param ex      The non-fatal exception that occurred while parsing the element, or
{@code null}.
+     */
+    final void warning(final InternationalString message, final Exception ex) {
+        if (warnings == null) {
+            warnings = new Warnings(errorLocale, (byte) 1, ignoredElements);
+        }
+        warnings.add(message, ex, null);
+    }
+
+    /**
      * Returns the warnings, or {@code null} if none.
      * This method clears the warnings after the call.
      *
-     * <p>The returned object is valid only until a new parsing starts. If a longer
lifetime is desired,
-     * then the callers <strong>must</strong> invoke {@link Warnings#publish()}.</p>
+     * <p>The returned object is valid only before a new parsing starts. If a longer
lifetime is desired,
+     * then the caller <strong>must</strong> invokes {@link Warnings#publish()}.</p>
      *
      * @param object The object that resulted from the parsing operation, or {@code null}.
      */

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1685555&r1=1685554&r2=1685555&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -822,6 +822,10 @@ public class Formatter implements Locali
      *   <li>“{@code VerticalExtent[102, 108, LengthUnit["m", 1]]}”       (Δz
=   6)</li>
      *   <li>“{@code VerticalExtent[100.2, 100.8, LengthUnit["m", 1]]}”   (Δz
= 0.6)</li>
      * </ul>
+     *
+     * Note that according ISO 19162, heights are positive toward up and relative to an unspecified
mean sea level.
+     * It is caller's responsibility to ensure that the given range complies with that specification
as much as
+     * possible.
      */
     private void appendVerticalExtent(final MeasurementRange<Double> range) {
         if (range != null) {

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java?rev=1685555&r1=1685554&r2=1685555&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -145,12 +145,29 @@ final class GeodeticObjectParser extends
     private final Convention convention;
 
     /**
-     * A map of properties to be given the factory constructor methods.
+     * A map of properties to be given to the factory constructor methods.
      * This map will be recycled for each object to be parsed.
      */
     private final Map<String,Object> properties = new HashMap<>(4);
 
     /**
+     * The last vertical CRS found during the parsing, or {@code null} if none.
+     * This information is needed for creating {@link DefaultVerticalExtent} instances.
+     *
+     * <p>ISO 19162 said that we should have at most one vertical CRS per WKT. Apache
SIS does
+     * not enforce this constraint, but if a WKT contains more than one vertical CRS then
the
+     * instance used for completing the {@link DefaultVerticalExtent} instances is unspecified.</p>
+     */
+    private transient VerticalCRS verticalCRS;
+
+    /**
+     * A chained list of temporary information needed for completing the construction of
{@link DefaultVerticalExtent}
+     * instances. In particular, stores the unit of measurement until the {@link VerticalCRS}
instance to associate to
+     * the extents is known.
+     */
+    private transient VerticalInfo verticalElements;
+
+    /**
      * Creates a parser using the default set of symbols and factories.
      */
     public GeodeticObjectParser() {
@@ -232,11 +249,38 @@ final class GeodeticObjectParser extends
      */
     @Override
     public Object parseObject(final String text, final ParsePosition position) throws ParseException
{
+        final Object object;
         try {
-            return super.parseObject(text, position);
+            object = super.parseObject(text, position);
+            /*
+             * After parsing the object, we may have been unable to set the VerticalCRS of
VerticalExtent instances.
+             * First, try to set a default VerticalCRS for Mean Sea Level Height in metres.
In the majority of cases
+             * that should be enough. If not (typically because the vertical extent uses
other unit than metre), try
+             * to create a new CRS using the unit declared in the WKT.
+             */
+            if (verticalElements != null) {
+                Exception ex = null;
+                try {
+                    verticalElements = verticalElements.resolve(referencing.getMSLH()); 
   // Optional operation.
+                } catch (UnsupportedOperationException e) {
+                    ex = e;
+                }
+                if (verticalElements != null) try {
+                    verticalElements = verticalElements.complete(crsFactory, csFactory);
+                } catch (FactoryException e) {
+                    if (ex == null) ex = e;
+                }
+                if (verticalElements != null) {
+                    warning(Errors.formatInternational(Errors.Keys.CanNotAssignUnitToDimension_2,
+                            WKTKeywords.VerticalExtent, verticalElements.unit), ex);
+                }
+            }
         } finally {
+            verticalCRS = null;
+            verticalElements = null;
             properties.clear();     // for letting the garbage collector do its work.
         }
+        return object;
     }
 
     /**
@@ -393,10 +437,9 @@ final class GeodeticObjectParser extends
                 final double minimum = element.pullDouble("minimum");
                 final double maximum = element.pullDouble("maximum");
                 final Unit<Length> unit = parseUnit(element, WKTKeywords.LengthUnit,
SI.METRE);
-                final VerticalCRS crs = null; // TODO: create here a vertical CRS above mean
sea level using the given units.
                 element.close(ignoredElements);
                 if (extent == null) extent = new DefaultExtent();
-                extent.getVerticalElements().add(new DefaultVerticalExtent(minimum, maximum,
crs));
+                verticalElements = new VerticalInfo(verticalElements, extent, minimum, maximum,
unit).resolve(verticalCRS);
             }
             /*
              * Example: TIMEEXTENT[2013-01-01, 2013-12-31]
@@ -877,8 +920,16 @@ final class GeodeticObjectParser extends
                 }
                 axis = createAxis(sn, abbreviation, direction, linearUnit);
             }
-            return crsFactory.createVerticalCRS(parseMetadataAndClose(element, name), datum,
-                    csFactory.createVerticalCS(singletonMap("name", name), axis));
+            verticalCRS = crsFactory.createVerticalCRS(parseMetadataAndClose(element, name),
datum,
+                           csFactory.createVerticalCS(singletonMap("name", name), axis));
+            /*
+             * Some DefaultVerticalExtent objects may be waiting for the VerticalCRS before
to complete
+             * their construction. If this is the case, try to complete them now.
+             */
+            if (verticalElements != null) {
+                verticalElements = verticalElements.resolve(verticalCRS);
+            }
+            return verticalCRS;
         } catch (FactoryException exception) {
             throw element.parseFailed(exception);
         }

Added: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java?rev=1685555&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java
(added)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -0,0 +1,191 @@
+/*
+ * 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.io.wkt;
+
+import java.util.Map;
+import java.util.Collections;
+import javax.measure.unit.Unit;
+import javax.measure.quantity.Length;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.cs.AxisDirection;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.cs.CSFactory;
+import org.opengis.referencing.cs.VerticalCS;
+import org.opengis.referencing.crs.CRSFactory;
+import org.opengis.referencing.crs.VerticalCRS;
+import org.opengis.referencing.datum.VerticalDatumType;
+import org.apache.sis.internal.metadata.AxisNames;
+import org.apache.sis.metadata.iso.extent.DefaultExtent;
+import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
+
+
+/**
+ * Stores temporary information needed for completing the construction of an {@link DefaultVerticalExtent}
instance.
+ * WKT of vertical extents looks like:
+ *
+ * {@preformat wkt
+ *     VERTICALEXTENT[-1000, 0, LENGTHUNIT[“metre”, 1]]
+ * }
+ *
+ * But {@code DefaultVerticalExtent} has no {@code unit} property. Instead, {@code DefaultVerticalExtent}
has a
+ * {@code verticalCRS} property. The WKT specification said that heights are positive toward
up and relative to
+ * an unspecified mean sea level, but we will try to use the parsed vertical CRS instance
if we find a suitable
+ * one.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+final class VerticalInfo {
+    /**
+     * The next instance to resolve. This form a chained list.
+     */
+    private VerticalInfo next;
+
+    /**
+     * The vertical extent pending completion.
+     */
+    private final DefaultVerticalExtent extent;
+
+    /**
+     * The unit specified in the {@code VERTICALEXTENT} WKT element.
+     */
+    final Unit<Length> unit;
+
+    /**
+     * If a vertical CRS could be used pending only a change of units, that CRS.
+     * Otherwise {@code null}.
+     */
+    private VerticalCRS compatibleCRS;
+
+    /**
+     * Adds to the chained list a new {@code DefaultVerticalExtent} instance pending completion.
+     *
+     * @param next    The existing {@code VerticalInfo} instance. Will become the next instance
+     *                to process after {@code this} in a chain of {@code VerticalInfo}.
+     * @param extents Where to add the vertical extent.
+     * @param unit    The unit to assign to the {@code extent}. Can not be null.
+     */
+    VerticalInfo(final VerticalInfo next, final DefaultExtent extents, final double minimum,
final double maximum, final Unit<Length> unit) {
+        this.next   = next;
+        this.unit   = unit;
+        this.extent = new DefaultVerticalExtent(minimum, maximum, null);
+        extents.getVerticalElements().add(extent);
+    }
+
+    /**
+     * If the pending {@code DefaultVerticalExtent} can use the given CRS, completes the
extent now.
+     * This method invokes {@link DefaultVerticalExtent#setVerticalCRS(VerticalCRS)} with
the given CRS if:
+     *
+     * <ul>
+     *   <li>datum type is {@link VerticalDatumType#GEOIDAL},</li>
+     *   <li>increasing height values are up, and</li>
+     *   <li>axis unit of measurement is the given linear unit.</li>
+     * </ul>
+     *
+     * This method processes also all other {@code VerticalInfo} instances in the chained
list.
+     *
+     * @return The new head of the chained list (may be {@code this}), or {@code null} if
the list
+     *         became empty as a result of this operation.
+     */
+    final VerticalInfo resolve(final VerticalCRS crs) {
+        if (crs != null && VerticalDatumType.GEOIDAL.equals(crs.getDatum().getVerticalDatumType()))
{
+            return resolve(crs, crs.getCoordinateSystem().getAxis(0));
+        }
+        return this;
+    }
+
+    /**
+     * Implementation of {@link #resolve(VerticalCRS, CoordinateSystemAxis)} to be invoked
recursively,
+     * after we checked the datum type and fetched the axis once for all.
+     */
+    private VerticalInfo resolve(final VerticalCRS crs, final CoordinateSystemAxis axis)
{
+        if (next != null) {
+            next = next.resolve(crs, axis);
+        }
+        final Unit<?> crsUnit = axis.getUnit();
+        if (AxisDirection.UP.equals(axis.getDirection()) && unit.equals(crsUnit))
{
+            extent.setVerticalCRS(crs);
+            return next;
+        } else if (unit.isCompatible(crsUnit)) {
+            compatibleCRS = crs;
+        }
+        return this;
+    }
+
+    /**
+     * Completes the extent with a new CRS using the units specified at construction time.
+     * The CRS created by this method is implementation-dependent. The only guarantees are:
+     *
+     * <ul>
+     *   <li>datum type is {@link VerticalDatumType#GEOIDAL},</li>
+     *   <li>increasing height values are up, and</li>
+     *   <li>axis unit of measurement is the given linear unit.</li>
+     * </ul>
+     *
+     * If this method can not propose a suitable CRS, then it returns {@code this}.
+     */
+    final VerticalInfo complete(final CRSFactory crsFactory, final CSFactory csFactory) throws
FactoryException {
+        if (next != null) {
+            next = next.complete(crsFactory, csFactory);
+        }
+        if (compatibleCRS == null) {
+            return this;
+        }
+        final Object name;
+        final String abbreviation;
+        CoordinateSystemAxis axis = compatibleCRS.getCoordinateSystem().getAxis(0);
+        final boolean isUP = AxisDirection.UP.equals(axis.getDirection());
+        if (isUP) {
+            name = axis.getName();
+            abbreviation = axis.getAbbreviation();
+        } else {
+            name = AxisNames.GRAVITY_RELATED_HEIGHT;
+            abbreviation = "H";
+        }
+        axis = csFactory.createCoordinateSystemAxis(properties(name), abbreviation, AxisDirection.UP,
unit);
+        /*
+         * Naming policy (based on usage of names in the EPSG database):
+         *
+         *   - We can reuse the old axis name if (and only if) the direction is the same,
because the axis
+         *     names are constrained by the ISO 19111 specification in a way that do not
include the units
+         *     of measurement. Examples: "Gravity-related height", "Depth".
+         *
+         *   - We can not reuse the previous Coordinate System name, because it often contains
the axis
+         *     abbreviation and unit. Examples: "Vertical CS. Axis: height (H). Orientation:
up. UoM: m.".
+         *     Since we are lazy, we will reuse the axis name instead, which is more neutral.
+         *
+         *   - We generally can reuse the CRS name because those names tend to refer to the
datum (which is
+         *     unchanged) rather than the coordinate system. Examples: "Low Water depth",
"NGF Lallemand height",
+         *     "JGD2011 (vertical) height". However we make an exception if the direction
is down, because in such
+         *     cases the previous name may contain terms like "depth", which are not appropriate
for our new CRS.
+         */
+        final VerticalCS cs = csFactory.createVerticalCS (properties(axis.getName()), axis);
+        extent.setVerticalCRS(crsFactory.createVerticalCRS(
+                properties((isUP ? compatibleCRS : axis).getName()), compatibleCRS.getDatum(),
cs));
+        return next;
+    }
+
+    /**
+     * Convenience method for creating the map of properties to give to the factory method.
+     */
+    private static Map<String,?> properties(final Object name) {
+        return Collections.singletonMap(IdentifiedObject.NAME_KEY, name);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/VerticalInfo.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java?rev=1685555&r1=1685554&r2=1685555&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -28,6 +28,8 @@ import org.opengis.metadata.extent.Geogr
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.crs.VerticalCRS;
+import org.opengis.referencing.datum.VerticalDatum;
+import org.opengis.referencing.datum.VerticalDatumType;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.Range;
@@ -54,7 +56,7 @@ import static org.apache.sis.internal.me
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.4
+ * @version 0.6
  * @module
  *
  * @see org.apache.sis.geometry.Envelopes
@@ -132,32 +134,60 @@ public final class Extents extends Stati
     }
 
     /**
-     * Returns the union of all vertical ranges found in the given extent, or {@code null}
if none.
-     * Depths have negative height values: if the {@linkplain CoordinateSystemAxis#getDirection()
axis direction}
+     * Returns the union of chosen vertical ranges found in the given extent, or {@code null}
if none.
+     * This method gives preference to heights above the Mean Sea Level when possible.
+     * Depths have negative height values: if the
+     * {@linkplain org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis#getDirection()
axis direction}
      * is toward down, then this method reverses the sign of minimum and maximum values.
      *
      * <div class="section">Multi-occurrences</div>
      * If the given {@code Extent} object contains more than one vertical extent, then this
method
-     * performs the following choices:
+     * performs a choice based on the vertical datum and the unit of measurement:
      *
      * <ul>
-     *   <li>If no range specify a unit of measurement, return the first range and
ignore all others.</li>
-     *   <li>Otherwise take the first range having a unit of measurement. Then:<ul>
-     *     <li>All other ranges having an incompatible unit of measurement will be
ignored.</li>
-     *     <li>All other ranges having a compatible unit of measurement will be converted
to
-     *         the unit of the first retained range, and their union will be computed.</li>
-     *   </ul></li>
+     *   <li><p><b>Choice based on vertical datum</b><br>
+     *   Only the extents associated (indirectly, through their CRS) to the same non-null
{@link VerticalDatumType}
+     *   will be taken in account. If all datum types are null, then this method conservatively
uses only the first
+     *   vertical extent. Otherwise the datum type used for filtering the vertical extents
is:</p>
+     *
+     *   <ul>
+     *     <li>{@link VerticalDatumType#GEOIDAL} or {@link VerticalDatumType#DEPTH
DEPTH} if at least one extent
+     *         uses those datum types. For this method, {@code DEPTH} is considered as equivalent
to {@code GEOIDAL}
+     *         except for the axis direction.</li>
+     *     <li>Otherwise, the first non-null datum type found in iteration order.</li>
+     *   </ul>
+     *
+     *   <div class="note"><b>Rational:</b> like {@linkplain #getGeographicBoundingBox(Extent)
geographic bounding box},
+     *   the vertical range is an approximative information; the range returned by this method
does not carry any
+     *   information about the vertical CRS and this method does not attempt to perform coordinate
transformation.
+     *   But this method is more useful if the returned ranges are close so a frequently
used surface, like the
+     *   Mean Sea Level. The same simplification is applied in the
+     *   <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#31">{@code
VerticalExtent} element of
+     *   Well Known Text (WKT) format</a>, which specifies that <cite>“Vertical
extent is an approximate description
+     *   of location; heights are relative to an unspecified mean sea level.”</cite></div></li>
+     *
+     *   <li><p><b>Choice based on units of measurement</b><br>
+     *   If, after the choice based on the vertical datum described above, there is still
more than one vertical
+     *   extent to consider, then the next criterion checks for the units of measurement.</p>
+     *   <ul>
+     *     <li>If no range specify a unit of measurement, return the first range and
ignore all others.</li>
+     *     <li>Otherwise take the first range having a unit of measurement. Then:<ul>
+     *       <li>All other ranges having an incompatible unit of measurement will be
ignored.</li>
+     *       <li>All other ranges having a compatible unit of measurement will be converted
to
+     *           the unit of the first retained range, and their union will be computed.</li>
+     *     </ul></li>
+     *   </ul>
+     *
+     *   <div class="note"><b>Example:</b>
+     *   Heights or depths are often measured using some pressure units, for example hectopascals
(hPa).
+     *   An {@code Extent} could contain two vertical elements: one with the height measurements
in hPa,
+     *   and the other element with heights transformed to metres using an empirical formula.
+     *   In such case this method will select the first vertical element on the assumption
that it is
+     *   the "main" one that the metadata producer intended to show. Next, this method will
search for
+     *   other vertical elements using pressure unit. In our example there is none, but if
such elements
+     *   were found, this method would compute their union.</div></li>
      * </ul>
      *
-     * <div class="note"><b>Example:</b>
-     * Heights or depths are often measured using some pressure units, for example hectopascals
(hPa).
-     * An {@code Extent} could contain two vertical elements: one with the height measurements
in hPa,
-     * and the other element with heights transformed to metres using an empirical formula.
-     * In such case this method will select the first vertical element on the assumption
that it is
-     * the "main" one that the metadata producer intended to show. Next, this method will
search for
-     * other vertical elements using pressure unit. In our example there is none, but if
such elements
-     * were found, this method would compute their union.</div>
-     *
      * @param  extent The extent to convert to a vertical measurement range, or {@code null}.
      * @return A vertical measurement range created from the given extent, or {@code null}
if none.
      *
@@ -165,13 +195,22 @@ public final class Extents extends Stati
      */
     public static MeasurementRange<Double> getVerticalRange(final Extent extent) {
         MeasurementRange<Double> range = null;
+        VerticalDatumType selectedType = null;
         if (extent != null) {
             for (final VerticalExtent element : extent.getVerticalElements()) {
                 double min = element.getMinimumValue();
                 double max = element.getMaximumValue();
                 final VerticalCRS crs = element.getVerticalCRS();
+                VerticalDatumType type = null;
                 Unit<?> unit = null;
                 if (crs != null) {
+                    final VerticalDatum datum = crs.getDatum();
+                    if (datum != null) {
+                        type = datum.getVerticalDatumType();
+                        if (VerticalDatumType.DEPTH.equals(type)) {
+                            type = VerticalDatumType.GEOIDAL;
+                        }
+                    }
                     final CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(0);
                     unit = axis.getUnit();
                     if (AxisDirection.DOWN.equals(axis.getDirection())) {
@@ -182,27 +221,39 @@ public final class Extents extends Stati
                 }
                 if (range != null) {
                     /*
-                     * If the new range does not specify any unit, then we do not know how
to convert
-                     * the values before to perform the union operation. Conservatively do
nothing.
+                     * If the new range does not specify any datum type or unit, then we
do not know how to
+                     * convert the values before to perform the union operation. Conservatively
do nothing.
                      */
-                    if (unit == null) {
+                    if (type == null || unit == null) {
                         continue;
                     }
                     /*
-                     * If previous range did not specify any unit, then unconditionally replace
it by
-                     * the new range since it provides more information. If both ranges specify
units,
-                     * then we will compute the union if we can, or ignore the new range
otherwise.
+                     * If the new range is not measured relative to the same kind of surface
than the previous range,
+                     * then we do not know how to combine those ranges. Do nothing, unless
the new range is a Mean Sea
+                     * Level Height in which case we forget all previous ranges and use the
new one instead.
                      */
-                    final Unit<?> previous = range.unit();
-                    if (previous != null) {
-                        if (previous.isCompatible(unit)) {
-                            range = (MeasurementRange<Double>) range.union(
-                                    MeasurementRange.create(min, true, max, true, unit));
+                    if (!type.equals(selectedType)) {
+                        if (!type.equals(VerticalDatumType.GEOIDAL)) {
+                            continue;
+                        }
+                    } else if (selectedType != null) {
+                        /*
+                         * If previous range did not specify any unit, then unconditionally
replace it by
+                         * the new range since it provides more information. If both ranges
specify units,
+                         * then we will compute the union if we can, or ignore the new range
otherwise.
+                         */
+                        final Unit<?> previous = range.unit();
+                        if (previous != null) {
+                            if (previous.isCompatible(unit)) {
+                                range = (MeasurementRange<Double>) range.union(
+                                        MeasurementRange.create(min, true, max, true, unit));
+                            }
+                            continue;
                         }
-                        continue;
                     }
                 }
                 range = MeasurementRange.create(min, true, max, true, unit);
+                selectedType = type;
             }
         }
         return range;

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java?rev=1685555&r1=1685554&r2=1685555&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
[UTF-8] Mon Jun 15 11:34:42 2015
@@ -446,6 +446,18 @@ public final class ServicesForMetadata e
     ///////////////////////////////////////////////////////////////////////////////////////
 
     /**
+     * Returns a coordinate reference system for heights above the mean seal level.
+     *
+     * @return The "Mean Seal Level (MSL) height" coordinate reference system.
+     *
+     * @since 0.6
+     */
+    @Override
+    public VerticalCRS getMSLH() {
+        return CommonCRS.Vertical.MEAN_SEA_LEVEL.crs();
+    }
+
+    /**
      * Returns the Greenwich prime meridian.
      *
      * @return The Greenwich prime meridian.



Mime
View raw message