This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 33b6d27 First draft of "Sinusoidal equal-area" projection, also known
as "Sanson-Flamsteed". This commit put only the spherical formulas. Ellipsoidal
formulas will come later. https://issues.apache.org/jira/browse/SIS-450
33b6d27 is described below
commit 33b6d27e2d5507e44d07656868298b6ae9289a3b
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Apr 20 23:29:14 2019 +0200
First draft of "Sinusoidal equal-area" projection, also known as
"Sanson-Flamsteed".
This commit put only the spherical formulas. Ellipsoidal formulas will come
later.
https://issues.apache.org/jira/browse/SIS-450
---
.../referencing/provider/MercatorSpherical.java | 2 +-
.../internal/referencing/provider/Sinusoidal.java | 97 +++++++++
.../operation/projection/Sinusoidal.java | 221 +++++++++++++++++++++
...g.opengis.referencing.operation.OperationMethod | 1 +
.../referencing/provider/ProvidersTest.java | 1 +
.../operation/projection/SinusoidalTest.java | 109 ++++++++++
.../sis/test/suite/ReferencingTestSuite.java | 1 +
7 files changed, 431 insertions(+), 1 deletion(-)
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
index 481eeef..64cd5f5 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
@@ -58,7 +58,7 @@ public final class MercatorSpherical extends AbstractMercator
{
* Visualisation CRS / Mercator", now deprecated). But at the
difference of what we did in Mercator2SP, we keep
* the EPSG name here since there is no "standard parallel" parameter.
So the "Scale factor at natural origin"
* parameter name still okay provided that the "Latitude of natural
origin" parameter value stay zero (which is
- * normally enforced by the Mercator1SP.LATITUDE_OF_ORIGIN minimum and
maximum values).
+ * enforced by the Mercator1SP.LATITUDE_OF_ORIGIN minimum and maximum
values).
*/
final ParameterDescriptor<Double> scaleFactor = createScale(builder
.addNamesAndIdentifiers(Mercator1SP.SCALE_FACTOR)
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java
new file mode 100644
index 0000000..9590bc7
--- /dev/null
+++
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java
@@ -0,0 +1,97 @@
+/*
+ * 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.parameter.ParameterNotFoundException;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+
+
+/**
+ * The provider for <cite>"sinusoidal equal-area"</cite> projection.
+ * This projection has no associated EPSG code.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ *
+ * @see <a
href="https://en.wikipedia.org/wiki/Sinusoidal_projection">Sinusoidal
projection on Wikipedia</a>
+ * @see <a
href="http://geotiff.maptools.org/proj_list/sinusoidal.html">GeoTIFF parameters
for Sinusoidal</a>
+ *
+ * @since 1.0
+ * @module
+ */
+@XmlTransient
+public final class Sinusoidal extends MapProjection {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -3236247448683326299L;
+
+ /**
+ * The operation parameter descriptor for the <cite>Longitude of
projection center</cite> (λ₀) parameter value.
+ * Valid values range is [-180 … 180]° and default value is 0°.
+ */
+ public static final ParameterDescriptor<Double> CENTRAL_MERIDIAN =
ESRI.CENTRAL_MERIDIAN;
+
+ /**
+ * The operation parameter descriptor for the <cite>False easting</cite>
(FE) parameter value.
+ * Valid values range is unrestricted and default value is 0 metre.
+ */
+ public static final ParameterDescriptor<Double> FALSE_EASTING =
ESRI.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.
+ */
+ public static final ParameterDescriptor<Double> FALSE_NORTHING =
ESRI.FALSE_NORTHING;
+
+ /**
+ * The group of all parameters expected by this coordinate operation.
+ */
+ private static final ParameterDescriptorGroup PARAMETERS;
+ static {
+ PARAMETERS = builder().setCodeSpace(Citations.OGC, Constants.OGC)
+ .addName ("Sinusoidal")
+ .addName ("Sanson-Flamsteed")
+ .addName (Citations.GEOTIFF, "CT_Sinusoidal")
+ .addIdentifier(Citations.GEOTIFF, "24")
+ .addName (Citations.PROJ4, "sinu")
+ .createGroupForMapProjection(CENTRAL_MERIDIAN, FALSE_EASTING,
FALSE_NORTHING);
+ }
+
+ /**
+ * Constructs a new provider.
+ */
+ public Sinusoidal() {
+ super(PARAMETERS);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the map projection created from the given parameter values.
+ */
+ @Override
+ protected NormalizedProjection createProjection(final Parameters
parameters) throws ParameterNotFoundException {
+ return new
org.apache.sis.referencing.operation.projection.Sinusoidal(this, parameters);
+ }
+}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java
new file mode 100644
index 0000000..e323f04
--- /dev/null
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java
@@ -0,0 +1,221 @@
+/*
+ * 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.util.FactoryException;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+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.Sinusoidal.*;
+
+
+/**
+ * <cite>Sinusoidal equal-area</cite> projection, also known as
<cite>"Sanson-Flamsteed"</cite>.
+ * See the <a
href="https://en.wikipedia.org/wiki/Sinusoidal_projection">Sinusoidal
projection on Wikipedia</a>
+ * for an overview.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public class Sinusoidal extends NormalizedProjection {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 7908925241331303236L;
+
+ /**
+ * 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, CENTRAL_MERIDIAN);
+ roles.put(ParameterRole.FALSE_EASTING, FALSE_EASTING);
+ roles.put(ParameterRole.FALSE_NORTHING, FALSE_NORTHING);
+ return new Initializer(method, parameters, roles, (byte) 0);
+ }
+
+ /**
+ * Creates a Sinusoidal projection from the given parameters.
+ * The {@code method} argument can be the description of one of the
following:
+ *
+ * <ul>
+ * <li><cite>"Sinusoidal"</cite>, also known as
<cite>"Sanson-Flamsteed"</cite>.</li>
+ * </ul>
+ *
+ * @param method description of the projection parameters.
+ * @param parameters the parameter values of the projection to create.
+ */
+ public Sinusoidal(final OperationMethod method, final Parameters
parameters) {
+ super(initializer(method, parameters));
+ }
+
+ /**
+ * Creates a new projection initialized to the same parameters than the
given one.
+ */
+ Sinusoidal(final Sinusoidal other) {
+ super(other);
+ }
+
+ /**
+ * Returns the sequence of <cite>normalization</cite> → {@code this} →
<cite>denormalization</cite> transforms
+ * as a whole. The transform returned by this method expects
(<var>longitude</var>, <var>latitude</var>)
+ * coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>)
coordinates in <em>metres</em>.
+ *
+ * <p>The non-linear part of the returned transform will be {@code this}
transform, except if the ellipsoid
+ * is spherical. In the later case, {@code this} transform will be
replaced by a simplified implementation.</p>
+ *
+ * @param factory the factory to use for creating the transform.
+ * @return the map projection from (λ,φ) to (<var>x</var>,<var>y</var>)
coordinates.
+ * @throws FactoryException if an error occurred while creating a
transform.
+ */
+ @Override
+ public MathTransform createMapProjection(final MathTransformFactory
factory) throws FactoryException {
+ Sinusoidal kernel = this;
+ if (eccentricity == 0) {
+ kernel = new Spherical(this);
+ }
+ return context.completeTransform(factory, kernel);
+ }
+
+ /**
+ * Converts the specified (λ,φ) coordinate (units in radians) and stores
the result in {@code dstPts}
+ * (linear distance on a unit sphere). In addition, opportunistically
computes the projection derivative
+ * if {@code derivate} is {@code true}.
+ *
+ * @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(φ);
+ // TODO: replace by ellipsoidal formulas.
+ if (dstPts != null) {
+ dstPts[dstOff] = λ * cosφ;
+ dstPts[dstOff+1] = φ;
+ }
+ return derivate ? new Matrix2(cosφ, -λ*sin(φ), 0, 1) : null;
+ }
+
+ /**
+ * 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)
+ {
+ final double x = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
+ // TODO: replace by ellipsoidal formulas.
+ dstPts[dstOff ] = x / cos(φ);
+ dstPts[dstOff+1] = φ;
+ }
+
+
+ /**
+ * Provides the transform equations for the spherical case of the
Sinusoidal projection.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ private static final class Spherical extends Sinusoidal {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -5843301120207230310L;
+
+ /**
+ * Constructs a new map projection from the parameters of the given
projection.
+ *
+ * @param other the other projection (usually ellipsoidal) from
which to copy the parameters.
+ */
+ Spherical(final Sinusoidal other) {
+ super(other);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @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(φ);
+ if (dstPts != null) {
+ dstPts[dstOff ] = λ * cosφ; // Part of Snyder 30-1
+ dstPts[dstOff+1] = φ; // Part of Snyder 30-2
+ }
+ return derivate ? new Matrix2(cosφ, -λ*sin(φ), 0, 1) : null;
+ }
+
+ /**
+ * Converts a list of coordinate points. This method performs the same
calculation than above
+ * {@link #transform(double[], int, double[], int, boolean)} method,
but is overridden for efficiency.
+ */
+ @Override
+ public void transform(final double[] srcPts, int srcOff,
+ final double[] dstPts, int dstOff, int numPts)
+ throws TransformException
+ {
+ if (srcPts != dstPts || srcOff != dstOff) {
+ super.transform(srcPts, srcOff, dstPts, dstOff, numPts);
+ } else {
+ while (--numPts >= 0) {
+ dstPts[dstOff] *= cos(dstPts[dstOff+1]);
+ dstOff += DIMENSION;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void inverseTransform(final double[] srcPts, final int
srcOff,
+ final double[] dstPts, final int
dstOff)
+ {
+ final double x = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
+ dstPts[dstOff ] = x / cos(φ); // Part of Snyder 30-5
+ dstPts[dstOff+1] = φ; // Part of Snyder 30-6
+ }
+ }
+}
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 9e2aa85..7868784 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,7 @@
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.ZonedTransverseMercator
+org.apache.sis.internal.referencing.provider.Sinusoidal
org.apache.sis.internal.referencing.provider.Mollweide
org.apache.sis.internal.referencing.provider.NTv2
org.apache.sis.internal.referencing.provider.NADCON
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 b544c6e..64e2d69 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
@@ -83,6 +83,7 @@ public final strictfp class ProvidersTest extends TestCase {
PseudoMercator.class,
RegionalMercator.class,
MillerCylindrical.class,
+ Sinusoidal.class,
Mollweide.class,
LambertConformal1SP.class,
LambertConformal2SP.class,
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/SinusoidalTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/SinusoidalTest.java
new file mode 100644
index 0000000..acd8869
--- /dev/null
+++
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/SinusoidalTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link Sinusoidal} projection.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+@DependsOn(NormalizedProjectionTest.class)
+public final strictfp class SinusoidalTest extends MapProjectionTestCase {
+ /**
+ * Creates a new instance of {@link Sinusoidal} concatenated with the
(de)normalization matrices.
+ * The new instance is stored in the inherited {@link #transform} field.
+ *
+ * @param ellipse {@code false} for a sphere, or {@code true} for WGS84
ellipsoid.
+ */
+ private void createProjection(final boolean ellipse) throws
FactoryException {
+ createCompleteProjection(new
org.apache.sis.internal.referencing.provider.Sinusoidal(),
+ ellipse ? WGS84_A : 6400000,
+ ellipse ? WGS84_B : 6400000,
+ Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN,
Double.NaN, Double.NaN);
+ tolerance = Formulas.LINEAR_TOLERANCE; // Not NORMALIZED_TOLERANCE
since this is not a NormalizedProjection.
+ }
+
+ /**
+ * Tests the projection of a few points on a sphere.
+ *
+ * @throws FactoryException if an error occurred while creating the map
projection.
+ * @throws TransformException if an error occurred while projecting a
point.
+ */
+ @Test
+ public void testSpherical() throws FactoryException, TransformException {
+ createProjection(false);
+ assertTrue(isInverseTransformSupported);
+ verifyTransform(
+ new double[] { // (λ,φ) coordinates in degrees to
project.
+ 2, 1
+ },
+ new double[] { // Expected (x,y) results in metres.
+ 223368.12, 111701.07 // Values taken from PROJ.4.
+ });
+ }
+
+ /**
+ * Tests the projection of a few points on an ellipsoid.
+ *
+ * @throws FactoryException if an error occurred while creating the map
projection.
+ * @throws TransformException if an error occurred while projecting a
point.
+ */
+ @Test
+ @org.junit.Ignore("Ellipsoidal formula not yet implemented.")
+ public void testEllipsoidal() throws FactoryException, TransformException {
+ createProjection(true);
+ assertTrue(isInverseTransformSupported);
+ verifyTransform(
+ new double[] { // (λ,φ) coordinates in degrees to
project.
+ 2, 1
+ },
+ new double[] { // Expected (x,y) results in metres.
+ 222605.30, 110574.39 // Values taken from PROJ.4.
+ });
+ }
+
+ /**
+ * Tests the derivatives at a few points. 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
+ @DependsOnMethod("testInverseDerivative")
+ public void testDerivative() throws FactoryException, TransformException {
+ createProjection(false);
+ final double delta = (100.0 / 60) / 1852; //
Approximatively 100 metres.
+ derivativeDeltas = new double[] {delta, delta};
+ tolerance = 1E-6; // More severe
than Formulas.LINEAR_TOLERANCE.
+ verifyDerivative(15, 30);
+ verifyDerivative(10, -60);
+ }
+}
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 040e08d..972fcdb 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
@@ -175,6 +175,7 @@ import org.junit.BeforeClass;
org.apache.sis.referencing.operation.projection.ObliqueMercatorTest.class,
org.apache.sis.referencing.operation.projection.CylindricalEqualAreaTest.class,
org.apache.sis.referencing.operation.projection.AlbersEqualAreaTest.class,
+ org.apache.sis.referencing.operation.projection.SinusoidalTest.class,
org.apache.sis.referencing.operation.projection.MollweideTest.class,
// Coordinate operation and derived Coordinate Reference Systems (cyclic
dependency).