Return-Path: X-Original-To: apmail-sis-commits-archive@www.apache.org Delivered-To: apmail-sis-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B1DF010497 for ; Thu, 30 Jan 2014 12:37:40 +0000 (UTC) Received: (qmail 25964 invoked by uid 500); 30 Jan 2014 12:37:40 -0000 Delivered-To: apmail-sis-commits-archive@sis.apache.org Received: (qmail 25942 invoked by uid 500); 30 Jan 2014 12:37:38 -0000 Mailing-List: contact commits-help@sis.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sis-dev@sis.apache.org Delivered-To: mailing list commits@sis.apache.org Received: (qmail 25933 invoked by uid 99); 30 Jan 2014 12:37:38 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 30 Jan 2014 12:37:38 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 30 Jan 2014 12:37:33 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 368BB2388868; Thu, 30 Jan 2014 12:37:13 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1562800 [1/3] - in /sis/trunk: ./ core/sis-referencing/src/main/java/org/apache/sis/geometry/ core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ core/sis-referencing/src/main/java/org/apache/sis/io/wkt/ core/sis-refere... Date: Thu, 30 Jan 2014 12:37:11 -0000 To: commits@sis.apache.org From: desruisseaux@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140130123713.368BB2388868@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: desruisseaux Date: Thu Jan 30 12:37:10 2014 New Revision: 1562800 URL: http://svn.apache.org/r1562800 Log: Merge from the JDK6 branch. Added: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Legacy.java - copied unchanged from r1562799, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Legacy.java Removed: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Legacy.java Modified: sis/trunk/ (props changed) sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/SubEnvelope.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/VerticalDatumTypes.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Colors.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Convention.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/FormattableObject.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/UnformattableObjectException.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTFormat.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/package-info.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CRS.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/AxisDirectionsTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/ConventionTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/FormatterTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/SymbolsTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/Assert.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/CRSTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRS.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/HardCodedCRSTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/DefaultCoordinateSystemAxisTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/cs/HardCodedAxes.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultEllipsoidTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultGeodeticDatumTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ComparisonMode.java sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties sis/trunk/pom.xml Propchange: sis/trunk/ ------------------------------------------------------------------------------ Merged /sis/branches/JDK7:r1560379-1562797 Merged /sis/branches/JDK6:r1560381-1562799 Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -526,6 +526,7 @@ public abstract class AbstractEnvelope i * @return A representation of this envelope as an array of non-empty envelope. * * @see Envelope2D#toRectangles() + * @see GeneralEnvelope#simplify() * * @since 0.4 */ @@ -1035,11 +1036,11 @@ public abstract class AbstractEnvelope i * where n is the {@linkplain #getDimension() number of dimensions}. * The number of dimension is written only if different than 2. * - *

Example:

