Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java?rev=1686282&r1=1686281&r2=1686282&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] Thu Jun 18 17:38:16 2015
@@ -21,12 +21,14 @@ import java.util.List;
import java.util.Locale;
import java.util.HashMap;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.ParseException;
import javax.measure.unit.Unit;
+import javax.measure.unit.UnitFormat;
import javax.measure.unit.SI;
import javax.measure.unit.NonSI;
import javax.measure.quantity.Angle;
@@ -36,9 +38,9 @@ import javax.measure.quantity.Duration;
import org.opengis.util.Factory;
import org.opengis.metadata.Identifier;
-import org.opengis.metadata.citation.Citation;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.ReferenceSystem;
import org.opengis.referencing.ObjectFactory;
import org.opengis.util.FactoryException;
@@ -52,6 +54,11 @@ import org.opengis.referencing.operation
import org.apache.sis.measure.Units;
import org.apache.sis.metadata.iso.ImmutableIdentifier;
import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.metadata.iso.extent.DefaultExtent;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicDescription;
+import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
+import org.apache.sis.metadata.iso.extent.DefaultTemporalExtent;
import org.apache.sis.internal.metadata.AxisNames;
import org.apache.sis.internal.metadata.WKTKeywords;
import org.apache.sis.internal.metadata.VerticalDatumTypes;
@@ -71,21 +78,6 @@ import static java.util.Collections.sing
* {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform Math Transform} objects.
* Note that math transforms are part of the WKT 1 {@code "FITTED_CS"} element.
*
- * <div class="section">Default axis names</div>
- * The WKT 1 specification defined axis names different than the ISO 19111 ones.
- * This parser replaces the WKT 1 names by the ISO names and abbreviations when possible.
- *
- * <table class="sis">
- * <tr><th>CRS type</th> <th>WKT1 names</th> <th>ISO abbreviations</th></tr>
- * <tr><td>Geographic</td> <td>Lon, Lat</td> <td>λ, φ</td></tr>
- * <tr><td>Vertical</td> <td>H</td> <td>h</td></tr>
- * <tr><td>Projected</td> <td>X, Y</td> <td>x, y</td></tr>
- * <tr><td>Geocentric</td> <td>X, Y, Z</td> <td>X, Y, Z</td></tr>
- * </table>
- *
- * The default behavior is to use the ISO identifiers.
- * This behavior can be changed by setting the parsing conventions to {@link Convention#WKT1}.
- *
* @author Rémi Eve (IRD)
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.6
@@ -94,6 +86,23 @@ import static java.util.Collections.sing
*/
final class GeodeticObjectParser extends MathTransformParser {
/**
+ * The keywords of unit elements. Most frequently used keywords should be first.
+ */
+ private static final String[] UNIT_KEYWORDS = {
+ WKTKeywords.Unit, // Ignored since it does not allow us to know the quantity dimension.
+ WKTKeywords.LengthUnit, WKTKeywords.AngleUnit, WKTKeywords.ScaleUnit, WKTKeywords.TimeUnit,
+ WKTKeywords.ParametricUnit // Ignored for the same reason than "Unit".
+ };
+
+ /**
+ * The base unit associated to the {@link #UNIT_KEYWORDS}, ignoring {@link WKTKeywords#Unit}.
+ * For each {@code UNIT_KEYWORDS[i]} element, the associated base unit is {@code BASE_UNIT[i]}.
+ */
+ private static final Unit<?>[] BASE_UNITS = {
+ SI.METRE, SI.RADIAN, Unit.ONE, SI.SECOND
+ };
+
+ /**
* The names of the 7 parameters in a {@code TOWGS84[…]} element.
* Those names are derived from the <cite>Well Known Text</cite> (WKT) version 1 specification.
* They are not the same than the {@link org.apache.sis.referencing.datum.BursaWolfParameters}
@@ -134,31 +143,41 @@ final class GeodeticObjectParser extends
* <li>{@link Convention#WKT1_COMMON_UNITS} means that {@code PRIMEM} and {@code PARAMETER} angular units
* need to be forced to {@code NonSI.DEGREE_ANGLE} instead than inferred from the context.
* Note that this rule does not apply to {@code AXIS} elements.</li>
+ *
+ * <li>{@link Convention#WKT1_IGNORE_AXES} means that axes should be parsed only for verifying the syntax,
+ * but otherwise parsing should behave as if axes were not declared.</li>
* </ul>
*/
private final Convention convention;
/**
- * {@code true} if {@code AXIS[...]} elements should be ignored.
- * This is sometime used for simulating a "force longitude first axis order" behavior.
- * This is also used for compatibility with softwares that ignore axis elements.
+ * A map of properties to be given to the factory constructor methods.
+ * This map will be recycled for each object to be parsed.
+ */
+ private final Map<String,Object> properties = new HashMap<String,Object>(4);
+
+ /**
+ * The last vertical CRS found during the parsing, or {@code null} if none.
+ * This information is needed for creating {@link DefaultVerticalExtent} instances.
*
- * <p>Note that {@code AXIS} elements still need to be well formed even when this flag is set to {@code true}.
- * Malformed axis elements will continue to cause a {@link ParseException} despite their content being ignored.</p>
+ * <p>ISO 19162 said that we should have at most one vertical CRS per WKT. Apache SIS does
+ * not enforce this constraint, but if a WKT contains more than one vertical CRS then the
+ * instance used for completing the {@link DefaultVerticalExtent} instances is unspecified.</p>
*/
- private final boolean isAxisIgnored;
+ private transient VerticalCRS verticalCRS;
/**
- * A map of properties to be given the factory constructor methods.
- * This map will be recycled for each object to be parsed.
+ * A chained list of temporary information needed for completing the construction of {@link DefaultVerticalExtent}
+ * instances. In particular, stores the unit of measurement until the {@link VerticalCRS} instance to associate to
+ * the extents become known.
*/
- private final Map<String,Object> properties = new HashMap<String,Object>(4);
+ private transient VerticalInfo verticalElements;
/**
* Creates a parser using the default set of symbols and factories.
*/
public GeodeticObjectParser() {
- this(Symbols.getDefault(), null, null, Convention.DEFAULT, false, null, null);
+ this(Symbols.getDefault(), null, null, null, Convention.DEFAULT, null, null);
}
/**
@@ -178,14 +197,13 @@ final class GeodeticObjectParser extends
public GeodeticObjectParser(final Map<String,?> defaultProperties,
final ObjectFactory factories, final MathTransformFactory mtFactory)
{
- super(Symbols.getDefault(), null, null, mtFactory, (Locale) defaultProperties.get(Errors.LOCALE_KEY));
- crsFactory = (CRSFactory) factories;
- csFactory = (CSFactory) factories;
- datumFactory = (DatumFactory) factories;
- referencing = ReferencingServices.getInstance();
- opFactory = referencing.getCoordinateOperationFactory(defaultProperties, mtFactory);
- convention = Convention.DEFAULT;
- isAxisIgnored = false;
+ super(Symbols.getDefault(), null, null, null, mtFactory, (Locale) defaultProperties.get(Errors.LOCALE_KEY));
+ crsFactory = (CRSFactory) factories;
+ csFactory = (CSFactory) factories;
+ datumFactory = (DatumFactory) factories;
+ referencing = ReferencingServices.getInstance();
+ opFactory = referencing.getCoordinateOperationFactory(defaultProperties, mtFactory);
+ convention = Convention.DEFAULT;
}
/**
@@ -195,23 +213,22 @@ final class GeodeticObjectParser extends
* @param symbols The set of symbols to use.
* @param numberFormat The number format provided by {@link WKTFormat}, or {@code null} for a default format.
* @param dateFormat The date format provided by {@link WKTFormat}, or {@code null} for a default format.
+ * @param unitFormat The unit format provided by {@link WKTFormat}, or {@code null} for a default format.
* @param convention The WKT convention to use.
- * @param isAxisIgnored {@code true} if {@code AXIS} elements should be ignored.
* @param errorLocale The locale for error messages (not for parsing), or {@code null} for the system default.
* @param factories On input, the factories to use. On output, the factories used. Can be null.
*/
GeodeticObjectParser(final Symbols symbols, final NumberFormat numberFormat, final DateFormat dateFormat,
- final Convention convention, final boolean isAxisIgnored, final Locale errorLocale,
+ final UnitFormat unitFormat, final Convention convention, final Locale errorLocale,
final Map<Class<?>,Factory> factories)
{
- super(symbols, numberFormat, dateFormat, getFactory(MathTransformFactory.class, factories), errorLocale);
+ super(symbols, numberFormat, dateFormat, unitFormat, getFactory(MathTransformFactory.class, factories), errorLocale);
crsFactory = getFactory(CRSFactory.class, factories);
csFactory = getFactory(CSFactory.class, factories);
datumFactory = getFactory(DatumFactory.class, factories);
referencing = ReferencingServices.getInstance();
opFactory = referencing.getCoordinateOperationFactory(null, mtFactory);
this.convention = convention;
- this.isAxisIgnored = isAxisIgnored;
}
/**
@@ -240,11 +257,38 @@ final class GeodeticObjectParser extends
*/
@Override
public Object parseObject(final String text, final ParsePosition position) throws ParseException {
+ final Object object;
try {
- return super.parseObject(text, position);
+ object = super.parseObject(text, position);
+ /*
+ * After parsing the object, we may have been unable to set the VerticalCRS of VerticalExtent instances.
+ * First, try to set a default VerticalCRS for Mean Sea Level Height in metres. In the majority of cases
+ * that should be enough. If not (typically because the vertical extent uses other unit than metre), try
+ * to create a new CRS using the unit declared in the WKT.
+ */
+ if (verticalElements != null) {
+ Exception ex = null;
+ try {
+ verticalElements = verticalElements.resolve(referencing.getMSLH()); // Optional operation.
+ } catch (UnsupportedOperationException e) {
+ ex = e;
+ }
+ if (verticalElements != null) try {
+ verticalElements = verticalElements.complete(crsFactory, csFactory);
+ } catch (FactoryException e) {
+ if (ex == null) ex = e;
+ }
+ if (verticalElements != null) {
+ warning(Errors.formatInternational(Errors.Keys.CanNotAssignUnitToDimension_2,
+ WKTKeywords.VerticalExtent, verticalElements.unit), ex);
+ }
+ }
} finally {
+ verticalCRS = null;
+ verticalElements = null;
properties.clear(); // for letting the garbage collector do its work.
}
+ return object;
}
/**
@@ -264,21 +308,20 @@ final class GeodeticObjectParser extends
if (value != null) {
return value;
}
- String keyword = WKTKeywords.GeogCS;
- final Object child = element.peek();
- if (child instanceof Element) {
- keyword = ((Element) child).keyword;
- if (keyword != null) {
- if (keyword.equalsIgnoreCase(WKTKeywords.Axis)) return parseAxis (element, false, SI.METRE, true);
- if (keyword.equalsIgnoreCase(WKTKeywords.PrimeM)) return parsePrimem (element, NonSI.DEGREE_ANGLE);
- if (keyword.equalsIgnoreCase(WKTKeywords.ToWGS84)) return parseToWGS84 (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Spheroid)) return parseSpheroid (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Vert_Datum)) return parseVertDatum (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Local_Datum)) return parseLocalDatum(element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Datum)) return parseDatum (element, referencing.getGreenwich());
- }
+ Object object;
+ if ((object = parseAxis (FIRST, element, false, SI.METRE)) == null &&
+ (object = parsePrimeMeridian (FIRST, element, NonSI.DEGREE_ANGLE)) == null &&
+ (object = parseDatum (FIRST, element, null)) == null &&
+ (object = parseEllipsoid (FIRST, element)) == null &&
+ (object = parseToWGS84 (FIRST, element)) == null &&
+ (object = parseVerticalDatum (FIRST, element)) == null &&
+ (object = parseTimeDatum (FIRST, element)) == null &&
+ (object = parseEngineeringDatum (FIRST, element)) == null &&
+ (object = parseProjection (FIRST, element, SI.METRE, NonSI.DEGREE_ANGLE)) == null)
+ {
+ throw element.missingOrUnknownComponent(WKTKeywords.GeodeticCRS);
}
- throw element.keywordNotFound(keyword, keyword == WKTKeywords.GeogCS);
+ return object;
}
/**
@@ -292,30 +335,27 @@ final class GeodeticObjectParser extends
private CoordinateReferenceSystem parseCoordinateReferenceSystem(final Element element, final boolean mandatory)
throws ParseException
{
- String keyword = WKTKeywords.GeogCS;
- final Object child = element.peek();
- if (child instanceof Element) {
- keyword = ((Element) child).keyword;
- if (keyword != null) {
- if (keyword.equalsIgnoreCase(WKTKeywords.GeogCS)) return parseGeoGCS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.ProjCS)) return parseProjCS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.GeocCS)) return parseGeoCCS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Vert_CS)) return parseVertCS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.TimeCRS)) return parseTimeCRS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Local_CS)) return parseLocalCS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Compd_CS)) return parseCompdCS (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Fitted_CS)) return parseFittedCS(element);
+ CoordinateReferenceSystem crs;
+ if ((crs = parseGeographicCRS (FIRST, element)) == null &&
+ (crs = parseProjectedCRS (FIRST, element)) == null &&
+ (crs = parseGeocentricCRS (FIRST, element)) == null &&
+ (crs = parseVerticalCRS (FIRST, element)) == null &&
+ (crs = parseTimeCRS (FIRST, element)) == null &&
+ (crs = parseEngineeringCRS (FIRST, element)) == null &&
+ (crs = parseCompoundCRS (FIRST, element)) == null &&
+ (crs = parseFittedCS (FIRST, element)) == null)
+ {
+ if (mandatory) {
+ throw element.missingOrUnknownComponent(WKTKeywords.GeodeticCRS);
}
}
- if (mandatory) {
- throw element.keywordNotFound(keyword, keyword == WKTKeywords.GeogCS);
- }
- return null;
+ return crs;
}
/**
- * Parses an <strong>optional</strong> {@code "AUTHORITY"} element.
- * This element has the following pattern:
+ * Parses an <strong>optional</strong> metadata elements and close.
+ * This include elements like {@code "SCOPE"}, {@code "ID"} (WKT 2) or {@code "AUTHORITY"} (WKT 1).
+ * This WKT 1 element has the following pattern:
*
* {@preformat text
* AUTHORITY["<name>", "<code>"]
@@ -324,19 +364,31 @@ final class GeodeticObjectParser extends
* @param parent The parent element.
* @param name The name of the parent object being parsed, either a {@link String} or {@link Identifier} instance.
* @return A properties map with the parent name and the optional authority code.
- * @throws ParseException if the {@code "AUTHORITY"} can not be parsed.
+ * @throws ParseException if an element can not be parsed.
*/
- private Map<String,Object> parseAuthorityAndClose(final Element parent, Object name) throws ParseException {
+ private Map<String,Object> parseMetadataAndClose(final Element parent, Object name) throws ParseException {
assert (name instanceof String) || (name instanceof Identifier);
properties.clear();
properties.put(IdentifiedObject.NAME_KEY, name);
- final Element element = parent.pullOptionalElement(WKTKeywords.Authority);
- if (element != null) {
- final String auth = element.pullString("name");
- final String code = element.pullObject("code").toString(); // Accepts Integer as well as String.
+ Element element;
+ while ((element = parent.pullElement(OPTIONAL, WKTKeywords.Id, WKTKeywords.Authority)) != null) {
+ final String codeSpace = element.pullString("name");
+ final String code = element.pullObject("code").toString(); // Accepts Integer as well as String.
+ final Object version = element.pullOptional(Object.class); // Accepts Number as well as String.
+ final Element citation = element.pullElement(OPTIONAL, WKTKeywords.Citation);
+ final String authority;
+ if (citation != null) {
+ authority = citation.pullString("authority");
+ citation.close(ignoredElements);
+ } else {
+ authority = codeSpace;
+ }
+ final Element uri = element.pullElement(OPTIONAL, WKTKeywords.URI);
+ if (uri != null) {
+ uri.pullString("URI"); // TODO: not yet stored, since often redundant with other informations.
+ uri.close(ignoredElements);
+ }
element.close(ignoredElements);
- final Citation authority = Citations.fromName(auth);
- properties.put(IdentifiedObject.IDENTIFIERS_KEY, new ImmutableIdentifier(authority, auth, code));
/*
* Note: we could be tempted to assign the authority to the name as well, like below:
*
@@ -350,35 +402,166 @@ final class GeodeticObjectParser extends
* (for example "WGS84" for the datum instead than "World Geodetic System 1984"),
* so the name in WKT is often not compliant with the name actually defined by the authority.
*/
+ final ImmutableIdentifier id = new ImmutableIdentifier(Citations.fromName(authority),
+ codeSpace, code, (version != null) ? version.toString() : null, null);
+ final Object previous = properties.put(IdentifiedObject.IDENTIFIERS_KEY, id);
+ if (previous != null) {
+ Identifier[] identifiers;
+ if (previous instanceof Identifier) {
+ identifiers = new Identifier[] {(Identifier) previous, id};
+ } else {
+ identifiers = (Identifier[]) previous;
+ final int n = identifiers.length;
+ identifiers = Arrays.copyOf(identifiers, n + 1);
+ identifiers[n] = id;
+ }
+ properties.put(IdentifiedObject.IDENTIFIERS_KEY, identifiers);
+ }
+ }
+ /*
+ * Other metadata (SCOPE, AREA, etc.). ISO 19162 said that at most one of each type shall be present,
+ * but our parser accepts an arbitrary amount of some kinds of metadata. They can be recognized by the
+ * 'while' loop.
+ *
+ * Most WKT do not contain any of those metadata, so we perform an 'isEmpty()' check as an optimization
+ * for those common cases.
+ */
+ if (!parent.isEmpty()) {
+ /*
+ * Example: SCOPE["Large scale topographic mapping and cadastre."]
+ */
+ element = parent.pullElement(OPTIONAL, WKTKeywords.Scope);
+ if (element != null) {
+ properties.put(ReferenceSystem.SCOPE_KEY, element.pullString("scope")); // Other types like Datum use the same key.
+ element.close(ignoredElements);
+ }
+ /*
+ * Example: AREA["Netherlands offshore."]
+ */
+ DefaultExtent extent = null;
+ while ((element = parent.pullElement(OPTIONAL, WKTKeywords.Area)) != null) {
+ final String area = element.pullString("area");
+ element.close(ignoredElements);
+ if (extent == null) extent = new DefaultExtent();
+ extent.getGeographicElements().add(new DefaultGeographicDescription(area));
+ }
+ /*
+ * Example: BBOX[51.43, 2.54, 55.77, 6.40]
+ */
+ while ((element = parent.pullElement(OPTIONAL, WKTKeywords.BBox)) != null) {
+ final double southBoundLatitude = element.pullDouble("southBoundLatitude");
+ final double westBoundLongitude = element.pullDouble("westBoundLongitude");
+ final double northBoundLatitude = element.pullDouble("northBoundLatitude");
+ final double eastBoundLongitude = element.pullDouble("eastBoundLongitude");
+ element.close(ignoredElements);
+ if (extent == null) extent = new DefaultExtent();
+ extent.getGeographicElements().add(new DefaultGeographicBoundingBox(
+ westBoundLongitude, eastBoundLongitude, southBoundLatitude, northBoundLatitude));
+ }
+ /*
+ * Example: VERTICALEXTENT[-1000, 0, LENGTHUNIT[“metre”, 1]]
+ *
+ * Units are optional, default to metres (no "contextual units" here).
+ */
+ while ((element = parent.pullElement(OPTIONAL, WKTKeywords.VerticalExtent)) != null) {
+ final double minimum = element.pullDouble("minimum");
+ final double maximum = element.pullDouble("maximum");
+ Unit<Length> unit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+ element.close(ignoredElements);
+ if (unit == null) unit = SI.METRE;
+ if (extent == null) extent = new DefaultExtent();
+ verticalElements = new VerticalInfo(verticalElements, extent, minimum, maximum, unit).resolve(verticalCRS);
+ }
+ /*
+ * Example: TIMEEXTENT[2013-01-01, 2013-12-31]
+ *
+ * TODO: syntax like TIMEEXTENT[“Jurassic”, “Quaternary”] is not yet supported.
+ * See https://issues.apache.org/jira/browse/SIS-163
+ *
+ * This operation requires the the sis-temporal module. If not available,
+ * we will report a warning and leave the temporal extent missing.
+ */
+ while ((element = parent.pullElement(OPTIONAL, WKTKeywords.TimeExtent)) != null) {
+ final Date startTime = element.pullDate("startTime");
+ final Date endTime = element.pullDate("endTime");
+ element.close(ignoredElements);
+ try {
+ final DefaultTemporalExtent t = new DefaultTemporalExtent();
+ t.setBounds(startTime, endTime);
+ if (extent == null) extent = new DefaultExtent();
+ extent.getTemporalElements().add(t);
+ } catch (UnsupportedOperationException e) {
+ warning(parent, element, e);
+ }
+ }
+ /*
+ * Example: REMARK["Замечание на русском языке"]
+ */
+ element = parent.pullElement(OPTIONAL, WKTKeywords.Remark);
+ if (element != null) {
+ properties.put(IdentifiedObject.REMARKS_KEY, element.pullString("remarks"));
+ element.close(ignoredElements);
+ }
}
parent.close(ignoredElements);
return properties;
}
/**
- * Parses a {@code "UNIT"} element.
+ * Parses an optional {@code "UNIT"} element of a known dimension.
* This element has the following pattern:
*
* {@preformat text
* UNIT["<name>", <conversion factor> {,<authority>}]
* }
*
- * @param parent The parent element.
- * @param unit The contextual unit, usually {@code SI.METRE} or {@code SI.RADIAN}.
- * @return The {@code "UNIT"} element as an {@link Unit} object.
+ * Unit was a mandatory element in WKT 1, but became optional in WKT 2 because the unit may be specified
+ * in each {@code AXIS[…]} element instead than for the whole coordinate system.
+ *
+ * @param parent The parent element.
+ * @param keyword The unit keyword (e.g. {@code "LengthUnit"} or {@code "AngleUnit"}).
+ * @param baseUnit The base unit, usually {@code SI.METRE} or {@code SI.RADIAN}.
+ * @return The {@code "UNIT"} element as an {@link Unit} object, or {@code null} if none.
* @throws ParseException if the {@code "UNIT"} can not be parsed.
*
* @todo Authority code is currently ignored. We may consider to create a subclass of
* {@link Unit} which implements {@link IdentifiedObject} in a future version.
*/
- private <Q extends Quantity> Unit<Q> parseUnit(final Element parent, final Unit<Q> unit)
- throws ParseException
+ private <Q extends Quantity> Unit<Q> parseDerivedUnit(final Element parent,
+ final String keyword, final Unit<Q> baseUnit) throws ParseException
{
- final Element element = parent.pullElement("Unit");
- final String name = element.pullString("name");
- final double factor = element.pullDouble("factor");
- parseAuthorityAndClose(element, name);
- return Units.multiply(unit, factor);
+ final Element element = parent.pullElement(OPTIONAL, keyword, WKTKeywords.Unit);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString("name");
+ final double factor = element.pullDouble("factor");
+ parseMetadataAndClose(element, name);
+ return Units.multiply(baseUnit, factor);
+ }
+
+ /**
+ * Parses an optional {@code "UNIT"} element of unknown dimension.
+ * This method tries to infer the quantity dimension from the unit keyword.
+ *
+ * @param parent The parent element.
+ * @return The {@code "UNIT"} element, or {@code null} if none.
+ * @throws ParseException if the {@code "UNIT"} can not be parsed.
+ */
+ private Unit<?> parseUnit(final Element parent) throws ParseException {
+ final Element element = parent.pullElement(OPTIONAL, UNIT_KEYWORDS);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString("name");
+ final double factor = element.pullDouble("factor");
+ final int index = element.getKeywordIndex() - 1;
+ parseMetadataAndClose(element, name);
+ if (index >= 0 && index < BASE_UNITS.length) {
+ return Units.multiply(BASE_UNITS[index - 1], factor);
+ }
+ // If we can not infer the base type, we have to rely on the name.
+ return parseUnit(name);
}
/**
@@ -394,25 +577,20 @@ final class GeodeticObjectParser extends
* to make the parser more tolerant to non-100% compliant WKT. Note that AXIS is really the only element without
* such AUTHORITY clause and the EPSG database provides authority code for all axis.</div>
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @param isGeographic {@code true} if the parent element is a geodetic CRS having an ellipsoidal coordinate system.
- * @param unit The contextual unit, usually {@code SI.METRE} or {@code SI.RADIAN}.
- * @param mandatory {@code true} if the axis is mandatory, or {@code false} if it is optional.
+ * @param defaultUnit The contextual unit (usually {@code SI.METRE} or {@code SI.RADIAN}), or {@code null} if unknown.
* @return The {@code "AXIS"} element as a {@link CoordinateSystemAxis} object, or {@code null}
* if the axis was not required and there is no axis object.
* @throws ParseException if the {@code "AXIS"} element can not be parsed.
*/
- private CoordinateSystemAxis parseAxis(final Element parent, final boolean isGeographic,
- final Unit<?> unit, final boolean mandatory) throws ParseException
+ private CoordinateSystemAxis parseAxis(final int mode, final Element parent, final boolean isGeographic,
+ final Unit<?> defaultUnit) throws ParseException
{
- final Element element;
- if (mandatory) {
- element = parent.pullElement(WKTKeywords.Axis);
- } else {
- element = parent.pullOptionalElement(WKTKeywords.Axis);
- if (element == null) {
- return null;
- }
+ final Element element = parent.pullElement(mode, WKTKeywords.Axis);
+ if (element == null) {
+ return null;
}
String name = CharSequences.trimWhitespaces(element.pullString("name"));
if (isGeographic) {
@@ -429,7 +607,14 @@ final class GeodeticObjectParser extends
}
}
final Element orientation = element.pullVoidElement("orientation");
- final AxisDirection direction = Types.forCodeName(AxisDirection.class, orientation.keyword, mandatory);
+ Unit<?> unit = parseUnit(element);
+ if (unit == null) {
+ if (defaultUnit == null) {
+ throw element.missingComponent(WKTKeywords.Unit);
+ }
+ unit = defaultUnit;
+ }
+ final AxisDirection direction = Types.forCodeName(AxisDirection.class, orientation.keyword, mode == MANDATORY);
/*
* According ISO 19162, the abbreviation should be inserted between parenthesis in the name.
* Example: "Easting (E)", "Longitude (L)". If we do not find an abbreviation, then we will
@@ -447,7 +632,7 @@ final class GeodeticObjectParser extends
abbreviation = referencing.suggestAbbreviation(name, direction, unit);
}
try {
- return csFactory.createCoordinateSystemAxis(parseAuthorityAndClose(element, name), abbreviation, direction, unit);
+ return csFactory.createCoordinateSystemAxis(parseMetadataAndClose(element, name), abbreviation, direction, unit);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -464,26 +649,45 @@ final class GeodeticObjectParser extends
}
/**
+ * Returns {@code true} if axes should be ignored.
+ *
+ * @param axis The first parsed axis (can be {@code null}).
+ * @return {@code true} for ignoring the given axis and all other ones in the current coordinate system.
+ */
+ private boolean isAxisIgnored(final CoordinateSystemAxis axis) {
+ return (axis == null) || (convention == Convention.WKT1_IGNORE_AXES);
+ }
+
+ /**
* Parses a {@code "PRIMEM"} element. This element has the following pattern:
*
* {@preformat text
* PRIMEM["<name>", <longitude> {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @param angularUnit The contextual unit.
* @return The {@code "PRIMEM"} element as a {@link PrimeMeridian} object.
* @throws ParseException if the {@code "PRIMEM"} element can not be parsed.
*/
- private PrimeMeridian parsePrimem(final Element parent, Unit<Angle> angularUnit) throws ParseException {
- if (convention == Convention.WKT1_COMMON_UNITS) {
+ private PrimeMeridian parsePrimeMeridian(final int mode, final Element parent, Unit<Angle> angularUnit)
+ throws ParseException
+ {
+ if (convention.usesCommonUnits) {
angularUnit = NonSI.DEGREE_ANGLE;
}
- final Element element = parent.pullElement(WKTKeywords.PrimeM);
- final String name = element.pullString("name");
- final double longitude = element.pullDouble("longitude");
+ final Element element = parent.pullElement(mode, WKTKeywords.PrimeMeridian, WKTKeywords.PrimeM);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString("name");
+ final double longitude = element.pullDouble("longitude");
+ if (angularUnit == null) {
+ throw element.missingComponent(WKTKeywords.AngleUnit);
+ }
try {
- return datumFactory.createPrimeMeridian(parseAuthorityAndClose(element, name), longitude, angularUnit);
+ return datumFactory.createPrimeMeridian(parseMetadataAndClose(element, name), longitude, angularUnit);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -497,20 +701,21 @@ final class GeodeticObjectParser extends
* TOWGS84[<dx>, <dy>, <dz>, <ex>, <ey>, <ez>, <ppm>]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "TOWGS84"} element as a {@link org.apache.sis.referencing.datum.BursaWolfParameters} object,
* or {@code null} if no {@code "TOWGS84"} has been found.
* @throws ParseException if the {@code "TOWGS84"} can not be parsed.
*/
- private Object parseToWGS84(final Element parent) throws ParseException {
- final Element element = parent.pullOptionalElement(WKTKeywords.ToWGS84);
+ private Object parseToWGS84(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.ToWGS84);
if (element == null) {
return null;
}
final double[] values = new double[ToWGS84.length];
for (int i=0; i<values.length;) {
values[i] = element.pullDouble(ToWGS84[i]);
- if ((++i % 3) == 0 && element.peek() == null) {
+ if ((++i % 3) == 0 && element.isEmpty()) {
break; // It is legal to have only 3 or 6 elements.
}
}
@@ -525,12 +730,16 @@ final class GeodeticObjectParser extends
* SPHEROID["<name>", <semi-major axis>, <inverse flattening> {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "SPHEROID"} element as an {@link Ellipsoid} object.
* @throws ParseException if the {@code "SPHEROID"} element can not be parsed.
*/
- private Ellipsoid parseSpheroid(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Spheroid);
+ private Ellipsoid parseEllipsoid(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.Ellipsoid, WKTKeywords.Spheroid);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
final double semiMajorAxis = element.pullDouble("semiMajorAxis");
double inverseFlattening = element.pullDouble("inverseFlattening");
@@ -538,7 +747,7 @@ final class GeodeticObjectParser extends
inverseFlattening = Double.POSITIVE_INFINITY;
}
try {
- return datumFactory.createFlattenedSphere(parseAuthorityAndClose(element, name),
+ return datumFactory.createFlattenedSphere(parseMetadataAndClose(element, name),
semiMajorAxis, inverseFlattening, SI.METRE);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
@@ -552,18 +761,20 @@ final class GeodeticObjectParser extends
* PROJECTION["<name>" {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @param linearUnit The linear unit of the parent {@code PROJCS} element, or {@code null}.
* @param angularUnit The angular unit of the parent {@code GEOCS} element, or {@code null}.
* @return The {@code "PROJECTION"} element as a defining conversion.
* @throws ParseException if the {@code "PROJECTION"} element can not be parsed.
*/
- private Conversion parseProjection(final Element parent,
- final Unit<Length> linearUnit,
- final Unit<Angle> angularUnit)
- throws ParseException
+ private Conversion parseProjection(final int mode, final Element parent,
+ final Unit<Length> linearUnit, final Unit<Angle> angularUnit) throws ParseException
{
- final Element element = parent.pullElement(WKTKeywords.Projection);
+ final Element element = parent.pullElement(mode, WKTKeywords.Projection);
+ if (element == null) {
+ return null;
+ }
final String classification = element.pullString("name");
/*
* Set the list of parameters. NOTE: Parameters are defined in the parent
@@ -573,7 +784,7 @@ final class GeodeticObjectParser extends
final OperationMethod method = referencing.getOperationMethod(opFactory, mtFactory, classification);
final ParameterValueGroup parameters = method.getParameters().createValue();
parseParameters(parent, parameters, linearUnit, angularUnit);
- return opFactory.createDefiningConversion(parseAuthorityAndClose(element, classification), method, parameters);
+ return opFactory.createDefiningConversion(parseMetadataAndClose(element, classification), method, parameters);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -586,17 +797,24 @@ final class GeodeticObjectParser extends
* DATUM["<name>", <spheroid> {,<to wgs84>} {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
- * @param meridian the prime meridian.
+ * @param meridian the prime meridian, or {@code null} for Greenwich.
* @return The {@code "DATUM"} element as a {@link GeodeticDatum} object.
* @throws ParseException if the {@code "DATUM"} element can not be parsed.
*/
- private GeodeticDatum parseDatum(final Element parent, final PrimeMeridian meridian) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Datum);
- final String name = element.pullString("name");
- final Ellipsoid ellipsoid = parseSpheroid(element);
- final Object toWGS84 = parseToWGS84(element); // Optional; may be null.
- final Map<String,Object> properties = parseAuthorityAndClose(element, name);
+ private GeodeticDatum parseDatum(final int mode, final Element parent, PrimeMeridian meridian) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.Datum);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString("name");
+ final Ellipsoid ellipsoid = parseEllipsoid(MANDATORY, element);
+ final Object toWGS84 = parseToWGS84(OPTIONAL, element);
+ final Map<String,Object> properties = parseMetadataAndClose(element, name);
+ if (meridian == null) {
+ meridian = referencing.getGreenwich();
+ }
if (toWGS84 != null) {
properties.put(ReferencingServices.BURSA_WOLF_KEY, toWGS84);
}
@@ -614,18 +832,22 @@ final class GeodeticObjectParser extends
* VERT_DATUM["<name>", <datum type> {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "VERT_DATUM"} element as a {@link VerticalDatum} object.
* @throws ParseException if the {@code "VERT_DATUM"} element can not be parsed.
*/
- private VerticalDatum parseVertDatum(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Vert_Datum);
- final String name = element.pullString ("name");
- final int datum = element.pullInteger("datum");
+ private VerticalDatum parseVerticalDatum(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.VerticalDatum, WKTKeywords.Vert_Datum);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString ("name");
+ final int datum = element.pullInteger("datum");
final VerticalDatumType type = VerticalDatumTypes.fromLegacy(datum);
// TODO: need to find a default value if null.
try {
- return datumFactory.createVerticalDatum(parseAuthorityAndClose(element, name), type);
+ return datumFactory.createVerticalDatum(parseMetadataAndClose(element, name), type);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -638,18 +860,22 @@ final class GeodeticObjectParser extends
* TimeDatum["<name>", TimeOrigin[<time origin>] {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "TimeDatum"} element as a {@link TemporalDatum} object.
* @throws ParseException if the {@code "TimeDatum"} element can not be parsed.
*/
- private TemporalDatum parseTimeDatum(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.TimeDatum);
- final String name = element.pullString ("name");
- final Element origin = element.pullElement(WKTKeywords.TimeOrigin);
- final Date epoch = origin .pullDate("origin");
+ private TemporalDatum parseTimeDatum(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.TimeDatum);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString ("name");
+ final Element origin = element.pullElement(MANDATORY, WKTKeywords.TimeOrigin);
+ final Date epoch = origin .pullDate("origin");
origin.close(ignoredElements);
try {
- return datumFactory.createTemporalDatum(parseAuthorityAndClose(element, name), epoch);
+ return datumFactory.createTemporalDatum(parseMetadataAndClose(element, name), epoch);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -662,18 +888,22 @@ final class GeodeticObjectParser extends
* LOCAL_DATUM["<name>", <datum type> {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "LOCAL_DATUM"} element as an {@link EngineeringDatum} object.
* @throws ParseException if the {@code "LOCAL_DATUM"} element can not be parsed.
*
* @todo The vertical datum type is currently ignored.
*/
- private EngineeringDatum parseLocalDatum(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Local_Datum);
- final String name = element.pullString ("name");
- final int datum = element.pullInteger("datum"); // Ignored for now.
+ private EngineeringDatum parseEngineeringDatum(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.EngineeringDatum, WKTKeywords.Local_Datum);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString ("name");
+ final int datum = element.pullInteger("datum"); // Ignored for now.
try {
- return datumFactory.createEngineeringDatum(parseAuthorityAndClose(element, name));
+ return datumFactory.createEngineeringDatum(parseMetadataAndClose(element, name));
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -687,6 +917,7 @@ final class GeodeticObjectParser extends
* LOCAL_CS["<name>", <local datum>, <unit>, <axis>, {,<axis>}* {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "LOCAL_CS"} element as an {@link EngineeringCRS} object.
* @throws ParseException if the {@code "LOCAL_CS"} element can not be parsed.
@@ -695,20 +926,23 @@ final class GeodeticObjectParser extends
* know which method to invokes in the {@link CSFactory} (is it a Cartesian
* coordinate system? a spherical one? etc.).
*/
- private EngineeringCRS parseLocalCS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Local_CS);
+ private EngineeringCRS parseEngineeringCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.EngineeringCRS, WKTKeywords.Local_CS);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
- final EngineeringDatum datum = parseLocalDatum(element);
- final Unit<Length> linearUnit = parseUnit(element, SI.METRE);
- CoordinateSystemAxis axis = parseAxis(element, false, linearUnit, true);
+ final EngineeringDatum datum = parseEngineeringDatum(MANDATORY, element);
+ final Unit<Length> linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+ CoordinateSystemAxis axis = parseAxis(MANDATORY, element, false, linearUnit);
final List<CoordinateSystemAxis> list = new ArrayList<CoordinateSystemAxis>();
do {
list.add(axis);
- axis = parseAxis(element, false, linearUnit, false);
+ axis = parseAxis(OPTIONAL, element, false, linearUnit);
} while (axis != null);
final CoordinateSystem cs = referencing.createAbstractCS(list.toArray(new CoordinateSystemAxis[list.size()]));
try {
- return crsFactory.createEngineeringCRS(parseAuthorityAndClose(element, name), datum, cs);
+ return crsFactory.createEngineeringCRS(parseMetadataAndClose(element, name), datum, cs);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -723,29 +957,36 @@ final class GeodeticObjectParser extends
* {,<axis> ,<axis> ,<axis>} {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "GEOCCS"} element as a {@link GeocentricCRS} object.
* @throws ParseException if the {@code "GEOCCS"} element can not be parsed.
*/
- private GeocentricCRS parseGeoCCS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.GeocCS);
+ private GeocentricCRS parseGeocentricCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.GeocCS);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
- final PrimeMeridian meridian = parsePrimem(element, NonSI.DEGREE_ANGLE);
- final GeodeticDatum datum = parseDatum (element, meridian);
- final Unit<Length> linearUnit = parseUnit (element, SI.METRE);
+ final PrimeMeridian meridian = parsePrimeMeridian(MANDATORY, element, NonSI.DEGREE_ANGLE);
+ final GeodeticDatum datum = parseDatum (MANDATORY, element, meridian);
+ final Unit<Length> linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
CartesianCS cs;
CoordinateSystemAxis axis0, axis1 = null, axis2 = null;
- axis0 = parseAxis(element, false, linearUnit, false);
+ axis0 = parseAxis(OPTIONAL, element, false, linearUnit);
try {
if (axis0 != null) {
- axis1 = parseAxis(element, false, linearUnit, true);
- axis2 = parseAxis(element, false, linearUnit, true);
+ axis1 = parseAxis(MANDATORY, element, false, linearUnit);
+ axis2 = parseAxis(MANDATORY, element, false, linearUnit);
}
- final Map<String,?> properties = parseAuthorityAndClose(element, name);
- if (axis0 != null && !isAxisIgnored) {
+ final Map<String,?> properties = parseMetadataAndClose(element, name);
+ if (!isAxisIgnored(axis0)) {
cs = csFactory.createCartesianCS(properties, axis0, axis1, axis2);
cs = referencing.upgradeGeocentricCS(cs);
} else {
+ if (linearUnit == null) {
+ throw element.missingComponent(WKTKeywords.Unit);
+ }
cs = referencing.getGeocentricCS(linearUnit);
}
return crsFactory.createGeocentricCRS(properties, datum, cs);
@@ -762,18 +1003,25 @@ final class GeodeticObjectParser extends
* VERT_CS["<name>", <vert datum>, <linear unit>, {<axis>,} {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "VERT_CS"} element as a {@link VerticalCRS} object.
* @throws ParseException if the {@code "VERT_CS"} element can not be parsed.
*/
- private VerticalCRS parseVertCS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Vert_CS);
+ private VerticalCRS parseVerticalCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.VerticalCRS, WKTKeywords.Vert_CS);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
- final VerticalDatum datum = parseVertDatum(element);
- final Unit<Length> linearUnit = parseUnit(element, SI.METRE);
- CoordinateSystemAxis axis = parseAxis(element, false, linearUnit, false);
+ final VerticalDatum datum = parseVerticalDatum(MANDATORY, element);
+ final Unit<Length> linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+ CoordinateSystemAxis axis = parseAxis(OPTIONAL, element, false, linearUnit);
try {
- if (axis == null || isAxisIgnored) {
+ if (isAxisIgnored(axis)) {
+ if (linearUnit == null) {
+ throw element.missingComponent(WKTKeywords.Unit);
+ }
String sn = "Height", abbreviation = "h";
AxisDirection direction = AxisDirection.UP;
final VerticalDatumType type = datum.getVerticalDatumType();
@@ -789,8 +1037,16 @@ final class GeodeticObjectParser extends
}
axis = createAxis(sn, abbreviation, direction, linearUnit);
}
- return crsFactory.createVerticalCRS(parseAuthorityAndClose(element, name), datum,
- csFactory.createVerticalCS(singletonMap("name", name), axis));
+ verticalCRS = crsFactory.createVerticalCRS(parseMetadataAndClose(element, name), datum,
+ csFactory.createVerticalCS(singletonMap("name", name), axis));
+ /*
+ * Some DefaultVerticalExtent objects may be waiting for the VerticalCRS before to complete
+ * their construction. If this is the case, try to complete them now.
+ */
+ if (verticalElements != null) {
+ verticalElements = verticalElements.resolve(verticalCRS);
+ }
+ return verticalCRS;
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -799,21 +1055,28 @@ final class GeodeticObjectParser extends
/**
* Parses an <strong>optional</strong> {@code "TIMECRS"} element.
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "TIMECRS"} element as a {@link TemporalCRS} object.
* @throws ParseException if the {@code "TIMECRS"} element can not be parsed.
*/
- private TemporalCRS parseTimeCRS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.TimeCRS);
+ private TemporalCRS parseTimeCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.TimeCRS);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
- final TemporalDatum datum = parseTimeDatum(element);
- final Unit<Duration> timeUnit = parseUnit(element, SI.SECOND);
- CoordinateSystemAxis axis = parseAxis(element, false, timeUnit, false);
+ final TemporalDatum datum = parseTimeDatum(MANDATORY, element);
+ final Unit<Duration> timeUnit = parseDerivedUnit(element, WKTKeywords.TimeUnit, SI.SECOND);
+ CoordinateSystemAxis axis = parseAxis(OPTIONAL, element, false, timeUnit);
try {
- if (axis == null || isAxisIgnored) {
+ if (isAxisIgnored(axis)) {
+ if (timeUnit == null) {
+ throw element.missingComponent(WKTKeywords.TimeUnit);
+ }
axis = createAxis("Time", "t", AxisDirection.FUTURE, timeUnit);
}
- return crsFactory.createTemporalCRS(parseAuthorityAndClose(element, name), datum,
+ return crsFactory.createTemporalCRS(parseMetadataAndClose(element, name), datum,
csFactory.createTimeCS(singletonMap("name", name), axis));
} catch (FactoryException exception) {
throw element.parseFailed(exception);
@@ -827,16 +1090,20 @@ final class GeodeticObjectParser extends
* GEOGCS["<name>", <datum>, <prime meridian>, <angular unit> {,<twin axes>} {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "GEOGCS"} element as a {@link GeographicCRS} object.
* @throws ParseException if the {@code "GEOGCS"} element can not be parsed.
*/
- private GeographicCRS parseGeoGCS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.GeogCS);
- Object name = element.pullString("name");
- final Unit<Angle> angularUnit = parseUnit (element, SI.RADIAN);
- final PrimeMeridian meridian = parsePrimem(element, angularUnit);
- final GeodeticDatum datum = parseDatum (element, meridian);
+ private GeographicCRS parseGeographicCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.GeodeticCRS, WKTKeywords.GeodCRS, WKTKeywords.GeogCS);
+ if (element == null) {
+ return null;
+ }
+ Object name = element.pullString("name");
+ final Unit<Angle> angularUnit = parseDerivedUnit(element, WKTKeywords.AngleUnit, SI.RADIAN);
+ final PrimeMeridian meridian = parsePrimeMeridian(MANDATORY, element, angularUnit);
+ final GeodeticDatum datum = parseDatum(MANDATORY, element, meridian);
if (((String) name).isEmpty()) {
/*
* GeographicCRS name is a mandatory property, but some invalid WKT with an empty string exist.
@@ -845,17 +1112,20 @@ final class GeodeticObjectParser extends
*/
name = datum.getName();
}
- CoordinateSystemAxis axis0 = parseAxis(element, true, angularUnit, false);
+ CoordinateSystemAxis axis0 = parseAxis(OPTIONAL, element, true, angularUnit);
CoordinateSystemAxis axis1 = null;
try {
if (axis0 != null) {
- axis1 = parseAxis(element, true, angularUnit, true);
+ axis1 = parseAxis(MANDATORY, element, true, angularUnit);
}
- if (axis0 == null || isAxisIgnored) {
+ if (isAxisIgnored(axis0)) {
+ if (angularUnit == null) {
+ throw element.missingComponent(WKTKeywords.AngleUnit);
+ }
axis0 = createAxis(AxisNames.GEODETIC_LONGITUDE, "λ", AxisDirection.EAST, angularUnit);
axis1 = createAxis(AxisNames.GEODETIC_LATITUDE, "φ", AxisDirection.NORTH, angularUnit);
}
- final Map<String,?> properties = parseAuthorityAndClose(element, name);
+ final Map<String,?> properties = parseMetadataAndClose(element, name);
return crsFactory.createGeographicCRS(properties, datum,
csFactory.createEllipsoidalCS(properties, axis0, axis1));
} catch (FactoryException exception) {
@@ -872,30 +1142,37 @@ final class GeodeticObjectParser extends
* <linear unit> {,<twin axes>}{,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "PROJCS"} element as a {@link ProjectedCRS} object.
* @throws ParseException if the {@code "GEOGCS"} element can not be parsed.
*/
- private ProjectedCRS parseProjCS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.ProjCS);
+ private ProjectedCRS parseProjectedCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.ProjCS);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
- final GeographicCRS geoCRS = parseGeoGCS(element);
- final Unit<Length> linearUnit = parseUnit(element, SI.METRE);
- final boolean usesCommonUnits = convention.usesCommonUnits();
- final Conversion conversion = parseProjection(element,
+ final GeographicCRS geoCRS = parseGeographicCRS(MANDATORY, element);
+ final Unit<Length> linearUnit = parseDerivedUnit(element, WKTKeywords.LengthUnit, SI.METRE);
+ final boolean usesCommonUnits = convention.usesCommonUnits;
+ final Conversion conversion = parseProjection(MANDATORY, element,
usesCommonUnits ? SI.METRE : linearUnit,
usesCommonUnits ? NonSI.DEGREE_ANGLE : geoCRS.getCoordinateSystem().getAxis(0).getUnit().asType(Angle.class));
- CoordinateSystemAxis axis0 = parseAxis(element, false, linearUnit, false);
+ CoordinateSystemAxis axis0 = parseAxis(OPTIONAL, element, false, linearUnit);
CoordinateSystemAxis axis1 = null;
try {
if (axis0 != null) {
- axis1 = parseAxis(element, false, linearUnit, true);
+ axis1 = parseAxis(MANDATORY, element, false, linearUnit);
}
- if (axis0 == null || isAxisIgnored) {
+ if (isAxisIgnored(axis0)) {
+ if (linearUnit == null) {
+ throw element.missingComponent(WKTKeywords.LengthUnit);
+ }
axis0 = createAxis(AxisNames.EASTING, "E", AxisDirection.EAST, linearUnit);
axis1 = createAxis(AxisNames.NORTHING, "N", AxisDirection.NORTH, linearUnit);
}
- final Map<String,?> properties = parseAuthorityAndClose(element, name);
+ final Map<String,?> properties = parseMetadataAndClose(element, name);
return crsFactory.createProjectedCRS(properties, geoCRS, conversion,
csFactory.createCartesianCS(properties, axis0, axis1));
} catch (FactoryException exception) {
@@ -911,20 +1188,24 @@ final class GeodeticObjectParser extends
* COMPD_CS["<name>", <head cs>, <tail cs> {,<authority>}]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "COMPD_CS"} element as a {@link CompoundCRS} object.
* @throws ParseException if the {@code "COMPD_CS"} element can not be parsed.
*/
- private CompoundCRS parseCompdCS(final Element parent) throws ParseException {
- final List<CoordinateReferenceSystem> components = new ArrayList<CoordinateReferenceSystem>(4);
- final Element element = parent.pullElement(WKTKeywords.Compd_CS);
- final String name = element.pullString("name");
+ private CompoundCRS parseCompoundCRS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.CompoundCRS, WKTKeywords.Compd_CS);
+ if (element == null) {
+ return null;
+ }
+ final String name = element.pullString("name");
CoordinateReferenceSystem crs;
+ final List<CoordinateReferenceSystem> components = new ArrayList<CoordinateReferenceSystem>(4);
while ((crs = parseCoordinateReferenceSystem(element, components.size() < 2)) != null) {
components.add(crs);
}
try {
- return crsFactory.createCompoundCRS(parseAuthorityAndClose(element, name),
+ return crsFactory.createCompoundCRS(parseMetadataAndClose(element, name),
components.toArray(new CoordinateReferenceSystem[components.size()]));
} catch (FactoryException exception) {
throw element.parseFailed(exception);
@@ -939,12 +1220,16 @@ final class GeodeticObjectParser extends
* FITTED_CS["<name>", <to base>, <base cs>]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "FITTED_CS"} element as a {@link CompoundCRS} object.
* @throws ParseException if the {@code "COMPD_CS"} element can not be parsed.
*/
- private DerivedCRS parseFittedCS(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Fitted_CS);
+ private DerivedCRS parseFittedCS(final int mode, final Element parent) throws ParseException {
+ final Element element = parent.pullElement(mode, WKTKeywords.Fitted_CS);
+ if (element == null) {
+ return null;
+ }
final String name = element.pullString("name");
final MathTransform toBase = parseMathTransform(element, true);
final OperationMethod method = getOperationMethod();
@@ -970,7 +1255,7 @@ final class GeodeticObjectParser extends
singletonMap(IdentifiedObject.NAME_KEY, buffer.toString()),
number, AxisDirection.OTHER, Unit.ONE);
}
- final Map<String,Object> properties = parseAuthorityAndClose(element, name);
+ final Map<String,Object> properties = parseMetadataAndClose(element, name);
final CoordinateSystem derivedCS = referencing.createAbstractCS(axes);
/*
* We do not know which name to give to the conversion method.
Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/MathTransformParser.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/MathTransformParser.java?rev=1686282&r1=1686281&r2=1686282&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/MathTransformParser.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/MathTransformParser.java [UTF-8] Thu Jun 18 17:38:16 2015
@@ -21,6 +21,7 @@ import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.measure.unit.Unit;
+import javax.measure.unit.UnitFormat;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Length;
import org.opengis.util.FactoryException;
@@ -58,7 +59,7 @@ import static org.apache.sis.util.Argume
*
* @see <a href="http://www.geoapi.org/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">Well Know Text specification</a>
*/
-class MathTransformParser extends Parser {
+class MathTransformParser extends AbstractParser {
/**
* The factory to use for creating math transforms.
*/
@@ -94,7 +95,7 @@ class MathTransformParser extends Parser
* @param mtFactory The factory to use to create {@link MathTransform} objects.
*/
public MathTransformParser(final MathTransformFactory mtFactory) {
- this(Symbols.getDefault(), null, null, mtFactory, null);
+ this(Symbols.getDefault(), null, null, null, mtFactory, null);
}
/**
@@ -103,13 +104,14 @@ class MathTransformParser extends Parser
* @param symbols The set of symbols to use.
* @param numberFormat The number format provided by {@link WKTFormat}, or {@code null} for a default format.
* @param dateFormat The date format provided by {@link WKTFormat}, or {@code null} for a default format.
+ * @param unitFormat The unit format provided by {@link WKTFormat}, or {@code null} for a default format.
* @param mtFactory The factory to use to create {@link MathTransform} objects.
* @param errorLocale The locale for error messages (not for parsing), or {@code null} for the system default.
*/
MathTransformParser(final Symbols symbols, final NumberFormat numberFormat, final DateFormat dateFormat,
- final MathTransformFactory mtFactory, final Locale errorLocale)
+ final UnitFormat unitFormat, final MathTransformFactory mtFactory, final Locale errorLocale)
{
- super(symbols, numberFormat, dateFormat, errorLocale);
+ super(symbols, numberFormat, dateFormat, unitFormat, errorLocale);
this.mtFactory = mtFactory;
ensureNonNull("mtFactory", mtFactory);
}
@@ -137,21 +139,17 @@ class MathTransformParser extends Parser
final MathTransform parseMathTransform(final Element element, final boolean mandatory) throws ParseException {
lastMethod = null;
classification = null;
- String keyword = WKTKeywords.Param_MT;
- final Object child = element.peek();
- if (child instanceof Element) {
- keyword = ((Element) child).keyword;
- if (keyword != null) {
- if (keyword.equalsIgnoreCase(WKTKeywords.Param_MT)) return parseParamMT (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Concat_MT)) return parseConcatMT (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.Inverse_MT)) return parseInverseMT (element);
- if (keyword.equalsIgnoreCase(WKTKeywords.PassThrough_MT)) return parsePassThroughMT(element);
+ MathTransform tr;
+ if ((tr = parseParamMT (element)) == null &&
+ (tr = parseConcatMT (element)) == null &&
+ (tr = parseInverseMT (element)) == null &&
+ (tr = parsePassThroughMT (element)) == null)
+ {
+ if (mandatory) {
+ throw element.missingOrUnknownComponent(WKTKeywords.Param_MT);
}
}
- if (mandatory) {
- throw element.keywordNotFound(keyword, keyword == WKTKeywords.Param_MT);
- }
- return null;
+ return tr;
}
/**
@@ -168,7 +166,7 @@ class MathTransformParser extends Parser
{
Element param = element;
try {
- while ((param = element.pullOptionalElement(WKTKeywords.Parameter)) != null) {
+ while ((param = element.pullElement(OPTIONAL, WKTKeywords.Parameter)) != null) {
final String name = param.pullString("name");
final ParameterValue<?> parameter = parameters.parameter(name);
final ParameterDescriptor<?> descriptor = parameter.getDescriptor();
@@ -211,8 +209,11 @@ class MathTransformParser extends Parser
* @return The {@code "PARAM_MT"} element as an {@link MathTransform} object.
* @throws ParseException if the {@code "PARAM_MT"} element can not be parsed.
*/
- final MathTransform parseParamMT(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Param_MT);
+ private MathTransform parseParamMT(final Element parent) throws ParseException {
+ final Element element = parent.pullElement(FIRST, WKTKeywords.Param_MT);
+ if (element == null) {
+ return null;
+ }
classification = element.pullString("classification");
final ParameterValueGroup parameters;
try {
@@ -250,8 +251,11 @@ class MathTransformParser extends Parser
* @return The {@code "INVERSE_MT"} element as an {@link MathTransform} object.
* @throws ParseException if the {@code "INVERSE_MT"} element can not be parsed.
*/
- final MathTransform parseInverseMT(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Inverse_MT);
+ private MathTransform parseInverseMT(final Element parent) throws ParseException {
+ final Element element = parent.pullElement(FIRST, WKTKeywords.Inverse_MT);
+ if (element == null) {
+ return null;
+ }
MathTransform transform = parseMathTransform(element, true);
try {
transform = transform.inverse();
@@ -273,8 +277,11 @@ class MathTransformParser extends Parser
* @return The {@code "PASSTHROUGH_MT"} element as an {@link MathTransform} object.
* @throws ParseException if the {@code "PASSTHROUGH_MT"} element can not be parsed.
*/
- final MathTransform parsePassThroughMT(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.PassThrough_MT);
+ private MathTransform parsePassThroughMT(final Element parent) throws ParseException {
+ final Element element = parent.pullElement(FIRST, WKTKeywords.PassThrough_MT);
+ if (element == null) {
+ return null;
+ }
final int firstAffectedOrdinate = parent.pullInteger("firstAffectedOrdinate");
final MathTransform transform = parseMathTransform(element, true);
element.close(ignoredElements);
@@ -292,12 +299,16 @@ class MathTransformParser extends Parser
* CONCAT_MT[<math transform> {,<math transform>}*]
* }
*
+ * @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent The parent element.
* @return The {@code "CONCAT_MT"} element as an {@link MathTransform} object.
* @throws ParseException if the {@code "CONCAT_MT"} element can not be parsed.
*/
- final MathTransform parseConcatMT(final Element parent) throws ParseException {
- final Element element = parent.pullElement(WKTKeywords.Concat_MT);
+ private MathTransform parseConcatMT(final Element parent) throws ParseException {
+ final Element element = parent.pullElement(FIRST, WKTKeywords.Concat_MT);
+ if (element == null) {
+ return null;
+ }
MathTransform transform = parseMathTransform(element, true);
MathTransform optionalTransform;
while ((optionalTransform = parseMathTransform(element, false)) != null) {
@@ -313,7 +324,7 @@ class MathTransformParser extends Parser
/**
* Returns the operation method for the last math transform parsed. This is used by
- * {@link Parser} in order to built {@link org.opengis.referencing.crs.DerivedCRS}.
+ * {@link GeodeticObjectParser} in order to built {@link org.opengis.referencing.crs.DerivedCRS}.
*/
final OperationMethod getOperationMethod() {
if (lastMethod == null) {
Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java?rev=1686282&r1=1686281&r2=1686282&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] Thu Jun 18 17:38:16 2015
@@ -21,6 +21,8 @@ import java.util.Locale;
import java.util.TimeZone;
import java.util.Map;
import java.util.HashMap;
+import java.util.Collections;
+import java.util.List;
import java.io.IOException;
import java.text.Format;
import java.text.NumberFormat;
@@ -31,6 +33,7 @@ import java.text.ParseException;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import org.opengis.util.Factory;
+import org.opengis.util.InternationalString;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.IdentifiedObject;
import org.apache.sis.io.CompoundFormat;
@@ -76,9 +79,15 @@ import org.apache.sis.util.resources.Err
* PROJECTION[</code> <i>…etc…</i> <code>]]");</code></blockquote>
* </div>
*
- * <div class="section">Thread safety</div>
- * {@code WKTFormat}s are not synchronized. It is recommended to create separated format instances for each thread.
- * If multiple threads access a {@code WKTFormat} concurrently, it must be synchronized externally.
+ * <div class="section">Limitations</div>
+ * <ul>
+ * <li>Instances of this class are not synchronized for multi-threading.
+ * It is recommended to create separated format instances for each thread.
+ * If multiple threads access a {@code WKTFormat} concurrently, it must be synchronized externally.</li>
+ * <li>Serialized objects of this class are not guaranteed to be compatible with future Apache SIS releases.
+ * Serialization support is appropriate for short term storage or RMI between applications running the
+ * same version of Apache SIS.</li>
+ * </ul>
*
* @author Martin Desruisseaux (Geomatys)
* @author Rémi Eve (IRD)
@@ -182,7 +191,7 @@ public class WKTFormat extends CompoundF
/**
* The parser. Will be created when first needed.
*/
- private transient Parser parser;
+ private transient AbstractParser parser;
/**
* The factories needed by the parser. Will be created when first needed.
@@ -190,9 +199,15 @@ public class WKTFormat extends CompoundF
private transient Map<Class<?>,Factory> factories;
/**
+ * The warning produced by the last parsing or formatting operation, or {@code null} if none.
+ *
+ * @see #getWarnings()
+ */
+ private transient Warnings warnings;
+
+ /**
* Creates a format for the given locale and timezone. The given locale will be used for
- * {@link org.opengis.util.InternationalString} localization; this is <strong>not</strong>
- * the locale for number format.
+ * {@link InternationalString} localization; this is <strong>not</strong> the locale for number format.
*
* @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.
@@ -482,16 +497,26 @@ public class WKTFormat extends CompoundF
*/
@Override
public Object parse(final CharSequence text, final ParsePosition pos) throws ParseException {
+ warnings = null;
+ ArgumentChecks.ensureNonNull("text", text);
+ ArgumentChecks.ensureNonNull("pos", pos);
+ AbstractParser parser = this.parser;
if (parser == null) {
if (factories == null) {
factories = new HashMap<Class<?>,Factory>();
}
- parser = new GeodeticObjectParser(symbols,
+ this.parser = parser = new GeodeticObjectParser(symbols,
(NumberFormat) getFormat(Number.class),
(DateFormat) getFormat(Date.class),
- convention, false, getLocale(), factories);
+ (UnitFormat) getFormat(Unit.class),
+ convention, getLocale(), factories);
+ }
+ Object object = null;
+ try {
+ return object = parser.parseObject(text.toString(), pos);
+ } finally {
+ warnings = parser.getAndClearWarnings(object);
}
- return parser.parseObject(text.toString(), pos);
}
/**
@@ -507,10 +532,11 @@ public class WKTFormat extends CompoundF
* @param toAppendTo Where the text is to be appended.
* @throws IOException If an error occurred while writing to {@code toAppendTo}.
*
- * @see #getWarning()
+ * @see FormattableObject#toWKT()
*/
@Override
public void format(final Object object, final Appendable toAppendTo) throws IOException {
+ warnings = null;
ArgumentChecks.ensureNonNull("object", object);
ArgumentChecks.ensureNonNull("toAppendTo", toAppendTo);
/*
@@ -538,13 +564,20 @@ public class WKTFormat extends CompoundF
this.formatter = formatter;
}
final boolean valid;
+ final InternationalString warning;
try {
formatter.setBuffer(buffer);
valid = formatter.appendElement(object) || formatter.appendValue(object);
} finally {
+ warning = formatter.getErrorMessage(); // Must be saved before formatter.clear() is invoked.
formatter.setBuffer(null);
formatter.clear();
}
+ if (warning != null) {
+ warnings = new Warnings(getLocale(), (byte) 0, Collections.<String, List<String>>emptyMap());
+ warnings.add(warning, formatter.getErrorCause(), null);
+ warnings.setRoot(object);
+ }
if (!valid) {
throw new ClassCastException(Errors.format(
Errors.Keys.IllegalArgumentClass_2, "object", object.getClass()));
@@ -579,13 +612,34 @@ public class WKTFormat extends CompoundF
}
/**
- * If a warning occurred during the last WKT {@linkplain #format(Object, Appendable) formatting}, returns
- * the warning. Otherwise returns {@code null}. The warning is cleared every time a new object is formatted.
+ * If warnings occurred during the last WKT {@linkplain #parse(CharSequence, ParsePosition) parsing} or
+ * {@linkplain #format(Object, Appendable) formatting}, returns the warnings. Otherwise returns {@code null}.
+ * The warnings are cleared every time a new object is parsed or formatted.
+ *
+ * @return The warnings of the last parsing of formatting operation, or {@code null} if none.
+ *
+ * @since 0.6
+ */
+ public Warnings getWarnings() {
+ final Warnings w = warnings;
+ if (w != null) {
+ w.publish();
+ }
+ return w;
+ }
+
+ /**
+ * If a warning occurred during the last WKT {@linkplain #parse(CharSequence, ParsePosition) parsing} or
+ * {@linkplain #format(Object, Appendable) formatting}, returns the warning. Otherwise returns {@code null}.
+ * The warning is cleared every time a new object is parsed or formatted.
*
* @return The last warning, or {@code null} if none.
+ *
+ * @deprecated Replaced by {@link #getWarnings()}.
*/
+ @Deprecated
public String getWarning() {
- return (formatter != null) ? formatter.getErrorMessage() : null;
+ return (warnings != null) ? warnings.toString() : null;
}
/**
@@ -597,6 +651,8 @@ public class WKTFormat extends CompoundF
public WKTFormat clone() {
final WKTFormat clone = (WKTFormat) super.clone();
clone.formatter = null; // Do not share the formatter.
+ clone.parser = null;
+ clone.warnings = null;
return clone;
}
}
Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java?rev=1686282&r1=1686281&r2=1686282&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java [UTF-8] Thu Jun 18 17:38:16 2015
@@ -17,21 +17,30 @@
/**
* <cite>Well Known Text</cite> (WKT) parsing and formatting.
- * This package implements the services provided by the {@link org.apache.sis.referencing.CRS#parseWKT(String)}
- * and {@link org.opengis.referencing.IdentifiedObject#toWKT()} convenience methods, with more control.
+ * This package implements the services provided by various convenience methods:
+ *
+ * <ul>
+ * <li>{@link org.apache.sis.referencing.CRS#fromWKT(String)} (SIS parsing static method)</li>
+ * <li>{@link org.opengis.referencing.crs.CRSFactory#createFromWKT(String)} (GeoAPI parsing method)</li>
+ * <li>{@link org.opengis.referencing.operation.MathTransformFactory#createFromWKT(String)} (GeoAPI parsing method)</li>
+ * <li>{@link org.opengis.referencing.IdentifiedObject#toWKT()} (GeoAPI formatting method)</li>
+ * </ul>
+ *
+ * However the {@link org.apache.sis.io.wkt.WKTFormat} class provided in this package gives more control.
* For example this package allows to:
*
* <ul>
- * <li>Format projection and parameters using the names of a chosen authority. For example the
- * <cite>"Mercator (variant A)"</cite> projection is named {@code "Mercator_1SP"} by OGC 01-009
- * and {@code "CT_Mercator"} by GeoTIFF.</li>
- * <li>Format the elements with curly brackets instead than square ones.
+ * <li>Format projection and parameters using the names of a chosen authority.
+ * For example the <cite>"Mercator (variant A)"</cite> projection is named
+ * {@code "Mercator_1SP"} by OGC 01-009 and {@code "CT_Mercator"} by GeoTIFF.</li>
+ * <li>Format the elements with different quote characters or brackets style.
* For example both {@code ID["EPSG",4326]} and {@code ID("EPSG",4326)} are legal WKT.</li>
* <li>Format with a different indentation or format the whole WKT on a single line.</li>
* <li>Apply syntactic coloring on terminal supporting <cite>ANSI escape codes</cite>
* (a.k.a. ECMA-48, ISO/IEC 6429 and X3.64).</li>
- * <li>Ignore the {@code AXIS[…]} elements at parsing time. This approach can be used as a way to force
- * the (<var>longitude</var>, <var>latitude</var>) axes order.</li>
+ * <li>Alter the parsing in a way compatible with non-standard (but commonly used) WKT.
+ * For example some others softwares ignore the {@code AXIS[…]} elements at parsing time.</li>
+ * <li>Report warnings that occurred during parsing or formatting.</li>
* </ul>
*
* <div class="section">Referencing WKT</div>
@@ -56,11 +65,25 @@
* provide their own, limited, WKT parsing and formatting services for the {@code BOX} and {@code POINT} elements.
* A description for this WKT format can be found on <a href="http://en.wikipedia.org/wiki/Well-known_text">Wikipedia</a>.
*
+ * <div class="section">Where to find WKT examples</div>
+ * An excellent source of well-formed WKT is the online <cite>EPSG Geodetic Parameter Registry</cite>.
+ * The WKT of many Coordinate Reference System object can be viewed using the pattern below
+ * (replace {@code 3395} by the EPSG code of the desired CRS):
+ *
+ * <blockquote><b>Example</b>: <cite>"WGS 84 / World Mercator"</cite>:
+ * <a href="http://epsg-registry.org/export.htm?wkt=urn:ogc:def:crs:EPSG::3395">http://epsg-registry.org/export.htm?wkt=urn:ogc:def:crs:EPSG::3395</a>
+ * </blockquote>
+ *
+ * Readers should be aware that some popular other sources of WKT are actually invalid,
+ * since many of them do not comply with EPSG definitions (especially on axis order).
+ * The above-cited EPSG registry is <strong>the</strong> authoritative source
+ * of CRS definitions in the EPSG namespace.
+ *
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Rémi Eve (IRD)
* @author Rueben Schulz (UBC)
* @since 0.4
- * @version 0.4
+ * @version 0.6
* @module
*
* @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
|