You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2015/08/03 16:46:31 UTC
svn commit: r1693912 [2/3] - in /sis/trunk: ./
core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/
core/sis-metadata/src/main/java/org/apache/sis/io/wkt/
core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-metad...
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -16,7 +16,6 @@
*/
package org.apache.sis.referencing.operation.projection;
-import java.util.Map;
import java.util.EnumMap;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterDescriptor;
@@ -39,6 +38,7 @@ import org.apache.sis.util.Workaround;
import static java.lang.Math.*;
import static java.lang.Double.*;
import static org.apache.sis.math.MathFunctions.isPositive;
+import static org.apache.sis.internal.util.DoubleDouble.verbatim;
/**
@@ -125,13 +125,33 @@ public class Mercator extends ConformalP
private final byte variant;
/**
- * Returns the (<var>role</var> → <var>parameter</var>) associations for a Mercator projection of the given variant.
+ * Creates a Mercator projection from the given parameters.
+ * The {@code method} argument can be the description of one of the following:
+ *
+ * <ul>
+ * <li><cite>"Mercator (variant A)"</cite>, also known as <cite>"Mercator (1SP)"</cite>.</li>
+ * <li><cite>"Mercator (variant B)"</cite>, also known as <cite>"Mercator (2SP)"</cite>.</li>
+ * <li><cite>"Mercator (variant C)"</cite>.</li>
+ * <li><cite>"Mercator (Spherical)"</cite>.</li>
+ * <li><cite>"Popular Visualisation Pseudo Mercator"</cite>.</li>
+ * <li><cite>"Miller Cylindrical"</cite>.</li>
+ * </ul>
*
- * @param variant One of {@link #REGIONAL}, {@link #SPHERICAL}, {@link #PSEUDO} or {@link #MILLER} constants.
- * @return The roles map to give to super-class constructor.
+ * @param method Description of the projection parameters.
+ * @param parameters The parameter values of the projection to create.
+ */
+ public Mercator(final OperationMethod method, final Parameters parameters) {
+ this(initializer(method, parameters));
+ }
+
+ /**
+ * Work around for RFE #4093999 in Sun's bug database
+ * ("Relax constraint on placement of this()/super() call in constructors").
*/
@SuppressWarnings("fallthrough")
- private static Map<ParameterRole, ParameterDescriptor<Double>> roles(final byte variant) {
+ @Workaround(library="JDK", version="1.7")
+ private static Initializer initializer(final OperationMethod method, final Parameters parameters) {
+ final byte variant = getVariant(method);
final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles =
new EnumMap<ParameterRole, ParameterDescriptor<Double>>(ParameterRole.class);
/*
@@ -172,27 +192,7 @@ public class Mercator extends ConformalP
break;
}
}
- return roles;
- }
-
- /**
- * Creates a Mercator projection from the given parameters.
- * The {@code method} argument can be the description of one of the following:
- *
- * <ul>
- * <li><cite>"Mercator (variant A)"</cite>, also known as <cite>"Mercator (1SP)"</cite>.</li>
- * <li><cite>"Mercator (variant B)"</cite>, also known as <cite>"Mercator (2SP)"</cite>.</li>
- * <li><cite>"Mercator (variant C)"</cite>.</li>
- * <li><cite>"Mercator (Spherical)"</cite>.</li>
- * <li><cite>"Popular Visualisation Pseudo Mercator"</cite>.</li>
- * <li><cite>"Miller Cylindrical"</cite>.</li>
- * </ul>
- *
- * @param method Description of the projection parameters.
- * @param parameters The parameter values of the projection to create.
- */
- public Mercator(final OperationMethod method, final Parameters parameters) {
- this(method, parameters, getVariant(method));
+ return new Initializer(method, parameters, roles, variant);
}
/**
@@ -200,15 +200,15 @@ public class Mercator extends ConformalP
* ("Relax constraint on placement of this()/super() call in constructors").
*/
@Workaround(library="JDK", version="1.7")
- private Mercator(final OperationMethod method, final Parameters parameters, final byte variant) {
- super(method, parameters, roles(variant));
- this.variant = variant;
+ private Mercator(final Initializer initializer) {
+ super(initializer);
+ this.variant = initializer.variant;
/*
* The "Longitude of natural origin" parameter is found in all Mercator projections and is mandatory.
* Since this is usually the Greenwich meridian, the default value is 0°. We keep the value in degrees
* for now; it will be converted to radians later.
*/
- final double λ0 = getAndStore(parameters, Mercator1SP.LONGITUDE_OF_ORIGIN);
+ final double λ0 = initializer.getAndStore(Mercator1SP.LONGITUDE_OF_ORIGIN);
/*
* The "Latitude of natural origin" is not formally a parameter of Mercator projection. But the parameter
* is included for completeness in CRS labelling, with the restriction (specified in EPSG documentation)
@@ -221,7 +221,7 @@ public class Mercator extends ConformalP
* "Latitude of origin" can not have a non-zero value, if it still have non-zero value we will process as
* for "Latitude of false origin".
*/
- final double φ0 = toRadians(getAndStore(parameters, (variant == REGIONAL)
+ final double φ0 = toRadians(initializer.getAndStore((variant == REGIONAL)
? RegionalMercator.LATITUDE_OF_FALSE_ORIGIN : Mercator1SP.LATITUDE_OF_ORIGIN));
/*
* In theory, the "Latitude of 1st standard parallel" and the "Scale factor at natural origin" parameters
@@ -229,9 +229,8 @@ public class Mercator extends ConformalP
* the later is for projections "1SP" (namely variant A and spherical). However we let users specify both
* if they really want, since we sometime see such CRS definitions.
*/
- final double φ1 = toRadians(getAndStore(parameters, Mercator2SP.STANDARD_PARALLEL));
- final DoubleDouble k0 = new DoubleDouble(cos(φ1), 0);
- k0.divide(rν(sin(φ1)), 0);
+ final double φ1 = toRadians(initializer.getAndStore(Mercator2SP.STANDARD_PARALLEL));
+ final Number k0 = verbatim(initializer.scaleAtφ(sin(φ1), cos(φ1)));
/*
* In principle we should rotate the central meridian (λ0) in the normalization transform, as below:
*
@@ -248,16 +247,21 @@ public class Mercator extends ConformalP
denormalize.convertBefore(0, k0, null);
denormalize.convertBefore(1, k0, null);
if (λ0 != 0) {
+ /*
+ * Use double-double arithmetic here for consistency with the work done in the normalization matrix.
+ * The intend is to have exact value at 'double' precision when computing Matrix.invert(). Note that
+ * there is no such goal for other parameters computed from sine or consine functions.
+ */
final DoubleDouble offset = DoubleDouble.createDegreesToRadians();
offset.multiply(-λ0);
denormalize.convertBefore(0, null, offset);
}
if (φ0 != 0) {
- denormalize.convertBefore(1, null, new DoubleDouble(-log(expOfNorthing(φ0, excentricity * sin(φ0)))));
+ denormalize.convertBefore(1, null, verbatim(-log(expOfNorthing(φ0, excentricity * sin(φ0)))));
}
if (variant == MILLER) {
- normalize.convertBefore(1, new DoubleDouble(0.80), null);
- denormalize.convertBefore(1, new DoubleDouble(1.25), null);
+ normalize .convertBefore(1, 0.80, null);
+ denormalize.convertBefore(1, 1.25, null);
}
/*
* At this point we are done, but we add here a little bit a maniac precision hunting.
@@ -283,9 +287,9 @@ public class Mercator extends ConformalP
* those remaning lines of code.
*/
if (φ0 == 0 && isPositive(φ1 != 0 ? φ1 : φ0)) {
- final DoubleDouble revert = new DoubleDouble(-1, 0);
- normalize.convertBefore(1, revert, null);
- denormalize.convertBefore(1, revert, null); // Must be before false easting/northing.
+ final Number reverseSign = verbatim(-1);
+ normalize .convertBefore(1, reverseSign, null);
+ denormalize.convertBefore(1, reverseSign, null); // Must be before false easting/northing.
}
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -16,15 +16,13 @@
*/
package org.apache.sis.referencing.operation.projection;
-import java.util.List;
-import java.util.ArrayList;
import java.util.Map;
+import java.util.HashMap;
import java.io.Serializable;
-import org.opengis.parameter.ParameterValue;
+import java.lang.reflect.Modifier;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
@@ -33,22 +31,22 @@ import org.opengis.referencing.operation
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.util.FactoryException;
import org.apache.sis.util.Debug;
+import org.apache.sis.util.CharSequences;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.parameter.Parameters;
-import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
-import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D;
import org.apache.sis.referencing.operation.transform.ContextualParameters;
-import org.apache.sis.internal.referencing.provider.MapProjection;
import org.apache.sis.internal.referencing.Formulas;
-import org.apache.sis.internal.util.DoubleDouble;
+import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.Utilities;
import org.apache.sis.internal.util.Numerics;
import static java.lang.Math.*;
-import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
// Branch-dependent imports
import org.apache.sis.internal.jdk7.Objects;
@@ -132,14 +130,19 @@ public abstract class NormalizedProjecti
/**
* Maximum difference allowed when comparing longitudes or latitudes in radians.
- * The current value take the system-wide angular tolerance value (equivalent to
+ * The current value takes the system-wide angular tolerance value (equivalent to
* about 1 cm on Earth) converted to radians.
*
* <p>Some formulas use this tolerance value for testing sines or cosines of an angle.
* In the sine case, this is justified because sin(θ) ≅ θ when θ is small.
* Similar reasoning applies to cosine with cos(θ) ≅ θ + π/2 when θ is small.</p>
+ *
+ * <p>Some formulas may use this tolerance value as a <em>linear</em> tolerance on the unit sphere.
+ * This is okay because the arc length for an angular tolerance θ is r⋅θ, but in this class r=1.</p>
*/
static final double ANGULAR_TOLERANCE = Formulas.ANGULAR_TOLERANCE * (PI/180);
+ // Note: an alternative way to compute this value could be Formulas.LINEAR_TOLERANCE / AUTHALIC_RADIUS.
+ // But the later is only 0.07% lower than the current value.
/**
* Desired accuracy for the result of iterative computations, in radians.
@@ -149,7 +152,7 @@ public abstract class NormalizedProjecti
* So if the linear tolerance is 1 cm, then the accuracy that we will seek for is 0.25 cm (about
* 4E-10 radians). The 0.25 factor is a safety margin for meeting the 1 cm accuracy.</p>
*/
- static final double ITERATION_TOLERANCE = Formulas.ANGULAR_TOLERANCE * (PI/180) * 0.25;
+ static final double ITERATION_TOLERANCE = ANGULAR_TOLERANCE * 0.25;
/**
* Maximum number of iterations for iterative computations.
@@ -160,6 +163,17 @@ public abstract class NormalizedProjecti
static final int MAXIMUM_ITERATIONS = 15;
/**
+ * The internal parameter descriptors. Keys are implementation classes. Values are parameter descriptor groups
+ * containing at least a parameter for the {@link #excentricity} value, and optionally other internal parameter
+ * added by some subclasses.
+ *
+ * <p>Entries are created only when first needed. Those descriptors are usually never created since they are
+ * used only by {@link #getParameterDescriptors()}, which is itself invoked mostly for debugging purpose.</p>
+ */
+ @Debug
+ private static final Map<Class<?>,ParameterDescriptorGroup> DESCRIPTORS = new HashMap<Class<?>,ParameterDescriptorGroup>();
+
+ /**
* The parameters used for creating this projection. They are used for formatting <cite>Well Known Text</cite> (WKT)
* and error messages. Subclasses shall not use the values defined in this object for computation purpose, except at
* construction time.
@@ -383,60 +397,21 @@ public abstract class NormalizedProjecti
* <cite>false easting</cite>, <cite>false northing</cite> and other values.
*/
protected NormalizedProjection(final OperationMethod method, final Parameters parameters,
- final Map<ParameterRole, ? extends ParameterDescriptor<Double>> roles)
+ final Map<ParameterRole, ? extends ParameterDescriptor<? extends Number>> roles)
{
- ensureNonNull("method", method);
- ensureNonNull("parameters", parameters);
- ensureNonNull("roles", roles);
- context = new ContextualParameters(method);
- /*
- * Note: we do not use Map.getOrDefault(K,V) below because the user could have explicitly associated
- * a null value to keys (we are paranoiac...) and because it conflicts with the "? extends" part of
- * in this constructor signature.
- */
- ParameterDescriptor<Double> semiMajor = roles.get(ParameterRole.SEMI_MAJOR);
- ParameterDescriptor<Double> semiMinor = roles.get(ParameterRole.SEMI_MINOR);
- if (semiMajor == null) semiMajor = MapProjection.SEMI_MAJOR;
- if (semiMinor == null) semiMinor = MapProjection.SEMI_MINOR;
-
- double a = getAndStore(parameters, semiMajor);
- final double b = getAndStore(parameters, semiMinor);
- final double λ0 = getAndStore(parameters, roles.get(ParameterRole.CENTRAL_MERIDIAN));
- final double fe = getAndStore(parameters, roles.get(ParameterRole.FALSE_EASTING))
- - getAndStore(parameters, roles.get(ParameterRole.FALSE_WESTING));
- final double fn = getAndStore(parameters, roles.get(ParameterRole.FALSE_NORTHING))
- - getAndStore(parameters, roles.get(ParameterRole.FALSE_SOUTHING));
- final double rs = b / a;
- excentricitySquared = 1 - (rs * rs);
- excentricity = sqrt(excentricitySquared);
- if (excentricitySquared != 0) {
- final ParameterDescriptor<Double> radius = roles.get(ParameterRole.LATITUDE_OF_CONFORMAL_SPHERE_RADIUS);
- if (radius != null) {
- /*
- * EPSG said: R is the radius of the sphere and will normally be one of the CRS parameters.
- * If the figure of the earth used is an ellipsoid rather than a sphere then R should be calculated
- * as the radius of the conformal sphere at the projection origin at latitude φ₀ using the formula
- * for Rc given in section 1.2, table 3.
- *
- * Table 3 gives:
- * Radius of conformal sphere Rc = a √(1 – ℯ²) / (1 – ℯ²⋅sin²φ)
- *
- * Using √(1 – ℯ²) = b/a we rewrite as: Rc = b / (1 – ℯ²⋅sin²φ)
- */
- final double sinφ = sin(toRadians(parameters.doubleValue(radius)));
- a = b / (1 - excentricitySquared * (sinφ*sinφ));
- }
- }
- context.normalizeGeographicInputs(λ0);
- final DoubleDouble k = new DoubleDouble(a);
- final ParameterDescriptor<Double> scaleFactor = roles.get(ParameterRole.SCALE_FACTOR);
- if (scaleFactor != null) {
- k.multiply(getAndStore(parameters, scaleFactor));
- }
- final MatrixSIS denormalize = context.getMatrix(false);
- denormalize.convertAfter(0, k, new DoubleDouble(fe));
- denormalize.convertAfter(1, k, new DoubleDouble(fn));
- inverse = new Inverse();
+ this(new Initializer(method, parameters, roles, (byte) 0));
+ }
+
+ /**
+ * Creates a new normalized projection from the parameters computed by the given initializer.
+ *
+ * @param initializer The initializer for computing map projection internal parameters.
+ */
+ NormalizedProjection(final Initializer initializer) {
+ context = initializer.context;
+ excentricitySquared = initializer.excentricitySquared.value;
+ excentricity = sqrt(excentricitySquared); // DoubleDouble.sqrt() does not make any difference here.
+ inverse = new Inverse();
}
/**
@@ -480,50 +455,6 @@ public abstract class NormalizedProjecti
}
/**
- * Gets a parameter value identified by the given descriptor and stores it in the {@link #context}.
- * A "contextual parameter" is a parameter that apply to the normalize → {@code this} → denormalize
- * chain as a whole. It does not really apply to this {@code NormalizedProjection} instance when taken alone.
- *
- * <p>This method performs the following actions:</p>
- * <ul>
- * <li>Convert the value to the units specified by the descriptor.</li>
- * <li>Ensure that the value is contained in the range specified by the descriptor.</li>
- * <li>Store the value only if different than the default value.</li>
- * </ul>
- *
- * This method shall be invoked at construction time only.
- */
- final double getAndStore(final Parameters parameters, final ParameterDescriptor<Double> descriptor) {
- if (descriptor == null) {
- return 0; // Default value for all parameters except scale factor.
- }
- final double value = parameters.doubleValue(descriptor); // Apply a unit conversion if needed.
- final Double defaultValue = descriptor.getDefaultValue();
- if (defaultValue == null || !defaultValue.equals(value)) {
- MapProjection.validate(descriptor, value);
- context.getOrCreate(descriptor).setValue(value);
- }
- return value;
- }
-
- /**
- * Same as {@link #getAndStore(Parameters, ParameterDescriptor)}, but returns the given default value
- * if the parameter is not specified. This method shall be used only for parameters having a default
- * value more complex than what we can represent in {@link ParameterDescriptor#getDefaultValue()}.
- */
- final double getAndStore(final Parameters parameters, final ParameterDescriptor<Double> descriptor,
- final double defaultValue)
- {
- final Double value = parameters.getValue(descriptor); // Apply a unit conversion if needed.
- if (value == null) {
- return defaultValue;
- }
- MapProjection.validate(descriptor, value);
- context.parameter(descriptor.getName().getCode()).setValue(value);
- return value;
- }
-
- /**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
* as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
@@ -566,18 +497,11 @@ public abstract class NormalizedProjecti
}
/**
- * Returns a copy of the parameter values for this projection.
- * This base class supplies a value only for the following parameters:
- *
- * <ul>
- * <li>Semi-major axis length, which is set to 1.</li>
- * <li>Semi-minor axis length, which is set to
- * <code>sqrt(1 - {@linkplain #excentricitySquared ℯ²})</code>.</li>
- * </ul>
- *
- * Subclasses must complete if needed. Many projections will not need to complete,
- * because most parameters like the scale factor or the false easting/northing can
- * be handled by the (de)normalization affine transforms.
+ * Returns a copy of non-linear internal parameter values of this {@code NormalizedProjection}.
+ * The returned group contained at least the {@link #excentricity} parameter value.
+ * Some subclasses add more non-linear parameters, but most of them do not because many parameters
+ * like the <cite>scale factor</cite> or the <cite>false easting/northing</cite> are handled by the
+ * {@linkplain ContextualParameters#getMatrix(boolean) (de)normalization affine transforms} instead.
*
* <div class="note"><b>Note:</b>
* This method is mostly for {@linkplain org.apache.sis.io.wkt.Convention#INTERNAL debugging purposes}
@@ -585,59 +509,85 @@ public abstract class NormalizedProjecti
* Most GIS applications will instead be interested in the {@linkplain #getContextualParameters()
* contextual parameters}.</div>
*
- * @return A copy of the parameter values for this normalized projection.
+ * @return A copy of the internal parameter values for this normalized projection.
*/
@Debug
@Override
public ParameterValueGroup getParameterValues() {
- return getParameterValues(new String[] {
- Constants.SEMI_MAJOR,
- Constants.SEMI_MINOR
- });
+ final ParameterValueGroup group = getParameterDescriptors().createValue();
+ group.parameter("excentricity").setValue(excentricity);
+ final String[] names = getInternalParameterNames();
+ final double[] values = getInternalParameterValues();
+ for (int i=0; i<names.length; i++) {
+ group.parameter(names[i]).setValue(values[i]);
+ }
+ return group;
}
/**
- * Filters the parameter descriptor in order to retain only the parameters of the given names, and
- * sets the semi-major and semi-minor axis lengths. The specified parameters list should contains at
- * least the {@code "semi_major"} and {@code "semi_minor"} strings.
+ * Returns a description of the non-linear internal parameters of this {@code NormalizedProjection}.
+ * The returned group contained at least a descriptor for the {@link #excentricity} parameter.
+ * Subclasses may add more parameters.
+ *
+ * <p>This method is for inspecting the parameter values of this non-linear kernel only,
+ * not for inspecting the {@linkplain #getContextualParameters() contextual parameters}.
+ * Inspecting the kernel parameter values is usually for debugging purpose only.</p>
*
- * <p>This filtered descriptor is used for displaying the parameter values of this non-linear kernel only,
- * not for displaying the {@linkplain #getContextualParameters() contextual parameters}. Since displaying
- * the kernel parameter values is for debugging purpose only, it is not worth to cache this descriptor.</p>
+ * @return A description of the internal parameters.
*/
@Debug
- final ParameterValueGroup getParameterValues(final String[] nonLinearParameters) {
- ParameterDescriptorGroup descriptor = getParameterDescriptors();
- final List<GeneralParameterDescriptor> filtered =
- new ArrayList<GeneralParameterDescriptor>(nonLinearParameters.length);
- for (final GeneralParameterDescriptor p : descriptor.descriptors()) {
- for (final String name : nonLinearParameters) {
- if (IdentifiedObjects.isHeuristicMatchForName(p, name)) {
- filtered.add(p);
- break;
+ @Override
+ public ParameterDescriptorGroup getParameterDescriptors() {
+ Class<?> type = getClass();
+ while (!Modifier.isPublic(type.getModifiers())) {
+ type = type.getSuperclass();
+ }
+ ParameterDescriptorGroup group;
+ synchronized (DESCRIPTORS) {
+ group = DESCRIPTORS.get(type);
+ if (group == null) {
+ final ParameterBuilder builder = new ParameterBuilder().setRequired(true);
+ if (Utilities.isSIS(type)) {
+ builder.setCodeSpace(Citations.SIS, "SIS");
}
+ final String[] names = getInternalParameterNames();
+ final ParameterDescriptor<?>[] parameters = new ParameterDescriptor<?>[names.length + 1];
+ for (int i=0; i<parameters.length; i++) {
+ final ParameterDescriptor<?> p;
+ if (i == 0) {
+ final ParameterDescriptorGroup existing = CollectionsExt.first(DESCRIPTORS.values());
+ if (existing != null) {
+ p = (ParameterDescriptor<?>) existing.descriptor("excentricity");
+ } else {
+ p = builder.addName(Citations.SIS, "excentricity").createBounded(0, 1, Double.NaN, null);
+ }
+ } else {
+ p = builder.addName(names[i-1]).create(Double.class, null);
+ }
+ parameters[i] = p;
+ }
+ group = builder.addName(CharSequences.camelCaseToSentence(type.getSimpleName())).createGroup(1, 1, parameters);
+ DESCRIPTORS.put(type, group);
}
}
- descriptor = new DefaultParameterDescriptorGroup(IdentifiedObjects.getProperties(descriptor),
- 1, 1, filtered.toArray(new GeneralParameterDescriptor[filtered.size()]));
- /*
- * Parameter values for the ellipsoid semi-major and semi-minor axis lengths are 1 and <= 1
- * respectively because the denormalization (e.g. multiplication by a scale factor) will be
- * applied by an affine transform after this NormalizedProjection.
- */
- final ParameterValueGroup values = descriptor.createValue();
- for (final GeneralParameterDescriptor desc : filtered) {
- final String name = desc.getName().getCode();
- final ParameterValue<?> p = values.parameter(name);
- if (name.equals(Constants.SEMI_MAJOR)) {
- p.setValue(1.0);
- } else if (name.equals(Constants.SEMI_MINOR)) {
- p.setValue(sqrt(1 - excentricitySquared));
- } else {
- p.setValue(context.parameter(name).getValue());
- }
- }
- return values;
+ return group;
+ }
+
+ /**
+ * Returns the names of any additional internal parameters (other than {@link #excentricity})
+ * that this projection has. The length of this array must be the same than the length of the
+ * {@link #getInternalParameterValues()} array, if the later is non-null.
+ */
+ String[] getInternalParameterNames() {
+ return CharSequences.EMPTY_ARRAY;
+ }
+
+ /**
+ * Returns the values of any additional internal parameters (other than {@link #excentricity}) that
+ * this projection has. Those values are also compared by {@link #equals(Object, ComparisonMode)}.
+ */
+ double[] getInternalParameterValues() {
+ return null;
}
/**
@@ -763,14 +713,20 @@ public abstract class NormalizedProjecti
}
/**
- * Computes a hash code value for this map projection.
- * The default implementation computes a value from the parameters given at construction time.
+ * Computes a hash code value for this {@code NormalizedProjection}.
*
* @return The hash code value.
*/
@Override
protected int computeHashCode() {
- return context.hashCode() + 31 * super.computeHashCode();
+ long c = Double.doubleToLongBits(excentricity);
+ final double[] parameters = getInternalParameterValues();
+ if (parameters != null) {
+ for (int i=0; i<parameters.length; i++) {
+ c = c*31 + Double.doubleToLongBits(parameters[i]);
+ }
+ }
+ return super.computeHashCode() ^ Numerics.hashCode(c);
}
/**
@@ -795,70 +751,82 @@ public abstract class NormalizedProjecti
* @return {@code true} if the given object is equivalent to this map projection.
*/
@Override
+ @SuppressWarnings("fallthrough")
public boolean equals(final Object object, final ComparisonMode mode) {
if (object == this) {
return true;
}
- if (super.equals(object, mode)) {
- final double e1, e2;
- final NormalizedProjection that = (NormalizedProjection) object;
- if (mode.ordinal() < ComparisonMode.IGNORE_METADATA.ordinal()) {
+ if (!super.equals(object, mode)) {
+ return false;
+ }
+ final NormalizedProjection that = (NormalizedProjection) object;
+ switch (mode) {
+ case STRICT:
+ case BY_CONTRACT: {
if (!Objects.equals(context, that.context)) {
return false;
}
- e1 = this.excentricitySquared;
- e2 = that.excentricitySquared;
- } else {
- e1 = this.excentricity;
- e2 = that.excentricity;
+ // Fall through for comparing the excentricity.
+ }
+ case IGNORE_METADATA: {
+ /*
+ * There is no need to compare both 'excentricity' and 'excentricitySquared' since the former
+ * is computed from the later. We are better to compare 'excentricitySquared' since it is the
+ * original value from which the other value is derived.
+ */
+ if (!Numerics.equals(excentricitySquared, that.excentricitySquared)) {
+ return false;
+ }
+ break;
}
+ default: {
+ /*
+ * We want to compare the excentricity with a tolerance threshold corresponding approximatively
+ * to an error of 1 cm on Earth. The excentricity for an ellipsoid of semi-major axis a=1 is:
+ *
+ * ℯ² = 1 - b²
+ *
+ * If we add a slight ε error to the semi-minor axis length (where ε will be our linear tolerance
+ * threshold), we get:
+ *
+ * (ℯ + ε′)² = 1 - (b + ε)² ≈ 1 - (b² + 2⋅b⋅ε) assuming ε ≪ b
+ *
+ * Replacing 1 - b² by ℯ²:
+ *
+ * ℯ² + 2⋅ℯ⋅ε′ ≈ ℯ² - 2⋅b⋅ε
+ *
+ * After a few rearrangements:
+ *
+ * ε′ ≈ ε⋅(ℯ - 1/ℯ)
+ *
+ * Note that ε′ is negative for ℯ < 1 so we actually need to compute ε⋅(1/ℯ - ℯ) instead.
+ * The result is less than 2E-8 for the excentricity of the Earth.
+ */
+ final double e = max(excentricity, that.excentricity);
+ if (!Numerics.epsilonEqual(excentricity, that.excentricity, ANGULAR_TOLERANCE * (1/e - e))) {
+ assert (mode != ComparisonMode.DEBUG) : Numerics.messageForDifference(
+ "excentricity", excentricity, that.excentricity);
+ return false;
+ }
+ break;
+ }
+ }
+ final double[] parameters = getInternalParameterValues();
+ if (parameters != null) {
/*
- * There is no need to compare both 'excentricity' and 'excentricitySquared' since
- * the former is computed from the later. In strict comparison mode, we are better
- * to compare the 'excentricitySquared' since it is the original value from which
- * the other value is derived. However in approximative comparison mode, we need
- * to use the 'excentricity', otherwise we would need to take the square of the
- * tolerance factor before comparing 'excentricitySquared'.
+ * super.equals(…) guarantees that the two objects are of the same class.
+ * So in SIS implementation, this implies that the arrays have the same length.
*/
- return Numerics.epsilonEqual(e1, e2, mode);
+ final double[] others = that.getInternalParameterValues();
+ assert others.length == parameters.length;
+ for (int i=0; i<parameters.length; i++) {
+ if (!Numerics.epsilonEqual(parameters[i], others[i], mode)) {
+ assert (mode != ComparisonMode.DEBUG) : Numerics.messageForDifference(
+ getInternalParameterNames()[i], parameters[i], others[i]);
+ return false;
+ }
+ }
}
- return false;
- }
-
-
-
-
- //////////////////////////////////////////////////////////////////////////////////////////
- //////// ////////
- //////// FORMULAS FROM EPSG or SNYDER ////////
- //////// ////////
- //////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Computes the reciprocal of the radius of curvature of the ellipsoid perpendicular to the meridian at latitude φ.
- * That radius of curvature is:
- *
- * <blockquote>ν = 1 / √(1 - ℯ²⋅sin²φ)</blockquote>
- *
- * This method returns 1/ν.
- *
- * <div class="section">Relationship with Snyder</div>
- * This is related to functions (14-15) from Snyder (used for computation of scale factors
- * at the true scale latitude) as below:
- *
- * <blockquote>m = cosφ / rν</blockquote>
- *
- * Special cases:
- * <ul>
- * <li>If φ is 0°, then <var>m</var> is 1.</li>
- * <li>If φ is ±90°, then <var>m</var> is 0 provided that we are not in the spherical case
- * (otherwise we get {@link Double#NaN}).</li>
- * </ul>
- *
- * @param sinφ The sine of the φ latitude in radians.
- * @return Reciprocal of the radius of curvature of the ellipsoid perpendicular to the meridian at latitude φ.
- */
- final double rν(final double sinφ) {
- return sqrt(1 - excentricitySquared * (sinφ*sinφ));
+ return true;
}
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -16,7 +16,6 @@
*/
package org.apache.sis.referencing.operation.projection;
-import java.util.Map;
import java.util.EnumMap;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterDescriptor;
@@ -34,8 +33,10 @@ import org.apache.sis.parameter.Paramete
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.Workaround;
import org.apache.sis.measure.Latitude;
+import org.apache.sis.math.MathFunctions;
import static java.lang.Math.*;
+import static org.apache.sis.internal.util.DoubleDouble.verbatim;
/**
@@ -53,7 +54,7 @@ import static java.lang.Math.*;
* @see EquatorialStereographic
* @see ObliqueStereographic
*/
-public class PolarStereographic extends ConformalProjection { // Seen as a special case of LambertConformal.
+public class PolarStereographic extends ConformalProjection {
/**
* For cross-version compatibility.
*/
@@ -85,12 +86,30 @@ public class PolarStereographic extends
}
/**
- * Returns the (<var>role</var> → <var>parameter</var>) associations for a Polar Stereographic projection.
+ * Creates a Polar Stereographic projection from the given parameters.
+ * The {@code method} argument can be the description of one of the following:
+ *
+ * <ul>
+ * <li><cite>"Polar Stereographic (Variant A)"</cite>.</li>
+ * <li><cite>"Polar Stereographic (Variant B)"</cite>.</li>
+ * <li><cite>"Polar Stereographic (Variant C)"</cite>.</li>
+ * </ul>
*
- * @param variant One of {@link #A}, {@link #B}, {@link #C}, {@link #NORTH} or {@link #SOUTH} constants.
- * @return The roles map to give to super-class constructor.
+ * @param method Description of the projection parameters.
+ * @param parameters The parameter values of the projection to create.
*/
- private static Map<ParameterRole, ParameterDescriptor<Double>> roles(final byte variant) {
+ public PolarStereographic(final OperationMethod method, final Parameters parameters) {
+ this(initializer(method, parameters));
+ }
+
+ /**
+ * Work around for RFE #4093999 in Sun's bug database
+ * ("Relax constraint on placement of this()/super() call in constructors").
+ */
+ @SuppressWarnings("fallthrough")
+ @Workaround(library="JDK", version="1.7")
+ private static Initializer initializer(final OperationMethod method, final Parameters parameters) {
+ final byte variant = getVariant(method);
final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles =
new EnumMap<ParameterRole, ParameterDescriptor<Double>>(ParameterRole.class);
ParameterDescriptor<Double> falseEasting = PolarStereographicA.FALSE_EASTING;
@@ -105,24 +124,7 @@ public class PolarStereographic extends
roles.put(ParameterRole.CENTRAL_MERIDIAN, (variant == A)
? PolarStereographicA.LONGITUDE_OF_ORIGIN
: PolarStereographicB.LONGITUDE_OF_ORIGIN);
- return roles;
- }
-
- /**
- * Creates a Polar Stereographic projection from the given parameters.
- * The {@code method} argument can be the description of one of the following:
- *
- * <ul>
- * <li><cite>"Polar Stereographic (Variant A)"</cite>.</li>
- * <li><cite>"Polar Stereographic (Variant B)"</cite>.</li>
- * <li><cite>"Polar Stereographic (Variant C)"</cite>.</li>
- * </ul>
- *
- * @param method Description of the projection parameters.
- * @param parameters The parameter values of the projection to create.
- */
- public PolarStereographic(final OperationMethod method, final Parameters parameters) {
- this(method, parameters, getVariant(method));
+ return new Initializer(method, parameters, roles, variant);
}
/**
@@ -130,8 +132,9 @@ public class PolarStereographic extends
* ("Relax constraint on placement of this()/super() call in constructors").
*/
@Workaround(library="JDK", version="1.7")
- private PolarStereographic(final OperationMethod method, final Parameters parameters, final byte variant) {
- super(method, parameters, roles(variant));
+ private PolarStereographic(final Initializer initializer) {
+ super(initializer);
+ final byte variant = initializer.variant;
/*
* "Standard parallel" and "Latitude of origin" should be mutually exclusive,
* but this is not a strict requirement for the constructor.
@@ -149,9 +152,9 @@ public class PolarStereographic extends
*/
double φ0;
if (variant == A) {
- φ0 = getAndStore(parameters, PolarStereographicA.LATITUDE_OF_ORIGIN); // Mandatory
+ φ0 = initializer.getAndStore(PolarStereographicA.LATITUDE_OF_ORIGIN); // Mandatory
} else {
- φ0 = getAndStore(parameters, PolarStereographicA.LATITUDE_OF_ORIGIN, // Optional (should not be present)
+ φ0 = initializer.getAndStore(PolarStereographicA.LATITUDE_OF_ORIGIN, // Optional (should not be present)
(variant == NORTH) ? Latitude.MAX_VALUE :
(variant == SOUTH) ? Latitude.MIN_VALUE : Double.NaN);
}
@@ -161,9 +164,9 @@ public class PolarStereographic extends
}
double φ1;
if (variant == B || variant == C || Double.isNaN(φ0)) {
- φ1 = getAndStore(parameters, PolarStereographicB.STANDARD_PARALLEL); // Mandatory
+ φ1 = initializer.getAndStore(PolarStereographicB.STANDARD_PARALLEL); // Mandatory
} else {
- φ1 = getAndStore(parameters, PolarStereographicB.STANDARD_PARALLEL, φ0); // Optional
+ φ1 = initializer.getAndStore(PolarStereographicB.STANDARD_PARALLEL, φ0); // Optional
}
/*
* At this point we should ensure that the sign of φ0 is the same than the sign of φ1,
@@ -172,10 +175,18 @@ public class PolarStereographic extends
* It may be possible to specify φ0 and φ1 if the caller used his own parameter descriptor,
* in which case maybe he really wanted different sign (e.g. for testing purpose).
*/
- final boolean isNorthPole = (φ1 >= 0);
- φ1 = -abs(toRadians(φ1)); // May be anything in [-π/2 … 0] range.
- final double ρ;
- Double ρF = null; // Actually -ρF (compared to EPSG guide).
+ final boolean isNorth = MathFunctions.isPositive(φ1);
+ if (isNorth) {
+ /*
+ * The South case has the most "natural" formulas. For the North case, we use the same formulas
+ * with only the sign reversed before and after projection. This sign reversal is done in the
+ * (de)normalization matrices at the end of this method. But we need to apply the same politic
+ * on the parameters that we will use below.
+ */
+ φ1 = -φ1;
+ }
+ φ1 = toRadians(φ1); // May be anything in [-π/2 … 0] range.
+ final Number ρ, ρF; // This ρF is actually -ρF in EPSG guide.
if (abs(φ1 + PI/2) < ANGULAR_TOLERANCE) {
/*
* Polar Stereographic (variant A)
@@ -189,7 +200,8 @@ public class PolarStereographic extends
*
* In the spherical case, should give ρ == 2.
*/
- ρ = 2 / sqrt(pow(1+excentricity, 1+excentricity) * pow(1-excentricity, 1-excentricity));
+ ρ = verbatim(2 / sqrt(pow(1+excentricity, 1+excentricity) * pow(1-excentricity, 1-excentricity)));
+ ρF = null;
} else {
/*
* Polar Stereographic (variant B or C)
@@ -209,11 +221,9 @@ public class PolarStereographic extends
* In the spherical case, should give ρ = 1 + sinφ1 (Synder 21-7 and 21-11).
*/
final double sinφ1 = sin(φ1);
- final double mF = cos(φ1) / rν(sinφ1);
- ρ = mF / expOfNorthing(φ1, excentricity*sinφ1);
- if (variant == C) {
- ρF = -mF;
- }
+ final double mF = initializer.scaleAtφ(sinφ1, cos(φ1));
+ ρ = verbatim(mF / expOfNorthing(φ1, excentricity*sinφ1));
+ ρF = (variant == C) ? verbatim(-mF) : null;
}
/*
* At this point, all parameters have been processed. Now process to their
@@ -222,9 +232,11 @@ public class PolarStereographic extends
final MatrixSIS denormalize = context.getMatrix(false);
denormalize.convertBefore(0, ρ, null);
denormalize.convertBefore(1, ρ, ρF);
- if (isNorthPole) {
- context.getMatrix(true).convertAfter(1, -1, null);
- denormalize.convertBefore(1, -1, null);
+ if (isNorth) {
+ final Number reverseSign = verbatim(-1);
+ final MatrixSIS normalize = context.getMatrix(true);
+ normalize .convertAfter (1, reverseSign, null);
+ denormalize.convertBefore(1, reverseSign, null);
}
}
@@ -270,7 +282,7 @@ public class PolarStereographic extends
final boolean derivate) throws ProjectionException
{
/*
- * Note: formulas below are very similar to LambertConformal.transform(…) with n = -1.
+ * Note: formulas below are very similar to LambertConicConformal.transform(…) with n = -1.
*/
final double θ = srcPts[srcOff ]; // θ = λ - λ₀
final double φ = srcPts[srcOff+1]; // Sign may be reversed
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -16,17 +16,16 @@
*/
package org.apache.sis.referencing.operation.projection;
-import java.util.Map;
import java.util.EnumMap;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.OperationMethod;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
-import org.apache.sis.internal.referencing.provider.MapProjection;
import org.apache.sis.internal.referencing.provider.TransverseMercatorSouth;
import org.apache.sis.internal.util.DoubleDouble;
import org.apache.sis.parameter.Parameters;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.Workaround;
import static java.lang.Math.*;
import static org.apache.sis.math.MathFunctions.asinh;
@@ -72,11 +71,29 @@ public class TransverseMercator extends
private final double h1, h2, h3, h4, ih1, ih2, ih3, ih4;
/**
- * Returns the (<var>role</var> → <var>parameter</var>) associations for a Transverse Mercator projection.
+ * Creates a Transverse Mercator projection from the given parameters.
+ * The {@code method} argument can be the description of one of the following:
*
- * @return The roles map to give to super-class constructor.
+ * <ul>
+ * <li><cite>"Transverse Mercator"</cite>.</li>
+ * <li><cite>"Transverse Mercator (South Orientated)"</cite>.</li>
+ * </ul>
+ *
+ * @param method Description of the projection parameters.
+ * @param parameters The parameter values of the projection to create.
*/
- private static Map<ParameterRole, ParameterDescriptor<Double>> roles(final boolean isSouth) {
+ public TransverseMercator(final OperationMethod method, final Parameters parameters) {
+ this(initializer(method, parameters));
+ }
+
+ /**
+ * Work around for RFE #4093999 in Sun's bug database
+ * ("Relax constraint on placement of this()/super() call in constructors").
+ */
+ @SuppressWarnings("fallthrough")
+ @Workaround(library="JDK", version="1.7")
+ private static Initializer initializer(final OperationMethod method, final Parameters parameters) {
+ final boolean isSouth = identMatch(method, "(?i).*\\bSouth\\b.*", TransverseMercatorSouth.IDENTIFIER);
final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles =
new EnumMap<ParameterRole, ParameterDescriptor<Double>>(ParameterRole.class);
ParameterRole xOffset = ParameterRole.FALSE_EASTING;
@@ -89,47 +106,45 @@ public class TransverseMercator extends
roles.put(ParameterRole.SCALE_FACTOR, org.apache.sis.internal.referencing.provider.TransverseMercator.SCALE_FACTOR);
roles.put(xOffset, org.apache.sis.internal.referencing.provider.TransverseMercator.FALSE_EASTING);
roles.put(yOffset, org.apache.sis.internal.referencing.provider.TransverseMercator.FALSE_NORTHING);
- return roles;
+ return new Initializer(method, parameters, roles, isSouth ? (byte) 1 : (byte) 0);
}
/**
- * Returns the type of the projection based on the name and identifier of the given operation method.
+ * Work around for RFE #4093999 in Sun's bug database
+ * ("Relax constraint on placement of this()/super() call in constructors").
*/
- private static boolean isSouth(final OperationMethod method) {
- return identMatch(method, "(?i).*\\bSouth\\b.*", TransverseMercatorSouth.IDENTIFIER);
- }
-
- /**
- * Creates a Transverse Mercator projection from the given parameters.
- * The {@code method} argument can be the description of one of the following:
- *
- * <ul>
- * <li><cite>"Transverse Mercator"</cite>.</li>
- * <li><cite>"Transverse Mercator (South Orientated)"</cite>.</li>
- * </ul>
- *
- * @param method Description of the projection parameters.
- * @param parameters The parameter values of the projection to create.
- */
- public TransverseMercator(final OperationMethod method, final Parameters parameters) {
- super(method, parameters, roles(isSouth(method)));
- final double φ0 = toRadians(getAndStore(parameters,
+ @Workaround(library="JDK", version="1.7")
+ private TransverseMercator(final Initializer initializer) {
+ super(initializer);
+ final double φ0 = toRadians(initializer.getAndStore(
org.apache.sis.internal.referencing.provider.TransverseMercator.LATITUDE_OF_ORIGIN));
- final double rs = parameters.doubleValue(MapProjection.SEMI_MINOR)
- / parameters.doubleValue(MapProjection.SEMI_MAJOR);
-
- final double n = (1 - rs) / (1 + rs); // Rewrite of n = f / (2-f)
+ /*
+ * Opportunistically use double-double arithmetic for computation of B since we will store
+ * it in the denormalization matrix, and there is no sine/cosine functions involved here.
+ */
+ final double n;
+ final DoubleDouble B;
+ { // For keeping the 't' variable locale.
+ /*
+ * EPSG gives: n = f / (2-f)
+ * We rewrite as: n = (1 - b/a) / (1 + b/a)
+ */
+ final DoubleDouble t = initializer.axisLengthRatio(); // t = b/a
+ t.ratio_1m_1p(); // t = (1 - t) / (1 + t)
+ n = t.doubleValue();
+ /*
+ * Compute B = (1 + n²/4 + n⁴/64) / (1 + n)
+ */
+ B = new DoubleDouble(t); // B = n
+ B.square();
+ B.series(1, 0.25, 1./64); // B = (1 + n²/4 + n⁴/64)
+ t.add(1,0);
+ B.divide(t); // B = (1 + n²/4 + n⁴/64) / (1 + n)
+ }
final double n2 = n * n;
final double n3 = n2 * n;
final double n4 = n2 * n2;
/*
- * Compute B = (n4/64 + n2/4 + 1) / (n + 1)
- * Opportunistically uses double-double arithmetic since we use it anyway for denormalization matrix.
- */
- final DoubleDouble B = new DoubleDouble(n);
- B.add(1);
- B.inverseDivide(1, n4/64 + n2/4);
- /*
* Coefficients for direct projection.
* Add the smallest values first in order to reduce rounding errors.
*/
@@ -147,21 +162,22 @@ public class TransverseMercator extends
ih4 = (4397. / 161280)*n4;
/*
* Compute M₀ = B⋅(ξ₁ + ξ₂ + ξ₃ + ξ₄) and negate in anticipation for what will be needed
- * in the denormalization matrix. We opportunistically use double-double arithmetic, but
- * the precision is actually not better than double (in current SIS version) because of
- * the precision of trigonometric functions. We may improve on that in the future if it
- * seems useful.
+ * in the denormalization matrix. We opportunistically use double-double arithmetic but
+ * only for the final multiplication by B, for consistency with the translation term to
+ * be stored in the denormalization matrix. It is not worth to use double-double in the
+ * sum of sine functions because the extra digits would be meaningless.
*
* NOTE: the EPSG documentation makes special cases for φ₀ = 0 or ±π/2. This is not
* needed here; we verified that the code below produces naturally the expected values.
*/
final double Q = asinh(tan(φ0)) - excentricity * atanh(excentricity * sin(φ0));
final double β = atan(sinh(Q));
- final DoubleDouble M0 = new DoubleDouble(β, 0);
- M0.add(h1 * sin(2*β), 0);
- M0.add(h2 * sin(4*β), 0);
- M0.add(h3 * sin(6*β), 0);
- M0.add(h4 * sin(8*β), 0);
+ final DoubleDouble M0 = new DoubleDouble();
+ M0.value = h4 * sin(8*β)
+ + h3 * sin(6*β)
+ + h2 * sin(4*β)
+ + h1 * sin(2*β)
+ + β;
M0.multiply(B);
M0.negate();
/*
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -64,6 +64,7 @@ abstract class AbstractLinearTransform e
* Returns a copy of the matrix that user can modify.
*/
@Override
+ @SuppressWarnings("CloneDoesntCallSuperClone")
public final Matrix clone() {
return Matrices.copy(this);
}
@@ -151,10 +152,8 @@ abstract class AbstractLinearTransform e
return true;
}
if (object != null) {
- if (getClass() == object.getClass()) {
- if (mode.ordinal() < ComparisonMode.APPROXIMATIVE.ordinal()) {
- return equalsSameClass(object);
- }
+ if (getClass() == object.getClass() && !mode.isApproximative()) {
+ return equalsSameClass(object);
}
if (mode != ComparisonMode.STRICT) {
if (object instanceof LinearTransform) {
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -867,14 +867,13 @@ public abstract class AbstractMathTransf
* <p>The default implementation returns {@code true} if the following conditions are meet:</p>
* <ul>
* <li>{@code object} is an instance of the same class than {@code this}. We require the
- * same class because there is no interface for the various kinds of transform.</li>
- * <li>The {@linkplain #getParameterDescriptors() parameter descriptors} are equal according
+ * same class because there is no interface for the various kinds of transform.</li>
+ * <li>If the hash code value has already been {@linkplain #computeHashCode() computed} for both
+ * instances, their values are the same <i>(opportunist performance enhancement)</i>.</li>
+ * <li>The {@linkplain #getContextualParameters() contextual parameters} are equal according
* the given comparison mode.</li>
* </ul>
*
- * The {@linkplain #getParameterValues() parameter values} are <strong>not</strong> compared because
- * subclasses can typically compare those values more efficiently by accessing to their member fields.
- *
* @param object The object to compare with this transform.
* @param mode The strictness level of the comparison. Default to {@link ComparisonMode#STRICT STRICT}.
* @return {@code true} if the given object is considered equals to this math transform.
@@ -889,7 +888,7 @@ public abstract class AbstractMathTransf
* If the classes are the same, then the hash codes should be computed in the same way. Since those
* codes are cached, this is an efficient way to quickly check if the two objects are different.
*/
- if (mode.ordinal() < ComparisonMode.APPROXIMATIVE.ordinal()) {
+ if (!mode.isApproximative()) {
final int tc = hashCode;
if (tc != 0) {
final int oc = that.hashCode;
@@ -899,11 +898,11 @@ public abstract class AbstractMathTransf
}
}
// See the policy documented in the LenientComparable javadoc.
- if (mode.ordinal() >= ComparisonMode.IGNORE_METADATA.ordinal()) {
+ if (mode.isIgnoringMetadata()) {
return true;
}
- return Utilities.deepEquals(this.getParameterDescriptors(),
- that.getParameterDescriptors(), mode);
+ return Utilities.deepEquals(this.getContextualParameters(),
+ that.getContextualParameters(), mode);
}
return false;
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -428,6 +428,35 @@ class ConcatenatedTransform extends Abst
i = ((AbstractMathTransform) step).beforeFormat(transforms, i, false);
}
}
+ /*
+ * Merge consecutive affine transforms. The transforms list should never contain consecutive instances
+ * of LinearTransform because the ConcatenatedTransform.create(…) method already merged them (this is
+ * verified by assertions in MathTransforms). However the above loop may have created synthetic affine
+ * transforms for WKT formatting purpose. Those synthetic affine transforms are actually represented by
+ * Matrix objects (rather than full MathTransform objects), and two matrices may have been generated
+ * consecutively.
+ */
+ Matrix after = null;
+ for (int i=transforms.size(); --i >= 0;) {
+ final Object step = transforms.get(i);
+ if (step instanceof Matrix) {
+ if (after != null) {
+ final Matrix merged = Matrices.multiply(after, (Matrix) step);
+ if (merged.isIdentity()) {
+ transforms.subList(i, i+2).clear();
+ after = null;
+ } else {
+ transforms.set(i, MathTransforms.linear(merged));
+ transforms.remove(i+1);
+ after = merged;
+ }
+ } else {
+ after = (Matrix) step;
+ }
+ } else {
+ after = null;
+ }
+ }
return transforms;
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -36,6 +36,7 @@ import org.opengis.referencing.operation
import org.opengis.referencing.operation.MathTransformFactory;
import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
import org.apache.sis.internal.referencing.WKTUtilities;
+import org.apache.sis.internal.referencing.Formulas;
import org.apache.sis.internal.metadata.WKTKeywords;
import org.apache.sis.internal.system.Loggers;
import org.apache.sis.internal.util.DoubleDouble;
@@ -565,7 +566,7 @@ public class ContextualParameters extend
*/
@Override
public int hashCode() {
- return (normalize.hashCode() + 31*denormalize.hashCode()) ^ (int) serialVersionUID;
+ return (normalize.hashCode() + 31*denormalize.hashCode()) ^ Arrays.hashCode(values) ^ (int) serialVersionUID;
}
/**
@@ -578,9 +579,10 @@ public class ContextualParameters extend
public boolean equals(final Object object) {
if (object != null && object.getClass() == getClass()) {
final ContextualParameters that = (ContextualParameters) object;
- return Objects.equals(descriptor, that.descriptor) &&
- Objects.equals(normalize, that.normalize) &&
- Objects.equals(denormalize, that.denormalize);
+ return Objects.equals(descriptor, that.descriptor) &&
+ Objects.equals(normalize, that.normalize) &&
+ Objects.equals(denormalize, that.denormalize) &&
+ Arrays.equals(values, that.values);
}
return false;
}
@@ -705,11 +707,31 @@ public class ContextualParameters extend
* At this point "userDefined" is the affine transform to show to user instead of the
* "before" affine transform. Replaces "before" by "userDefined" locally (but not yet
* in the list), or set it to null (meaning that it will be removed from the list) if
- * it is identity, which happen quite often. Note that in the former (non-null) case,
- * the coefficients are often either 0 or 1 since the transform is often for changing
- * axis order, so it is worth to attempt rounding coefficents.
+ * it is identity, which happen quite often.
+ *
+ * Note on rounding error: the coefficients are often either 0 or 1 since the transform
+ * is often for changing axis order. Thanks to double-double arithmetic in SIS matrices,
+ * the non-zero values are usually accurate. But the values that should be zero are much
+ * harder to get right. Sometime we see small values (around 1E-12) in the last column of
+ * the 'before' matrix below. Since this column contains translation terms, those numbers
+ * are in the unit of measurement of input values of the MathTransform after the matrix.
+ *
+ * - For forward map projections, those values are conceptually in decimal degrees
+ * (in fact the values are converted to radians but not by this 'before' matrix).
+ *
+ * - For inverse map projections, those values are conceptually in metres (in fact
+ * converted to distances on a unitary ellipsoid but not by this 'before' matrix).
+ *
+ * - Geographic/Geocentric transformations behave like map projections in regard to units.
+ * Molodensky transformations conceptually use always decimal degrees. There is not much
+ * other cases since this mechanism is internal to SIS (not in public API).
+ *
+ * Consequently we set the tolerance threshold to ANGULAR_TOLERANCE. We do not bother (at least
+ * for now) to identify the cases where we could use LINEAR_TOLERANCE because just checking the
+ * 'inverse' flag is not sufficient (e.g. the Molodensky case). Since the angular tolerance is
+ * smaller than the linear one, unconditional usage of ANGULAR_TOLERANCE is more conservative.
*/
- before = userDefined.isIdentity() ? null : userDefined;
+ before = Matrices.isIdentity(userDefined, Formulas.ANGULAR_TOLERANCE) ? null : userDefined;
/*
* Compute the "after" affine transform in a way similar than the "before" affine.
* Note that if this operation fails, we will cancel everything we would have done
@@ -725,7 +747,23 @@ public class ContextualParameters extend
if (hasAfter) {
userDefined = Matrices.multiply(after, userDefined);
}
- after = userDefined.isIdentity() ? null : userDefined;
+ /*
+ * Note on rounding error: same discussion than the "note on rounding error" of the 'before' matrix,
+ * with the following differences:
+ *
+ * - For forward map projections, unit of measurements of translation terms are conceptually
+ * metres (instead than degrees) multiplied by the scale factors in the 'after' matrix.
+ *
+ * - For inverse map projections, unit of measurements of translation terms are conceptually
+ * degrees (instead than metres) multiplied by the scale factors in the 'after' matrix.
+ *
+ * - And so on for all cases: swap the units of the forward and inverse cases, then multiply
+ * by the scale factor. Note that the multiplication step does not exist in the 'before' case.
+ *
+ * Since we are seeking for the identity matrix, the scale factor is 1. We do not bother to distinguish
+ * the ANGULAR_TOLERANCE and LINEAR_TOLERANCE cases for the same reasons than for the 'before' matrix.
+ */
+ after = Matrices.isIdentity(userDefined, Formulas.ANGULAR_TOLERANCE) ? null : userDefined;
/*
* At this point we have computed all the affine transforms to show to the user.
* We can replace the elements in the list. The transform referenced by transforms.get(index)
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -36,6 +36,7 @@ import org.opengis.parameter.ParameterVa
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.datum.Ellipsoid;
@@ -540,12 +541,34 @@ public class DefaultMathTransformFactory
} catch (IllegalStateException e) {
failure = e;
}
+ final boolean isIvfDefinitive;
if (mismatchedParam != null) {
final LogRecord record = Messages.getResources((Locale) null).getLogRecord(Level.WARNING,
Messages.Keys.MismatchedEllipsoidAxisLength_3, ellipsoid.getName().getCode(),
mismatchedParam.getDescriptor().getName().getCode(), mismatchedValue);
record.setLoggerName(Loggers.COORDINATE_OPERATION);
Logging.log(DefaultMathTransformFactory.class, "createBaseToDerived", record);
+ isIvfDefinitive = false;
+ } else {
+ isIvfDefinitive = ellipsoid.isIvfDefinitive();
+ }
+ /*
+ * Following is specific to Apache SIS. We use this non-standard API for allowing the
+ * NormalizedProjection class (our base class for all map projection implementations)
+ * to known that the ellipsoid definitive parameter is the inverse flattening factor
+ * instead than the semi-major axis length. It makes a small difference in the accuracy
+ * of the excentricity parameter.
+ */
+ if (isIvfDefinitive) try {
+ parameters.parameter(Constants.INVERSE_FLATTENING).setValue(ellipsoid.getInverseFlattening());
+ } catch (ParameterNotFoundException e) {
+ /*
+ * Should never happen with Apache SIS implementation, but may happen if the given parameters come
+ * from another implementation. We can safely abandon our attempt to set the inverse flattening value,
+ * since it was redundant with semi-minor axis length.
+ */
+ Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
+ DefaultMathTransformFactory.class, "createBaseToDerived", e);
}
}
MathTransform baseToDerived;
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -78,4 +78,25 @@ public final strictfp class FormulasTest
public void testGetAuthalicRadius() {
assertEquals(ReferencingServices.AUTHALIC_RADIUS, Formulas.getAuthalicRadius(6378137, 6356752), 0.5);
}
+
+ /**
+ * Tests {@link Formulas#getSemiMinor(double, double)}.
+ */
+ @Test
+ public void testGetSemiMinor() {
+ assertEquals("WGS 84", 6356752.314245179, Formulas.getSemiMinor(6378137, 298.257223563), 1E-9);
+ assertEquals("International 1924", 6356911.9461279465, Formulas.getSemiMinor(6378388, 297), 1E-9);
+ assertEquals("Clarke 1858", 20855233, // Unit in feet. Is the definitive parameter for this ellipsoid.
+ Formulas.getSemiMinor(20926348, 294.26067636926103), 1E-8);
+ }
+
+ /**
+ * Tests {@link Formulas#getInverseFlattening(double, double)}.
+ */
+ @Test
+ public void testGetInverseFlattening() {
+ assertEquals("WGS 84", 298.2572235629972, Formulas.getInverseFlattening(6378137, 6356752.314245179), 1E-11);
+ assertEquals("International 1924", 297, Formulas.getInverseFlattening(6378388, 6356911.9461279465), 1E-11);
+ assertEquals("Clarke 1858", 294.26067636926103, Formulas.getInverseFlattening(20926348, 20855233), 1E-11);
+ }
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -22,7 +22,6 @@ import org.opengis.referencing.cs.*;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.datum.PrimeMeridian;
-import org.apache.sis.referencing.datum.DefaultPrimeMeridian;
import org.apache.sis.referencing.datum.HardCodedDatum;
import org.apache.sis.referencing.crs.HardCodedCRS;
import org.apache.sis.internal.metadata.WKTKeywords;
@@ -31,8 +30,6 @@ import org.apache.sis.test.TestCase;
import org.junit.Test;
import static org.junit.Assert.*;
-import static java.util.Collections.singletonMap;
-import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
import static org.apache.sis.internal.referencing.ReferencingUtilities.*;
@@ -46,26 +43,6 @@ import static org.apache.sis.internal.re
*/
public final strictfp class ReferencingUtilitiesTest extends TestCase {
/**
- * Tests {@link ReferencingUtilities#isGreenwichLongitudeEquals(PrimeMeridian, PrimeMeridian)}.
- */
- @Test
- public void testIsGreenwichLongitudeEquals() {
- assertFalse(isGreenwichLongitudeEquals(null, null)); // "null" interpreted as "unknown".
- assertFalse(isGreenwichLongitudeEquals(null, HardCodedDatum.GREENWICH));
- assertFalse(isGreenwichLongitudeEquals(HardCodedDatum.GREENWICH, null));
- assertFalse(isGreenwichLongitudeEquals(HardCodedDatum.GREENWICH, HardCodedDatum.PARIS));
- assertFalse(isGreenwichLongitudeEquals(HardCodedDatum.PARIS, HardCodedDatum.PARIS_RGS));
- assertFalse(isGreenwichLongitudeEquals(HardCodedDatum.PARIS_RGS, HardCodedDatum.PARIS));
- assertTrue (isGreenwichLongitudeEquals(HardCodedDatum.PARIS, HardCodedDatum.PARIS));
- /*
- * Test two prime meridians using different units (Paris in grade and Paris in degrees).
- */
- final PrimeMeridian pd = new DefaultPrimeMeridian(singletonMap(NAME_KEY, "Paris"), 2.33722917, NonSI.DEGREE_ANGLE);
- assertTrue(isGreenwichLongitudeEquals(HardCodedDatum.PARIS, pd));
- assertTrue(isGreenwichLongitudeEquals(pd, HardCodedDatum.PARIS));
- }
-
- /**
* Tests {@link ReferencingUtilities#getGreenwichLongitude(PrimeMeridian, Unit)}.
*/
@Test
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/parameter/MapProjectionParametersTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/parameter/MapProjectionParametersTest.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/parameter/MapProjectionParametersTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/parameter/MapProjectionParametersTest.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -28,12 +28,13 @@ import org.junit.Test;
import static org.junit.Assert.*;
import static org.apache.sis.internal.util.Constants.SEMI_MAJOR;
import static org.apache.sis.internal.util.Constants.SEMI_MINOR;
+import static org.apache.sis.internal.util.Constants.EARTH_RADIUS;
+import static org.apache.sis.internal.util.Constants.INVERSE_FLATTENING;
+import static org.apache.sis.internal.util.Constants.IS_IVF_DEFINITIVE;
import static org.apache.sis.internal.util.Constants.CENTRAL_MERIDIAN;
+import static org.apache.sis.internal.util.Constants.STANDARD_PARALLEL;
import static org.apache.sis.internal.util.Constants.STANDARD_PARALLEL_1;
import static org.apache.sis.internal.util.Constants.STANDARD_PARALLEL_2;
-import static org.apache.sis.parameter.MapProjectionDescriptor.EARTH_RADIUS;
-import static org.apache.sis.parameter.MapProjectionDescriptor.INVERSE_FLATTENING;
-import static org.apache.sis.parameter.MapProjectionDescriptor.STANDARD_PARALLEL;
/**
@@ -103,17 +104,24 @@ public final strictfp class MapProjectio
final MapProjectionDescriptor descriptor = createDescriptor(0);
final ParameterValueGroup parameters = descriptor.createValue();
- parameters.parameter(SEMI_MAJOR).setValue(6378206.4); // Clarke 1866
+ parameters.parameter(SEMI_MAJOR).setValue(6378206.4); // Clarke 1866
parameters.parameter(SEMI_MINOR).setValue(6356583.8);
assertEquals(294.97870, parameters.parameter(INVERSE_FLATTENING).doubleValue(), 0.00001);
assertEquals(6378206.4, parameters.parameter(SEMI_MAJOR) .doubleValue(), 0.5);
assertEquals(6356583.8, parameters.parameter(SEMI_MINOR) .doubleValue(), 0.5);
+ assertFalse("isIvfDefinitive", parameters.parameter(IS_IVF_DEFINITIVE).booleanValue());
- parameters.parameter(SEMI_MAJOR).setValue(6378137.000); // WGS84
+ parameters.parameter(SEMI_MAJOR).setValue(6378137.0); // WGS84
parameters.parameter(INVERSE_FLATTENING).setValue(298.257223563);
assertEquals(298.257, parameters.parameter(INVERSE_FLATTENING).doubleValue(), 0.001);
assertEquals(6378137, parameters.parameter(SEMI_MAJOR) .doubleValue(), 0.5);
assertEquals(6356752, parameters.parameter(SEMI_MINOR) .doubleValue(), 0.5);
+ assertTrue("isIvfDefinitive", parameters.parameter(IS_IVF_DEFINITIVE).booleanValue());
+
+ parameters.parameter(SEMI_MAJOR).setValue(6378350.9); // Clarke 1858 (approximative)
+ parameters.parameter(SEMI_MINOR).setValue(6356675.0);
+ assertEquals(294.26, parameters.parameter(INVERSE_FLATTENING).doubleValue(), 0.001);
+ assertFalse("isIvfDefinitive", parameters.parameter(IS_IVF_DEFINITIVE).booleanValue());
}
/**
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderMock.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderMock.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderMock.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderMock.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -32,6 +32,19 @@ import org.opengis.util.GenericName;
*/
final strictfp class BuilderMock extends Builder<BuilderMock> {
/**
+ * Creates a new builder.
+ */
+ BuilderMock() {
+ }
+
+ /**
+ * Creates a new builder initialized to the given object.
+ */
+ BuilderMock(final IdentifiedObject object) {
+ super(object);
+ }
+
+ /**
* Convenience accessor for the property value assigned to {@link IdentifiedObject#NAME_KEY}.
*/
Object getName() {
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderTest.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/BuilderTest.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -17,6 +17,7 @@
package org.apache.sis.referencing;
import java.util.Map;
+import java.util.HashMap;
import org.opengis.util.NameSpace;
import org.opengis.util.LocalName;
import org.opengis.util.GenericName;
@@ -25,6 +26,7 @@ import org.opengis.metadata.citation.Cit
import org.opengis.metadata.Identifier;
import org.opengis.referencing.ReferenceIdentifier;
import org.apache.sis.internal.simple.SimpleCitation;
+import org.apache.sis.internal.simple.SimpleIdentifier;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.metadata.iso.ImmutableIdentifier;
import org.apache.sis.metadata.iso.citation.Citations;
@@ -50,6 +52,7 @@ public final strictfp class BuilderTest
* Tests {@link Builder#verifyParameterizedType(Class)}.
*/
@Test
+ @SuppressWarnings("ResultOfObjectAllocationIgnored")
public void testVerifyParameterizedType() {
final class Invalid extends Builder<BuilderMock> {
}
@@ -278,4 +281,36 @@ public final strictfp class BuilderTest
"GeoTIFF:CT_Mercator"
}, builder.getAsStrings(1));
}
+
+ /**
+ * Tests the {@link Builder#Builder(IdentifiedObject)} constructor.
+ *
+ * @since 0.6
+ */
+ @Test
+ public void testCreationFromObject() {
+ final Map<String,Object> properties = new HashMap<String,Object>();
+ final Identifier id = new SimpleIdentifier(null, "An identifier", false);
+ assertNull(properties.put(AbstractIdentifiedObject.IDENTIFIERS_KEY, id));
+ assertNull(properties.put(AbstractIdentifiedObject.ALIAS_KEY, "An alias"));
+ assertNull(properties.put(AbstractIdentifiedObject.NAME_KEY, "Dummy object"));
+ assertNull(properties.put(AbstractIdentifiedObject.REMARKS_KEY, "Some remarks"));
+ final BuilderMock builder = new BuilderMock(new AbstractIdentifiedObject(properties));
+
+ assertEquals("Expected only name and remarks.", 2, builder.properties.size());
+ builder.onCreate(false);
+ assertEquals("Expected name, aliases, identifiers and remarks.", 4, builder.properties.size());
+
+ assertEquals(AbstractIdentifiedObject.NAME_KEY, "Dummy object",
+ builder.properties.get(AbstractIdentifiedObject.NAME_KEY).toString());
+
+ assertEquals(AbstractIdentifiedObject.REMARKS_KEY, "Some remarks",
+ builder.properties.get(AbstractIdentifiedObject.REMARKS_KEY).toString());
+
+ assertEquals(AbstractIdentifiedObject.ALIAS_KEY, "An alias",
+ ((Object[]) builder.properties.get(AbstractIdentifiedObject.ALIAS_KEY))[0].toString());
+
+ assertSame(AbstractIdentifiedObject.IDENTIFIERS_KEY, id,
+ ((Object[]) builder.properties.get(AbstractIdentifiedObject.IDENTIFIERS_KEY))[0]);
+ }
}
Copied: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java (from r1693911, sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java?p2=sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java&p1=sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java&r1=1693911&r2=1693912&rev=1693912&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -39,6 +39,10 @@ import static java.lang.StrictMath.*;
import static java.lang.Double.*;
import static org.junit.Assert.*;
+// Branch-specific imports
+import static org.junit.Assume.assumeTrue;
+import static org.apache.sis.test.Assert.PENDING_NEXT_GEOAPI_RELEASE;
+
/**
* Tests the {@link LambertConicConformal} class. We test using various values of the latitude of origin.
@@ -234,7 +238,7 @@ public final strictfp class LambertConic
@Test
@DependsOnMethod("testLambertConicConformal2SP")
public void testLambertConicConformalMichigan() throws FactoryException, TransformException {
- createGeoApiTest(new LambertConformalMichigan()).testLambertConicConformalMichigan();
+ assumeTrue(PENDING_NEXT_GEOAPI_RELEASE); // Test not available in GeoAPI 3.0
}
/**
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java?rev=1693912&r1=1693911&r2=1693912&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/MapProjectionTestCase.java [UTF-8] Mon Aug 3 14:46:30 2015
@@ -65,6 +65,9 @@ strictfp class MapProjectionTestCase ext
final Ellipsoid ellipsoid = (ellipse ? GeodeticDatumMock.WGS84 : GeodeticDatumMock.SPHERE).getEllipsoid();
parameters.parameter(Constants.SEMI_MAJOR).setValue(ellipsoid.getSemiMajorAxis());
parameters.parameter(Constants.SEMI_MINOR).setValue(ellipsoid.getSemiMinorAxis());
+ if (ellipse) {
+ parameters.parameter(Constants.INVERSE_FLATTENING).setValue(ellipsoid.getInverseFlattening());
+ }
return parameters;
}