- * - * {@preformat wkt - * BOX(-90 -180, 90 180) - * BOX3D(-90 -180 0, 90 180 1) + * {@example + *
    + *
  • BOX(-90 -180, 90 180)
  • + *
  • BOX3D(-90 -180 0, 90 180 1)
  • + *
* } * * {@note The BOX element is not part of the standard Well Known Text @@ -1182,7 +1183,10 @@ public abstract class AbstractEnvelope i * @param dimension The dimension to set. * @param lower The limit in the direction of decreasing ordinate values. * @param upper The limit in the direction of increasing ordinate values. + * @throws UnmodifiableGeometryException If this envelope is not modifiable. * @throws IndexOutOfBoundsException If the given index is out of bounds. + * @throws IllegalArgumentException If {@code lower > upper}, this envelope has a CRS + * and the axis range meaning at the given dimension is not "wraparound". */ void setRange(final int dimension, final double lower, final double upper) throws IndexOutOfBoundsException Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -27,8 +27,10 @@ import org.opengis.geometry.Envelope; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.metadata.extent.GeographicBoundingBox; +import org.opengis.referencing.cs.RangeMeaning; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.referencing.CommonCRS; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.CharSequences; @@ -115,6 +117,7 @@ class ArrayEnvelope extends AbstractEnve ordinates[i ] = lowerCorner.getOrdinate(i); ordinates[i + dimension] = upperCorner.getOrdinate(i); } + verifyRanges(crs, ordinates); } /** @@ -171,6 +174,7 @@ class ArrayEnvelope extends AbstractEnve ordinates[i] = lowerCorner.getOrdinate(i); ordinates[i+dimension] = upperCorner.getOrdinate(i); } + verifyRanges(crs, ordinates); } /** @@ -196,6 +200,7 @@ class ArrayEnvelope extends AbstractEnve } } crs = CommonCRS.defaultGeographic(); + verifyRanges(crs, ordinates); } /** @@ -316,6 +321,59 @@ scanNumber: while ((i += Character.charC } /** + * Verifies the validity of the range of ordinates values in the given array. + * If the given CRS is null, then this method conservatively does nothing. + * Otherwise this method performs the following verifications: + * + *
    + *
  • {@code lower > upper} is allowed only for axes having {@link RangeMeaning#WRAPAROUND}.
  • + *
+ * + * This method does not verify if the ordinate values are between the axis minimum and + * maximum values. This is because out-of-range values exist in practice but do not impact the working + * of {@code add(…)}, {@code intersect(…)}, {@code contains(…)} and similar methods. This in contrast + * with the checks listed above, where failure to meet those conditions will cause the methods to + * behave in an unexpected way. + * + * {@section Implementation consistency} + * The checks performed by this method shall be consistent with the checks performed by the following methods: + *
    + *
  • {@link GeneralEnvelope#setCoordinateReferenceSystem(CoordinateReferenceSystem)}
  • + *
  • {@link GeneralEnvelope#setRange(int, double, double)}
  • + *
  • {@link SubEnvelope#setRange(int, double, double)}
  • + *
+ * + * @param crs The coordinate reference system, or {@code null}. + * @param ordinates The array of ordinate values to verify. + */ + static void verifyRanges(final CoordinateReferenceSystem crs, final double[] ordinates) { + if (crs != null) { + final int dimension = ordinates.length >>> 1; + for (int i=0; i upper && !isWrapAround(crs, i)) { + throw new IllegalArgumentException(illegalRange(crs, i, lower, upper)); + } + } + } + } + + /** + * Creates an error message for an illegal ordinates range at the given dimension. + * This is used for formatting the exception message. + */ + static String illegalRange(final CoordinateReferenceSystem crs, + final int dimension, final double lower, final double upper) + { + Object name = IdentifiedObjects.getName(getAxis(crs, dimension), null); + if (name == null) { + name = dimension; // Paranoiac fallback (name should never be null). + } + return Errors.format(Errors.Keys.IllegalOrdinateRange_3, lower, upper, name); + } + + /** * Returns the index of the first valid ordinate value of the lower corner in the {@link #ordinates} array. * This is always 0, unless this envelope is a {@link SubEnvelope}. * Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -93,7 +93,7 @@ public final class Envelopes extends Sta ensureNonNull("wkt", wkt); try { return new GeneralEnvelope(wkt); - } catch (RuntimeException e) { + } catch (IllegalArgumentException e) { throw new FactoryException(Errors.format( Errors.Keys.UnparsableStringForClass_2, Envelope.class), e); } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -90,6 +90,24 @@ import static org.apache.sis.math.MathFu * * * + * {@section Envelope validation} + * If and only if this envelope is associated to a non-null CRS, then constructors and setter methods + * in this class perform the following checks: + * + *
    + *
  • The number of CRS dimensions must be equals to this.{@linkplain #getDimension()}.
  • + *
  • For each dimension i, + * {@linkplain #getLower(int) getLower}(i) > {@linkplain #getUpper(int) getUpper}(i) is allowed + * only if the {@linkplain org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis#getRangeMeaning() coordinate + * system axis range meaning} is {@code WRAPAROUND}.
  • + *
+ * + * Note that this class does not require the ordinate values to be between the axis minimum and + * maximum values. This flexibility exists because out-of-range values happen in practice, while they do + * not hurt the working of {@code add(…)}, {@code intersect(…)}, {@code contains(…)} and similar methods. + * This in contrast with the {@code lower > upper} case, which cause the above-cited methods to behave in + * an unexpected way if the axis does not have wraparound range meaning. + * * @author Martin Desruisseaux (IRD, Geomatys) * @author Johann Sorel (Geomatys) * @since 0.3 (derived from geotk-2.4) @@ -218,14 +236,14 @@ public class GeneralEnvelope extends Arr * check that every points in a {@code LINESTRING} have the same dimension. However this * constructor ensures that the parenthesis are balanced, in order to catch some malformed WKT. * - *

The following examples can be parsed by this constructor in addition of the usual - * {@code BOX} element. This constructor creates the bounding box of those geometries:

+ * {@example The following texts can be parsed by this constructor in addition of the usual + * BOX element. This constructor creates the bounding box of those geometries: * *
    - *
  • {@code POINT(6 10)}
  • - *
  • {@code MULTIPOLYGON(((1 1, 5 1, 1 5, 1 1),(2 2, 3 2, 3 3, 2 2)))}
  • - *
  • {@code GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(3 8,7 10))}
  • - *
+ *
  • POINT(6 10)
  • + *
  • MULTIPOLYGON(((1 1, 5 1, 1 5, 1 1),(2 2, 3 2, 3 3, 2 2)))
  • + *
  • GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(3 8,7 10))
  • + * } * * @param wkt The {@code BOX}, {@code POLYGON} or other kind of element to parse. * @throws IllegalArgumentException If the given string can not be parsed. @@ -258,21 +276,42 @@ public class GeneralEnvelope extends Arr /** * Sets the coordinate reference system in which the coordinate are given. - * This method does not reproject the envelope, and do not - * check if the envelope is contained in the new domain of validity. + * This method does not reproject the envelope, and does + * not check if the envelope is contained in the new domain of validity. * *

    If the envelope coordinates need to be transformed to the new CRS, consider * using {@link Envelopes#transform(Envelope, CoordinateReferenceSystem)} instead.

    * * @param crs The new coordinate reference system, or {@code null}. - * @throws MismatchedDimensionException if the specified CRS doesn't have the expected - * number of dimensions. + * @throws MismatchedDimensionException if the specified CRS doesn't have the expected number of dimensions. + * @throws IllegalStateException if a range of ordinate values in this envelope is compatible with the given CRS. + * See Envelope validation in class javadoc for more details. */ public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs) throws MismatchedDimensionException { ensureDimensionMatches("crs", getDimension(), crs); - this.crs = crs; + /* + * The check performed here shall be identical to ArrayEnvelope.verifyRanges(crs, ordinates) + * except that it may verify only a subset of the ordinate array and throws a different kind + * of exception in caseo of failure. + */ + if (crs != null) { + final int beginIndex = beginIndex(); + final int endIndex = endIndex(); + final int d = ordinates.length >>> 1; + for (int i=beginIndex; i upper) { + final int j = i - beginIndex; + if (!isWrapAround(crs, j)) { + throw new IllegalStateException(illegalRange(crs, j, lower, upper)); + } + } + } + } + this.crs = crs; // Set only on success. } /** @@ -282,6 +321,8 @@ public class GeneralEnvelope extends Arr * @param lower The limit in the direction of decreasing ordinate values. * @param upper The limit in the direction of increasing ordinate values. * @throws IndexOutOfBoundsException If the given index is out of bounds. + * @throws IllegalArgumentException If {@code lower > upper} and the axis range meaning at the given dimension + * is not "wraparound". See Envelope validation in class javadoc for more details. */ @Override // Must also be overridden in SubEnvelope public void setRange(final int dimension, final double lower, final double upper) @@ -289,6 +330,13 @@ public class GeneralEnvelope extends Arr { final int d = ordinates.length >>> 1; ensureValidIndex(d, dimension); + /* + * The check performed here shall be identical to ArrayEnvelope.verifyRanges(crs, ordinates), + * except that there is no loop. + */ + if (lower > upper && crs != null && !isWrapAround(crs, dimension)) { + throw new IllegalArgumentException(illegalRange(crs, dimension, lower, upper)); + } ordinates[dimension + d] = upper; ordinates[dimension] = lower; } @@ -299,14 +347,16 @@ public class GeneralEnvelope extends Arr * this {@linkplain #getDimension() envelope dimension}, and minimum shall not be greater * than maximum. * - *

    Example:

    + * {@example * (xmin, ymin, zmin, * xmax, ymax, zmax) + * } * * @param corners Ordinates of the new lower corner followed by the new upper corner. */ public void setEnvelope(final double... corners) { verifyArrayLength(ordinates.length >>> 1, corners); + verifyRanges(crs, corners); System.arraycopy(corners, 0, ordinates, 0, ordinates.length); } @@ -876,6 +926,8 @@ public class GeneralEnvelope extends Arr * or {@code false} if no change has been done. * @throws IllegalStateException If a upper ordinate value is less than a lower ordinate * value on an axis which does not have the {@code WRAPAROUND} range meaning. + * + * @see #toSimpleEnvelopes() */ public boolean simplify() throws IllegalStateException { boolean changed = false; Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/SubEnvelope.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/SubEnvelope.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/SubEnvelope.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/SubEnvelope.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -126,6 +126,13 @@ final class SubEnvelope extends GeneralE throws IndexOutOfBoundsException { ensureValidIndex(endIndex, dimension); + /* + * The check performed here shall be identical to the super-class method, which is itself + * identical to ArrayEnvelope.verifyRanges(crs, ordinates) except that there is no loop. + */ + if (lower > upper && crs != null && !isWrapAround(crs, dimension)) { + throw new IllegalArgumentException(illegalRange(crs, dimension, lower, upper)); + } dimension += beginIndex; ordinates[dimension + (ordinates.length >>> 1)] = upper; ordinates[dimension] = lower; @@ -138,6 +145,7 @@ final class SubEnvelope extends GeneralE public void setEnvelope(final double... corners) { final int dimension = getDimension(); verifyArrayLength(dimension, corners); + verifyRanges(crs, corners); final int d = ordinates.length >>> 1; System.arraycopy(corners, 0, ordinates, beginIndex, dimension); System.arraycopy(corners, dimension, ordinates, beginIndex + d, dimension); Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -334,17 +334,16 @@ public final class AxisDirections extend * @param direction The direction of the axis to search. * @return The dimension of the axis using the given direction or its opposite, or -1 if none. */ - public static int indexOf(final CoordinateSystem cs, final AxisDirection direction) { + public static int indexOfColinear(final CoordinateSystem cs, final AxisDirection direction) { int fallback = -1; if (cs != null) { - final AxisDirection opposite = opposite(direction); final int dimension = cs.getDimension(); for (int i=0; iCoordinate Transformation Services - * (OGC 01-009), which also defined the + * OGC 01-009 + * (Coordinate Transformation Services), which also defined the version 1 of * Well - * Known Text (WKT) format. This method is used for WKT parsing. + * Known Text format (WKT 1). This method is used for WKT 1 parsing. * * @param code The legacy vertical datum code. * @return The vertical datum type, or {@code null} if the code is unrecognized. Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Colors.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Colors.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Colors.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Colors.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -50,13 +50,15 @@ public class Colors implements Serializa private static final long serialVersionUID = 256160285861027191L; /** - * The immutable default set of colors. + * A map of colors for outputs to the {@link java.io.Console}. + * Those colors give better results on a black background. + * This map is immutable. * - * @see Symbols#DEFAULT + * @see FormattableObject#print() */ - public static final Colors DEFAULT = new Immutable(); + public static final Colors CONSOLE = new Immutable(); static { - final EnumMap map = DEFAULT.map; + final EnumMap map = CONSOLE.map; map.put(ElementKind.NUMBER, X364.FOREGROUND_YELLOW); map.put(ElementKind.INTEGER, X364.FOREGROUND_YELLOW); map.put(ElementKind.UNIT, X364.FOREGROUND_YELLOW); @@ -134,7 +136,7 @@ public class Colors implements Serializa } /** - * An immutable subclass of {@link Colors} for the {@link Colors#DEFAULT} constant + * An immutable subclass of {@link Colors} for the {@link Colors#CONSOLE} constant * or for the object to be used by {@link WKTFormat}. */ private static final class Immutable extends Colors { @@ -174,10 +176,10 @@ public class Colors implements Serializa } /** - * Replaces the deserialized instance by {@link #DEFAULT} one if possible. + * Replaces the deserialized instance by {@link #CONSOLE} one if possible. */ Object readResolve() throws ObjectStreamException { - return super.map.equals(DEFAULT.map) ? DEFAULT : this; + return super.map.equals(CONSOLE.map) ? CONSOLE : this; } } } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Convention.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Convention.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Convention.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Convention.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -20,11 +20,7 @@ import javax.measure.unit.Unit; import javax.measure.quantity.Angle; import javax.measure.quantity.Quantity; import org.opengis.metadata.citation.Citation; -import org.opengis.referencing.cs.CartesianCS; -import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.crs.GeocentricCRS; -import org.opengis.referencing.operation.OperationMethod; -import org.opengis.referencing.operation.CoordinateOperation; import org.apache.sis.util.Debug; import org.apache.sis.metadata.iso.citation.Citations; @@ -33,21 +29,26 @@ import static javax.measure.unit.NonSI.D /** * The convention to use for WKT formatting. - * This enumeration attempts to address some of the variability documented in the Frank Warmerdam's - * OGC WKT Coordinate System Issues page. - * The various conventions enumerated in this class differ mostly in: + * This enumeration specifies whether to use the Well Known Text format defined by ISO 19162 + * (also known as “WKT 2”), or whether to use the format previously defined in OGC 01-009 (referenced as “WKT 1”). + * + * {@section WKT 1 variants} + * The WKT 2 format should be parsed and formatted consistently by all softwares. + * But the WKT 1 format has been interpreted differently by various implementors. + * Apache SIS can adapt itself to different WKT variants, sometime automatically. But some aspects can not be guessed. + * One noticeable source of confusion is the unit of measurement of {@code PRIMEM[…]} and {@code PARAMETER[…]} elements: * *
      - *
    • Parameter names - for example a parameter named "Longitude of natural origin" - * according {@linkplain #EPSG} may be named "{@code central_meridian}" according {@linkplain #OGC} - * and "{@code NatOriginLong}" according {@linkplain #GEOTIFF GeoTIFF}.
    • - *
    • WKT syntax - for example {@linkplain #ORACLE Oracle} does not enclose Bursa-Wolf parameters in a - * {@code TOWGS84[…]} element.
    • - *
    • Unit of measurement - for example the unit of the Prime Meridian shall be the angular unit of the - * enclosing Geographic CRS according the {@linkplain #OGC} standard, but is restricted to decimal degrees by - * {@linkplain #ESRI}.
    • + *
    • The unit of the Prime Meridian shall be the angular unit of the enclosing Geographic CRS + * according the OGC 01-009 (Coordinate transformation services) specification.
    • + *
    • An older specification — Simple Features — was unclear on this matter and has been + * interpreted by many softwares as fixing the unit to decimal degrees.
    • *
    * + * Despite the first interpretation being specified by both OGC 01-009 and ISO 19162 standards, the second + * interpretation appears to be in wide use for WKT 1. Apache SIS uses the standard interpretation by default, + * but the {@link #WKT1_COMMON_UNITS} enumeration allows parsing and formatting using the older interpretation. + * * @author Martin Desruisseaux (Geomatys) * @since 0.4 (derived from geotk-3.20) * @version 0.4 @@ -58,219 +59,139 @@ import static javax.measure.unit.NonSI.D */ public enum Convention { /** - * The Open Geospatial consortium convention. + * The ISO 19162 format, also known as “WKT 2”. * This is the default convention for all WKT formatting in the Apache SIS library. - * Some worthy aspects to note: - * - *
      - *
    • For {@link GeocentricCRS}, this convention uses the legacy set of Cartesian axes. - * Those axes were defined in OGC 01-009 as Other, Easting and Northing - * in metres, where the "Other" axis is toward prime meridian.
    • - *
    - * - * @see Citations#OGC - * @see #toConformCS(CoordinateSystem) - */ - OGC(Citations.OGC, null, false), - - /** - * The European Petroleum Survey Group convention. - * This convention uses the most descriptive parameter and projection names. - * Some worthy aspects to note: * - *
      - *
    • For {@link GeocentricCRS}, this convention uses the new set of Cartesian axes. - * Those axes are defined in ISO 19111 as Geocentric X, Geocentric Y - * and Geocentric Z in metres.
    • - *
    + *

    Unless otherwise specified by {@link WKTFormat#setNameAuthority(Citation)}, when using + * this convention SIS will favor {@linkplain Citations#EPSG EPSG} definitions of projection + * and parameter names.

    + */ + WKT2(Citations.EPSG, false, false), + + /** + * The OGC 01-009 format, also known as “WKT 1”. + * A definition for this format is shown in Extended Backus Naur Form (EBNF) + * on GeoAPI. + * + *

    Unless otherwise specified by {@link WKTFormat#setNameAuthority(Citation)}, when using + * this convention SIS will favor {@linkplain Citations#OGC OGC} definitions of projection + * and parameter names.

    + * + * {@section Differences compared to WKT 2} + * WKT 1 and WKT 2 differ in their keywords and syntax, but also in more subtle ways regarding parameter + * and code list values. For {@link GeocentricCRS}, WKT 1 uses a legacy set of Cartesian axes which were + * defined in OGC 01-009. Those axes use the Other, Easting and Northing + * {@linkplain org.opengis.referencing.cs.AxisDirection axis directions} instead than the geocentric ones, + * as shown in the following table: * - * @see Citations#EPSG - * @see #toConformCS(CoordinateSystem) + * + * + * + * + * + *
    ISO 19111 OGC 01-009 Description
    Geocentric X Other Toward prime meridian
    Geocentric Y Easting Toward 90°E longitude
    Geocentric Z Northing Toward north pole
    */ - EPSG(Citations.EPSG, null, false) { - @Override - public CoordinateSystem toConformCS(CoordinateSystem cs) { - if (cs instanceof CartesianCS) { - cs = Legacy.replace((CartesianCS) cs, false); - } - return cs; - } - }, + WKT1(Citations.OGC, true, false), /** - * The ESRI convention. - * This convention is similar to the {@link #OGC} convention except in the following aspects: + * The Simple Feature format, also known as “WKT 1”. + * Simple Feature is anterior to OGC 01-009 and defines the same format, + * but was unclear about the unit of measurement for prime meridians and projection parameters. + * Consequently many implementations interpreted those angular units as fixed to degrees instead + * than being context-dependent. * + *

    This convention is identical to {@link #WKT1} except for the following aspects:

    *
      *
    • The angular units of {@code PRIMEM} and {@code PARAMETER} elements are always degrees, * no matter the units of the enclosing {@code GEOGCS} element.
    • - *
    • The {@code AXIS} elements are ignored at parsing time.
    • *
    • Unit names use American spelling instead than the international ones * (e.g. "meter" instead than "metre").
    • - *
    • At parsing time, the {@code AXIS} elements are ignored.
    • *
    - * - * @see Citations#ESRI */ - ESRI(Citations.ESRI, DEGREE_ANGLE, true), + WKT1_COMMON_UNITS(Citations.OGC, true, true), /** - * The Oracle convention. - * This convention is similar to the {@link #OGC} convention except in the following aspects: - * - *
      - *
    • The Bursa-Wolf parameters are inserted straight into the {@code DATUM} element, - * without enclosing them in a {@code TOWGS84} element.
    • - *
    • The {@code PROJECTION} names are {@linkplain CoordinateOperation Coordinate Operation} - * names instead than {@linkplain OperationMethod Operation Method} names.
    • - *
    • Unit names use American spelling instead than the international ones - * (e.g. "meter" instead than "metre").
    • - *
    + * A special convention for formatting objects as stored internally by Apache SIS. + * In the majority of cases, the result will be identical to the one we would get using the {@link #WKT2} convention. + * However in the particular case of map projections, the result may be quite different because of the way + * SIS separates the linear from the non-linear parameters. * - * @see Citations#ORACLE + *

    This convention is used only for debugging purpose.

    */ - ORACLE(Citations.ORACLE, null, true), + @Debug + INTERNAL(Citations.OGC, false, false); /** - * The NetCDF convention. - * This convention is similar to the {@link #OGC} convention except in the following aspects: - * - *
      - *
    • Parameter and projection names.
    • - *
    + * The default conventions. * - * @see Citations#NETCDF + * @todo Make final after we completed the migration from Geotk. */ - NETCDF(Citations.NETCDF, null, false), + static Convention DEFAULT = WKT2; /** - * The GeoTIFF convention. - * This convention is similar to the {@link #OGC} convention except in the following aspects: - * - *
      - *
    • Parameter and projection names.
    • - *
    - * - * @see Citations#GEOTIFF + * {@code true} for using WKT 1 syntax, or {@code false} for using WKT 2 syntax. */ - GEOTIFF(Citations.GEOTIFF, null, false), + final boolean isWKT1; /** - * The Proj.4 convention. - * This convention is similar to the {@link #OGC} convention except in the following aspects: - * + * {@code true} for a frequently-used convention about units instead than the standard one. *
      - *
    • Very short parameter and projection names.
    • - *
    • The angular units of {@code PRIMEM} and {@code PARAMETER} elements are always degrees, - * no matter the units of the enclosing {@code GEOGCS} element.
    • + *
    • If {@code true}, forces {@code PRIMEM} and {@code PARAMETER} angular units to degrees + * instead than inferring the unit from the context. The standard value is {@code false}, + * which means that the angular units are inferred from the context as required by the + * WKT 1 specification.
    • + *
    • If {@code true}, uses US unit names instead of the international names. + * For example Americans said {@code "meter"} instead of {@code "metre"}.
    • *
    * - * @see Citations#PROJ4 - */ - PROJ4(Citations.PROJ4, DEGREE_ANGLE, false), - - /** - * A special convention for formatting objects as stored internally by Apache SIS. - * In the majority of cases, the result will be identical to the one we would get using the {@link #OGC} convention. - * However in the particular case of map projections, the result may be quite different because of the way - * SIS separates the linear from the non-linear parameters. - * - *

    This convention is used only for debugging purpose.

    - */ - @Debug - INTERNAL(Citations.OGC, null, false) { - /** - * Declares publicly that this convention is defined by Apache SIS, despite the - * package-private {@link #authority} field being set to OGC for {@link Formatter} needs. - */ - @Override - public Citation getAuthority() { - return Citations.SIS; - } - - @Override - public CoordinateSystem toConformCS(final CoordinateSystem cs) { - return cs; // Prevent any modification on the internal CS. - } - }; - - /** - * If non-null, forces {@code PRIMEM} and {@code PARAMETER} angular units to this field - * value instead than inferring it from the context. The standard value is {@code null}, - * which means that the angular units are inferred from the context as required by the - * WKT specification. - * * @see #getForcedUnit(Class) */ - final Unit forcedAngularUnit; + final boolean commonUnits; /** - * {@code true} if the convention uses US unit names instead of the international names. - * For example Americans said [@code "meter"} instead of {@code "metre"}. + * The organization, standard or project to look for when fetching Map Projection parameter names. + * Should be one of the authorities known to {@link org.apache.sis.referencing.operation.provider}. */ - final boolean unitUS; - - /** - * The organization, standard or project to use for fetching Map Projection parameter names. - * Shall be one of the authorities known to {@link org.apache.sis.referencing.operation.provider}. - */ - final Citation authority; + private final Citation authority; /** * Creates a new enumeration value. */ - private Convention(final Citation authority, final Unit angularUnit, final boolean unitUS) { - this.authority = authority; - this.forcedAngularUnit = angularUnit; - this.unitUS = unitUS; + private Convention(final Citation authority, final boolean isWKT1, final boolean commonUnits) { + this.authority = authority; + this.isWKT1 = isWKT1; + this.commonUnits = commonUnits; } /** - * Returns the convention for the organization, standard or project specified by the given citation. + * Returns {@code true} if this convention is one of the WKT 1 variants. * - * @param authority The organization, standard or project for which to get the convention, or {@code null}. - * @param defaultConvention The default convention to return if none where found for the given citation. - * @return The convention, or {@code null} if no matching convention were found and the - * {@code defaultConvention} argument is {@code null}. - */ - public static Convention forCitation(final Citation authority, final Convention defaultConvention) { - if (authority != null) { - for (final Convention candidate : values()) { - if (Citations.identifierMatches(candidate.getAuthority(), authority)) { - return candidate; - } - } - } - return defaultConvention; + * @return {@code true} if this convention is one of the WKT 1 variants. + */ + public boolean isWKT1() { + return isWKT1; } /** - * Returns the convention for the organization, standard or project specified by the given identifier. + * Returns the default authority to look for when fetching Map Projection parameter names. + * The value returned by this method can be overwritten by {@link WKTFormat#setNameAuthority(Citation)}. * - * @param authority The organization, standard or project for which to get the convention, or {@code null}. - * @param defaultConvention The default convention to return if none where found for the given identifier. - * @return The convention, or {@code null} if no matching convention were found and the - * {@code defaultConvention} argument is {@code null}. - */ - public static Convention forIdentifier(final String authority, final Convention defaultConvention) { - if (authority != null) { - for (final Convention candidate : values()) { - if (Citations.identifierMatches(candidate.getAuthority(), authority)) { - return candidate; - } - } - } - return defaultConvention; - } - - /** - * Returns the citation for the organization, standard of project that defines this convention. + * {@example The following table shows the names given by various organizations or projects for the same projection: + * + * + * + * + * + * + *
    Authority Projection name
    EPSG Mercator (variant A)
    OGC Mercator_1SP
    GEOTIFF CT_Mercator
    } * - * @return The organization, standard or project that defines this convention. + * @return The organization, standard or project to look for when fetching Map Projection parameter names. * - * @see WKTFormat#getAuthority() + * @see WKTFormat#getNameAuthority() + * @see Citations#EPSG + * @see Citations#OGC */ - public Citation getAuthority() { + public Citation getNameAuthority() { return authority; } @@ -287,35 +208,11 @@ public enum Convention { */ @SuppressWarnings("unchecked") public Unit getForcedUnit(final Class quantity) { - if (quantity == Angle.class) { - return (Unit) forcedAngularUnit; + if (commonUnits) { + if (quantity == Angle.class) { + return (Unit) DEGREE_ANGLE; + } } return null; } - - /** - * Makes the given coordinate system conform to this convention. This method is used mostly - * for converting between the legacy (OGC 01-009) {@link GeocentricCRS} axis directions, - * and the new (ISO 19111) directions. Those directions are: - * - * - * - * - * - * - *
    ISO 19111 OGC 01-009
    Geocentric X Other
    Geocentric Y Easting
    Geocentric Z Northing
    - * - * @param cs The coordinate system. - * @return A coordinate system equivalent to the given one but with conform axis names, - * or the given {@code cs} if no change apply to the given coordinate system. - * - * @see #OGC - * @see #EPSG - */ - public CoordinateSystem toConformCS(CoordinateSystem cs) { - if (cs instanceof CartesianCS) { - cs = Legacy.replace((CartesianCS) cs, true); - } - return cs; - } } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/FormattableObject.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/FormattableObject.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/FormattableObject.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/FormattableObject.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -36,15 +36,14 @@ import org.apache.sis.internal.util.X364 * representation of this object:

    * *
      - *
    • {@link #toWKT()} returns a strictly compliant WKT or throw an exception.
    • - *
    • {@link #toString()} is like {@code toWKT()} with some rules relaxed in order - * to never throw exception.
    • + *
    • {@link #toWKT()} returns a strictly compliant WKT or throws {@link UnformattableObjectException} + * if this object contains elements not defined by the ISO 19162 standard.
    • + *
    • {@link #toString()} returns a WKT with some rules relaxed in order to never throw exception, + * using non-standard representation if necessary. In some cases {@code toString()} may also use + * an alternative text representation for better readability, for example a matrix instead of + * a list of {@code PARAMETER["elt_…", …]} elements for linear transforms.
    • *
    * - * The {@code toWKT()} method may throw {@link UnformattableObjectException} if an object - * contains elements not defined by the official standard, while {@code toString()} can - * fallback on a non-standard representation. - * * {@section Syntax coloring} * A convenience {@link #print()} method is provided, which is roughly equivalent to * {@code System.out.println(this)} except that syntax coloring is automatically applied @@ -90,25 +89,23 @@ public class FormattableObject { * @see org.opengis.referencing.IdentifiedObject#toWKT() */ public String toWKT() throws UnformattableObjectException { - return formatWKT(Convention.OGC, WKTFormat.DEFAULT_INDENTATION, false, true); + return formatWKT(Convention.DEFAULT, WKTFormat.DEFAULT_INDENTATION, false, true); } /** - * Returns a Well Known Text (WKT) using the default convention, symbols and indentation. + * Returns a Well Known Text (WKT) or an alternative text representation for this object. * If this object can not be represented in a standard way, then this method fallbacks on a non-standard * representation. * - * @return The Well Known Text (WKT) or a pseudo-WKT representation of this object. + * @return The Well Known Text (WKT) or an alternative representation of this object. */ @Override public String toString() { - return formatWKT(Convention.OGC, WKTFormat.DEFAULT_INDENTATION, false, false); + return formatWKT(Convention.DEFAULT, WKTFormat.DEFAULT_INDENTATION, false, false); } /** * Returns a Well Known Text (WKT) for this object using the specified convention. - * The convention is usually {@link Convention#OGC OGC}, but other common conventions are - * {@link Convention#GEOTIFF GEOTIFF} and {@link Convention#EPSG EPSG}. * The {@link Convention#INTERNAL INTERNAL} convention is a special value for debugging map projections. * * @param convention The WKT convention to use. @@ -126,12 +123,14 @@ public class FormattableObject { * the console supports the ANSI escape codes (a.k.a. X3.64), then a syntax coloring will be applied. * *

    This is a convenience method for debugging purpose and for console applications.

    + * + * @see Colors#CONSOLE */ @Debug public void print() { final Console console = System.console(); final PrintWriter out = (console != null) ? console.writer() : null; - final String wkt = formatWKT(Convention.OGC, WKTFormat.DEFAULT_INDENTATION, + final String wkt = formatWKT(Convention.DEFAULT, WKTFormat.DEFAULT_INDENTATION, (out != null) && X364.isAnsiSupported(), false); if (out != null) { out.println(wkt); @@ -144,7 +143,7 @@ public class FormattableObject { * Returns a WKT for this object using the specified convention. * If {@code strict} is true, then an exception is thrown if the WKT is not standard-compliant. * - * @param convention The convention for choosing WKT entities names. + * @param convention The convention for choosing WKT element names. * @param indentation The indentation to apply, or {@link WKTFormat#SINGLE_LINE}. * @param colorize {@code true} for applying syntax coloring, or {@code false} otherwise. * @param strict {@code true} if an exception shall be thrown for unformattable objects, @@ -160,7 +159,7 @@ public class FormattableObject { formatter = new Formatter(); } formatter.indentation = indentation; - formatter.colors = colorize ? Colors.DEFAULT : null; + formatter.colors = colorize ? Colors.CONSOLE : null; formatter.setConvention(convention, null); final String wkt; try { @@ -192,7 +191,7 @@ public class FormattableObject { * Formats the inner part of this Well Known Text (WKT) element into the given formatter. * This method is automatically invoked by {@link WKTFormat} when a formattable element is found. * - *

    Element keyword and authority code shall not be formatted here. + *

    Keywords and authority codes shall not be formatted here. * For example if this formattable element is for a {@code GEOGCS} element, * then this method shall write the content starting at the insertion point shows below:

    * Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -127,8 +127,8 @@ public class Formatter { /** * The preferred authority for objects or parameter names. * - * @see WKTFormat#getAuthority(Citation) - * @see WKTFormat#setAuthority(Citation) + * @see WKTFormat#getNameAuthority() + * @see WKTFormat#setNameAuthority(Citation) */ private Citation authority; @@ -191,7 +191,7 @@ public class Formatter { private boolean requestNewLine; /** - * {@code true} if the last formatted element was invalid WKT and shall be highlighted with syntatic coloration. + * {@code true} if the last formatted element was invalid WKT and shall be highlighted with syntactic coloration. * This field has no effect if {@link #colors} is null. This field is reset to {@code false} after the invalid * part has been processed by {@link #append(FormattableObject)}, in order to highlight only the first erroneous * element without clearing the {@link #invalidElement} value. @@ -218,7 +218,7 @@ public class Formatter { * Creates a new formatter instance with the default symbols, no syntax coloring and the default indentation. */ public Formatter() { - this(Convention.OGC, Symbols.DEFAULT, null, WKTFormat.DEFAULT_INDENTATION); + this(Convention.DEFAULT, Symbols.DEFAULT, null, WKTFormat.DEFAULT_INDENTATION); } /** @@ -250,7 +250,7 @@ public class Formatter { * This constructor helps to share some objects with {@link Parser}. */ Formatter(final Symbols symbols, final NumberFormat numberFormat) { - this.convention = Convention.OGC; + this.convention = Convention.DEFAULT; this.symbols = symbols; this.indentation = WKTFormat.DEFAULT_INDENTATION; this.numberFormat = numberFormat; // No clone needed. @@ -259,8 +259,7 @@ public class Formatter { } /** - * Returns the convention to use for formatting the WKT. The default convention is {@link Convention#OGC OGC}. - * A different convention will usually result in different parameter names, but may also change the WKT syntax. + * Returns the convention to use for formatting the WKT. The default is {@link Convention#WKT2}. * * @return The convention (never {@code null}). * @@ -278,30 +277,26 @@ public class Formatter { * @param authority The authority, or {@code null} for inferring it from the convention. */ final void setConvention(Convention convention, final Citation authority) { - if (convention == null) { - convention = Convention.forCitation(authority, Convention.OGC); - } this.convention = convention; - this.authority = (authority != null) ? authority : convention.authority; // NOT convention.getAuthority() + this.authority = (authority != null) ? authority : convention.getNameAuthority(); } /** * Returns the preferred name for the specified object. - * If the specified object contains a name from the preferred authority - * (usually {@linkplain org.apache.sis.metadata.iso.citation.Citations#OGC Open Geospatial}), - * then this name is returned. Otherwise, the first name found is returned. + * If the specified object contains a name from the preferred authority, then this name is returned. + * Otherwise, the first name found is returned. * - *

    The preferred authority can be set by the {@link WKTFormat#setAuthority(Citation)} method. + *

    The preferred authority can be set by the {@link WKTFormat#setNameAuthority(Citation)} method. * This is not necessarily the authority of the given {@linkplain IdentifiedObject#getName() object name}.

    * * {@example The EPSG name of the EPSG:6326 datum is "World Geodetic System 1984". - * However if the preferred authority is OGC (which is the case by default), then this method usually - * returns "WGS84" (the exact string to be returned depends on the object aliases).} + * However if the preferred authority is OGC, then this method usually returns "WGS84" + * (the exact string to be returned depends on the object aliases).} * * @param object The object to look for a preferred name. * @return The preferred name, or {@code null} if the given object has no name. * - * @see WKTFormat#getAuthority() + * @see WKTFormat#getNameAuthority() * @see IdentifiedObjects#getName(IdentifiedObject, Citation) */ public String getName(final IdentifiedObject object) { @@ -314,9 +309,8 @@ public class Formatter { /** * Returns the preferred identifier for the specified object. - * If the specified object contains an identifier from the preferred authority - * (usually {@linkplain org.apache.sis.metadata.iso.citation.Citations#OGC Open Geospatial}), - * then this identifier is returned. Otherwise, the first identifier is returned. + * If the specified object contains an identifier from the preferred authority, then this identifier is returned. + * Otherwise, the first identifier is returned. * If the specified object contains no identifier, then this method returns {@code null}. * * @param info The object to look for a preferred identifier, or {@code null} if none. @@ -548,8 +542,8 @@ public class Formatter { final int numCol = matrix.getNumCol(); final int openingBracket = symbols.getOpeningBracket(0); final int closingBracket = symbols.getClosingBracket(0); - final int openQuote = symbols.getOpenQuote(); - final int closeQuote = symbols.getCloseQuote(); + final int openQuote = symbols.getOpeningQuote(0); + final int closeQuote = symbols.getClosingQuote(0); final String separator = symbols.getSeparator(); final StringBuffer buffer = this.buffer; boolean columns = false; @@ -601,7 +595,7 @@ public class Formatter { if (contextUnit!=null && unit.isCompatible(contextUnit)) { unit = contextUnit; } else { - contextUnit = convention.forcedAngularUnit; + contextUnit = convention.getForcedUnit(Angle.class); if (contextUnit == null) { contextUnit = angularUnit; } @@ -710,7 +704,8 @@ public class Formatter { * Appends the given string as a quoted text. */ private void quote(final String text) { - buffer.appendCodePoint(symbols.getOpenQuote()).append(text).appendCodePoint(symbols.getCloseQuote()); + buffer.appendCodePoint(symbols.getOpeningQuote(0)).append(text) + .appendCodePoint(symbols.getClosingQuote(0)); } /** @@ -775,15 +770,15 @@ public class Formatter { appendSeparator(requestNewLine); buffer.append("UNIT").appendCodePoint(symbols.getOpeningBracket(0)); setColor(ElementKind.UNIT); - buffer.appendCodePoint(symbols.getOpenQuote()); + buffer.appendCodePoint(symbols.getOpeningQuote(0)); if (NonSI.DEGREE_ANGLE.equals(unit)) { buffer.append("degree"); } else if (SI.METRE.equals(unit)) { - buffer.append(convention.unitUS ? "meter" : "metre"); + buffer.append(convention.commonUnits ? "meter" : "metre"); } else { unitFormat.format(unit, buffer, dummy); } - buffer.appendCodePoint(symbols.getCloseQuote()); + buffer.appendCodePoint(symbols.getClosingQuote(0)); resetColor(); append(Units.toStandardUnit(unit)); buffer.appendCodePoint(symbols.getClosingBracket(0)); @@ -829,10 +824,9 @@ public class Formatter { } /** - * Returns {@code true} if the WKT written by this formatter is not strictly compliant to the - * WKT - * specification. This method returns {@code true} if {@link #setInvalidWKT(IdentifiedObject)} - * has been invoked at least once. The action to take regarding invalid WKT is caller-dependent. + * Returns {@code true} if the WKT written by this formatter is not strictly compliant to the WKT specification. + * This method returns {@code true} if {@link #setInvalidWKT(IdentifiedObject)} has been invoked at least once. + * The action to take regarding invalid WKT is caller-dependent. * For example {@link FormattableObject#toString()} will accepts loose WKT formatting and ignore * this flag, while {@link FormattableObject#toWKT()} requires strict WKT formatting and will * thrown an exception if this flag is set. Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java?rev=1562800&r1=1562799&r2=1562800&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java [UTF-8] Thu Jan 30 12:37:10 2014 @@ -34,15 +34,16 @@ import static org.apache.sis.util.Argume *
  • An English locale for {@linkplain java.text.DecimalFormatSymbols decimal format symbols}.
  • *
  • Square brackets, as in {@code DATUM["WGS84"]}. An alternative allowed by the WKT * specification is curly brackets as in {@code DATUM("WGS84")}.
  • - *
  • English quotation mark ({@code '"'}).
  • + *
  • English quotation mark ({@code '"'}). SIS also accepts {@code “…”} quotation marks + * for more readable {@link String} constants in Java code.
  • *
  • Coma separator followed by a space ({@code ", "}).
  • * * - * {@note Relationship between Symbols locale and WKTFormat locale
    - * The WKTFormat Locale property specifies the language to use when - * formatting InternationalString instances. This can be set to any value. - * On the contrary, the Locale property of this Symbols class controls - * the decimal format symbols and is very rarely set to an other locale than an English one.} + * {@section Relationship between Symbols locale and WKTFormat locale} + * The {@link WKTFormat#getLocale()} property specifies the language to use when formatting + * {@link org.opengis.util.InternationalString} instances. This can be set to any value. + * On the contrary, the {@code Locale} property of this {@code Symbols} class controls + * the decimal format symbols and is very rarely set to an other locale than an English one. * * @author Martin Desruisseaux (IRD) * @since 0.4 (derived from geotk-2.1) @@ -62,19 +63,19 @@ public class Symbols implements Localize * A set of symbols with values between square brackets, like {@code DATUM["WGS84"]}. * This is the most frequently used WKT format. */ - public static final Symbols SQUARE_BRACKETS = new Immutable('[', ']', '(', ')'); + public static final Symbols SQUARE_BRACKETS = new Immutable( + new int[] {'[', ']', '(', ')'}, new int[] {'"', '"', '“', '”'}); /** * A set of symbols with values between parentheses, like {@code DATUM("WGS84")}. * This is a less frequently used but legal WKT format. */ - public static final Symbols CURLY_BRACKETS = new Immutable('(', ')', '[', ']'); + public static final Symbols CURLY_BRACKETS = new Immutable( + new int[] {'(', ')', '[', ']'}, SQUARE_BRACKETS.quotes); /** * The default set of symbols, as documented in the class javadoc. * This is currently set to {@link #SQUARE_BRACKETS}. - * - * @see Colors#DEFAULT */ public static final Symbols DEFAULT = SQUARE_BRACKETS; @@ -100,16 +101,23 @@ public class Symbols implements Localize private int[] brackets; /** - * The character (as Unicode code point) used for opening ({@code openSequence}) - * or closing ({@code closeSequence}) an array or enumeration. + * List of characters (as Unicode code point) used for opening or closing a quoted text. + * The array shall comply to the following restrictions: + * + *
      + *
    • The characters at index 0 and 1 are the preferred opening and closing quotes respectively.
    • + *
    • For each even index i, {@code quotes[i+1]} is the closing quote matching {@code quotes[i]}.
    • + *
    + * + * Both opening and closing quotes are usually {@code '"'}. */ - private int openSequence, closeSequence; + private int[] quotes; /** - * The character (as Unicode code point) used for opening ({@code openQuote}) or - * closing ({@code closeQuote}) a quoted text. This is usually {@code '"'}. + * The character (as Unicode code point) used for opening ({@code openSequence}) + * or closing ({@code closeSequence}) an array or enumeration. */ - private int openQuote, closeQuote; + private int openSequence, closeSequence; /** * The string used as a separator in a list of values. This is usually {@code ", "}, @@ -132,10 +140,9 @@ public class Symbols implements Localize public Symbols(final Symbols symbols) { locale = symbols.locale; brackets = symbols.brackets; + quotes = symbols.quotes; openSequence = symbols.openSequence; closeSequence = symbols.closeSequence; - openQuote = symbols.openQuote; - closeQuote = symbols.closeQuote; separator = symbols.separator; } @@ -143,13 +150,12 @@ public class Symbols implements Localize * Constructor reserved to {@link #SQUARE_BRACKETS} and {@link #CURLY_BRACKETS} constants. * The given array is stored by reference - it is not cloned. */ - private Symbols(final int[] brackets) { + private Symbols(final int[] brackets, final int[] quotes) { this.locale = Locale.US; this.brackets = brackets; + this.quotes = quotes; this.openSequence = '{'; this.closeSequence = '}'; - this.openQuote = '"'; - this.closeQuote = '"'; this.separator = ", "; } @@ -164,10 +170,10 @@ public class Symbols implements Localize /** * Constructor reserved to {@link Symbols#SQUARE_BRACKETS} and {@link Symbols#CURLY_BRACKETS} constants. - * The given array is stored by reference - it is not cloned. + * The given arrays are stored by reference - they are not cloned. */ - Immutable(final int... brackets) { - super(brackets); + Immutable(final int[] brackets, final int[] quotes) { + super(brackets, quotes); } /** @@ -246,12 +252,15 @@ public class Symbols implements Localize /** * Returns the number of paired brackets. For example if the WKT parser accepts both the - * {@code [ ]} and {@code ( )} paired brackets, then this method returns 2. + * {@code […]} and {@code (…)} bracket pairs, then this method returns 2. * * @return The number of bracket pairs. + * + * @see #getOpeningBracket(int) + * @see #getClosingBracket(int) */ - public final int getNumPairedBracket() { - return brackets.length / 2; + public final int getNumPairedBrackets() { + return brackets.length >>> 1; } /** @@ -259,12 +268,12 @@ public class Symbols implements Localize * Index 0 stands for the default bracket used at formatting time. * All other index are for optional brackets accepted at parsing time. * - * @param index Index of the opening bracket to get, from 0 to {@link #getNumPairedBracket()} exclusive. + * @param index Index of the opening bracket to get, from 0 to {@link #getNumPairedBrackets()} exclusive. * @return The opening bracket at the given index, as a Unicode code point. * @throws IndexOutOfBoundsException if the given index is out of bounds. */ public final int getOpeningBracket(final int index) { - return brackets[index*2]; + return brackets[index << 1]; } /** @@ -272,47 +281,116 @@ public class Symbols implements Localize * Index 0 stands for the default bracket used at formatting time. * All other index are for optional brackets accepted at parsing time. * - * @param index Index of the closing bracket to get, from 0 to {@link #getNumPairedBracket()} exclusive. + * @param index Index of the closing bracket to get, from 0 to {@link #getNumPairedBrackets()} exclusive. * @return The closing bracket at the given index, as a Unicode code point. * @throws IndexOutOfBoundsException if the given index is out of bounds. */ public final int getClosingBracket(final int index) { - return brackets[index*2 + 1]; + return brackets[(index << 1) | 1]; } /** - * Sets the opening and closing brackets to the given characters. - * The given arrays shall comply to the following constraints: + * Sets the opening and closing brackets to the given pairs. + * Each string shall contain exactly two code points (usually two characters). + * The first code point is taken as the opening bracket, and the second code point as the closing bracket. * - *
      - *
    • The two arrays shall be non-empty and have the same length.
    • - *
    • The characters at index 0 are the preferred opening and closing brackets.
    • - *
    • For each index i, {@code closingBrackets[i]} is the closing bracket - * matching {@code openingBrackets[i]}.
    • - *
    + * {@example The following code will instruct the WKT formatter to use the (…) pair of brackets + * at formatting time, but still accept the more common […] pair of brackets at parsing + * time: + * + *
    setPairedBrackets("()", "[]");
    } * - * @param openingBrackets The opening brackets, as a Unicode code point. - * @param closingBrackets The closing brackets matching the opening ones. + * @param preferred The preferred pair of opening and closing quotes, used at formatting time. + * @param alternatives Alternative pairs of opening and closing quotes accepted at parsing time. */ - public void setBrackets(final int[] openingBrackets, final int[] closingBrackets) { + public void setPairedBrackets(final String preferred, final String... alternatives) { checkWritePermission(); - ensureNonNull("openingBrackets", openingBrackets); - ensureNonNull("closingBrackets", closingBrackets); - final int length = openingBrackets.length; - if (closingBrackets.length != length) { - throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedArrayLengths)); - } - if (length == 0) { - throw new IllegalArgumentException(Errors.format(Errors.Keys.EmptyArgument_1, "openingBrackets")); - } - final int[] brackets = new int[length * 2]; - for (int i=0,j=0; i>> 1; + } + + /** + * Returns the opening quote character at the given index. + * Index 0 stands for the default quote used at formatting time, which is usually {@code '"'}. + * All other index are for optional quotes accepted at parsing time. + * + * @param index Index of the opening quote to get, from 0 to {@link #getNumPairedQuotes()} exclusive. + * @return The opening quote at the given index, as a Unicode code point. + * @throws IndexOutOfBoundsException if the given index is out of bounds. + */ + public final int getOpeningQuote(final int index) { + return quotes[index << 1]; + } + + /** + * Returns the closing quote character at the given index. + * Index 0 stands for the default quote used at formatting time, which is usually {@code '"'}. + * All other index are for optional quotes accepted at parsing time. + * + * @param index Index of the closing quote to get, from 0 to {@link #getNumPairedQuotes()} exclusive. + * @return The closing quote at the given index, as a Unicode code point. + * @throws IndexOutOfBoundsException if the given index is out of bounds. + */ + public final int getClosingQuote(final int index) { + return quotes[(index << 1) | 1]; + } + + /** + * Sets the opening and closing quotes to the given pairs. + * Each string shall contain exactly two code points (usually two characters). + * The first code point is taken as the opening quote, and the second code point as the closing quote. + * + * {@example The following code will instruct the WKT formatter to use the prettier “…” + * quotation marks at formatting time (especially useful for String constants + * in Java code), but still accept the standard "…" quotation marks at parsing + * time: + * + *
    setPairedQuotes("“”", "\"\"");
    } + * + * @param preferred The preferred pair of opening and closing quotes, used at formatting time. + * @param alternatives Alternative pairs of opening and closing quotes accepted at parsing time. + */ + public void setPairedQuotes(final String preferred, final String... alternatives) { + checkWritePermission(); + quotes = toCodePoints(preferred, alternatives); + } + + /** + * Packs the given pairs of bracket or quotes in a single array of code points. + * This method also verifies arguments validity. + */ + private static int[] toCodePoints(final String preferred, final String[] alternatives) { + ensureNonNull("preferred", preferred); + final int n = (alternatives != null) ? alternatives.length : 0; + final int[] array = new int[(n+1) * 2]; + String name = "preferred"; + String pair = preferred; + int i=0, j=0; + while (true) { + if (pair.codePointCount(0, pair.length()) != 2) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, name, pair)); + } + final int c = pair.codePointAt(0); + ensureValidUnicodeCodePoint(name, array[j++] = c); + ensureValidUnicodeCodePoint(name, array[j++] = pair.codePointAt(Character.charCount(c))); + if (i >= n) { + break; + } + ensureNonNullElement(name = "alternatives", i, pair = alternatives[i++]); } - this.brackets = brackets; // Store only on success. + return array; } /** @@ -350,38 +428,6 @@ public class Symbols implements Localize } /** - * Returns the character used for opening a quoted text. This is usually {@code '"'}. - * - * @return The character used for opening a quoted text, as a Unicode code point. - */ - public final int getOpenQuote() { - return openQuote; - } - - /** - * Returns the character used for closing a quoted text. This is usually {@code '"'}. - * - * @return The character used for closing a quoted text, as a Unicode code point. - */ - public final int getCloseQuote() { - return closeQuote; - } - - /** - * Sets the characters used for opening and closing a quoted text. - * - * @param openQuote The character for opening a quoted text, as a Unicode code point. - * @param closeQuote The character for closing a quoted text, as a Unicode code point. - */ - public void setQuotes(final int openQuote, final int closeQuote) { - checkWritePermission(); - ensureValidUnicodeCodePoint("openQuote", openQuote); - ensureValidUnicodeCodePoint("closeQuote", closeQuote); - this.openQuote = openQuote; - this.closeQuote = closeQuote; - } - - /** * Returns the string used as a separator in a list of values. This is usually {@code ", "}, * but may be different if a non-English locale is used for formatting numbers. * @@ -428,8 +474,8 @@ public class Symbols implements Localize * *
      *
    • The search is case-insensitive.
    • - *
    • Characters between the {@linkplain #getOpenQuote() open quote} and the - * {@linkplain #getCloseQuote() close quote} are ignored.
    • + *
    • Characters between {@linkplain #getOpeningQuote(int) opening quotes} and + * {@linkplain #getClosingQuote(int) closing quotes} are ignored.
    • *
    • The element found in the given WKT can not be preceded by other * {@linkplain Character#isUnicodeIdentifierPart(int) Unicode identifier characters}.
    • *
    • The element found in the given WKT must be followed, ignoring space, by an @@ -457,12 +503,12 @@ public class Symbols implements Localize * Invoking this method is equivalent to invoking * {@linkplain #containsElement(CharSequence, String) containsElement}(wkt, "AXIS"). * - *

      The check for axis elements is of particular interest because the axis order is a frequent cause + * {@section Use case} + * The check for axis elements is of particular interest because the axis order is a frequent cause * of confusion when processing geographic data. Some applications just ignore any declared axis order * in favor of their own hard-coded (longitude, latitude) axis order. * Consequently, the presence of {@code AXIS[…]} elements in a WKT is an indication that the encoded * object may not be understood as intended by some external softwares. - * See for example {@link Convention#ESRI}.

      * * @param wkt The WKT to inspect. * @return {@code true} if the given WKT contains at least one instance of the {@code AXIS[…]} element. @@ -480,12 +526,22 @@ public class Symbols implements Localize * @param offset The index to start the search from. */ private boolean containsElement(final CharSequence wkt, final String element, int offset) { + final int[] quotes = this.quotes; final int length = wkt.length(); boolean isQuoting = false; + int closeQuote = 0; while (offset < length) { int c = Character.codePointAt(wkt, offset); - if (c == (isQuoting ? closeQuote : openQuote)) { - isQuoting = !isQuoting; + if (closeQuote != 0) { + if (c == closeQuote) { + isQuoting = false; + } + } else for (int i=0; i