This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/master by this push:
new fd8015a Implement "Modified Azimuthal Equidistant" projection method
(without derivative for now). https://issues.apache.org/jira/browse/SIS-237
fd8015a is described below
commit fd8015a050d71db2d7cced23056e65e02a424f12
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Mar 20 23:09:25 2020 +0100
Implement "Modified Azimuthal Equidistant" projection method (without
derivative for now).
https://issues.apache.org/jira/browse/SIS-237
---
.../apache/sis/internal/referencing/Formulas.java | 21 +-
.../provider/AzimuthalEquidistantSpherical.java | 83 ++++++++
.../provider/ModifiedAzimuthalEquidistant.java | 167 +++++++++++++++
.../operation/projection/AlbersEqualArea.java | 4 +-
.../operation/projection/AzimuthalEquidistant.java | 202 +++++++++++++++++++
.../projection/ModifiedAzimuthalEquidistant.java | 223 +++++++++++++++++++++
.../operation/projection/ObliqueStereographic.java | 4 +-
.../operation/projection/Polyconic.java | 3 +-
...g.opengis.referencing.operation.OperationMethod | 2 +
.../referencing/provider/ProvidersTest.java | 2 +
.../projection/AzimuthalEquidistantTest.java | 162 +++++++++++++++
.../ModifiedAzimuthalEquidistantTest.java | 81 ++++++++
.../sis/test/suite/ReferencingTestSuite.java | 2 +
13 files changed, 950 insertions(+), 6 deletions(-)
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
index 6cce1df..f0012a5 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
@@ -33,7 +33,7 @@ import static
org.apache.sis.internal.metadata.ReferencingServices.NAUTICAL_MILE
* do not want to expose publicly those arbitrary values (or at least not in a
too direct way).
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 0.4
* @module
*/
@@ -161,6 +161,25 @@ public final class Formulas extends Static {
}
/**
+ * Returns the radius of the conformal sphere at a given latitude.
+ * The radius of conformal sphere is computed as below:
+ *
+ * <blockquote>Rc = √(1 – ℯ²) / (1 – ℯ²sin²φ) where ℯ² = 1 -
(b/a)²</blockquote>
+ *
+ * This is a function of latitude and therefore not constant.
+ *
+ * @param ellipsoid the ellipsoid for which to compute the radius of
conformal sphere.
+ * @param φ the latitude in radians where to compute the radius
of conformal sphere.
+ * @return radius of the conformal sphere at latitude φ.
+ */
+ public static double radiusOfConformalSphere(final Ellipsoid ellipsoid,
final double φ) {
+ final double sinφ = Math.sin(φ);
+ final double a = ellipsoid.getSemiMajorAxis();
+ final double r = ellipsoid.getSemiMinorAxis() / a;
+ return a * (r / (1 - (1 - r*r) * (sinφ*sinφ)));
+ }
+
+ /**
* Computes the semi-minor axis length from the given semi-major axis and
inverse flattening factor.
*
* @param semiMajorAxis the semi-major axis length.
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AzimuthalEquidistantSpherical.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AzimuthalEquidistantSpherical.java
new file mode 100644
index 0000000..ebaea11
--- /dev/null
+++
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AzimuthalEquidistantSpherical.java
@@ -0,0 +1,83 @@
+/*
+ * 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.internal.referencing.provider;
+
+import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.PlanarProjection;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+
+
+/**
+ * The provider for <cite>"Azimuthal Equidistant (Spherical)"</cite>
projection.
+ * This projection method has no EPSG code.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see <a
href="http://geotiff.maptools.org/proj_list/azimuthal_equidistant.html">GeoTIFF
parameters for Azimuthal Equidistant</a>
+ *
+ * @since 1.1
+ * @module
+ */
+@XmlTransient
+public final class AzimuthalEquidistantSpherical extends MapProjection {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -1152512250113874950L;
+
+ /**
+ * The group of all parameters expected by this coordinate operation.
+ */
+ static final ParameterDescriptorGroup PARAMETERS;
+ static {
+ PARAMETERS = builder().setCodeSpace(null, null)
+ .addName("Azimuthal Equidistant (Spherical)")
+ .createGroupForMapProjection(
+ ModifiedAzimuthalEquidistant.LATITUDE_OF_ORIGIN,
+ ModifiedAzimuthalEquidistant.LONGITUDE_OF_ORIGIN,
+ ModifiedAzimuthalEquidistant.FALSE_EASTING,
+ ModifiedAzimuthalEquidistant.FALSE_NORTHING);
+ }
+
+ /**
+ * Constructs a new provider.
+ */
+ public AzimuthalEquidistantSpherical() {
+ super(PARAMETERS);
+ }
+
+ /**
+ * Returns the operation type for this map projection.
+ */
+ @Override
+ public Class<PlanarProjection> getOperationType() {
+ return PlanarProjection.class;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the map projection created from the given parameter values.
+ */
+ @Override
+ protected final NormalizedProjection createProjection(final Parameters
parameters) {
+ return new
org.apache.sis.referencing.operation.projection.AzimuthalEquidistant(this,
parameters);
+ }
+}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ModifiedAzimuthalEquidistant.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ModifiedAzimuthalEquidistant.java
new file mode 100644
index 0000000..5c2a680
--- /dev/null
+++
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ModifiedAzimuthalEquidistant.java
@@ -0,0 +1,167 @@
+/*
+ * 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.internal.referencing.provider;
+
+import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.PlanarProjection;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+
+
+/**
+ * The provider for <cite>"Modified Azimuthal Equidistant"</cite> projection
(EPSG:9832).
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see <a
href="http://geotiff.maptools.org/proj_list/azimuthal_equidistant.html">GeoTIFF
parameters for Azimuthal Equidistant</a>
+ *
+ * @since 1.1
+ * @module
+ */
+@XmlTransient
+public final class ModifiedAzimuthalEquidistant extends MapProjection {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -9025540136016917410L;
+
+ /**
+ * The operation parameter descriptor for the <cite>Latitude of natural
origin</cite> (φ₀) parameter value.
+ * Valid values range is (-90 … 90)° and default value is 0°.
+ *
+ * <!-- Generated by ParameterNameTableGenerator -->
+ * <table class="sis">
+ * <caption>Parameter names</caption>
+ * <tr><td> EPSG: </td><td> Latitude of natural origin </td></tr>
+ * <tr><td> OGC: </td><td> latitude_of_origin </td></tr>
+ * <tr><td> GeoTIFF: </td><td> CenterLat </td></tr>
+ * <tr><td> Proj4: </td><td> lat_0 </td></tr>
+ * </table>
+ */
+ public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
+
+ /**
+ * The operation parameter descriptor for the <cite>Longitude of natural
origin</cite> (λ₀) parameter value.
+ * Valid values range is [-180 … 180]° and default value is 0°.
+ *
+ * <!-- Generated by ParameterNameTableGenerator -->
+ * <table class="sis">
+ * <caption>Parameter names</caption>
+ * <tr><td> EPSG: </td><td> Longitude of natural origin </td></tr>
+ * <tr><td> OGC: </td><td> central_meridian </td></tr>
+ * <tr><td> GeoTIFF: </td><td> CenterLong </td></tr>
+ * <tr><td> Proj4: </td><td> lon_0 </td></tr>
+ * </table>
+ */
+ public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
+
+ /**
+ * The operation parameter descriptor for the <cite>False easting</cite>
(FE) parameter value.
+ * Valid values range is unrestricted and default value is 0 metre.
+ *
+ * <!-- Generated by ParameterNameTableGenerator -->
+ * <table class="sis">
+ * <caption>Parameter names</caption>
+ * <tr><td> EPSG: </td><td> False easting </td></tr>
+ * <tr><td> OGC: </td><td> false_easting </td></tr>
+ * <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+ * <tr><td> Proj4: </td><td> x_0 </td></tr>
+ * </table>
+ */
+ public static final ParameterDescriptor<Double> FALSE_EASTING;
+
+ /**
+ * The operation parameter descriptor for the <cite>False northing</cite>
(FN) parameter value.
+ * Valid values range is unrestricted and default value is 0 metre.
+ *
+ * <!-- Generated by ParameterNameTableGenerator -->
+ * <table class="sis">
+ * <caption>Parameter names</caption>
+ * <tr><td> EPSG: </td><td> False northing </td></tr>
+ * <tr><td> OGC: </td><td> false_northing </td></tr>
+ * <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+ * <tr><td> Proj4: </td><td> y_0 </td></tr>
+ * </table>
+ */
+ public static final ParameterDescriptor<Double> FALSE_NORTHING;
+
+ /**
+ * Returns a parameter with the same names and identifiers than the given
parameter,
+ * except (OGC), ESRI and netCDF names which are omitted. We omit those
names for now
+ * because we have not seen a reference about what those parameter names
should be.
+ * The OGC names are kept despite that because it uses the same names for
most projection.
+ * This may be revisited in future SIS versions.
+ *
+ * <p>The OGC and GeoTIFF names kept by this method are actually the names
for
+ * <cite>Azimuthal Equidistant</cite> (not modified) projection.</p>
+ */
+ private static ParameterBuilder erase(final ParameterBuilder builder,
ParameterDescriptor<?> template) {
+ return builder.addNamesAndIdentifiers(template)
// Copy from this parameter…
+ .rename(Citations.ESRI, (CharSequence[]) null)
// … except for those names.
+ .rename(Citations.NETCDF, (CharSequence[]) null);
+ }
+
+ /**
+ * The group of all parameters expected by this coordinate operation.
+ */
+ static final ParameterDescriptorGroup PARAMETERS;
+ static {
+ final ParameterBuilder builder = builder();
+ LATITUDE_OF_ORIGIN = createLatitude (erase(builder,
Orthographic.LATITUDE_OF_ORIGIN), true);
+ LONGITUDE_OF_ORIGIN = createLongitude(erase(builder,
Orthographic.LONGITUDE_OF_ORIGIN));
+ FALSE_EASTING = createShift (erase(builder,
Orthographic.FALSE_EASTING));
+ FALSE_NORTHING = createShift (erase(builder,
Orthographic.FALSE_NORTHING));
+
+ PARAMETERS = builder.addIdentifier("9832")
+ .addName("Modified Azimuthal Equidistant")
+ .createGroupForMapProjection(
+ LATITUDE_OF_ORIGIN,
+ LONGITUDE_OF_ORIGIN,
+ FALSE_EASTING,
+ FALSE_NORTHING);
+ }
+
+ /**
+ * Constructs a new provider.
+ */
+ public ModifiedAzimuthalEquidistant() {
+ super(PARAMETERS);
+ }
+
+ /**
+ * Returns the operation type for this map projection.
+ */
+ @Override
+ public Class<PlanarProjection> getOperationType() {
+ return PlanarProjection.class;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the map projection created from the given parameter values.
+ */
+ @Override
+ protected final NormalizedProjection createProjection(final Parameters
parameters) {
+ return new
org.apache.sis.referencing.operation.projection.ModifiedAzimuthalEquidistant(this,
parameters);
+ }
+}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
index 92f1538..c9aa589 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
@@ -255,8 +255,8 @@ public class AlbersEqualArea extends EqualAreaProjection {
/*
* End of map projection. Now compute the derivative.
*/
- final double me = 1 - eccentricitySquared;
- final double dρ_dφ = -0.5 * nm*dqm_dφ(sinφ, cos(φ)*me) / (me*ρ);
+ final double ome = 1 - eccentricitySquared;
+ final double dρ_dφ = -0.5 * nm*dqm_dφ(sinφ, cos(φ)*ome) / (ome*ρ);
return new Matrix2(cosθ*ρ, dρ_dφ*sinθ, // ∂x/∂λ, ∂x/∂φ
-sinθ*ρ, dρ_dφ*cosθ); // ∂y/∂λ, ∂y/∂φ
}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java
new file mode 100644
index 0000000..6bd1b4a
--- /dev/null
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistant.java
@@ -0,0 +1,202 @@
+/*
+ * 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.projection;
+
+import java.util.EnumMap;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.parameter.ParameterDescriptor;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.util.Workaround;
+
+import static java.lang.Math.*;
+import static
org.apache.sis.internal.referencing.provider.ModifiedAzimuthalEquidistant.*;
+
+
+/**
+ * <cite>Azimuthal Equidistant (Spherical)</cite> projection.
+ * This projection method has no EPSG code.
+ * See the following references for an overview:
+ * <ul>
+ * <li><a
href="https://en.wikipedia.org/wiki/Azimuthal_equidistant_projection">Azimuthal
equidistant projection</a></li>
+ * <li><a
href="https://mathworld.wolfram.com/AzimuthalEquidistantProjection.html">Azimuthal
Equidistant Projection</a></li>
+ * </ul>
+ *
+ * Current implementation supports only the spherical case.
+ * For ellipsoidal formulas, the {@link ModifiedAzimuthalEquidistant} provides
an approximation
+ * valid under 800 kilometres of the projection centre.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see ModifiedAzimuthalEquidistant
+ *
+ * @since 1.1
+ * @module
+ */
+public class AzimuthalEquidistant extends NormalizedProjection {
+ /**
+ * For compatibility with different versions during deserialization.
+ */
+ private static final long serialVersionUID = -6969752149232210847L;
+
+ /**
+ * Sine and cosine of the latitude of origin φ₀.
+ */
+ final double sinφ0, cosφ0;
+
+ /**
+ * Work around for RFE #4093999 in Sun's bug database
+ * ("Relax constraint on placement of this()/super() call in
constructors").
+ */
+ @Workaround(library="JDK", version="1.8")
+ private static Initializer initializer(final OperationMethod method, final
Parameters parameters) {
+ final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new
EnumMap<>(ParameterRole.class);
+ roles.put(ParameterRole.LATITUDE_OF_CONFORMAL_SPHERE_RADIUS,
LATITUDE_OF_ORIGIN);
+ roles.put(ParameterRole.CENTRAL_MERIDIAN,
LONGITUDE_OF_ORIGIN);
+ roles.put(ParameterRole.FALSE_EASTING,
FALSE_EASTING);
+ roles.put(ParameterRole.FALSE_NORTHING,
FALSE_NORTHING);
+ return new Initializer(method, parameters, roles, (byte) 0);
+ }
+
+ /**
+ * Creates a Azimuthal Equidistant projection from the given parameters.
+ * The {@code method} argument can be the description of one of the
following:
+ *
+ * <ul>
+ * <li><cite>"Azimuthal Equidistant (Spherical)"</cite>.</li>
+ * </ul>
+ *
+ * @param method description of the projection parameters.
+ * @param parameters the parameter values of the projection to create.
+ */
+ public AzimuthalEquidistant(final OperationMethod method, final Parameters
parameters) {
+ this(initializer(method, parameters));
+ }
+
+ /**
+ * Creates a new normalized projection from the parameters computed by the
given initializer.
+ *
+ * @param initializer the initializer for computing map projection
internal parameters.
+ */
+ AzimuthalEquidistant(final Initializer initializer) {
+ super(initializer);
+ final double φ0 =
toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));
+ cosφ0 = cos(φ0);
+ sinφ0 = sin(φ0);
+ }
+
+ /**
+ * Converts the specified (λ,φ) coordinate and stores the
(<var>x</var>,<var>y</var>) result in {@code dstPts}.
+ *
+ * @param srcPts source point coordinate, as (<var>longitude</var>,
<var>latitude</var>) in radians.
+ * @param srcOff the offset of the single coordinate to be converted
in the source array.
+ * @param dstPts the array into which the converted coordinate is
returned (may be the same than {@code srcPts}).
+ * @param dstOff the offset of the location of the converted
coordinate that is stored in the destination array.
+ * @param derivate {@code true} for computing the derivative, or {@code
false} if not needed.
+ * @return the matrix of the projection derivative at the given source
position,
+ * or {@code null} if the {@code derivate} argument is {@code
false}.
+ * @throws ProjectionException if the coordinate can not be converted.
+ */
+ @Override
+ public Matrix transform(final double[] srcPts, final int srcOff,
+ final double[] dstPts, final int dstOff,
+ final boolean derivate) throws ProjectionException
+ {
+ final double λ = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
+ final double cosλ = cos(λ);
+ final double sinλ = sin(λ);
+ final double cosφ = cos(φ);
+ final double sinφ = sin(φ);
+ final double cosc = sinφ0*sinφ + cosφ0*cosφ*cosλ;
+ final double c = acos(cosc);
+ final boolean ind = abs(c) < ANGULAR_TOLERANCE;
+ final double k = ind ? 1 : c/sin(c);
+ final double cφcλ = cosφ * cosλ;
+ final double cφsλ = cosφ * sinλ;
+ final double x = k * cφsλ;
+ final double y = k * (cosφ0*sinφ - sinφ0*cφcλ);
+ if (dstPts != null) {
+ dstPts[dstOff ] = x;
+ dstPts[dstOff+1] = y;
+ }
+ if (!derivate) {
+ return null;
+ }
+ /*
+ * Formulas below can be verified with Maxima.
+ *
+ *
https://svn.apache.org/repos/asf/sis/analysis/Azimuthal%20Equidistant%20(Spherical).wxmx
+ */
+ final double t = ind ? 1./3 : (1/k - cosc) / (1 - cosc*cosc);
+ final double sφcλ = sinφ * cosλ;
+ final double tλ = cφsλ * cosφ0*t;
+ final double tφ = (cosφ0*sφcλ - sinφ0*cosφ) * t;
+ return new Matrix2(x*tλ + k*cφcλ, // ∂x/∂λ
+ x*tφ - k*sinλ*sinφ, // ∂x/∂φ
+ y*tλ + x*sinφ0, // ∂y/∂λ
+ y*tφ + k*(sinφ0*sφcλ + cosφ0*cosφ)); // ∂y/∂φ
+ }
+
+ /**
+ * Converts the specified (<var>x</var>,<var>y</var>) coordinates
+ * and stores the result in {@code dstPts} (angles in radians).
+ *
+ * @param srcPts the array containing the source point coordinate, as
linear distance on a unit sphere or ellipse.
+ * @param srcOff the offset of the point to be converted in the source
array.
+ * @param dstPts the array into which the converted point coordinate is
returned (may be the same than {@code srcPts}).
+ * @param dstOff the offset of the location of the converted point that
is stored in the destination array.
+ * @throws ProjectionException if the point can not be converted.
+ */
+ @Override
+ protected void inverseTransform(final double[] srcPts, final int srcOff,
+ final double[] dstPts, final int dstOff)
+ throws ProjectionException
+ {
+ final double x = srcPts[srcOff ];
+ final double y = srcPts[srcOff+1];
+ final double D = hypot(x, y);
+ final double sinD = sin(D);
+ final double cosD = cos(D);
+ dstPts[dstOff ] = atan2(x*sinD, (cosφ0*cosD*D - sinφ0*sinD*y));
+ dstPts[dstOff+1] = asin(cosD*sinφ0 + sinD*cosφ0*y/D);
+ }
+
+ /**
+ * Returns the names of additional internal parameters which need to be
taken in account when
+ * comparing two {@code AzimuthalEquidistant} projections or formatting
them in debug mode.
+ *
+ * <p>We could report any of the internal parameters. But since they are
all derived from φ₀ and
+ * the {@linkplain #eccentricity eccentricity} and since the eccentricity
is already reported by
+ * the super-class, we report only φ₀ as a representative of the internal
parameters.</p>
+ */
+ @Override
+ final String[] getInternalParameterNames() {
+ return new String[] {"φ₀"};
+ }
+
+ /**
+ * Returns the values of additional internal parameters which need to be
taken in account when
+ * comparing two {@code ObliqueStereographic} projections or formatting
them in debug mode.
+ */
+ @Override
+ final double[] getInternalParameterValues() {
+ return new double[] {(cosφ0 < PI/4) ? acos(cosφ0) : asin(sinφ0)};
+ }
+}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ModifiedAzimuthalEquidistant.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ModifiedAzimuthalEquidistant.java
new file mode 100644
index 0000000..5e26b4c
--- /dev/null
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ModifiedAzimuthalEquidistant.java
@@ -0,0 +1,223 @@
+/*
+ * 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.projection;
+
+import java.util.EnumMap;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.parameter.ParameterDescriptor;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.util.Workaround;
+
+import static java.lang.Math.*;
+import static
org.apache.sis.internal.referencing.provider.ModifiedAzimuthalEquidistant.*;
+
+
+/**
+ * <cite>Modified Azimuthal Equidistant</cite> projection (EPSG:9832).
+ * See the following references for an overview:
+ * <ul>
+ * <li><a
href="https://en.wikipedia.org/wiki/Azimuthal_equidistant_projection">Azimuthal
equidistant projection</a></li>
+ * <li><a
href="https://mathworld.wolfram.com/AzimuthalEquidistantProjection.html">Azimuthal
Equidistant Projection</a></li>
+ * </ul>
+ *
+ * <h2>Description</h2>
+ * An approximation of the oblique form of the <cite>Azimuthal
Equidistant</cite> projection.
+ * For relatively short distances (e.g. under 800 kilometres) this
modification introduces no significant error.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see AzimuthalEquidistant
+ *
+ * @since 1.1
+ * @module
+ */
+public class ModifiedAzimuthalEquidistant extends AzimuthalEquidistant {
+ /**
+ * For compatibility with different versions during deserialization.
+ */
+ private static final long serialVersionUID = 96569177715708509L;
+
+ /**
+ * A term involving radius of curvature ν₀, the latitude of origin φ₀ and
the eccentricity.
+ * The semi-major axis length <var>a</var> is omitted since it is handled
outside this class.
+ */
+ private final double ℯ2_ν0_sinφ0;
+
+ /**
+ * The ℯ⋅sin(φ₀)/√(1 − ℯ²) term, used in direct projection.
+ */
+ private final double G;
+
+ /**
+ * The ℯ⋅cos(φ₀)/√(1 − ℯ²) term. This is the <var>H</var> term in EPSG
guidance notes
+ * but without the cos(α) term (omitted because α depends on the point to
project).
+ *
+ * <p>Note that during inverse projection, EPSG guidance notes has a
<var>A</var> as:
+ * −ℯ²⋅cos²φ₀/(1 − ℯ²)⋅cos²α. We opportunistically use Hp² for that
purpose.</p>
+ */
+ private final double Hp;
+
+ /**
+ * The 3⋅ℯ²⋅sin(φ₀)⋅cos(φ₀)/(1 − ℯ²) term. This is the <var>B</var> term
in EPSG guidance notes
+ * for inverse projection but without the terms that depend on coordinates
of transformed point.
+ */
+ private final double Bp;
+
+ /**
+ * Work around for RFE #4093999 in Sun's bug database
+ * ("Relax constraint on placement of this()/super() call in
constructors").
+ */
+ @Workaround(library="JDK", version="1.8")
+ private static Initializer initializer(final OperationMethod method, final
Parameters parameters) {
+ final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new
EnumMap<>(ParameterRole.class);
+ roles.put(ParameterRole.CENTRAL_MERIDIAN, LONGITUDE_OF_ORIGIN);
+ roles.put(ParameterRole.FALSE_EASTING, FALSE_EASTING);
+ roles.put(ParameterRole.FALSE_NORTHING, FALSE_NORTHING);
+ return new Initializer(method, parameters, roles, (byte) 0);
+ }
+
+ /**
+ * Creates a Modified Azimuthal Equidistant projection from the given
parameters.
+ * The {@code method} argument can be the description of one of the
following:
+ *
+ * <ul>
+ * <li><cite>"Modified Azimuthal Equidistant"</cite>.</li>
+ * </ul>
+ *
+ * @param method description of the projection parameters.
+ * @param parameters the parameter values of the projection to create.
+ */
+ public ModifiedAzimuthalEquidistant(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").
+ */
+ @Workaround(library="JDK", version="1.8")
+ private ModifiedAzimuthalEquidistant(final Initializer initializer) {
+ super(initializer);
+ final double axisRatio, ν0, f;
+ axisRatio = initializer.axisLengthRatio().doubleValue();
+ ν0 = initializer.radiusOfCurvature(sinφ0);
+ ℯ2_ν0_sinφ0 = eccentricitySquared * ν0 * sinφ0;
+ f = eccentricity / axisRatio; // √(1 - ℯ²) =
b/a
+ G = f * sinφ0;
+ Hp = f * cosφ0;
+ Bp = 3*eccentricitySquared * (sinφ0*cosφ0) / (1 -
eccentricitySquared);
+
+ final MatrixSIS denormalize =
context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+ denormalize.convertBefore(0, ν0, null);
+ denormalize.convertBefore(1, ν0, null);
+ }
+
+ /**
+ * Converts the specified (λ,φ) coordinate and stores the
(<var>x</var>,<var>y</var>) result in {@code dstPts}.
+ *
+ * @return the matrix of the projection derivative at the given source
position,
+ * or {@code null} if the {@code derivate} argument is {@code
false}.
+ * @throws ProjectionException if the coordinate can not be converted.
+ */
+ @Override
+ public Matrix transform(final double[] srcPts, final int srcOff,
+ final double[] dstPts, final int dstOff,
+ final boolean derivate) throws ProjectionException
+ {
+ final double λ = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
+ final double cosλ = cos(λ);
+ final double sinλ = sin(λ);
+ final double cosφ = cos(φ);
+ final double sinφ = sin(φ);
+ final double rν = sqrt(1 - eccentricitySquared*(sinφ*sinφ));
+ final double tanΨ = ((1 - eccentricitySquared)*sinφ + ℯ2_ν0_sinφ0*rν)
/ cosφ;
+ final double rcosΨ = sqrt(1 + tanΨ*tanΨ);
+ final double α = atan2(sinλ, cosφ0*tanΨ - sinφ0*cosλ);
+ final double sinα = sin(α);
+ final double cosα = cos(α);
+ final double H = cosα * Hp;
+ /*
+ * Equations are: s = asin(cos(φ₀)⋅sin(Ψ) − sin(φ₀)⋅cos(Ψ)) ⋅
signum(cos(α)) for small α
+ * s = asin(sin(λ)⋅cos(Ψ) / sin(α))
for other α
+ *
+ * Using identity: sin(atan(x)) = x / √(1 + x²)
+ * Rewrite as: sin(Ψ) = tan(Ψ) / √(1 + tan²Ψ)
+ */
+ double c;
+ if (abs(sinα) < ANGULAR_TOLERANCE) {
+ c = (cosφ0*tanΨ - sinφ0) / rcosΨ;
+ if (cosα < 0) c = -c;
+ } else {
+ c = sinλ / (rcosΨ * sinα);
+ }
+ c = asin(c); // After this line this is the `s`
value in EPSG guidance notes.
+ final double s2 = c * c;
+ final double s3 = s2 * c;
+ final double s4 = s2 * s2;
+ final double s5 = s4 * c;
+ final double H2 = H*H;
+ final double GH = G*H;
+ c *= 1 - (s2/6 * H2*(1 - H2))
+ + (s3/8 * GH*(1 - 2*H2))
+ + (s4/120 * (H2*(4 - 7*H2) - 3*(G*G)*(1 - 7*H2)))
+ - (s5/48 * GH);
+
+ if (dstPts != null) {
+ dstPts[dstOff ] = c * sinα;
+ dstPts[dstOff+1] = c * cosα;
+ }
+ if (!derivate) {
+ return null;
+ }
+ throw new ProjectionException("Derivative not yet implemented.");
+ }
+
+ /**
+ * Converts the specified (<var>x</var>,<var>y</var>) coordinates
+ * and stores the result in {@code dstPts} (angles in radians).
+ */
+ @Override
+ protected void inverseTransform(final double[] srcPts, final int srcOff,
+ final double[] dstPts, final int dstOff)
+ throws ProjectionException
+ {
+ final double x = srcPts[srcOff ];
+ final double y = srcPts[srcOff+1];
+ final double α = atan2(x, y); // Actually α′ in EPSG
guidance notes.
+ final double sinα = sin(α);
+ final double cosα = cos(α);
+ double negA = Hp * cosα; negA *= negA; // mA = −A compared
to EPSG guidance note.
+ final double B = Bp * (1 + negA) * cosα;
+ final double D2 = x*x + y*y;
+ final double D = sqrt(D2); // D = c′/ν₀, but
division by ν₀ is already done here.
+ final double J = D + (negA*(1 - negA)*(D2*D )/6)
+ - ( B*(1 - 3*negA)*(D2*D2)/24);
+ final double J2 = J*J;
+ final double K = 1 + (negA*J2/2) - (B*(J2*J)/6);
+ final double sinJ = sin(J);
+ final double sinΨ = sinφ0*cos(J) + cosφ0*sinJ*cosα;
+ final double cosΨ = sqrt(1 - sinΨ*sinΨ);
+ dstPts[dstOff ] = asin(sinα*sinJ / cosΨ);
+ dstPts[dstOff+1] = atan((1 - eccentricitySquared*sinφ0*K / sinΨ) *
(sinΨ/cosΨ)
+ / (1 - eccentricitySquared));
+ }
+}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
index 64adcd0..d443612 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
@@ -370,11 +370,11 @@ public class ObliqueStereographic extends
NormalizedProjection {
final double ψ = log((1 + sinχ) / ((1 - sinχ)*c)) / (2*n);
double φ = 2*atan(exp(ψ)) - PI/2; //
First approximation
final double he = eccentricity/2;
- final double me = 1 - eccentricitySquared;
+ final double ome = 1 - eccentricitySquared;
for (int it=0; it<MAXIMUM_ITERATIONS; it++) {
final double ℯsinφ = eccentricity * sin(φ);
final double ψi = log(tan(φ/2 + PI/4) * pow((1 - ℯsinφ) / (1 +
ℯsinφ), he));
- final double Δφ = (ψ - ψi) * cos(φ) * (1 - ℯsinφ*ℯsinφ) / me;
+ final double Δφ = (ψ - ψi) * cos(φ) * (1 - ℯsinφ*ℯsinφ) / ome;
φ += Δφ;
if (!(abs(Δφ) > ITERATION_TOLERANCE)) { // Use '!' for
accepting NaN.
dstPts[dstOff ] = λ;
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Polyconic.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Polyconic.java
index d9ef536..b4c4bcd 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Polyconic.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Polyconic.java
@@ -227,6 +227,7 @@ public class Polyconic extends MeridianArcBased {
final double y = srcPts[srcOff+1];
double φ = y; // A = (M₀ + (N-FE)/a) —
Snyder 18-18 with M₀=0, FE=0 and a=1.
final double B = y*y + x*x; // B = A² + ((E-FE)²/a²) —
Snyder 18-19 with FE=0 and a=1.
+ final double ome = 1 - eccentricitySquared;
int i = MAXIMUM_ITERATIONS;
double dφ;
do {
@@ -239,7 +240,7 @@ public class Polyconic extends MeridianArcBased {
final double rν = sqrt(1 - eccentricitySquared * sinφ2);
final double C = rν * sinφ/cosφ;
final double M = distance(φ, sinφ, cosφ);
- final double Mp = (1 - eccentricitySquared) + sinφ2*(ci2 +
sinφ2*(ci4 + sinφ2*ci6)); // Derived from Snyder 18-17
+ final double Mp = ome + sinφ2*(ci2 + sinφ2*(ci4 + sinφ2*ci6));
// Derived from Snyder 18-17
final double M2B = M*M + B;
final double sin2φ = sin(2*φ);
/*
diff --git
a/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
b/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
index ae5ddfe..11937c1 100644
---
a/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
+++
b/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
@@ -55,6 +55,8 @@
org.apache.sis.internal.referencing.provider.ObliqueMercatorCenter
org.apache.sis.internal.referencing.provider.ObliqueMercatorTwoPoints
org.apache.sis.internal.referencing.provider.ObliqueMercatorTwoPointsCenter
org.apache.sis.internal.referencing.provider.Orthographic
+org.apache.sis.internal.referencing.provider.ModifiedAzimuthalEquidistant
+org.apache.sis.internal.referencing.provider.AzimuthalEquidistantSpherical
org.apache.sis.internal.referencing.provider.ZonedTransverseMercator
org.apache.sis.internal.referencing.provider.Sinusoidal
org.apache.sis.internal.referencing.provider.Polyconic
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
index 12002e6..fd483a4 100644
---
a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
+++
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
@@ -105,6 +105,8 @@ public final strictfp class ProvidersTest extends TestCase {
ObliqueMercatorTwoPoints.class,
ObliqueMercatorTwoPointsCenter.class,
Orthographic.class,
+ ModifiedAzimuthalEquidistant.class,
+ AzimuthalEquidistantSpherical.class,
ZonedTransverseMercator.class,
SatelliteTracking.class,
Sinusoidal.class,
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistantTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistantTest.java
new file mode 100644
index 0000000..c716b81
--- /dev/null
+++
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/AzimuthalEquidistantTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.projection;
+
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.referencing.provider.MapProjection;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+
+/**
+ * Tests the {@link AzimuthalEquidistant} class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+@DependsOn(NormalizedProjectionTest.class)
+public strictfp class AzimuthalEquidistantTest extends MapProjectionTestCase {
+ /**
+ * Returns the method to be tested.
+ */
+ MapProjection method() {
+ return new
org.apache.sis.internal.referencing.provider.AzimuthalEquidistantSpherical();
+ }
+
+ /**
+ * Tests the forward and inverse projection using test point given in
Snyder page 337.
+ * The Snyder's test uses a sphere of radius R=3 and a center at 40°N and
100°W.
+ * The test in this class modify the longitude to 10°W for avoiding to mix
wraparound
+ * considerations in this test.
+ *
+ * @throws FactoryException if an error occurred while creating the
projection.
+ * @throws TransformException if an error occurred while projecting the
test point.
+ */
+ @Test
+ public void testSpherical() throws FactoryException, TransformException {
+ createCompleteProjection(method(),
+ 3, // Semi-major axis
+ 3, // Semi-minor axis
+ -10, // Longitude of natural origin
(central-meridian)
+ 40, // Latitude of natural origin
+ Double.NaN, // Standard parallel 1
+ Double.NaN, // Standard parallel 2
+ Double.NaN, // Scale factor
+ 0, // False easting
+ 0); // False Northing
+
+ tolerance = 2E-7;
+ verifyTransform(new double[] {
+ -170, // Was 1OO°E in Snyder test, shifted by 90° in
our test.
+ -20 // 20°S
+ }, new double[] {
+ -5.8311398,
+ 5.5444634
+ });
+ }
+
+ /**
+ * Tests with the point published in EPSG guidance note.
+ *
+ * @throws FactoryException if an error occurred while creating the
projection.
+ * @throws TransformException if an error occurred while projecting the
test point.
+ */
+ @Test
+ public void testWithEPSG() throws FactoryException, TransformException {
+ /*
+ * Since we are testing spherical formulas with a sample point
calculated
+ * for ellipsoidal formulas, we have to use a high tolerance threshold.
+ */
+ tolerance = 20;
+ testWithEPSG(CLARKE_A, CLARKE_B);
+ }
+
+ /**
+ * Tests with the point published in EPSG guidance note.
+ * Callers must set {@link #tolerance} before to invoke this method.
+ *
+ * @param semiMajor {@link #CLARKE_A}, or an alternative value if desired.
+ * @param semiMinor {@link #CLARKE_B}, or an alternative value if desired.
+ * @throws FactoryException if an error occurred while creating the
projection.
+ * @throws TransformException if an error occurred while projecting the
test point.
+ */
+ final void testWithEPSG(final double semiMajor, final double semiMinor)
throws FactoryException, TransformException {
+ createCompleteProjection(method(),
+ semiMajor,
+ semiMinor,
+ 138 + (10 + 7.48/60)/60, // Longitude of natural origin
(central-meridian)
+ 9 + (32 + 48.15/60)/60, // Latitude of natural origin
+ Double.NaN, // Standard parallel 1
+ Double.NaN, // Standard parallel 2
+ Double.NaN, // Scale factor
+ 40000, // False easting
+ 60000); // False Northing
+ /*
+ * Test point given in EPSG guidance note.
+ */
+ verifyTransform(new double[] {
+ 138 + (11 + 34.908/60)/60, // 138°11'34.908"E
+ 9 + (35 + 47.493/60)/60 // 9°35'47.493"N
+ }, new double[] {
+ 42665.90,
+ 65509.82
+ });
+ /*
+ * North of map origin, for entering in the special case for c/sin(c)
+ * when c is close to zero. This point is not given by EPSG guidance
+ * notes; this is an anti-regression test.
+ */
+ verifyTransform(new double[] {
+ 138 + (10 + 7.48/60)/60 + 0.00000000001,
+ 9 + (32 + 48.15/60)/60 + 0.01
+ }, new double[] {
+ 40000.00,
+ 61105.98
+ });
+ }
+
+ /**
+ * Tests the derivatives at a few points on a sphere. This method compares
the derivatives computed
+ * by the projection with an estimation of derivatives computed by the
finite differences method.
+ *
+ * @throws FactoryException if an error occurred while creating the map
projection.
+ * @throws TransformException if an error occurred while projecting a
point.
+ */
+ @Test
+ public void testDerivative() throws FactoryException, TransformException {
+ createCompleteProjection(method(),
+ CLARKE_A,
+ CLARKE_B,
+ 40, // Longitude of natural origin
(central-meridian)
+ 25, // Latitude of natural origin
+ Double.NaN, // Standard parallel 1
+ Double.NaN, // Standard parallel 2
+ Double.NaN, // Scale factor
+ 40000, // False easting
+ 60000); // False Northing
+ final double delta = (1.0 / 60) / 1852; //
Approximately 1 metre.
+ derivativeDeltas = new double[] {delta, delta};
+ tolerance = Formulas.LINEAR_TOLERANCE / 100;
+ verifyDerivative(30, 27);
+ verifyDerivative(27, 20);
+ verifyDerivative(40, 25);
+ }
+}
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ModifiedAzimuthalEquidistantTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ModifiedAzimuthalEquidistantTest.java
new file mode 100644
index 0000000..ddca1c2
--- /dev/null
+++
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ModifiedAzimuthalEquidistantTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.projection;
+
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.referencing.provider.MapProjection;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+
+/**
+ * Tests the {@link ModifiedAzimuthalEquidistant} class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+@DependsOn(NormalizedProjectionTest.class)
+public final strictfp class ModifiedAzimuthalEquidistantTest extends
AzimuthalEquidistantTest {
+ /**
+ * Returns the method to be tested.
+ */
+ @Override
+ MapProjection method() {
+ return new
org.apache.sis.internal.referencing.provider.ModifiedAzimuthalEquidistant();
+ }
+
+ /**
+ * Tests the projection on a sphere. We override the method provides in
parent class
+ * because the point provided in Snyder is too far from projection centre.
+ *
+ * @throws FactoryException if an error occurred while creating the
projection.
+ * @throws TransformException if an error occurred while projecting the
test point.
+ */
+ @Test
+ @Override
+ public void testSpherical() throws FactoryException, TransformException {
+ tolerance = 20; // Same tolerance than in parent
class.
+ final double r = 6357767.51; // Conformal sphere radius at the
latitude being tested.
+ testWithEPSG(r, r);
+ }
+
+ /**
+ * Tests with the point published in EPSG guidance note.
+ *
+ * @throws FactoryException if an error occurred while creating the
projection.
+ * @throws TransformException if an error occurred while projecting the
test point.
+ */
+ @Test
+ @Override
+ public void testWithEPSG() throws FactoryException, TransformException {
+ tolerance = 0.01;
+ testWithEPSG(CLARKE_A, CLARKE_B);
+ }
+
+ /**
+ * Not yet supported.
+ */
+ @Test
+ @Override
+ @org.junit.Ignore("Implementation not yet completed")
+ public void testDerivative() throws FactoryException, TransformException {
+ // TODO
+ }
+}
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
index c89627d..3cafaa9 100644
---
a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
+++
b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
@@ -192,6 +192,8 @@ import org.junit.BeforeClass;
org.apache.sis.referencing.operation.projection.PolyconicTest.class,
org.apache.sis.referencing.operation.projection.MollweideTest.class,
org.apache.sis.referencing.operation.projection.OrthographicTest.class,
+
org.apache.sis.referencing.operation.projection.AzimuthalEquidistantTest.class,
+
org.apache.sis.referencing.operation.projection.ModifiedAzimuthalEquidistantTest.class,
org.apache.sis.referencing.operation.projection.SatelliteTrackingTest.class,
// Coordinate operation and derived Coordinate Reference Systems (cyclic
dependency).