Author: desruisseaux
Date: Wed Feb 11 21:13:41 2015
New Revision: 1659071
URL: http://svn.apache.org/r1659071
Log:
Partial port of DefaultMathTransformFactory. Abstract for now, will become a
concrete class after the port has been completed.
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
(with props)
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -31,7 +31,7 @@ import static org.apache.sis.internal.me
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.4
- * @version 0.5
+ * @version 0.6
* @module
*/
public final class Formulas extends Static {
@@ -43,7 +43,7 @@ public final class Formulas extends Stat
* @see #ANGULAR_TOLERANCE
* @see org.apache.sis.internal.util.Numerics#COMPARISON_THRESHOLD
*/
- public static final double LINEAR_TOLERANCE = 1.0;
+ public static final double LINEAR_TOLERANCE = 0.01;
/**
* Default tolerance threshold for comparing ordinate values in a
geographic CRS,
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -24,6 +24,7 @@ import org.opengis.annotation.UML;
import org.opengis.annotation.Specification;
import org.opengis.referencing.cs.*;
import org.opengis.referencing.crs.*;
+import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.datum.PrimeMeridian;
import org.apache.sis.util.Static;
import org.apache.sis.util.Utilities;
@@ -49,7 +50,7 @@ import static org.apache.sis.internal.ut
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.5
- * @version 0.5
+ * @version 0.6
* @module
*/
public final class ReferencingUtilities extends Static {
@@ -167,6 +168,27 @@ public final class ReferencingUtilities
}
/**
+ * Returns the ellipsoid used by the specified coordinate reference
system, providing that
+ * the two first dimensions use an instance of {@link GeographicCRS}.
Otherwise (i.e. if the
+ * two first dimensions are not geographic), returns {@code null}.
+ *
+ * @param crs The coordinate reference system for which to get the
ellipsoid.
+ * @return The ellipsoid in the given CRS, or {@code null} if none.
+ *
+ * @since 0.6
+ */
+ public static Ellipsoid
getEllipsoidOfGeographicCRS(CoordinateReferenceSystem crs) {
+ while (!(crs instanceof GeographicCRS)) {
+ if (crs instanceof CompoundCRS) {
+ crs = ((CompoundCRS) crs).getComponents().get(0);
+ } else {
+ return null;
+ }
+ }
+ return ((GeographicCRS) crs).getDatum().getEllipsoid();
+ }
+
+ /**
* Derives a geographic CRS with (<var>longitude</var>,
<var>latitude</var>) axis order in decimal degrees.
* If no such CRS can be obtained or created, returns {@code null}.
*
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -377,7 +377,8 @@ public class DefaultParameterValue<T> ex
*/
@Override
public double doubleValue(final Unit<?> unit) throws
IllegalArgumentException, IllegalStateException {
- return getConverterTo(unit).convert(doubleValue());
+ final double value = doubleValue(); // Invoke first in case it throws
an exception.
+ return getConverterTo(unit).convert(value);
}
/**
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -395,6 +395,8 @@ public class DefaultOperationMethod exte
* which is the most conservative return value.
*
* @return Interface implemented by all coordinate operations that use
this method.
+ *
+ * @see
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#getAvailableMethods(Class)
*/
public Class<? extends SingleOperation> getOperationType() {
return SingleOperation.class;
@@ -409,6 +411,9 @@ public class DefaultOperationMethod exte
* this property is mandatory according ISO 19111, but optional in Apache
SIS.</div>
*
* @return The formula used by this method, or {@code null} if unknown.
+ *
+ * @see DefaultFormula
+ * @see
org.apache.sis.referencing.operation.transform.MathTransformProvider
*/
@Override
public Formula getFormula() {
@@ -420,6 +425,8 @@ public class DefaultOperationMethod exte
* May be null if unknown, as in an <cite>Affine Transform</cite>.
*
* @return The dimension of source CRS, or {@code null} if unknown.
+ *
+ * @see
org.apache.sis.referencing.operation.transform.AbstractMathTransform#getSourceDimensions()
*/
@Override
public Integer getSourceDimensions() {
@@ -431,6 +438,8 @@ public class DefaultOperationMethod exte
* May be null if unknown, as in an <cite>Affine Transform</cite>.
*
* @return The dimension of target CRS, or {@code null} if unknown.
+ *
+ * @see
org.apache.sis.referencing.operation.transform.AbstractMathTransform#getTargetDimensions()
*/
@Override
public Integer getTargetDimensions() {
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -130,6 +130,8 @@ public abstract class AbstractMathTransf
* Gets the dimension of input points.
*
* @return The dimension of input points.
+ *
+ * @see
org.apache.sis.referencing.operation.DefaultOperationMethod#getSourceDimensions()
*/
@Override
public abstract int getSourceDimensions();
@@ -138,6 +140,8 @@ public abstract class AbstractMathTransf
* Gets the dimension of output points.
*
* @return The dimension of output points.
+ *
+ * @see
org.apache.sis.referencing.operation.DefaultOperationMethod#getTargetDimensions()
*/
@Override
public abstract int getTargetDimensions();
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1659071&view=auto
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
(added)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -0,0 +1,603 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.transform;
+
+import java.util.IdentityHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import javax.measure.quantity.Length;
+import javax.measure.unit.SI;
+import javax.measure.unit.Unit;
+import org.opengis.metadata.Identifier;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.datum.Ellipsoid;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.SingleOperation;
+import org.opengis.util.FactoryException;
+import org.opengis.util.NoSuchIdentifierException;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Deprecable;
+import org.apache.sis.util.collection.WeakHashSet;
+import org.apache.sis.util.iso.AbstractFactory;
+import org.apache.sis.util.iso.DefaultNameSpace;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
+
+
+/**
+ * Low level factory for creating {@linkplain AbstractMathTransform math
transforms}.
+ * High level GIS applications usually do not need to use this factory
directly.
+ * They can use the static convenience methods in the {@link
org.apache.sis.referencing.CRS}
+ * or {@link MathTransforms} classes instead.
+ *
+ * {@section Math transforms discovery}
+ * Unless {@linkplain #DefaultMathTransformFactory(Iterable) specified
explicitely at construction time},
+ * {@code OperationMethod} implementations shall be listed in the following
file:
+ *
+ * {@preformat text
+ * META-INF/services/org.opengis.referencing.operation.OperationMethod
+ * }
+ *
+ * {@code DefaultMathTransformFactory} parses the above-cited files in all JAR
files in order to find all available
+ * operation methods. By default, only operation methods that implement the
{@link MathTransformProvider} interface
+ * can be used by the {@code create(…)} methods in this class.
+ *
+ * {@section Thread safety}
+ * This class is safe for multi-thread usage if all referenced {@code
OperationMethod} instances are thread-safe.
+ * There is typically only one {@code MathTransformFactory} instance for the
whole application.
+ *
+ * @author Martin Desruisseaux (Geomatys, IRD)
+ * @since 0.6
+ * @version 0.6
+ * @module
+ */
+public abstract class DefaultMathTransformFactory extends AbstractFactory
implements MathTransformFactory {
+ /**
+ * The separator character between an identifier and its namespace in the
argument given to
+ * {@link #getOperationMethod(String)}. For example this is the separator
in {@code "EPSG:9807"}.
+ *
+ * This is defined as a constant for now, but we may make it configurable
in a future version.
+ */
+ private static final char IDENTIFIER_SEPARATOR =
DefaultNameSpace.DEFAULT_SEPARATOR;
+
+ /**
+ * Minimal precision of ellipsoid semi-major and semi-minor axis lengths,
in metres.
+ * If the length difference between the axis of two ellipsoids is greater
than this threshold,
+ * we will report a mismatch. This is used for logging purpose only and do
not have any impact
+ * on the {@code MathTransform} objects to be created by this factory.
+ */
+ private static final double ELLIPSOID_PRECISION =
Formulas.LINEAR_TOLERANCE;
+
+ /**
+ * All methods specified at construction time or found on the classpath.
+ * If the iterable is an instance of {@link ServiceLoader}, then it will
+ * be reloaded when {@link #reload()} is invoked.
+ *
+ * <p>All uses of this field shall be synchronized on {@code methods}.</p>
+ */
+ private final Iterable<? extends OperationMethod> methods;
+
+ /**
+ * The methods by name, cached for faster lookup and for avoiding some
+ * synchronizations on {@link #methods} and {@link #pool}.
+ */
+ private final ConcurrentMap<String, OperationMethod> methodsByName;
+
+ /**
+ * The methods by type. All uses of this map shall be synchronized on
{@code methodsByType}.
+ *
+ * <div class="note"><b>Note:</b>
+ * we do not use a concurrent map here because the number of entries is
expected to be very small
+ * (about 2 entries), which make concurrent algorithms hardly efficient.
Furthermore this map is
+ * not used often.
+ * </div>
+ */
+ private final Map<Class<?>, OperationMethodSet> methodsByType;
+
+ /**
+ * The last method used by a {@code create(…)} method.
+ */
+ private final ThreadLocal<OperationMethod> lastMethod;
+
+ /**
+ * The math transforms created so far. This pool is used in order
+ * to return instances of existing math transforms when possible.
+ */
+ private final WeakHashSet<MathTransform> pool;
+
+ /**
+ * Creates a new factory which will discover operation methods with a
{@link ServiceLoader}.
+ * {@code OperationMethod} implementations shall be listed in the
following file:
+ *
+ * {@preformat text
+ * META-INF/services/org.opengis.referencing.operation.OperationMethod
+ * }
+ *
+ * {@code DefaultMathTransformFactory} parses the above-cited files in all
JAR files in order to find all available
+ * operation methods. By default, only operation methods that implement
the {@link MathTransformProvider} interface
+ * can be used by the {@code create(…)} methods in this class.
+ */
+ public DefaultMathTransformFactory() {
+ this(ServiceLoader.load(OperationMethod.class));
+ }
+
+ /**
+ * Creates a new factory which will use the given operation methods. The
given iterable is stored by reference —
+ * its content is <strong>not</strong> copied — in order to allow deferred
{@code OperationMethod} constructions.
+ * Note that by default, only operation methods that implement the {@link
MathTransformProvider} interface can be
+ * used by the {@code create(…)} methods in this class.
+ *
+ * <p><b>Requirements:</b></p>
+ * <ul>
+ * <li>The given iterable should not contain duplicated elements.</li>
+ * <li>The given iterable shall be stable: all elements returned by the
first iteration must also be
+ * returned by any subsequent iterations, unless {@link #reload()}
has been invoked.</li>
+ * <li>{@code OperationMethod} instances should also implement {@link
MathTransformProvider}.</li>
+ * <li>All {@code OperationMethod} instances shall be thread-safe.</li>
+ * <li>The {@code Iterable} itself does not need to be thread-safe since
all usages will be synchronized as below:
+ *
+ * {@preformat java
+ * synchronized (methods) {
+ * for (OperationMethod method : methods) {
+ * // Use the method here.
+ * }
+ * }
+ * }
+ * </li>
+ * </ul>
+ *
+ * @param methods The operation methods to use, stored by reference (not
copied).
+ */
+ public DefaultMathTransformFactory(final Iterable<? extends
OperationMethod> methods) {
+ ArgumentChecks.ensureNonNull("methods", methods);
+ this.methods = methods;
+ methodsByName = new ConcurrentHashMap<>();
+ methodsByType = new IdentityHashMap<>();
+ lastMethod = new ThreadLocal<>();
+ pool = new WeakHashSet<>(MathTransform.class);
+ }
+
+ /**
+ * Returns a set of available methods for coordinate operations of the
given type.
+ * The {@code type} argument can be used for filtering the kind of
operations described by the returned
+ * {@code OperationMethod}s. The argument is usually (but not restricted
to) one of the following types:
+ *
+ * <ul>
+ * <li>{@link org.opengis.referencing.operation.Transformation}
+ * for coordinate operations described by empirically derived
parameters.</li>
+ * <li>{@link org.opengis.referencing.operation.Conversion}
+ * for coordinate operations described by definitions.</li>
+ * <li>{@link org.opengis.referencing.operation.Projection}
+ * for conversions from geodetic latitudes and longitudes to plane
(map) coordinates.</li>
+ * <li>{@link SingleOperation} for all coordinate operations.</li>
+ * </ul>
+ *
+ * This method may conservatively return more {@code OperationMethod}
elements than requested
+ * if it does not support filtering by the given type.
+ *
+ * @param type <code>{@linkplain SingleOperation}.class</code> for
fetching all operation methods,
+ * <code>{@linkplain Projection}.class</code> for fetching only
map projection methods, <i>etc</i>.
+ * @return Methods available in this factory for coordinate operations of
the given type.
+ *
+ * @see #getDefaultParameters(String)
+ * @see #createParameterizedTransform(ParameterValueGroup)
+ * @see DefaultOperationMethod#getOperationType()
+ */
+ @Override
+ public Set<OperationMethod> getAvailableMethods(final Class<? extends
SingleOperation> type) {
+ OperationMethodSet set;
+ synchronized (methodsByType) {
+ set = methodsByType.get(type);
+ }
+ if (set == null) {
+ /*
+ * Implementation note: we are better to avoid holding a lock on
'methods' and 'methodsByType'
+ * in same time because the 'methods' iterator could be a user's
implementation which callback
+ * this factory.
+ */
+ synchronized (methods) {
+ set = new OperationMethodSet(type, methods);
+ }
+ final OperationMethodSet previous;
+ synchronized (methodsByType) {
+ previous = methodsByType.putIfAbsent(type, set);
+ }
+ if (previous != null) {
+ set = previous;
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Returns the operation method for the specified name or identifier. The
given argument shall be either
+ * a method {@linkplain DefaultOperationMethod#getName() name} (e.g.
<cite>"Transverse Mercator"</cite>)
+ * or one of its {@linkplain DefaultOperationMethod#getIdentifiers()
identifiers} (e.g. {@code "EPSG:9807"}).
+ *
+ * <p>The search is case-insensitive. Comparisons against method names can
be
+ * {@linkplain DefaultOperationMethod#isHeuristicMatchForName(String)
heuristic}.</p>
+ *
+ * <p>If more than one method match the given identifier, then the first
(according iteration order)
+ * non-{@linkplain Deprecable#isDeprecated() deprecated} matching method
is returned. If all matching
+ * methods are deprecated, the first one is returned.</p>
+ *
+ * @param identifier The name or identifier of the operation method to
search.
+ * @return The operation method for the given name or identifier.
+ * @throws NoSuchIdentifierException if there is no operation method
registered for the specified identifier.
+ */
+ public OperationMethod getOperationMethod(String identifier) throws
NoSuchIdentifierException {
+ identifier = CharSequences.trimWhitespaces(identifier);
+ ArgumentChecks.ensureNonEmpty("identifier", identifier);
+ OperationMethod method = methodsByName.get(identifier);
+ if (method == null) {
+ for (final OperationMethod m : methods) {
+ if (matches(m, identifier)) {
+ /*
+ * Stop the iteration at the first non-deprecated method.
+ * If we find only deprecated methods, take the first one.
+ */
+ final boolean isDeprecated = (m instanceof Deprecable) &&
((Deprecable) m).isDeprecated();
+ if (!isDeprecated || method == null) {
+ method = m;
+ if (!isDeprecated) {
+ break;
+ }
+ }
+ }
+ }
+ if (method == null) {
+ throw new
NoSuchIdentifierException(Errors.format(Errors.Keys.NoSuchOperationMethod_1,
method), identifier);
+ }
+ /*
+ * Remember the method we just found, for faster check next time.
+ */
+ final OperationMethod previous =
methodsByName.putIfAbsent(identifier.intern(), method);
+ if (previous != null) {
+ method = previous;
+ }
+ }
+ return method;
+ }
+
+ /**
+ * Returns {@code true} if the name or an identifier of the given method
matches the given {@code identifier}.
+ *
+ * This method is private and static for now, but we may consider to make
it a protected member in a future
+ * SIS version in order to give users a chance to override the matching
criterion. We don't do that yet
+ * because we would like to have at least one use case before doing so.
+ *
+ * @param method The method to test for a match.
+ * @param identifier The name or identifier of the operation method to
search.
+ * @return {@code true} if the given method is a match for the given
identifier.
+ */
+ private static boolean matches(final OperationMethod method, final String
identifier) {
+ if (IdentifiedObjects.isHeuristicMatchForName(method, identifier)) {
+ return true;
+ }
+ for (int s = identifier.indexOf(IDENTIFIER_SEPARATOR); s >= 0;
+ s = identifier.indexOf(IDENTIFIER_SEPARATOR, s))
+ {
+ final String codespace = identifier.substring(0, s).trim();
+ final String code = identifier.substring(++s).trim();
+ for (final Identifier id : method.getIdentifiers()) {
+ if (codespace.equalsIgnoreCase(id.getCodeSpace()) &&
code.equalsIgnoreCase(id.getCode())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the default parameter values for a math transform using the
given method.
+ * The method argument is the name of any operation method returned by the
{@link #getAvailableMethods(Class)} method.
+ * A typical example is
+ * "<a
href="http://www.remotesensing.org/geotiff/proj_list/transverse_mercator.html">Transverse
Mercator</a>").
+ *
+ * <p>This method creates new parameter instances at every call. The
returned object is intended to be modified
+ * by the user before to be passed to <code>{@linkplain
#createParameterizedTransform(ParameterValueGroup)
+ * createParameterizedTransform}(parameters)</code>.</p>
+ *
+ * @param method The name or identifier of the operation method to search.
+ * @return A new group of parameter values for the {@code OperationMethod}
identified by the given name.
+ * @throws NoSuchIdentifierException if there is no method registered for
the given name or identifier.
+ *
+ * @see #getAvailableMethods(Class)
+ * @see #createParameterizedTransform(ParameterValueGroup)
+ * @see
org.apache.sis.referencing.operation.transform.AbstractMathTransform#getParameterValues()
+ */
+ @Override
+ public ParameterValueGroup getDefaultParameters(final String method)
throws NoSuchIdentifierException {
+ return getOperationMethod(method).getParameters().createValue();
+ }
+
+ /**
+ * Returns the value of the given parameter in the given unit, or {@code
NaN} if the parameter is not set.
+ *
+ * <p><b>NOTE:</b> Do not merge this method with {@code ensureSet(…)}. We
keep those two methods
+ * separated in order to give to {@code createBaseToDerived(…)} a "all or
nothing" behavior.</p>
+ */
+ private static double getValue(final ParameterValue<?> parameter, final
Unit<Length> unit) {
+ return (parameter.getValue() != null) ? parameter.doubleValue(unit) :
Double.NaN;
+ }
+
+ /**
+ * Ensures that a value is set in the given parameter.
+ *
+ * <ul>
+ * <li>If the parameter has no value, then it is set to the given
value.<li>
+ * <li>If the parameter already has a value, then the parameter is left
unchanged
+ * but its value is compared to the given one for consistency.</li>
+ * </ul>
+ *
+ * @param parameter The parameter which must have a value.
+ * @param actual The current parameter value, or {@code NaN} if none.
+ * @param expected The expected parameter value, derived from the
ellipsoid.
+ * @param unit The unit of {@code value}.
+ * @param tolerance Maximal difference (in unit of {@code unit}) for
considering the two values as equivalent.
+ * @return {@code true} if there is a mismatch between the actual value
and the expected one.
+ */
+ private static boolean ensureSet(final ParameterValue<?> parameter, final
double actual,
+ final double expected, final Unit<?> unit, final double tolerance)
+ {
+ if (Math.abs(actual - expected) <= tolerance) {
+ return false;
+ }
+ if (Double.isNaN(actual)) {
+ parameter.setValue(expected, unit);
+ }
+ return true;
+ }
+
+ /**
+ * Creates a {@linkplain
#createParameterizedTransform(ParameterValueGroup) parameterized transform}
+ * from a base CRS to a derived CS. This convenience method {@linkplain
#createConcatenatedTransform
+ * concatenates} the parameterized transform with any other transform
required for performing units
+ * changes and ordinates swapping.
+ *
+ * In addition, this method infers the {@code "semi_major"} and {@code
"semi_minor"} parameters values
+ * from the {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid
ellipsoid} associated to the
+ * {@code baseCRS}, if those parameters are not explicitly given and if
they are applicable (typically
+ * for cartographic projections).
+ *
+ * <p>The {@code OperationMethod} instance used by this method can be
obtained by a call to
+ * {@link #getLastMethodUsed()}.</p>
+ *
+ * @param baseCRS The source coordinate reference system.
+ * @param parameters The parameter values for the transform.
+ * @param derivedCS The target coordinate system.
+ * @return The parameterized transform.
+ * @throws NoSuchIdentifierException if there is no transform registered
for the method.
+ * @throws FactoryException if the object creation failed. This exception
is thrown
+ * if some required parameter has not been supplied, or has
illegal value.
+ */
+ @Override
+ public MathTransform createBaseToDerived(final CoordinateReferenceSystem
baseCRS,
+ final ParameterValueGroup parameters, final CoordinateSystem
derivedCS)
+ throws NoSuchIdentifierException, FactoryException
+ {
+ /*
+ * If the user's parameters do not contain semi-major and semi-minor
axis lengths, infer
+ * them from the ellipsoid. We have to do that because those
parameters are often omitted,
+ * since the standard place where to provide this information is in
the ellipsoid object.
+ */
+ RuntimeException failure = null;
+ final Ellipsoid ellipsoid =
ReferencingUtilities.getEllipsoidOfGeographicCRS(baseCRS);
+ if (ellipsoid != null) {
+ ParameterValue<?> mismatchedParam = null;
+ double mismatchedValue = 0;
+ try {
+ final ParameterValue<?> semiMajor =
parameters.parameter("semi_major");
+ final ParameterValue<?> semiMinor =
parameters.parameter("semi_minor");
+ final Unit<Length> axisUnit = ellipsoid.getAxisUnit();
+ /*
+ * The two calls to getOptional(…) shall succeed before we
write anything, in order to have a
+ * "all or nothing" behavior as much as possible. Note that
Ellipsoid.getSemi**Axis() have no
+ * reason to fail, so we don't take precaution for them.
+ */
+ final double a = getValue(semiMajor, axisUnit);
+ final double b = getValue(semiMinor, axisUnit);
+ final double tol =
SI.METRE.getConverterTo(axisUnit).convert(ELLIPSOID_PRECISION);
+ if (ensureSet(semiMajor, a, ellipsoid.getSemiMajorAxis(),
axisUnit, tol)) {
+ mismatchedParam = semiMajor;
+ mismatchedValue = a;
+ }
+ if (ensureSet(semiMinor, b, ellipsoid.getSemiMinorAxis(),
axisUnit, tol)) {
+ mismatchedParam = semiMinor;
+ mismatchedValue = b;
+ }
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ /*
+ * Parameter not found, or is not numeric, or unit of
measurement is not linear.
+ * Those exceptions should never occur with map projections,
but may happen for
+ * some other operations like Molodenski¹.
+ *
+ * Do not touch to the parameters. We will see if
createParameterizedTransform(…)
+ * can do something about that. If it can not,
createParameterizedTransform(…) is
+ * the right place to throw the exception.
+ *
+ * ¹ The actual Molodenski parameter names are
"src_semi_major" and "src_semi_minor".
+ * But we do not try to set them because we have no way to
set the corresponding
+ * "tgt_semi_major" and "tgt_semi_minor" parameters anyway.
+ */
+ failure = e;
+ }
+ if (mismatchedParam != null) {
+ Logging.log(DefaultMathTransformFactory.class,
"createBaseToDerived",
+ Messages.getResources((Locale)
null).getLogRecord(Level.WARNING,
+ Messages.Keys.MismatchedEllipsoidAxisLength_3,
ellipsoid.getName().getCode(),
+
mismatchedParam.getDescriptor().getName().getCode(), mismatchedValue));
+ }
+ }
+ MathTransform baseToDerived;
+ try {
+ baseToDerived = createParameterizedTransform(parameters);
+ final OperationMethod method = lastMethod.get();
+ baseToDerived = createBaseToDerived(baseCRS, baseToDerived,
derivedCS);
+ lastMethod.set(method);
+ } catch (FactoryException e) {
+ if (failure != null) {
+ e.addSuppressed(failure);
+ }
+ throw e;
+ }
+ return baseToDerived;
+ }
+
+ /**
+ * Creates a transform from a base CRS to a derived CS. This method
expects a "raw" transform without
+ * unit conversion or axis swapping. Such "raw" transforms are typically
map projections working on
+ * (<cite>longitude</cite>, <cite>latitude</cite>) axes in degrees and
(<cite>x</cite>, <cite>y</cite>)
+ * axes in metres. This method inspects the coordinate systems and prepend
or append the unit conversions
+ * and axis swapping automatically.
+ *
+ * @param baseCRS The source coordinate reference system.
+ * @param projection The "raw" <cite>base to derived</cite> transform.
+ * @param derivedCS the target coordinate system.
+ * @return The parameterized transform.
+ * @throws FactoryException if the object creation failed. This exception
is thrown
+ * if some required parameter has not been supplied, or has
illegal value.
+ */
+ public abstract MathTransform createBaseToDerived(final
CoordinateReferenceSystem baseCRS,
+ final MathTransform projection, final CoordinateSystem derivedCS)
+ throws FactoryException;
+
+ /**
+ * Creates a transform by concatenating two existing transforms.
+ * A concatenated transform acts in the same way as applying two
transforms, one after the other.
+ *
+ * <p>The dimension of the output space of the first transform must match
the dimension of the input space
+ * in the second transform. In order to concatenate more than two
transforms, use this method repeatedly.</p>
+ *
+ * @param transform1 The first transform to apply to points.
+ * @param transform2 The second transform to apply to points.
+ * @return The concatenated transform.
+ * @throws FactoryException if the object creation failed.
+ */
+ @Override
+ public MathTransform createConcatenatedTransform(final MathTransform
transform1,
+ final MathTransform
transform2)
+ throws FactoryException
+ {
+ MathTransform tr;
+ try {
+ tr = MathTransforms.concatenate(transform1, transform2);
+ } catch (IllegalArgumentException exception) {
+ throw new FactoryException(exception);
+ }
+ tr = pool.unique(tr);
+ return tr;
+ }
+
+ /**
+ * Creates a transform which passes through a subset of ordinates to
another transform.
+ * This allows transforms to operate on a subset of ordinates.
+ *
+ * <div class="note"><b>Example:</b>
+ * Giving (<var>latitude</var>, <var>longitude</var>, <var>height</var>)
coordinates,
+ * a pass through transform can convert the height values from meters to
feet without
+ * affecting the (<var>latitude</var>, <var>longitude</var>) values.</div>
+ *
+ * The resulting transform will have the following dimensions:
+ *
+ * {@preformat java
+ * Source: firstAffectedOrdinate + subTransform.getSourceDimensions()
+ numTrailingOrdinates
+ * Target: firstAffectedOrdinate + subTransform.getTargetDimensions()
+ numTrailingOrdinates
+ * }
+ *
+ * @param firstAffectedOrdinate The lowest index of the affected
ordinates.
+ * @param subTransform Transform to use for affected ordinates.
+ * @param numTrailingOrdinates Number of trailing ordinates to pass
through. Affected ordinates will range
+ * from {@code firstAffectedOrdinate} inclusive to {@code
dimTarget-numTrailingOrdinates} exclusive.
+ * @return A pass through transform.
+ * @throws FactoryException if the object creation failed.
+ */
+ @Override
+ public MathTransform createPassThroughTransform(final int
firstAffectedOrdinate,
+ final MathTransform
subTransform,
+ final int
numTrailingOrdinates)
+ throws FactoryException
+ {
+ MathTransform tr;
+ try {
+ tr = PassThroughTransform.create(firstAffectedOrdinate,
subTransform, numTrailingOrdinates);
+ } catch (IllegalArgumentException exception) {
+ throw new FactoryException(exception);
+ }
+ tr = pool.unique(tr);
+ return tr;
+ }
+
+ /**
+ * Returns the operation method used for the latest call to
+ * {@link #createParameterizedTransform(ParameterValueGroup)} in the
currently running thread.
+ * Returns {@code null} if not applicable.
+ *
+ * @see #createParameterizedTransform(ParameterValueGroup)
+ */
+ @Override
+ public OperationMethod getLastMethodUsed() {
+ return lastMethod.get();
+ }
+
+ /**
+ * Notifies this factory that the elements provided by the {@code
Iterable<OperationMethod>} may have changed.
+ * This method performs the following steps:
+ *
+ * <ul>
+ * <li>Clears all caches.</li>
+ * <li>If the {@code Iterable} given at construction time is an instance
of {@link ServiceLoader},
+ * invokes its {@code reload()} method.</li>
+ * </ul>
+ *
+ * This method is useful to sophisticated applications which dynamically
make new plug-ins available at runtime,
+ * for example following changes of the application classpath.
+ *
+ * @see #DefaultMathTransformFactory(Iterable)
+ * @see ServiceLoader#reload()
+ */
+ public void reload() {
+ synchronized (methods) {
+ methodsByName.clear();
+ if (methods instanceof ServiceLoader<?>) {
+ ((ServiceLoader<?>) methods).reload();
+ }
+ synchronized (methodsByType) {
+ for (final OperationMethodSet c : methodsByType.values()) {
+ c.reset();
+ }
+ }
+ pool.clear();
+ }
+ }
+}
Propchange:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -585,6 +585,11 @@ public final class Errors extends Indexe
public static final short NoSuchAuthorityCode_3 = 137;
/**
+ * No operation method found for name of identifier “{0}”.
+ */
+ public static final short NoSuchOperationMethod_1 = 179;
+
+ /**
* No unit of measurement has been specified.
*/
public static final short NoUnit = 72;
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -150,6 +150,7 @@ NotASkewSymmetricMatrix = Matr
NotAUnicodeIdentifier_1 = Text \u201c{0}\u201d is not a Unicode
identifier.
NotComparableClass_1 = Class \u2018{0}\u2019 is not a comparable.
NoSuchAuthorityCode_3 = No code \u201c{2}\u201d from authority
\u201c{0}\u201d found for object of type \u2018{1}\u2019.
+NoSuchOperationMethod_1 = No operation method found for name of
identifier \u201c{0}\u201d.
NoUnit = No unit of measurement has been specified.
NullArgument_1 = Argument \u2018{0}\u2019 shall not be null.
NullCollectionElement_1 = \u2018{0}\u2019 collection does not accept
null elements.
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -140,6 +140,7 @@ NotASkewSymmetricMatrix = La m
NotAUnicodeIdentifier_1 = Le texte \u00ab\u202f{0}\u202f\u00bb
n\u2019est pas un identifiant Unicode.
NotComparableClass_1 = La classe \u2018{0}\u2019 n\u2019est pas
comparable.
NoSuchAuthorityCode_3 = Aucun code \u00ab\u202f{2}\u202f\u00bb de
l\u2019autorit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019a \u00e9t\u00e9
trouv\u00e9 pour un objet de type \u2018{1}\u2019.
+NoSuchOperationMethod_1 = Aucune m\u00e9thode n\u2019a \u00e9t\u00e9
trouv\u00e9e pour le nom ou l\u2019identifiant \u00ab\u202f{0}\u202f\u00bb.
NoUnit = Aucune unit\u00e9 de mesure n\u2019a
\u00e9t\u00e9 sp\u00e9cifi\u00e9e.
NullArgument_1 = L\u2019argument \u2018{0}\u2019 ne doit
pas \u00eatre nul.
NullCollectionElement_1 = La collection \u2018{0}\u2019
n\u2019accepte pas les valeurs nulles.
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
[UTF-8] Wed Feb 11 21:13:41 2015
@@ -92,6 +92,12 @@ public final class Messages extends Inde
public static final short LocalesDiscarded = 3;
/**
+ * The “{1}” parameter could have been omitted. But it has been given
a value of {2} which does
+ * not match the definition of the “{0}” ellipsoid.
+ */
+ public static final short MismatchedEllipsoidAxisLength_3 = 9;
+
+ /**
* Property “{0}” is hidden by “{1}”.
*/
public static final short PropertyHiddenBy_2 = 4;
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
[ISO-8859-1] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
[ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -22,4 +22,5 @@ IgnoredPropertiesAfterFirst_1 = Ignore
IgnoredPropertyAssociatedTo_1 = Ignored property associated to
\u2018{0}\u2019.
PropertyHiddenBy_2 = Property \u201c{0}\u201d is hidden by
\u201c{1}\u201d.
LocalesDiscarded = Text were discarded for some locales.
+MismatchedEllipsoidAxisLength_3 = The \u201c{1}\u201d parameter could have
been omitted. But it has been given a value of {2} which does not match the
definition of the \u201c{0}\u201d ellipsoid.
UnparsableValueStoredAsText_2 = Can not parse \u201c{1}\u201d as an instance
of \u2018{0}\u2019. The value is stored as plain text instead, but will be
ignored by some processing.
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
[ISO-8859-1] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
[ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -22,4 +22,5 @@ IgnoredPropertiesAfterFirst_1 = Des pr
IgnoredPropertyAssociatedTo_1 = Une propri\u00e9t\u00e9 associ\u00e9e \u00e0
\u2018{0}\u2019 a \u00e9t\u00e9 ignor\u00e9e.
PropertyHiddenBy_2 = La propri\u00e9t\u00e9
\u00ab\u202f{0}\u202f\u00bb est masqu\u00e9e par \u00ab\u202f{1}\u202f\u00bb.
LocalesDiscarded = Des textes ont \u00e9t\u00e9 ignor\u00e9s
pour certaines langues.
+MismatchedEllipsoidAxisLength_3 = Le param\u00e8tre
\u00ab\u202f{1}\u202f\u00bb aurait pu \u00eatre omis. Mais il lui a
\u00e9t\u00e9 donn\u00e9 la {2} qui ne correspond pas \u00e0 la d\u00e9finition
de l'ellipso\u00efde \u00ab\u202f{0}\u202f\u00bb.
UnparsableValueStoredAsText_2 = La valeur \u00ab\u202f{1}\u202f\u00bb ne
peut pas \u00eatre interpr\u00e9t\u00e9e comme une instance de \u2018{0}\u2019.
Elle est donc m\u00e9moris\u00e9e sous sa forme textuelle, mais sera
ignor\u00e9e par certains traitements.