/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.referencing.wkt;
import java.text.ParseException;
import java.text.ParsePosition;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchIdentifierException;
import org.opengis.referencing.operation.Operation;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.OperationMethod;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.AbstractIdentifiedObject;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
/**
* Parser for {@linkplain MathTransform math transform}
* <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
* Known Text</cite> (WKT)</A> of math transform.
*
* @since 2.0
* @source $URL$
* @version $Id$
* @author Remi Eve
* @author Martin Desruisseaux (IRD)
* @author Rueben Schulz
*/
public class MathTransformParser extends AbstractParser {
/**
* The factory to use for creating math transforms.
*/
protected final MathTransformFactory mtFactory;
/**
* The classification of the last math transform or projection parsed,
* or {@code null} if none.
*/
private String classification;
/**
* The method for the last math transform passed, or {@code null} if none.
*
* @see #getOperationMethod
*/
private OperationMethod lastMethod;
/**
* Constructs a parser using the default set of symbols.
*/
public MathTransformParser() {
this(Symbols.DEFAULT);
}
/**
* Constructs a parser using the specified set of symbols
* and the default factories.
*
* @param symbols The symbols for parsing and formatting numbers.
*
* @todo Pass hints in argument.
*/
public MathTransformParser(final Symbols symbols) {
this(symbols, ReferencingFactoryFinder.getMathTransformFactory(null));
}
/**
* Constructs a parser for the specified set of symbols and factory.
*
* @param symbols The symbols for parsing and formatting numbers.
* @param mtFactory The factory to use to create {@link MathTransform} objects.
*/
public MathTransformParser(final Symbols symbols, final MathTransformFactory mtFactory) {
super(symbols);
this.mtFactory = mtFactory;
}
/**
* Parses a math transform element.
*
* @param text The text to be parsed.
* @return The math transform.
* @throws ParseException if the string can't be parsed.
*/
public MathTransform parseMathTransform(final String text) throws ParseException {
final Element element = getTree(text, new ParsePosition(0));
final MathTransform mt = parseMathTransform(element, true);
element.close();
return mt;
}
/**
* Parses the next element in the specified <cite>Well Know Text</cite> (WKT) tree.
*
* @param element The element to be parsed.
* @return The object.
* @throws ParseException if the element can't be parsed.
*/
protected Object parse(final Element element) throws ParseException {
return parseMathTransform(element, true);
}
/**
* Parses the next element (a {@link MathTransform}) in the specified
* <cite>Well Know Text</cite> (WKT) tree.
*
* @param element The parent element.
* @param required True if parameter is required and false in other case.
* @return The next element as a {@link MathTransform} object.
* @throws ParseException if the next element can't be parsed.
*/
final MathTransform parseMathTransform(final Element element, final boolean required)
throws ParseException
{
lastMethod = null;
classification = null;
final Object key = element.peek();
if (key instanceof Element) {
final String keyword = ((Element) key).keyword.trim().toUpperCase(symbols.locale);
if ("PARAM_MT" .equals(keyword)) return parseParamMT (element);
if ("CONCAT_MT" .equals(keyword)) return parseConcatMT (element);
if ("INVERSE_MT" .equals(keyword)) return parseInverseMT (element);
if ("PASSTHROUGH_MT".equals(keyword)) return parsePassThroughMT(element);
}
if (required) {
throw element.parseFailed(null, Errors.format(ErrorKeys.UNKNOW_TYPE_$1, key));
}
return null;
}
/**
* Parses a "PARAM_MT" element. This element has the following pattern:
*
* <blockquote><code>
* PARAM_MT["<classification-name>" {,<parameter>}* ]
* </code></blockquote>
*
* @param parent The parent element.
* @return The "PARAM_MT" element as an {@link MathTransform} object.
* @throws ParseException if the "PARAM_MT" element can't be parsed.
*/
private MathTransform parseParamMT(final Element parent) throws ParseException {
final Element element = parent.pullElement("PARAM_MT");
classification = element.pullString("classification");
final ParameterValueGroup parameters;
try {
parameters = mtFactory.getDefaultParameters(classification);
} catch (NoSuchIdentifierException exception) {
throw element.parseFailed(exception, null);
}
/*
* Scan over all PARAMETER["name", value] elements and
* set the corresponding parameter in the parameter group.
*/
Element param;
while ((param=element.pullOptionalElement("PARAMETER")) != null) {
final String name = param.pullString("name");
final ParameterValue parameter = parameters.parameter(name);
final Class type = parameter.getDescriptor().getValueClass();
if (Integer.class.equals(type)) {
parameter.setValue(param.pullInteger("value"));
} else if (Double.class.equals(type)) {
parameter.setValue(param.pullDouble("value"));
} else {
parameter.setValue(param.pullString("value"));
}
param.close();
}
element.close();
/*
* We now have all informations for constructing the math transform. If the factory is
* a Geotools implementation, we will use a special method that returns the operation
* method used. Otherwise, we will use the ordinary method and will performs a slower
* search for the operation method later if the user ask for it.
*/
final MathTransform transform;
try {
transform = mtFactory.createParameterizedTransform(parameters);
} catch (FactoryException exception) {
throw element.parseFailed(exception, null);
}
lastMethod = mtFactory.getLastMethodUsed();
return transform;
}
/**
* Parses a "INVERSE_MT" element. This element has the following pattern:
*
* <blockquote><code>
* INVERSE_MT[<math transform>]
* </code></blockquote>
*
* @param parent The parent element.
* @return The "INVERSE_MT" element as an {@link MathTransform} object.
* @throws ParseException if the "INVERSE_MT" element can't be parsed.
*/
private MathTransform parseInverseMT(final Element parent) throws ParseException {
final Element element = parent.pullElement("INVERSE_MT");
try {
final MathTransform transform;
transform = parseMathTransform(element, true).inverse();
element.close();
return transform;
}
catch (NoninvertibleTransformException exception) {
throw element.parseFailed(exception, null);
}
}
/**
* Parses a "PASSTHROUGH_MT" element. This element has the following pattern:
*
* <blockquote><code>
* PASSTHROUGH_MT[<integer>, <math transform>]
* </code></blockquote>
*
* @param parent The parent element.
* @return The "PASSTHROUGH_MT" element as an {@link MathTransform} object.
* @throws ParseException if the "PASSTHROUGH_MT" element can't be parsed.
*/
private MathTransform parsePassThroughMT(final Element parent) throws ParseException {
final Element element = parent.pullElement("PASSTHROUGH_MT");
final int firstAffectedOrdinate = parent.pullInteger("firstAffectedOrdinate");
final MathTransform transform = parseMathTransform(element, true);
element.close();
try {
return mtFactory.createPassThroughTransform(firstAffectedOrdinate, transform, 0);
} catch (FactoryException exception) {
throw element.parseFailed(exception, null);
}
}
/**
* Parses a "CONCAT_MT" element. This element has the following pattern:
*
* <blockquote><code>
* CONCAT_MT[<math transform> {,<math transform>}*]
* </code></blockquote>
*
* @param parent The parent element.
* @return The "CONCAT_MT" element as an {@link MathTransform} object.
* @throws ParseException if the "CONCAT_MT" element can't be parsed.
*/
private MathTransform parseConcatMT(final Element parent) throws ParseException {
final Element element = parent.pullElement("CONCAT_MT");
MathTransform transform = parseMathTransform(element, true);
MathTransform optionalTransform;
while ((optionalTransform = parseMathTransform(element, false)) != null) {
try {
transform = mtFactory.createConcatenatedTransform(transform, optionalTransform);
} catch (FactoryException exception) {
throw element.parseFailed(exception, null);
}
}
element.close();
return transform;
}
/**
* 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}.
*/
final OperationMethod getOperationMethod() {
if (lastMethod == null) {
/*
* Safety in case come MathTransformFactory implementation do not support
* getLastMethod(). Performs a slower and less robust check as a fallback.
*/
if (classification != null) {
for (final OperationMethod method : mtFactory.getAvailableMethods(Operation.class)) {
if (AbstractIdentifiedObject.nameMatches(method, classification)) {
lastMethod = method;
break;
}
}
}
}
return lastMethod;
}
}