001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2002, Institut de Recherche pour le Développement
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: */
018: package org.geotools.referencing.wkt;
019:
020: // J2SE dependencies
021: import java.text.ParseException;
022: import java.text.ParsePosition;
023: import java.util.Iterator;
024:
025: // OpenGIS dependencies
026: import org.opengis.parameter.ParameterDescriptor;
027: import org.opengis.parameter.ParameterValue;
028: import org.opengis.parameter.ParameterValueGroup;
029: import org.opengis.referencing.FactoryException;
030: import org.opengis.referencing.NoSuchIdentifierException;
031: import org.opengis.referencing.operation.CoordinateOperation;
032: import org.opengis.referencing.operation.MathTransform;
033: import org.opengis.referencing.operation.MathTransformFactory;
034: import org.opengis.referencing.operation.NoninvertibleTransformException;
035: import org.opengis.referencing.operation.OperationMethod;
036:
037: // Geotools dependencies
038: import org.geotools.referencing.ReferencingFactoryFinder;
039: import org.geotools.referencing.AbstractIdentifiedObject;
040: import org.geotools.referencing.operation.DefaultMathTransformFactory;
041: import org.geotools.resources.i18n.ErrorKeys;
042: import org.geotools.resources.i18n.Errors;
043:
044: /**
045: * Parser for {@linkplain MathTransform math transform}
046: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
047: * Known Text</cite> (WKT)</A> of math transform.
048: *
049: * @since 2.0
050: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/wkt/MathTransformParser.java $
051: * @version $Id: MathTransformParser.java 25050 2007-04-06 00:41:49Z jgarnett $
052: * @author Remi Eve
053: * @author Martin Desruisseaux
054: * @author Rueben Schulz
055: */
056: public class MathTransformParser extends AbstractParser {
057: /**
058: * The factory to use for creating math transforms.
059: */
060: protected final MathTransformFactory mtFactory;
061:
062: /**
063: * The classification of the last math transform or projection parsed,
064: * or {@code null} if none.
065: */
066: private String classification;
067:
068: /**
069: * The method for the last math transform passed, or {@code null} if none.
070: *
071: * @see #getOperationMethod
072: */
073: private OperationMethod lastMethod;
074:
075: /**
076: * Constructs a parser using the default set of symbols.
077: */
078: public MathTransformParser() {
079: this (Symbols.DEFAULT);
080: }
081:
082: /**
083: * Constructs a parser using the specified set of symbols
084: * and the default factories.
085: *
086: * @param symbols The symbols for parsing and formatting numbers.
087: *
088: * @todo Pass hints in argument.
089: */
090: public MathTransformParser(final Symbols symbols) {
091: this (symbols, ReferencingFactoryFinder
092: .getMathTransformFactory(null));
093: }
094:
095: /**
096: * Constructs a parser for the specified set of symbols and factory.
097: *
098: * @param symbols The symbols for parsing and formatting numbers.
099: * @param mtFactory The factory to use to create {@link MathTransform} objects.
100: */
101: public MathTransformParser(final Symbols symbols,
102: final MathTransformFactory mtFactory) {
103: super (symbols);
104: this .mtFactory = mtFactory;
105: }
106:
107: /**
108: * Parses a math transform element.
109: *
110: * @param text The text to be parsed.
111: * @return The math transform.
112: * @throws ParseException if the string can't be parsed.
113: */
114: public MathTransform parseMathTransform(final String text)
115: throws ParseException {
116: final Element element = getTree(text, new ParsePosition(0));
117: final MathTransform mt = parseMathTransform(element, true);
118: element.close();
119: return mt;
120: }
121:
122: /**
123: * Parses the next element in the specified <cite>Well Know Text</cite> (WKT) tree.
124: *
125: * @param element The element to be parsed.
126: * @return The object.
127: * @throws ParseException if the element can't be parsed.
128: */
129: protected Object parse(final Element element) throws ParseException {
130: return parseMathTransform(element, true);
131: }
132:
133: /**
134: * Parses the next element (a {@link MathTransform}) in the specified
135: * <cite>Well Know Text</cite> (WKT) tree.
136: *
137: * @param element The parent element.
138: * @param required True if parameter is required and false in other case.
139: * @return The next element as a {@link MathTransform} object.
140: * @throws ParseException if the next element can't be parsed.
141: */
142: final MathTransform parseMathTransform(final Element element,
143: final boolean required) throws ParseException {
144: lastMethod = null;
145: classification = null;
146: final Object key = element.peek();
147: if (key instanceof Element) {
148: final String keyword = ((Element) key).keyword.trim()
149: .toUpperCase(symbols.locale);
150: if ("PARAM_MT".equals(keyword))
151: return parseParamMT(element);
152: if ("CONCAT_MT".equals(keyword))
153: return parseConcatMT(element);
154: if ("INVERSE_MT".equals(keyword))
155: return parseInverseMT(element);
156: if ("PASSTHROUGH_MT".equals(keyword))
157: return parsePassThroughMT(element);
158: }
159: if (required) {
160: throw element.parseFailed(null, Errors.format(
161: ErrorKeys.UNKNOW_TYPE_$1, key));
162: }
163: return null;
164: }
165:
166: /**
167: * Parses a "PARAM_MT" element. This element has the following pattern:
168: *
169: * <blockquote><code>
170: * PARAM_MT["<classification-name>" {,<parameter>}* ]
171: * </code></blockquote>
172: *
173: * @param parent The parent element.
174: * @return The "PARAM_MT" element as an {@link MathTransform} object.
175: * @throws ParseException if the "PARAM_MT" element can't be parsed.
176: */
177: private MathTransform parseParamMT(final Element parent)
178: throws ParseException {
179: final Element element = parent.pullElement("PARAM_MT");
180: classification = element.pullString("classification");
181: final ParameterValueGroup parameters;
182: try {
183: parameters = mtFactory.getDefaultParameters(classification);
184: } catch (NoSuchIdentifierException exception) {
185: throw element.parseFailed(exception, null);
186: }
187: /*
188: * Scan over all PARAMETER["name", value] elements and
189: * set the corresponding parameter in the parameter group.
190: */
191: Element param;
192: while ((param = element.pullOptionalElement("PARAMETER")) != null) {
193: final String name = param.pullString("name");
194: final ParameterValue parameter = parameters.parameter(name);
195: final Class type = ((ParameterDescriptor) parameter
196: .getDescriptor()).getValueClass();
197: if (Integer.class.equals(type)) {
198: parameter.setValue(param.pullInteger("value"));
199: } else if (Double.class.equals(type)) {
200: parameter.setValue(param.pullDouble("value"));
201: } else {
202: parameter.setValue(param.pullString("value"));
203: }
204: param.close();
205: }
206: element.close();
207: /*
208: * We now have all informations for constructing the math transform. If the factory is
209: * a Geotools implementation, we will use a special method that returns the operation
210: * method used. Otherwise, we will use the ordinary method and will performs a slower
211: * search for the operation method later if the user ask for it.
212: */
213: final MathTransform transform;
214: try {
215: transform = mtFactory
216: .createParameterizedTransform(parameters);
217: } catch (FactoryException exception) {
218: throw element.parseFailed(exception, null);
219: }
220: if (mtFactory instanceof DefaultMathTransformFactory) {
221: lastMethod = ((DefaultMathTransformFactory) mtFactory)
222: .getLastUsedMethod();
223: }
224: return transform;
225: }
226:
227: /**
228: * Parses a "INVERSE_MT" element. This element has the following pattern:
229: *
230: * <blockquote><code>
231: * INVERSE_MT[<math transform>]
232: * </code></blockquote>
233: *
234: * @param parent The parent element.
235: * @return The "INVERSE_MT" element as an {@link MathTransform} object.
236: * @throws ParseException if the "INVERSE_MT" element can't be parsed.
237: */
238: private MathTransform parseInverseMT(final Element parent)
239: throws ParseException {
240: final Element element = parent.pullElement("INVERSE_MT");
241: try {
242: final MathTransform transform;
243: transform = ((MathTransform) parseMathTransform(element,
244: true)).inverse();
245: element.close();
246: return transform;
247: } catch (NoninvertibleTransformException exception) {
248: throw element.parseFailed(exception, null);
249: }
250: }
251:
252: /**
253: * Parses a "PASSTHROUGH_MT" element. This element has the following pattern:
254: *
255: * <blockquote><code>
256: * PASSTHROUGH_MT[<integer>, <math transform>]
257: * </code></blockquote>
258: *
259: * @param parent The parent element.
260: * @return The "PASSTHROUGH_MT" element as an {@link MathTransform} object.
261: * @throws ParseException if the "PASSTHROUGH_MT" element can't be parsed.
262: */
263: private MathTransform parsePassThroughMT(final Element parent)
264: throws ParseException {
265: final Element element = parent.pullElement("PASSTHROUGH_MT");
266: final int firstAffectedOrdinate = parent
267: .pullInteger("firstAffectedOrdinate");
268: final MathTransform transform = parseMathTransform(element,
269: true);
270: element.close();
271: try {
272: return mtFactory.createPassThroughTransform(
273: firstAffectedOrdinate, transform, 0);
274: } catch (FactoryException exception) {
275: throw element.parseFailed(exception, null);
276: }
277: }
278:
279: /**
280: * Parses a "CONCAT_MT" element. This element has the following pattern:
281: *
282: * <blockquote><code>
283: * CONCAT_MT[<math transform> {,<math transform>}*]
284: * </code></blockquote>
285: *
286: * @param parent The parent element.
287: * @return The "CONCAT_MT" element as an {@link MathTransform} object.
288: * @throws ParseException if the "CONCAT_MT" element can't be parsed.
289: */
290: private MathTransform parseConcatMT(final Element parent)
291: throws ParseException {
292: final Element element = parent.pullElement("CONCAT_MT");
293: MathTransform transform = parseMathTransform(element, true);
294: MathTransform optionalTransform;
295: while ((optionalTransform = parseMathTransform(element, false)) != null) {
296: try {
297: transform = mtFactory.createConcatenatedTransform(
298: transform, optionalTransform);
299: } catch (FactoryException exception) {
300: throw element.parseFailed(exception, null);
301: }
302: }
303: element.close();
304: return transform;
305: }
306:
307: /**
308: * Returns the operation method for the last math transform parsed. This is used by
309: * {@link Parser} in order to built {@link org.opengis.referencing.crs.DerivedCRS}.
310: */
311: final OperationMethod getOperationMethod() {
312: /*
313: * Information available if the math transform used was a Geotools implementation.
314: */
315: if (lastMethod == null) {
316: /*
317: * Not a Geotools implementation. Unfortunatly, there is no obvious way to get this
318: * information using GeoAPI interfaces at this time. Performs a slower and less robust
319: * check.
320: */
321: if (classification != null) {
322: for (final Iterator it = mtFactory.getAvailableMethods(
323: CoordinateOperation.class).iterator(); it
324: .hasNext();) {
325: final OperationMethod method = (OperationMethod) it
326: .next();
327: if (AbstractIdentifiedObject.nameMatches(method,
328: classification)) {
329: lastMethod = method;
330: break;
331: }
332: }
333: }
334: }
335: return lastMethod;
336: }
337: }
|