sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1785578 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/ sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/ sis-referencing-by-identifiers/src/test/java/org/apache...
Date Sun, 05 Mar 2017 23:24:42 GMT
Author: desruisseaux
Date: Sun Mar  5 23:24:42 2017
New Revision: 1785578

URL: http://svn.apache.org/viewvc?rev=1785578&view=rev
Log:
First version of LocationFormat.

Added:
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java   (with props)
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java   (with props)
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
    sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/test/suite/ReferencingByIdentifiersTestSuite.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -196,6 +196,7 @@ public class DefaultGeographicBoundingBo
      * @param  object  the metadata to copy values from, or {@code null} if none.
      *
      * @see #castOrCopy(GeographicBoundingBox)
+     * @see #setBounds(GeographicBoundingBox)
      */
     public DefaultGeographicBoundingBox(final GeographicBoundingBox object) {
         super(object);
@@ -489,8 +490,8 @@ public class DefaultGeographicBoundingBo
     }
 
     /**
-     * Constructs a geographic bounding box from the specified envelope. If the envelope contains
-     * a CRS, then the bounding box may be projected to a geographic CRS. Otherwise, the envelope
+     * Sets the bounding box to the specified envelope. If the envelope contains* a CRS,
+     * then the bounding box may be projected to a geographic CRS. Otherwise, the envelope
      * is assumed already in appropriate CRS.
      *
      * <p>When coordinate transformation is required, the target geographic CRS is not necessarily

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -30,6 +30,7 @@ import org.opengis.util.InternationalStr
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.GeneralDirectPosition;
+import org.apache.sis.util.Debug;
 
 
 /**
@@ -78,7 +79,7 @@ public abstract class AbstractLocation i
 
     /**
      * Creates a new location for the given geographic identifier.
-     * This constructor accepts a {@code null} argument, but this is not recommended.
+     * This constructor accepts {@code null} arguments, but this is not recommended.
      *
      * @param type        the description of the nature of this geographic identifier.
      * @param identifier  the geographic identifier to be returned by {@link #getGeographicIdentifier()}.
@@ -237,4 +238,18 @@ public abstract class AbstractLocation i
     public Collection<? extends Location> getChildren() {
         return Collections.emptyList();
     }
+
+    /**
+     * Returns a string representation of this location.
+     * This representation is mostly for debugging purpose and may change in any future Apache SIS version.
+     *
+     * @return a string representation of this location for debugging purpose.
+     */
+    @Debug
+    @Override
+    public String toString() {
+        synchronized (LocationFormat.INSTANCE) {
+            return LocationFormat.INSTANCE.format(this);
+        }
+    }
 }

Added: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java?rev=1785578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java (added)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.gazetteer;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.Collection;
+import java.io.IOException;
+import java.text.Format;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import javax.measure.Unit;
+import org.opengis.util.FactoryException;
+import org.opengis.util.InternationalString;
+import org.opengis.referencing.gazetteer.Location;
+import org.opengis.referencing.gazetteer.LocationType;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.referencing.crs.GeographicCRS;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.citation.Party;
+import org.opengis.geometry.Envelope;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.geometry.coordinate.Position;
+import org.apache.sis.io.CompoundFormat;
+import org.apache.sis.io.TableAppender;
+import org.apache.sis.measure.Range;
+import org.apache.sis.measure.Angle;
+import org.apache.sis.measure.Latitude;
+import org.apache.sis.measure.Longitude;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.metadata.iso.extent.Extents;
+import org.apache.sis.metadata.iso.extent.DefaultExtent;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.crs.AbstractCRS;
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Vocabulary;
+
+
+/**
+ * Formats {@link Location} instances.
+ *
+ * <p>This class is not thread-safe.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+@SuppressWarnings("CloneableClassWithoutClone")                 // Because no additional field defined in this class.
+final class LocationFormat extends CompoundFormat<Location> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -3440801316594167279L;
+
+    /**
+     * Default instance for {@link AbstractLocation#toString()}.
+     * Use of this instance must be synchronized on {@code INSTANCE}.
+     */
+    static final LocationFormat INSTANCE = new LocationFormat(Locale.getDefault(), TimeZone.getDefault());
+
+    /**
+     * {@link Vocabulary.Keys} constants for the east, west, south and north bounds, in that order.
+     */
+    private static final short[] BOUND_KEY = {
+        Vocabulary.Keys.EastBound,
+        Vocabulary.Keys.WestBound,
+        Vocabulary.Keys.SouthBound,
+        Vocabulary.Keys.NorthBound,
+        Vocabulary.Keys.RepresentativePosition,
+        0
+    };
+
+    /**
+     * Creates a new format for the given locale. The given locale can be {@code null}
+     * or {@link Locale#ROOT} if this format shall format "unlocalized" strings.
+     *
+     * @param  locale    the locale for the new {@code Format}, or {@code null} for {@code Locale.ROOT}.
+     * @param  timezone  the timezone, or {@code null} for UTC.
+     */
+    public LocationFormat(final Locale locale, final TimeZone timezone) {
+        super(locale, timezone);
+    }
+
+    /**
+     * Returns the type of values formatted by this {@code Format} instance.
+     *
+     * @return the type of values formatted by this {@code Format} instance.
+     */
+    @Override
+    public Class<Location> getValueType() {
+        return Location.class;
+    }
+
+    /**
+     * Returns a CRS equivalent to the given one but with (longitude, latitude) or (easting, northing) axis order.
+     * This method does not change the units of measurement. If the given CRS is already normalized, then it is
+     * returned unchanged.
+     */
+    private static CoordinateReferenceSystem normalize(final CoordinateReferenceSystem crs) {
+        if (crs != null) {
+            AbstractCRS normalized = AbstractCRS.castOrCopy(crs);
+            if (normalized != (normalized = normalized.forConvention(AxesConvention.CONVENTIONALLY_ORIENTED))) {
+                return normalized;
+            }
+        }
+        return crs;
+    }
+
+    /**
+     * Transforms the given position from the given source to the given target CRS.
+     * If the source and target CRS are the same, then this method returns the position unchanged.
+     */
+    private static DirectPosition transform(DirectPosition position,
+                                            CoordinateReferenceSystem sourceCRS,
+                                            CoordinateReferenceSystem targetCRS)
+            throws FactoryException, TransformException
+    {
+        if (sourceCRS != targetCRS) {
+            position = CRS.findOperation(sourceCRS, targetCRS, null).getMathTransform().transform(position, null);
+        }
+        return position;
+    }
+
+    /**
+     * Returns the direct position for the given position, or {@code null} if none.
+     */
+    private static DirectPosition position(final Position p) {
+        return (p != null) ? p.getDirectPosition() : null;
+    }
+
+    /**
+     * Returns a localized version of the given international string, or {@code null} if none.
+     */
+    private static String toString(final InternationalString i18n, final Locale locale) {
+        return (i18n != null) ? i18n.toString(locale) : null;
+    }
+
+    /**
+     * Returns a localized version of the given date, or {@code null} if none.
+     */
+    private String toString(final Date date) {
+        return (date != null) ? getFormat(Date.class).format(date) : null;
+    }
+
+    /**
+     * Formats the given location.
+     */
+    @Override
+    @SuppressWarnings("fallthrough")
+    public void format(final Location location, final Appendable toAppendTo) throws IOException {
+        final Locale locale = getLocale(Locale.Category.DISPLAY);
+        final Vocabulary vocabulary = Vocabulary.getResources(locale);
+        final TableAppender table = new TableAppender(toAppendTo, " ");
+        table.setMultiLinesCells(true);
+        /*
+         * Location type.
+         */
+        final LocationType type = location.getLocationType();
+        if (type != null) {
+            append(table, vocabulary, Vocabulary.Keys.LocationType, toString(type.getName(), locale));
+        }
+        /*
+         * Geographic identifier and alternative identifiers, if any.
+         */
+        append(table, vocabulary, Vocabulary.Keys.GeographicIdentifier, toString(location.getGeographicIdentifier(), locale));
+        final Collection<? extends InternationalString> alt = location.getAlternativeGeographicIdentifiers();
+        if (alt != null && !alt.isEmpty()) {
+            boolean isFirst = true;
+            vocabulary.appendLabel(Vocabulary.Keys.AlternativeIdentifiers, table);
+            table.nextColumn();
+            for (final InternationalString id : alt) {
+                if (!isFirst) {
+                    isFirst = false;
+                    table.append(System.lineSeparator());
+                }
+                table.append(id);
+            }
+            table.nextLine();
+        }
+        /*
+         * Extents (temporal and geographic). If an envelope exists and the CRS is not geographic,
+         * then the envelope bounds will be appended on the same lines than the geographic bounds.
+         * But before writing the bounding box and/or the envelope, check if they are redundant.
+         * We may also need to change axis order (but not unit) of the envelope in order to match
+         * the axis order of the geographic bounding box.
+         */
+        final Extent extent = new DefaultExtent(null, location.getGeographicExtent(), null, location.getTemporalExtent());
+        final Range<Date> time = Extents.getTimeRange(extent);
+        if (time != null) {
+            append(table, vocabulary, Vocabulary.Keys.StartDate, toString(time.getMinValue()));
+            append(table, vocabulary, Vocabulary.Keys.EndDate,   toString(time.getMaxValue()));
+        }
+        GeographicBoundingBox     bbox     = Extents.getGeographicBoundingBox(extent);
+        Envelope                  envelope = location.getEnvelope();
+        DirectPosition            position = position(location.getPosition());
+        DirectPosition            geopos   = null;                      // Position in geographic CRS.
+        CoordinateReferenceSystem crs      = null;                      // Envelope Coordinate Reference System.
+        CoordinateReferenceSystem normCRS  = null;                      // CRS in conventional (x,y) axis order.
+        Exception                 warning  = null;                      // If failed to transform envelope.
+        try {
+            if (envelope != null) {
+                normCRS = normalize(crs = envelope.getCoordinateReferenceSystem());
+                if (normCRS != crs) {
+                    envelope = Envelopes.transform(envelope, normCRS);      // Should only change order and sign.
+                }
+            }
+            if (position != null) {
+                /*
+                 * If only one of the envelope or the position objects specify a CRS, assume that the other object
+                 * use the same CRS. If both the envelope and the position objects specify a CRS, the envelope CRS
+                 * will have precedence and the "representative position" will be projected to that CRS.
+                 */
+                final CoordinateReferenceSystem posCRS = position.getCoordinateReferenceSystem();
+                if (normCRS == null) {
+                    normCRS = normalize(crs = posCRS);
+                    if (normCRS != crs) {
+                        envelope = Envelopes.transform(envelope, normCRS);  // Should only change order and sign.
+                    }
+                }
+                GeographicCRS geogCRS = ReferencingUtilities.toNormalizedGeographicCRS(posCRS);
+                if (geogCRS != null) {
+                    geopos = transform(position, posCRS, geogCRS);
+                }
+                position = transform(position, posCRS, normCRS);
+            }
+        } catch (FactoryException | TransformException e) {
+            envelope = null;
+            position = null;
+            warning  = e;
+        }
+        /*
+         * At this point we got the final geographic bounding box and/or envelope to write.
+         * Since we will write the projected and geographic coordinates side-by-side in the same cells,
+         * we need to format them in advance so we can compute their width for internal right-alignment.
+         * We do the alignment ourselves instead than using TableAppender.setCellAlignment(ALIGN_RIGHT)
+         * because we do not want (projected geographic) tuple to appear far on the right side if other
+         * cells have long texts.
+         */
+        if (bbox != null || envelope != null) {
+            final CoordinateSystem cs = (crs != null) ? crs.getCoordinateSystem() : null;
+            String[] geographic = null;
+            String[] projected  = null;
+            String[] unitSymbol = null;
+            Format   geogFormat = null;
+            Format   projFormat = null;
+            Format   unitFormat = null;
+            int      maxGeogLength = 0;
+            int      maxProjLength = 0;
+            int      maxUnitLength = 0;
+            boolean  showProj  = false;
+            if (bbox != null) {
+                geographic = new String[BOUND_KEY.length];
+                geogFormat = getFormat(Angle.class);
+            }
+            if (envelope != null) {
+                projected  = new String[BOUND_KEY.length];
+                unitSymbol = new String[BOUND_KEY.length];
+                projFormat = getFormat(Number.class);
+                unitFormat = getFormat(Unit.class);
+            }
+            for (int i=0; i<BOUND_KEY.length; i++) {
+                double g = Double.NaN;
+                double p = Double.NaN;
+                int dimension = 0;
+                switch (i) {
+                    case 0: if (bbox     != null) g = bbox.getWestBoundLongitude();
+                            if (envelope != null) p = envelope.getMinimum(0);
+                            break;
+                    case 1: if (bbox     != null) g = bbox.getEastBoundLongitude();
+                            if (envelope != null) p = envelope.getMaximum(0);
+                            break;
+                    case 2: if (bbox     != null) g = bbox.getSouthBoundLatitude();
+                            if (envelope != null) p = envelope.getMinimum(1);
+                            dimension = 1;
+                            break;
+                    case 3: if (bbox     != null) g = bbox.getNorthBoundLatitude();
+                            if (envelope != null) p = envelope.getMaximum(1);
+                            dimension = 1;
+                            break;
+                    case 5: dimension = 1;                            // Fall through
+                    case 4: if (geopos   != null) g = geopos  .getOrdinate(dimension);
+                            if (position != null) p = position.getOrdinate(dimension);
+                            break;
+                }
+                if (!Double.isNaN(p)) {
+                    showProj |= (g != p);
+                    if (cs != null) {
+                        final Unit<?> unit = cs.getAxis(dimension).getUnit();
+                        if (unit != null) {
+                            @SuppressWarnings("null")
+                            final int length = (unitSymbol[i] = unitFormat.format(unit)).length();
+                            if (length > maxUnitLength) {
+                                maxUnitLength = length;
+                            }
+                        }
+                    }
+                    @SuppressWarnings("null")
+                    final int length = (projected[i] = projFormat.format(p)).length();
+                    if (length > maxProjLength) {
+                        maxProjLength = length;
+                    }
+                }
+                if (!Double.isNaN(g)) {
+                    final Angle angle = (dimension == 0) ? new Longitude(g) : new Latitude(g);
+                    @SuppressWarnings("null")
+                    final int length = (geographic[i] = geogFormat.format(angle)).length();
+                    if (length > maxGeogLength) {
+                        maxGeogLength = length;
+                    }
+                }
+            }
+            if (!showProj) {
+                projected  = null;          // All projected coordinates are identical to geographic ones.
+                unitSymbol = null;
+            } else if (maxProjLength != 0) {
+                maxGeogLength += 4;         // Arbitrary space between projected and geographic coordinates.
+            }
+            /*
+             * At this point all coordinates have been formatted in advance.
+             */
+            for (int i=0; i<BOUND_KEY.length; i++) {
+                final String p = (projected  != null) ? projected [i] : null;
+                final String g = (geographic != null) ? geographic[i] : null;
+                if (p != null || g != null) {
+                    final short key = BOUND_KEY[i];
+                    if (key != 0) {
+                        vocabulary.appendLabel(key, table);
+                    }
+                    table.nextColumn();
+                    if (p != null) {
+                        table.append(CharSequences.spaces(maxProjLength - p.length())).append(p);
+                        String unit = unitSymbol[i];
+                        if (unit == null) unit = "";
+                        table.append(CharSequences.spaces(maxUnitLength - unit.length())).append(unit);
+                    }
+                    if (g != null) {
+                        table.append(CharSequences.spaces(maxGeogLength - g.length())).append(g);
+                    }
+                    table.nextLine();
+                }
+            }
+        }
+        if (crs != null) {
+            append(table, vocabulary, Vocabulary.Keys.CoordinateRefSys, IdentifiedObjects.getName(crs, null));
+        }
+        /*
+         * Organization responsible for defining the characteristics of the location instance.
+         */
+        final Party administrator = location.getAdministrator();
+        if (administrator != null) {
+            append(table, vocabulary, Vocabulary.Keys.Administrator, toString(administrator.getName(), locale));
+        }
+        table.flush();
+        if (warning != null) {
+            vocabulary.appendLabel(Vocabulary.Keys.Warnings, toAppendTo);
+            toAppendTo.append(warning.toString()).append(System.lineSeparator());
+        }
+    }
+
+    /**
+     * Appends the given value in the given table if it is not null.
+     *
+     * @param table        the table where to append the value.
+     * @param vocabulary   localized resources for the labels.
+     * @param key          key of the label to append.
+     * @param value        value to append, or {@code null} if none.
+     */
+    private static void append(final TableAppender table, final Vocabulary vocabulary, final short key,
+            final String value) throws IOException
+    {
+        if (value != null) {
+            vocabulary.appendLabel(key, table);
+            table.nextColumn();
+            table.append(value).nextLine();
+        }
+    }
+
+    /**
+     * Unsupported operation.
+     */
+    @Override
+    public Location parse(CharSequence text, ParsePosition pos) throws ParseException {
+        throw new ParseException(Errors.format(Errors.Keys.UnsupportedOperation_1, "parse"), pos.getIndex());
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/LocationFormat.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -22,9 +22,7 @@ import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.ConcurrentModificationException;
 import org.opengis.util.FactoryException;
-import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
-import org.opengis.geometry.coordinate.Position;
 import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.MathTransform;
@@ -53,8 +51,8 @@ import org.apache.sis.util.logging.Loggi
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.geometry.DirectPosition2D;
-import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.internal.system.Modules;
+import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Latitude;
 
 // Branch-dependent imports
@@ -345,10 +343,10 @@ public class MilitaryGridReferenceSystem
          * Creates a new coder initialized to the default precision.
          */
         protected Coder() {
-            digits    = METRE_PRECISION_DIGITS;     // 1 metre precision.
-            separator = trimmedSeparator = "";
-            buffer    = new StringBuilder(18);      // Length of "4 Q FJ 12345 67890" sample value.
-            encoders  = new IdentityHashMap<>();
+            digits     = METRE_PRECISION_DIGITS;     // 1 metre precision.
+            separator  = trimmedSeparator = "";
+            buffer     = new StringBuilder(18);      // Length of "4 Q FJ 12345 67890" sample value.
+            encoders   = new IdentityHashMap<>();
         }
 
         /**
@@ -814,7 +812,7 @@ public class MilitaryGridReferenceSystem
      * @version 0.8
      * @module
      */
-    static final class Decoder extends AbstractLocation {
+    static final class Decoder extends SimpleLocation.Projected {
         /**
          * Number of bits reserved for storing the minimal northing value of latitude bands in the
          * {@link #ROW_RESOLVER} table.
@@ -887,19 +885,12 @@ public class MilitaryGridReferenceSystem
         };
 
         /**
-         * The position decoded from the MGRS reference given to the constructor.
-         * This is the center of a cell of size {@link #sx} × {@link #sy}.
-         * The CRS of that position will be a UTM or UPS projected CRS.
+         * The Coordinate Reference System of the decoded MGRS reference.
+         * This is an Universal Transverse Mercator (UTM) or Universal Polar Stereographic (UPS) projection.
          *
-         * @see #getPosition()
+         * @see #getCoordinateReferenceSystem()
          */
-        private final DirectPosition2D position;
-
-        /**
-         * The cell size along <var>x</var> and <var>y</var> axis. This is the scale factors used for
-         * multiplying the MGRS numerical values in order to get easting and northing values in metres.
-         */
-        private final double sx, sy;
+        private final ProjectedCRS crs;
 
         /**
          * Decodes the given MGRS reference.
@@ -909,7 +900,6 @@ public class MilitaryGridReferenceSystem
         Decoder(final Coder owner, final CharSequence reference) throws TransformException {
             super(owner.getReferenceSystem().rootType(), reference);
             final int zone;                     // UTM zone, or 0 if UPS.
-            final ProjectedCRS crs;             // UTM or UPS projection for the zone.
             boolean hasSquareIdentification;    // Whether a square identification is present (UTM only).
             final double φs;                    // Southernmost bound of latitude band (UTM only).
             final double λ0;                    // Central meridian of UTM zone (ignoring Norway and Svalbard).
@@ -979,8 +969,9 @@ parse:                  switch (part) {
                     }
                     throw new GazetteerException(Resources.format(key, token));
                 }
-                crs = owner.projection(φs = (south ? Latitude.MIN_VALUE : Latitude.MAX_VALUE), 0);
-                position = new DirectPosition2D(col * GRID_SQUARE_SIZE, row * GRID_SQUARE_SIZE);
+                crs  = owner.projection(φs = (south ? Latitude.MIN_VALUE : Latitude.MAX_VALUE), 0);
+                minX = col * GRID_SQUARE_SIZE;
+                minY = row * GRID_SQUARE_SIZE;
                 hasSquareIdentification = false;
                 λ0 = 0;
             } else {
@@ -1108,10 +1099,9 @@ parse:                  switch (part) {
                     }
                 }
                 row += (info & NORTHING_BITS_MASK) * GRID_ROW_COUNT;        // Add the pre-computed northing value.
-                position = new DirectPosition2D(col * GRID_SQUARE_SIZE,
-                                                row * GRID_SQUARE_SIZE);
+                minX = col * GRID_SQUARE_SIZE;
+                minY = row * GRID_SQUARE_SIZE;
             }
-            position.setCoordinateReferenceSystem(crs);
             /*
              * If we have not yet reached the end of string, parse the numerical location.
              * That location is normally encoded as a single number with an even number of digits.
@@ -1119,6 +1109,7 @@ parse:                  switch (part) {
              * 100 kilometer square. However some variants of MGRS use a separator, in which case we get
              * two distinct numbers. In both cases, the resolution is determined by the amount of digits.
              */
+            final double sx, sy;    // Scale factors for converting MGRS values in to easting and northing in metres.
             if (i < end) {
                 i = nextComponent(owner, reference, base, i, end);
                 int s = endOfDigits(reference, i, end);
@@ -1145,8 +1136,8 @@ parse:                  switch (part) {
                                 reference.subSequence(base, s), CharSequences.trimWhitespaces(reference, s, end)));
                     }
                 }
-                position.x += x;
-                position.y += y;
+                minX += x;
+                minY += y;
             } else if (hasSquareIdentification) {
                 sx = sy = GRID_SQUARE_SIZE;
             } else {
@@ -1158,45 +1149,87 @@ parse:                  switch (part) {
                 sx = (ZONER.easting - GRID_SQUARE_SIZE) * 2;
                 sy =  ZONER.northing;
             }
-            position.x += sx/2;
-            position.y += sy/2;
+            maxX = minX + sx;
+            maxY = minY + sy;
             /*
-             * At this point we finished computing the position. Now perform error detection, by verifying
-             * if the given 100 kilometres square identification is consistent with grid zone designation.
-             * We verify both φ and λ, but the verification of φ is actually redundant with the check of
-             * 100 km square validity that we did previously with the help of ROW_RESOLVER bitmask.
-             * We check φ anyway in case of bug, but we have to allow a tolerance threshold on the south
-             * bound because the 100 km square may overlap two latitude bands. We do not need equivalent
-             * tolerance threshold for the upper bound because the coordinate that we are testing is the
-             * lower-left corner of the cell area.
+             * At this point the non-clipped projected envelope has been computed. Now compute the geographic envelope.
+             * We need this information for clipping the projected envelope to the domain of validity of UTM zone.
              */
-            if (hasSquareIdentification && isValid) {
+            if (!hasSquareIdentification) {
+                if (zone != 0) {
+                    if (φs < 0) {
+                        southBoundLatitude = TransverseMercator.Zoner.SOUTH_BOUNDS;
+                        northBoundLatitude = 0;
+                    } else {
+                        southBoundLatitude = 0;
+                        northBoundLatitude = TransverseMercator.Zoner.NORTH_BOUNDS;
+                    }
+                    westBoundLongitude = λ0 - ZONER.width / 2;
+                    eastBoundLongitude = λ0 + ZONER.width / 2;
+                } else {
+                    if (φs < 0) {
+                        southBoundLatitude = Latitude.MIN_VALUE;
+                        northBoundLatitude = TransverseMercator.Zoner.SOUTH_BOUNDS;
+                    } else {
+                        southBoundLatitude = TransverseMercator.Zoner.NORTH_BOUNDS;
+                        northBoundLatitude = Latitude.MAX_VALUE;
+                    }
+                    westBoundLongitude = Longitude.MIN_VALUE;
+                    eastBoundLongitude = Longitude.MAX_VALUE;
+                }
+            } else {
                 final MathTransform inverse = crs.getConversionFromBase().getMathTransform().inverse();
-                DirectPosition geographic = owner.geographic;
-                geographic = inverse.transform(position, geographic);
-                final double λ = geographic.getOrdinate(1);
-                final double φ = geographic.getOrdinate(0);
-                owner.geographic = geographic;                                          // For future reuse.
-                isValid = (φ >= φs - LATITUDE_BAND_HEIGHT/2) && (φ < upperBounds(φs));  // See above comment.
+                computeGeographicBoundingBox(inverse);
+                final boolean changed;
+                if (zone != 0) {
+                    changed = clipGeographicBoundingBox(λ0 - ZONER.width/2, φs,
+                                                        λ0 + ZONER.width/2, φs + LATITUDE_BAND_HEIGHT);
+                } else if (φs < 0) {
+                    changed = clipGeographicBoundingBox(Longitude.MIN_VALUE, Latitude.MIN_VALUE,
+                                                        Longitude.MAX_VALUE, TransverseMercator.Zoner.SOUTH_BOUNDS);
+                } else {
+                    changed = clipGeographicBoundingBox(Longitude.MIN_VALUE, TransverseMercator.Zoner.NORTH_BOUNDS,
+                                                        Longitude.MAX_VALUE, Latitude.MAX_VALUE);
+                }
+                if (changed) {
+//                  clipProjectedEnvelope(inverse.inverse(), sx / 100, sy / 100);       // TODO
+                }
+                /*
+                 * At this point we finished computing the position. Now perform error detection, by verifying
+                 * if the given 100 kilometres square identification is consistent with grid zone designation.
+                 * We verify both φ and λ, but the verification of φ is actually redundant with the check of
+                 * 100 km square validity that we did previously with the help of ROW_RESOLVER bitmask.
+                 * We check φ anyway in case of bug, but we have to allow a tolerance threshold on the south
+                 * bound because the 100 km square may overlap two latitude bands. We do not need equivalent
+                 * tolerance threshold for the upper bound because the coordinate that we are testing is the
+                 * lower-left corner of the cell area.
+                 */
                 if (isValid) {
-                    /*
-                     * Verification of UTM zone. We allow a tolerance for latitudes close to a pole because
-                     * not all users may apply the UTM special rules for Norway and Svalbard. Anyway, using
-                     * the neighbor zone at those high latitudes is less significant. For other latitudes,
-                     * we allow a tolerance if the point is close to a line of zone change.
-                     */
-                    int zoneError = ZONER.zone(φ, λ) - zone;
-                    if (zoneError != 0) {
-                        final int zc = ZONER.zoneCount();
-                        if (zoneError > zc/2) zoneError -= zc;
-                        if (ZONER.isSpecialCase(zone, φ)) {
-                            isValid = Math.abs(zoneError) == 1;         // Tolerance in zone numbers for high latitudes.
-                        } else {
-                            final double rλ = Math.IEEEremainder(λ - ZONER.origin, ZONER.width);    // Distance to closest zone change, in degrees of longitude.
-                            final double cv = (position.x - ZONER.easting) / (λ - λ0);              // Approximative conversion factor from degrees to metres.
-                            isValid = (Math.abs(rλ) * cv <= sx);                                    // Be tolerant if distance in metres is less than resolution.
-                            if (isValid) {
-                                isValid = (zoneError == (rλ < 0 ? -1 : +1));                        // Verify also that the error is on the side of the zone change.
+                    final DirectPosition geographic = inverse.transform(getDirectPosition(), owner.geographic);
+                    final double λ = geographic.getOrdinate(1);
+                    final double φ = geographic.getOrdinate(0);
+                    owner.geographic = geographic;                                          // For future reuse.
+                    isValid = (φ >= φs - LATITUDE_BAND_HEIGHT/2) && (φ < upperBounds(φs));  // See above comment.
+                    if (isValid) {
+                        /*
+                         * Verification of UTM zone. We allow a tolerance for latitudes close to a pole because
+                         * not all users may apply the UTM special rules for Norway and Svalbard. Anyway, using
+                         * the neighbor zone at those high latitudes is less significant. For other latitudes,
+                         * we allow a tolerance if the point is close to a line of zone change.
+                         */
+                        int zoneError = ZONER.zone(φ, λ) - zone;
+                        if (zoneError != 0) {
+                            final int zc = ZONER.zoneCount();
+                            if (zoneError > zc/2) zoneError -= zc;
+                            if (ZONER.isSpecialCase(zone, φ)) {
+                                isValid = Math.abs(zoneError) == 1;         // Tolerance in zone numbers for high latitudes.
+                            } else {
+                                final double rλ = Math.IEEEremainder(λ - ZONER.origin, ZONER.width);    // Distance to closest zone change, in degrees of longitude.
+                                final double cv = (minX - ZONER.easting) / (λ - λ0);                    // Approximative conversion factor from degrees to metres.
+                                isValid = (Math.abs(rλ) * cv <= sx);                                    // Be tolerant if distance in metres is less than resolution.
+                                if (isValid) {
+                                    isValid = (zoneError == (rλ < 0 ? -1 : +1));                        // Verify also that the error is on the side of the zone change.
+                                }
                             }
                         }
                     }
@@ -1205,7 +1238,7 @@ parse:                  switch (part) {
             if (!isValid) {
                 final String gzd;
                 try {
-                    gzd = owner.encoder(crs).encode(owner, position, "", 0);
+                    gzd = owner.encoder(crs).encode(owner, getDirectPosition(), "", 0);
                 } catch (IllegalArgumentException | FactoryException e) {
                     throw new GazetteerException(e.getLocalizedMessage(), e);
                 }
@@ -1318,23 +1351,12 @@ parse:                  switch (part) {
         }
 
         /**
-         * Returns the lower-left corner of the decoded MGRS cell.
-         */
-        @Override
-        public Position getPosition() {
-            return position;
-        }
-
-        /**
-         * Returns an envelope that encompass the location. This property is partially redundant with
-         * {@link #getGeographicExtent()}, except that this method allows envelopes in non-geographic CRS.
-         *
-         * @return envelope that encompass the location.
+         * Returns the Coordinate Reference System of the decoded MGRS reference.
+         * This is an Universal Transverse Mercator (UTM) or Universal Polar Stereographic (UPS) projection.
          */
         @Override
-        public Envelope getEnvelope() {
-            return new Envelope2D(position.getCoordinateReferenceSystem(),
-                    position.x - sx/2, position.y - sy/2, sx, sy);
+        public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+            return crs;
         }
     }
 }

Added: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java?rev=1785578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java (added)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -0,0 +1,470 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.gazetteer;
+
+import org.opengis.geometry.Envelope;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.geometry.coordinate.Position;
+import org.opengis.metadata.extent.GeographicExtent;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.referencing.gazetteer.LocationType;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * A location described by an unmodifiable direct position that defines the centroid of an envelope.
+ * This class encompasses most information in a single object, which make it lightweight to create
+ * (less pressure on the garbage collector). However this is not a clear separation of responsibility,
+ * so this class should not be in public API.
+ *
+ * <p>Subclasses <strong>must</strong> override the following methods if the above coordinate reference
+ * system is not a geographic CRS with (<var>longitude</var>, <var>latitude</var>) axes in degrees:</p>
+ * <ul>
+ *   <li>{@link #getCoordinateReferenceSystem()}</li>
+ *   <li>{@link #getWestBoundLongitude()}</li>
+ *   <li>{@link #getEastBoundLongitude()}</li>
+ *   <li>{@link #getSouthBoundLatitude()}</li>
+ *   <li>{@link #getNorthBoundLatitude()}</li>
+ * </ul>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+class SimpleLocation extends AbstractLocation implements DirectPosition, Envelope, GeographicBoundingBox {
+    /**
+     * The westernmost bound of the envelope, or {@code NaN} if not yet computed.
+     *
+     * @see #getLowerCorner()
+     * @see #getWestBoundLongitude()
+     */
+    protected double minX = Double.NaN;
+
+    /**
+     * The southernmost bound of the envelope, or {@code NaN} if not yet computed.
+     *
+     * @see #getLowerCorner()
+     * @see #getSouthBoundLatitude()
+     */
+    protected double minY = Double.NaN;
+
+    /**
+     * The easternmost bound of the envelope, or {@code NaN} if not yet computed.
+     *
+     * @see #getUpperCorner()
+     * @see #getEastBoundLongitude()
+     */
+    protected double maxX = Double.NaN;
+
+    /**
+     * The northernmost bound of the envelope, or {@code NaN} if not yet computed.
+     *
+     * @see #getUpperCorner()
+     * @see #getNorthBoundLatitude()
+     */
+    protected double maxY = Double.NaN;
+
+    /**
+     * Creates a new location for the given geographic identifier.
+     * This constructor accepts {@code null} arguments, but this is not recommended.
+     *
+     * @param type        the description of the nature of this geographic identifier.
+     * @param identifier  the geographic identifier to be returned by {@link #getGeographicIdentifier()}.
+     */
+    SimpleLocation(final LocationType type, final CharSequence identifier) {
+        super(type, identifier);
+    }
+
+    /**
+     * Returns a description of the location instance.
+     * In this simple implementation, this instance is its own geographic extent.
+     */
+    @Override
+    public final GeographicExtent getGeographicExtent() {
+        return this;
+    }
+
+    /**
+     * Returns an envelope that encompass the location.
+     * In this simple implementation, this instance is its own envelope.
+     */
+    @Override
+    public final Envelope getEnvelope() {
+        return this;
+    }
+
+    /**
+     * Returns coordinates of a centroid point for the location instance.
+     * In this simple implementation, this instance is its own centroid coordinate.
+     */
+    @Override
+    public final Position getPosition() {
+        return this;
+    }
+
+    /**
+     * Returns the direct position, which is itself.
+     */
+    @Override
+    public final DirectPosition getDirectPosition() {
+        return this;
+    }
+
+    /**
+     * Returns the coordinate reference system the envelope and the position.
+     * Default implementation returns {@link CommonCRS#defaultGeographic()}.
+     * Subclasses must override this method if the another CRS.
+     */
+    @Override
+    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+        return CommonCRS.defaultGeographic();
+    }
+
+    /**
+     * Returns the number of dimensions, which is fixed to 2.
+     */
+    @Override
+    public final int getDimension() {
+        return 2;
+    }
+
+    /**
+     * Returns the coordinates of the centroid.
+     */
+    @Override
+    public final double[] getCoordinate() {
+        return new double[] {getOrdinate(0), getOrdinate(1)};
+    }
+
+    /**
+     * Returns the centroid ordinate value for the specified dimension.
+     */
+    @Override
+    public final double getOrdinate(final int dimension) {
+        return getMedian(dimension);
+    }
+
+    /**
+     * Returns the minimal ordinate value for the specified dimension.
+     */
+    @Override
+    public final double getMinimum(final int dimension) {
+        switch (dimension) {
+            case 0:  return minX;
+            case 1:  return minY;
+            default: throw new IndexOutOfBoundsException(indexOutOfBounds(dimension));
+        }
+    }
+
+    /**
+     * Returns the maximal ordinate value for the specified dimension.
+     */
+    @Override
+    public final double getMaximum(final int dimension) {
+        switch (dimension) {
+            case 0:  return maxX;
+            case 1:  return maxY;
+            default: throw new IndexOutOfBoundsException(indexOutOfBounds(dimension));
+        }
+    }
+
+    /**
+     * Returns the median ordinate value for the specified dimension.
+     */
+    @Override
+    public final double getMedian(final int dimension) {
+        switch (dimension) {
+            case 0:  return (minX + maxX) / 2;
+            case 1:  return (minY + maxY) / 2;
+            default: throw new IndexOutOfBoundsException(indexOutOfBounds(dimension));
+        }
+    }
+
+    /**
+     * Returns the envelope width or height along the specified dimension.
+     */
+    @Override
+    public final double getSpan(final int dimension) {
+        switch (dimension) {
+            case 0:  return (maxX - minX);
+            case 1:  return (maxY - minY);
+            default: throw new IndexOutOfBoundsException(indexOutOfBounds(dimension));
+        }
+    }
+
+    /**
+     * Returns the error message for an index out of bounds.
+     */
+    private static String indexOutOfBounds(final int dimension) {
+        return Errors.format(Errors.Keys.IndexOutOfBounds_1, dimension);
+    }
+
+    /**
+     * Returns a copy of the lower-left corner.
+     */
+    @Override
+    public final DirectPosition getLowerCorner() {
+        return new DirectPosition2D(getCoordinateReferenceSystem(), minX, minY);
+    }
+
+    /**
+     * Returns a copy of the upper-right corner.
+     */
+    @Override
+    public final DirectPosition getUpperCorner() {
+        return new DirectPosition2D(getCoordinateReferenceSystem(), maxX, maxY);
+    }
+
+    /**
+     * Do not allow modification of the direct position.
+     */
+    @Override
+    public final void setOrdinate(int dimension, double value) {
+        throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, DirectPosition.class));
+    }
+
+    /**
+     * Indication of whether the bounding polygon encompasses an area covered by the data
+     * (<cite>inclusion</cite>) or an area where data is not present (<cite>exclusion</cite>).
+     * The value is fixed to {@code true}.
+     */
+    @Override
+    public final Boolean getInclusion() {
+        return Boolean.TRUE;
+    }
+
+    /**
+     * Returns the westernmost longitude in degrees. Default implementation assumes that the
+     * {@linkplain #getCoordinateReferenceSystem() coordinate reference system} is geographic
+     * with (<var>longitude</var>, <var>latitude</var>) axes in degrees.
+     * Subclasses <strong>must</strong> override if this is not the case.
+     */
+    @Override
+    public double getWestBoundLongitude() {
+        return minX;
+    }
+
+    /**
+     * Returns the easternmost longitude in degrees. Default implementation assumes that the
+     * {@linkplain #getCoordinateReferenceSystem() coordinate reference system} is geographic
+     * with (<var>longitude</var>, <var>latitude</var>) axes in degrees.
+     * Subclasses <strong>must</strong> override if this is not the case.
+     */
+    @Override
+    public double getEastBoundLongitude() {
+        return maxX;
+    }
+
+    /**
+     * Returns the southernmost latitude in degrees. Default implementation assumes that the
+     * {@linkplain #getCoordinateReferenceSystem() coordinate reference system} is geographic
+     * with (<var>longitude</var>, <var>latitude</var>) axes in degrees.
+     * Subclasses <strong>must</strong> override if this is not the case.
+     */
+    @Override
+    public double getSouthBoundLatitude() {
+        return minY;
+    }
+
+    /**
+     * Returns the northernmost latitude in degrees. Default implementation assumes that the
+     * {@linkplain #getCoordinateReferenceSystem() coordinate reference system} is geographic
+     * with (<var>longitude</var>, <var>latitude</var>) axes in degrees.
+     * Subclasses <strong>must</strong> override if this is not the case.
+     */
+    @Override
+    public double getNorthBoundLatitude() {
+        return maxY;
+    }
+
+    /**
+     * A {@code SimpleLocation} for non-geographic CRS.
+     * Subclasses should invoke {@link #computeGeographicBoundingBox(MathTransform, DirectPosition2D)}
+     * after the {@link #minX}, {@link #minY}, {@link #maxX} and {@link #maxY} fields have been set.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.8
+     * @version 0.8
+     * @module
+     */
+    static abstract class Projected extends SimpleLocation implements GeographicBoundingBox {
+        /**
+         * The western-most coordinate of the limit of the dataset extent.
+         * The value is expressed in longitude in decimal degrees (positive east).
+         */
+        protected double westBoundLongitude = Double.NaN;
+
+        /**
+         * The eastern-most coordinate of the limit of the dataset extent.
+         * The value is expressed in longitude in decimal degrees (positive east).
+         */
+        protected double eastBoundLongitude = Double.NaN;
+
+        /**
+         * The southern-most coordinate of the limit of the dataset extent.
+         * The value is expressed in latitude in decimal degrees (positive north).
+         */
+        protected double southBoundLatitude = Double.NaN;
+
+        /**
+         * The northern-most, coordinate of the limit of the dataset extent.
+         * The value is expressed in latitude in decimal degrees (positive north).
+         */
+        protected double northBoundLatitude = Double.NaN;
+
+        /**
+         * Creates a new location for the given geographic identifier.
+         * This constructor accepts {@code null} arguments, but this is not recommended.
+         *
+         * @param type        the description of the nature of this geographic identifier.
+         * @param identifier  the geographic identifier to be returned by {@link #getGeographicIdentifier()}.
+         */
+        Projected(final LocationType type, final CharSequence identifier) {
+            super(type, identifier);
+        }
+
+        /**
+         * Computes the geographic bounding box from the current values of {@link #minX}, {@link #minY}, {@link #maxX}
+         * and {@link #maxY} fields. This method performs a work similar to the {@code Envelopes.transform(…)} methods
+         * but using a much simpler (and faster) algorithm: this method projects only the 4 corners, without any check
+         * for the number of dimensions, projection of median coordinates,  use of projection derivatives for locating
+         * the envelope extremum or special checks for polar cases. This method is okay only when the current envelope
+         * is the envelope of a cell of a grid that divide the projection area in a very regular way (for example with
+         * the guarantee that the projection central meridian will never be in the middle of grid cell, <i>etc</i>).
+         *
+         * <p>If a geographic bounding box was already defined before invoking this method, then it will be expanded
+         * (if needed) for encompassing the bounding box computed by this method.</p>
+         *
+         * @param  inverse  the transform from projected coordinates to geographic coordinates.
+         * @throws TransformException if a coordinate operation failed.
+         *
+         * @see org.apache.sis.geometry.Envelopes#transform(MathTransform, Envelope)
+         */
+        final void computeGeographicBoundingBox(final MathTransform inverse) throws TransformException {
+            final double[] points = new double[] {
+                minX, minY,
+                minX, maxY,
+                maxX, minY,
+                maxX, maxY
+            };
+            inverse.transform(points, 0, points, 0, 4);
+            for (int i=0; i < points.length;) {
+                final double φ = points[i++];
+                final double λ = points[i++];
+                if (Double.isNaN(φ) || Double.isNaN(λ)) {
+                    throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope));
+                }
+                if (!(φ >= southBoundLatitude)) southBoundLatitude = φ;     // Use '!' for accepting NaN.
+                if (!(φ <= northBoundLatitude)) northBoundLatitude = φ;
+                if (!(λ >= westBoundLongitude)) westBoundLongitude = λ;
+                if (!(λ <= eastBoundLongitude)) eastBoundLongitude = λ;
+            }
+        }
+
+        /**
+         * Clips the geographic bounding box to the given area. If the bounding box changed as a result of this method
+         * call, then caller should consider invoking {@link #clipProjectedEnvelope(MathTransform, double, double)}.
+         *
+         * @return whether the geographic bounding box changed as a result of this method call.
+         */
+        final boolean clipGeographicBoundingBox(final double λmin, final double φmin, final double λmax, final double φmax) {
+            boolean changed = false;
+            if (westBoundLongitude < λmin) {westBoundLongitude = λmin; changed = true;}
+            if (eastBoundLongitude > λmax) {eastBoundLongitude = λmax; changed = true;}
+            if (southBoundLatitude < φmin) {southBoundLatitude = φmin; changed = true;}
+            if (northBoundLatitude > φmax) {northBoundLatitude = φmax; changed = true;}
+            return changed;
+        }
+
+        /**
+         * Projects the geographic bounding box and clips the current envelope to the result of that projection.
+         * This method should be invoked when {@link #clipGeographicBoundingBox(double, double, double, double)}
+         * returned {@code true}.
+         *
+         * @param  forward  the transform from geographic coordinates to projected coordinates.
+         * @param  tx       tolerance threshold in easting  values for changing {@link #minX} or {@link #maxX}.
+         * @param  ty       tolerance threshold in northing values for changing {@link #minY} or {@link #maxY}.
+         * @throws TransformException if a coordinate operation failed.
+         */
+        final void clipProjectedEnvelope(final MathTransform forward, final double tx, final double ty) throws TransformException {
+            final double[] points = new double[] {
+                southBoundLatitude, westBoundLongitude,
+                northBoundLatitude, westBoundLongitude,
+                southBoundLatitude, eastBoundLongitude,
+                northBoundLatitude, eastBoundLongitude
+            };
+            forward.transform(points, 0, points, 0, 4);
+            double xmin, ymin, xmax, ymax;
+            xmin = xmax = points[0];
+            ymin = ymax = points[1];
+            for (int i=2; i < points.length;) {
+                final double x = points[i++];
+                final double y = points[i++];
+                if (x < xmin) xmin = x;
+                if (x > xmax) xmax = x;
+                if (y < ymin) ymin = y;
+                if (y > ymax) ymax = y;
+            }
+            if (xmin > minX + tx) minX = xmin;
+            if (xmax < maxX - tx) maxX = xmax;
+            if (ymin > minY + ty) minY = ymin;
+            if (ymax < maxY - ty) maxY = ymax;
+        }
+
+        /**
+         * Returns the western-most coordinate of the limit of the dataset extent.
+         * The value is expressed in longitude in decimal degrees (positive east).
+         */
+        @Override
+        public final double getWestBoundLongitude() {
+            return westBoundLongitude;
+        }
+
+        /**
+         * Returns the eastern-most coordinate of the limit of the dataset extent.
+         * The value is expressed in longitude in decimal degrees (positive east).
+         */
+        @Override
+        public final double getEastBoundLongitude() {
+            return eastBoundLongitude;
+        }
+
+        /**
+         * Returns the southern-most coordinate of the limit of the dataset extent.
+         * The value is expressed in latitude in decimal degrees (positive north).
+         */
+        @Override
+        public final double getSouthBoundLatitude()  {
+            return southBoundLatitude;
+        }
+
+        /**
+         * Returns the northern-most, coordinate of the limit of the dataset extent.
+         * The value is expressed in latitude in decimal degrees (positive north).
+         */
+        @Override
+        public final double getNorthBoundLatitude()   {
+            return northBoundLatitude;
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Added: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java?rev=1785578&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java (added)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.gazetteer;
+
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests {@link LocationFormat}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public final strictfp class LocationFormatTest extends TestCase {
+    /**
+     * Tests formatting of an instance having only geographic coordinates.
+     */
+    @Test
+    public void testGeographic() {
+        final SimpleLocation loc = new SimpleLocation(null, "A location");
+        loc.minX =   8;
+        loc.maxX =  20;
+        loc.minY = -10;
+        loc.maxY =  30;
+        final LocationFormat format = new LocationFormat(null, null);
+        assertMultilinesEquals(
+                "Geographic identifier:       A location\n" +
+                "East bound:                   8°E\n" +
+                "West bound:                  20°E\n" +
+                "South bound:                 10°S\n" +
+                "North bound:                 30°N\n" +
+                "Representative position:     14°E\n" +
+                "                             10°N\n" +
+                "Coordinate reference system: WGS 84\n", format.format(loc));
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/LocationFormatTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystemTest.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -206,8 +206,9 @@ public final strictfp class MilitaryGrid
             throws TransformException
     {
         final Location loc = coder.decode(reference);
-        final DirectPosition2D pos = (DirectPosition2D) loc.getPosition().getDirectPosition();
-        assertTrue(reference, ((Envelope2D) loc.getEnvelope()).contains(pos));
+        final Envelope2D envelope = new Envelope2D(loc.getEnvelope());
+        final DirectPosition2D pos = new DirectPosition2D(loc.getPosition().getDirectPosition());
+        assertTrue(reference, envelope.contains(pos));
         return pos;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/test/suite/ReferencingByIdentifiersTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/test/suite/ReferencingByIdentifiersTestSuite.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/test/suite/ReferencingByIdentifiersTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing-by-identifiers/src/test/java/org/apache/sis/test/suite/ReferencingByIdentifiersTestSuite.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -30,6 +30,7 @@ import org.junit.BeforeClass;
  * @module
  */
 @Suite.SuiteClasses({
+    org.apache.sis.referencing.gazetteer.LocationFormatTest.class,
     org.apache.sis.referencing.gazetteer.LocationTypeTest.class,
     org.apache.sis.referencing.gazetteer.ReferencingByIdentifiersTest.class,
     org.apache.sis.referencing.gazetteer.MilitaryGridReferenceSystemTest.class

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -42,7 +42,7 @@ import static org.apache.sis.util.Charac
  * <p>For example, the following code:</p>
  *
  * {@preformat java
- *     StringBuilder  buffer = new StringBuilder();
+ *     StringBuilder buffer = new StringBuilder();
  *     TableAppender table  = new TableAppender(buffer);
  *     table.nextLine('═');
  *     table.append("English\tFrench\tr.e.d.\n");

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] Sun Mar  5 23:24:42 2017
@@ -62,11 +62,21 @@ public final class Vocabulary extends In
         public static final short Accuracy = 1;
 
         /**
+         * Administrator
+         */
+        public static final short Administrator = 130;
+
+        /**
          * Aliases
          */
         public static final short Aliases = 2;
 
         /**
+         * Alternative identifiers
+         */
+        public static final short AlternativeIdentifiers = 131;
+
+        /**
          * Angle
          */
         public static final short Angle = 3;
@@ -157,6 +167,11 @@ public final class Vocabulary extends In
         public static final short Coordinate = 129;
 
         /**
+         * Coordinate reference system
+         */
+        public static final short CoordinateRefSys = 132;
+
+        /**
          * Correlation
          */
         public static final short Correlation = 19;
@@ -262,6 +277,11 @@ public final class Vocabulary extends In
         public static final short DublinJulian = 38;
 
         /**
+         * East bound
+         */
+        public static final short EastBound = 133;
+
+        /**
          * Ellipsoid
          */
         public static final short Ellipsoid = 39;
@@ -277,6 +297,11 @@ public final class Vocabulary extends In
         public static final short EllipsoidalHeight = 41;
 
         /**
+         * End date
+         */
+        public static final short EndDate = 134;
+
+        /**
          * {0} entr{0,choice,0#y|2#ies}
          */
         public static final short EntryCount_1 = 121;
@@ -302,6 +327,11 @@ public final class Vocabulary extends In
         public static final short GeodeticDataset = 45;
 
         /**
+         * Geographic identifier
+         */
+        public static final short GeographicIdentifier = 135;
+
+        /**
          * Height
          */
         public static final short Height = 46;
@@ -392,6 +422,11 @@ public final class Vocabulary extends In
         public static final short Localization = 63;
 
         /**
+         * Location type
+         */
+        public static final short LocationType = 136;
+
+        /**
          * Logging
          */
         public static final short Logging = 64;
@@ -447,6 +482,11 @@ public final class Vocabulary extends In
         public static final short None = 73;
 
         /**
+         * North bound
+         */
+        public static final short NorthBound = 137;
+
+        /**
          * Note
          */
         public static final short Note = 74;
@@ -542,6 +582,11 @@ public final class Vocabulary extends In
         public static final short RemoteConfiguration = 89;
 
         /**
+         * Representative position
+         */
+        public static final short RepresentativePosition = 141;
+
+        /**
          * Root
          */
         public static final short Root = 90;
@@ -567,11 +612,21 @@ public final class Vocabulary extends In
         public static final short Source = 93;
 
         /**
+         * South bound
+         */
+        public static final short SouthBound = 138;
+
+        /**
          * Standard deviation
          */
         public static final short StandardDeviation = 94;
 
         /**
+         * Start date
+         */
+        public static final short StartDate = 139;
+
+        /**
          * Subset of {0}
          */
         public static final short SubsetOf_1 = 95;
@@ -692,6 +747,11 @@ public final class Vocabulary extends In
         public static final short Warnings = 117;
 
         /**
+         * West bound
+         */
+        public static final short WestBound = 140;
+
+        /**
          * World
          */
         public static final short World = 118;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] Sun Mar  5 23:24:42 2017
@@ -15,7 +15,9 @@
 # limitations under the License.
 #
 Accuracy                = Accuracy
+Administrator           = Administrator
 Aliases                 = Aliases
+AlternativeIdentifiers  = Alternative identifiers
 Angle                   = Angle
 AngularDegrees          = Degrees
 AngularMinutes          = Minutes
@@ -34,6 +36,7 @@ Commands                = Commands
 ConstantPressureSurface = Constant pressure surface
 Container               = Container
 Coordinate              = Coordinate
+CoordinateRefSys        = Coordinate reference system
 Correlation             = Correlation
 CurrentDateTime         = Current date and time
 CurrentDirectory        = Current directory
@@ -55,14 +58,17 @@ Directory               = Directory
 DittoMark               = \u2033
 Domain                  = Domain
 DublinJulian            = Dublin Julian
+EastBound               = East bound
 Ellipsoid               = Ellipsoid
 EllipsoidChange         = Ellipsoid change
 EllipsoidalHeight       = Ellipsoidal height
+EndDate                 = End date
 EntryCount_1            = {0} entr{0,choice,0#y|2#ies}
 Geocentric              = Geocentric
 GeocentricRadius        = Geocentric radius
 GeocentricConversion    = Geocentric conversion
 GeodeticDataset         = Geodetic dataset
+GeographicIdentifier    = Geographic identifier
 Height                  = Height
 Identifier              = Identifier
 Implementation          = Implementation
@@ -82,6 +88,7 @@ Libraries               = Libraries
 LocalConfiguration      = Local configuration
 Locale                  = Locale
 Localization            = Localization
+LocationType            = Location type
 Logging                 = Logging
 Mandatory               = Mandatory
 Mapping                 = Mapping
@@ -93,6 +100,7 @@ ModifiedJulian          = Modified Julia
 Name                    = Name
 None                    = None
 Note                    = Note
+NorthBound              = North bound
 NumberOfValues          = Number of values
 NumberOfNaN             = Number of \u2018NaN\u2019
 Obligation              = Obligation
@@ -111,12 +119,15 @@ Quoted_1                = \u201c{0}\u201
 Read                    = Read
 Remarks                 = Remarks
 RemoteConfiguration     = Remote configuration
+RepresentativePosition  = Representative position
 Root                    = Root
 RootMeanSquare          = Root Mean Square
 Scale                   = Scale
 SlashSeparatedList_2    = {0}/{1}
 Source                  = Source
+SouthBound              = South bound
 StandardDeviation       = Standard deviation
+StartDate               = Start date
 SubsetOf_1              = Subset of {0}
 SupersededBy_1          = Superseded by {0}.
 TemporaryFiles          = Temporary files
@@ -141,5 +152,6 @@ Version_2               = {0} version {1
 Versions                = Versions
 Vertical                = Vertical
 Warnings                = Warnings
+WestBound               = West bound
 World                   = World
 Write                   = Write

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1785578&r1=1785577&r2=1785578&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Sun Mar  5 23:24:42 2017
@@ -22,7 +22,9 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 Accuracy                = Pr\u00e9cision
+Administrator           = Administrateur
 Aliases                 = Alias
+AlternativeIdentifiers  = Identifiants alternatifs
 Angle                   = Angle
 AngularDegrees          = Degr\u00e9s
 AngularMinutes          = Minutes
@@ -41,6 +43,7 @@ Commands                = Commandes
 ConstantPressureSurface = Surface \u00e0 pression constante
 Container               = Conteneur
 Coordinate              = Coordonn\u00e9e
+CoordinateRefSys        = Syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es
 Correlation             = Corr\u00e9lation
 CurrentDateTime         = Date et heure courantes
 CurrentDirectory        = R\u00e9pertoire courant
@@ -62,14 +65,17 @@ Directory               = R\u00e9pertoir
 DittoMark               = \u2033
 Domain                  = Domaine
 DublinJulian            = Julien Dublin
+EastBound               = Bord est
 Ellipsoid               = Ellipso\u00efde
 EllipsoidChange         = Changement d\u2019ellipso\u00efde
 EllipsoidalHeight       = Hauteur ellipso\u00efdale
 EntryCount_1            = {0} entr\u00e9e{0,choice,0#|2#s}
+EndDate                 = Date de fin
 Geocentric              = G\u00e9ocentrique
 GeocentricRadius        = Rayon g\u00e9ocentrique
 GeocentricConversion    = Conversion g\u00e9ocentrique
 GeodeticDataset         = Base de donn\u00e9es g\u00e9od\u00e9sique
+GeographicIdentifier    = Identifiant g\u00e9ographique
 Height                  = Hauteur
 Identifier              = Identifiant
 Implementation          = Impl\u00e9mentation
@@ -89,6 +95,7 @@ Libraries               = Biblioth\u00e8
 LocalConfiguration      = Configuration locale
 Locale                  = Locale
 Localization            = R\u00e9gionalisation
+LocationType            = Type de location
 Logging                 = Journalisation
 Mandatory               = Requis
 Mapping                 = Cartographie
@@ -100,6 +107,7 @@ ModifiedJulian          = Julien modifi\
 Name                    = Nom
 None                    = Aucun
 Note                    = Note
+NorthBound              = Bord nord
 NumberOfValues          = Nombre de valeurs
 NumberOfNaN             = Nombre de \u2018NaN\u2019
 Obligation              = Obligation
@@ -118,12 +126,15 @@ Quoted_1                = \u00ab\u202f{0
 Read                    = Lecture
 Remarks                 = Remarques
 RemoteConfiguration     = Configuration distante
+RepresentativePosition  = Position repr\u00e9sentative
 Root                    = Racine
 RootMeanSquare          = Moyenne quadratique
 Scale                   = \u00c9chelle
 SlashSeparatedList_2    = {0}/{1}
 Source                  = Source
+SouthBound              = Bord sud
 StandardDeviation       = \u00c9cart type
+StartDate               = Date de d\u00e9part
 SubsetOf_1              = Sous-ensemble de {0}
 SupersededBy_1          = Remplac\u00e9 par {0}.
 TemporaryFiles          = Fichiers temporaires
@@ -148,5 +159,6 @@ Version_2               = {0} version {1
 Versions                = Versions
 Vertical                = Vertical
 Warnings                = Avertissements
+WestBound               = Bord ouest
 World                   = Monde
 Write                   = \u00c9criture



Mime
View raw message