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 a65df89e7f Add Robinson projection.
https://issues.apache.org/jira/browse/SIS-599
a65df89e7f is described below
commit a65df89e7f226c5bf21479cb4a072f4386f66dca
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu May 2 19:15:20 2024 +0200
Add Robinson projection.
https://issues.apache.org/jira/browse/SIS-599
---
...g.opengis.referencing.operation.OperationMethod | 1 +
.../main/module-info.java | 1 +
.../operation/projection/Initializer.java | 7 +-
.../operation/projection/Mollweide.java | 11 -
.../operation/projection/ProjectionVariant.java | 19 +-
.../referencing/operation/projection/Robinson.java | 264 +++++++++++++++++++++
.../operation/projection/Sinusoidal.java | 5 -
.../referencing/operation/provider/Mollweide.java | 2 +-
.../provider/{Mollweide.java => Robinson.java} | 32 ++-
.../operation/transform/ContextualParameters.java | 17 +-
.../operation/projection/RobinsonTest.java | 118 +++++++++
.../operation/provider/ProvidersTest.java | 1 +
12 files changed, 434 insertions(+), 44 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.referencing/main/META-INF/services/org.opengis.referencing.operation.OperationMethod
b/endorsed/src/org.apache.sis.referencing/main/META-INF/services/org.opengis.referencing.operation.OperationMethod
index 9609787f4e..ae50990dda 100644
---
a/endorsed/src/org.apache.sis.referencing/main/META-INF/services/org.opengis.referencing.operation.OperationMethod
+++
b/endorsed/src/org.apache.sis.referencing/main/META-INF/services/org.opengis.referencing.operation.OperationMethod
@@ -66,6 +66,7 @@ org.apache.sis.referencing.operation.provider.Sinusoidal
org.apache.sis.referencing.operation.provider.PseudoSinusoidal
org.apache.sis.referencing.operation.provider.Polyconic
org.apache.sis.referencing.operation.provider.Mollweide
+org.apache.sis.referencing.operation.provider.Robinson
org.apache.sis.referencing.operation.provider.SouthPoleRotation
org.apache.sis.referencing.operation.provider.NorthPoleRotation
org.apache.sis.referencing.operation.provider.NTv2
diff --git a/endorsed/src/org.apache.sis.referencing/main/module-info.java
b/endorsed/src/org.apache.sis.referencing/main/module-info.java
index d79c190aac..c19d16bee7 100644
--- a/endorsed/src/org.apache.sis.referencing/main/module-info.java
+++ b/endorsed/src/org.apache.sis.referencing/main/module-info.java
@@ -133,6 +133,7 @@ module org.apache.sis.referencing {
org.apache.sis.referencing.operation.provider.PseudoSinusoidal,
org.apache.sis.referencing.operation.provider.Polyconic,
org.apache.sis.referencing.operation.provider.Mollweide,
+ org.apache.sis.referencing.operation.provider.Robinson,
org.apache.sis.referencing.operation.provider.SouthPoleRotation,
org.apache.sis.referencing.operation.provider.NorthPoleRotation,
org.apache.sis.referencing.operation.provider.NTv2,
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Initializer.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Initializer.java
index 2492127180..6b4535f28c 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Initializer.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Initializer.java
@@ -195,7 +195,12 @@ final class Initializer {
* Set meridian rotation, scale factor, false easting and false
northing parameter values
* in the (de)normalization matrices.
*/
- context.normalizeGeographicInputs(λ0);
+ if (variant == null || variant.useRadians()) {
+ context.normalizeGeographicInputs(λ0);
+ } else if (λ0 != 0) {
+ context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION)
+ .convertBefore(0, null, DoubleDouble.of(-λ0, true));
+ }
final MatrixSIS denormalize =
context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
denormalize.convertAfter(0, k, DoubleDouble.of(fe, true));
denormalize.convertAfter(1, k, DoubleDouble.of(fn, true));
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mollweide.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mollweide.java
index 16de7b69f7..845db2afba 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mollweide.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mollweide.java
@@ -17,7 +17,6 @@
package org.apache.sis.referencing.operation.projection;
import java.util.EnumMap;
-import java.util.regex.Pattern;
import static java.lang.Math.*;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.operation.Matrix;
@@ -64,16 +63,6 @@ public class Mollweide extends NormalizedProjection {
/** The spherical case. */
SPHERICAL;
- /** The expected name pattern of an operation method for this variant.
*/
- @Override public Pattern getOperationNamePattern() {
- return null;
- }
-
- /** EPSG identifier of an operation method for this variant. */
- @Override public String getIdentifier() {
- return null;
- }
-
/** Requests the use of authalic radius. */
@Override public boolean useAuthalicRadius() {
return true;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
index 76be73266b..61474e2059 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ProjectionVariant.java
@@ -32,7 +32,9 @@ interface ProjectionVariant {
*
* @return the operation name pattern for this variant, or {@code null} if
none.
*/
- Pattern getOperationNamePattern();
+ default Pattern getOperationNamePattern() {
+ return null;
+ }
/**
* Returns the EPSG identifier to compare against the operation method.
@@ -40,7 +42,9 @@ interface ProjectionVariant {
*
* @return EPSG identifier for this variant, or {@code null} if none.
*/
- String getIdentifier();
+ default String getIdentifier() {
+ return null;
+ }
/**
* Whether this variant is a spherical variant using authalic radius.
@@ -58,4 +62,15 @@ interface ProjectionVariant {
default boolean useAuthalicRadius() {
return false;
}
+
+ /**
+ * Whether this variant uses longitude and latitude values in radians.
+ * This is the case of almost all map projections.
+ * A value of {@code false} will cause the map projection to work in
degrees instead.
+ *
+ * @return whether this variant uses longitude and latitude values in
radians.
+ */
+ default boolean useRadians() {
+ return true;
+ }
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Robinson.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Robinson.java
new file mode 100644
index 0000000000..4d51ebd276
--- /dev/null
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Robinson.java
@@ -0,0 +1,264 @@
+/*
+ * 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 static java.lang.Math.*;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.math.Fraction;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.privy.DoubleDouble;
+import org.apache.sis.referencing.internal.Resources;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import static org.apache.sis.referencing.operation.provider.Robinson.*;
+
+
+/**
+ * <cite>Robinson</cite> projection.
+ * This projection is different than other projections in that it computes
+ * by interpolations of tabulated values instead of analytic function.
+ * The table is indexed by latitude at a constant interval.
+ *
+ * <p>While the current implementation supports only the Robinson projection,
+ * it could be generalized to any projection using interpolations in a similar
way.
+ * For example, the "Natural Earth" projection was initially defined by
interpolations.
+ * See {@link Variant} for a note about how to generalize.</p>
+ *
+ * <h2>References</h2>
+ * <p>Snyder, J. P. (1990). <u>The Robinson projection: A computation
algorithm.</u>
+ * Cartography and Geographic Information Systems, 17 (4), p. 301-305.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Robinson_projection">Robinson
projection on Wikipedia</a>
+ */
+public class Robinson extends NormalizedProjection {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -2998244461334786203L;
+
+ /**
+ * The projection variants supported by the enclosing class.
+ *
+ * <h2>Future evolution</h2>
+ * If there is a need to support other interpolated projection than
Robinson, then the {@link #TABLE},
+ * {@link #LAST_INDEX} and {@link #LATITUDE_INCREMENT} constants should
move in this enumeration.
+ */
+ private enum Variant implements ProjectionVariant {
+ /** The Robinson projection. */
+ ROBINSON;
+
+ /** Requests the use of authalic radius. */
+ @Override public boolean useAuthalicRadius() {
+ return true;
+ }
+
+ /** Requests the use of degrees. */
+ @Override public boolean useRadians() {
+ return false;
+ }
+ }
+
+ /**
+ * 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, Variant.ROBINSON);
+ }
+
+ /**
+ * Increment in degrees between two rows of the interpolation table.
+ *
+ * @see #TABLE
+ */
+ private static final int LATITUDE_INCREMENT = 5;
+
+ /**
+ * Conversion factor from latitude in degrees to index in the
interpolation table.
+ */
+ private static final Fraction TO_INDEX = new Fraction(1,
LATITUDE_INCREMENT);
+
+ /**
+ * Multiplication factors of <var>x</var> and <var>y</var> axes after
projection.
+ */
+ private static final Fraction XF = new Fraction( 8487, 10000), //
0.8487
+ YF = new Fraction(13523, 10000); //
1.3523
+
+ /**
+ * Creates a Robinson projection from the given parameters.
+ *
+ * @param method description of the projection parameters.
+ * @param parameters the parameter values of the projection to create.
+ */
+ public Robinson(final OperationMethod method, final Parameters parameters)
{
+ super(initializer(method, parameters), null);
+ final MatrixSIS normalize =
context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
+ final MatrixSIS denormalize =
context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+ normalize .convertAfter (0, DoubleDouble.DEGREES_TO_RADIANS, null);
+ normalize .convertAfter (1, TO_INDEX, null);
+ denormalize.convertBefore(0, XF, null);
+ denormalize.convertBefore(1, YF, null);
+ }
+
+ /**
+ * Interpolation table from 5°S to 90°N inclusive with a step of 5° of
latitude.
+ * The first column (XLR) is the ratio of the length of the parallel to
the length of the equator.
+ * The second column (PR) is proportional to the distance of from the
equator to the parallel
+ * divided by the equator length.
+ */
+ private static final double[] TABLE = {
+ // XLR PR
+ 0.9986, -0.0620, // -5°
+ 1.0000, 0.0000, // 0°
+ 0.9986, 0.0620, // 5°
+ 0.9954, 0.1240, // 10°
+ 0.9900, 0.1860, // 15°
+ 0.9822, 0.2480, // 20°
+ 0.9730, 0.3100, // 25°
+ 0.9600, 0.3720, // 30°
+ 0.9427, 0.4340, // 35°
+ 0.9216, 0.4958, // 40°
+ 0.8962, 0.5571, // 45°
+ 0.8679, 0.6176, // 50°
+ 0.8350, 0.6769, // 55°
+ 0.7986, 0.7346, // 60°
+ 0.7597, 0.7903, // 65°
+ 0.7186, 0.8435, // 70°
+ 0.6732, 0.8936, // 75°
+ 0.6213, 0.9394, // 80°
+ 0.5722, 0.9761, // 85°
+ 0.5322, 1.0000 // 90°
+ };
+
+ /**
+ * Highest valid value for the index of the latitude.
+ * Assertion: {@code (LAST_INDEX << 1) + 5 == TABLE.length - 1}.
+ */
+ private static final int LAST_INDEX = 17;
+
+ /**
+ * Projects the specified (Λ,φ) coordinates and stores the
(<var>x</var>,<var>y</var>) result in {@code dstPts}.
+ * The units of measurement are implementation-specific (see super-class
javadoc).
+ * The results must be multiplied by the denormalization matrix before to
get linear distances.
+ *
+ * @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 coordinates cannot 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 φm = srcPts[srcOff+1]; // In multiple of
`LATITUDE_INCREMENT`.
+ final double φa = abs(φm);
+ int i = Math.min((int) φa, LAST_INDEX);
+ final double p = φa - i;
+ double tb, t0, t1;
+
+ tb = TABLE[i <<= 1]; // Value of XLR for the range of latitudes
before current range.
+ t0 = TABLE[i+2]; // Value of XLR for the lower bound of current
range of latitudes.
+ t1 = TABLE[i+4]; // Value of XLR for the upper bound of current
range of latitudes.
+ final double xp1 = t1 - tb;
+ final double xp2 = p*(t1 - 2*t0 + tb);
+ final double xr = t0 + p*(xp1 + xp2)/2;
+
+ tb = TABLE[i+1]; // Value of PR for the range of latitudes
before current range.
+ t0 = TABLE[i+3]; // Value of PR for the lower bound of current
range of latitudes.
+ t1 = TABLE[i+5]; // Value of PR for the upper bound of current
range of latitudes.
+ final double yp1 = t1 - tb;
+ final double yp2 = p*(t1 - 2*t0 + tb);
+ final double ya = t0 + p*(yp1 + yp2)/2;
+
+ if (dstPts != null) {
+ dstPts[dstOff ] = xr * λ;
+ dstPts[dstOff+1] = copySign(ya, φm);
+ }
+ if (!derivate) return null;
+ return new Matrix2(xr, (xp1/2 + xp2)*abs(λ),
+ 0, (yp1/2 + yp2));
+ }
+
+ /**
+ * 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 ya = abs(y);
+ int i = Math.min((int) (ya*(90/LATITUDE_INCREMENT)), LAST_INDEX);
// First estimation.
+ double p;
+ do {
+ int ti = (i << 1) | 1;
+ double ym = TABLE[ti ];
+ double y0 = TABLE[ti+2];
+ double y1 = TABLE[ti+4];
+ double u = y1 - ym;
+ double t = 2*(ya - y0) / u;
+ double c = t*(y1 - 2*y0 + ym) / u;
+ p = t*(1 - c*(1 - 2*c));
+ } while (p < 0 && --i >= 0); // Recompute if the first estimation
was too high.
+ i = max(i, 0);
+ /*
+ * Above loop computed an estimated position for the latitude band
that contains φ.
+ * Refine the result for the actual latitude value (not only the band
containing it).
+ */
+ int nbIter = MAXIMUM_ITERATIONS;
+ double φm = p + i; // In multiple of
`LATITUDE_INCREMENT`.
+ do {
+ i <<= 1;
+ double tb, t0, t1;
+ tb = TABLE[i+1];
+ t0 = TABLE[i+3];
+ t1 = TABLE[i+5];
+ final double yp1 = t1 - tb; // Same formulas
as the forward case.
+ final double yp2 = p*(t1 - 2*t0 + tb);
+ final double err = t0 + p*(yp1 + yp2)/2 - ya; // Difference
between interpolated y and desired y.
+ final double dy = (yp1/2 + yp2); // Derivative
∂y/∂φ (last term in the Jacobian matrix).
+ φm -= err / dy; // Convert the
error in y to an error in φ.
+ if (!(abs(err) > ITERATION_TOLERANCE)) { // Use `!` for
accepting NaN.
+ tb = TABLE[i+0];
+ t0 = TABLE[i+2];
+ t1 = TABLE[i+4];
+ dstPts[dstOff] = x / (t0 + p*(t1 - tb + p*(t1 - 2*t0 + tb))/2);
+ dstPts[dstOff+1] = copySign(φm, y);
+ return;
+ }
+ i = Math.min((int) φm, LAST_INDEX);
+ p = φm - i;
+ } while (--nbIter >= 0);
+ throw new
ProjectionException(Resources.format(Resources.Keys.NoConvergence));
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Sinusoidal.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Sinusoidal.java
index 00123d060a..7dccde4ed5 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Sinusoidal.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Sinusoidal.java
@@ -73,11 +73,6 @@ public class Sinusoidal extends MeridianArcBased {
@Override public Pattern getOperationNamePattern() {
return operationName;
}
-
- /** EPSG identifier of an operation method for this variant. */
- @Override public String getIdentifier() {
- return null;
- }
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
index 742899dc80..9214a12e32 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
@@ -42,7 +42,7 @@ public final class Mollweide extends MapProjection {
private static final long serialVersionUID = -6434031854504431260L;
/**
- * The operation parameter descriptor for the <cite>Longitude of natural
origin</cite> (λ₀) parameter value.
+ * The operation parameter descriptor for the <cite>Longitude of
projection centre</cite> (λ₀) parameter value.
* Valid values range is [-180 … 180]° and default value is 0°.
*
* <!-- Generated by ParameterNameTableGenerator -->
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Robinson.java
similarity index 81%
copy from
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
copy to
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Robinson.java
index 742899dc80..94c694f99d 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Mollweide.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Robinson.java
@@ -20,29 +20,27 @@ import jakarta.xml.bind.annotation.XmlTransient;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.apache.sis.parameter.Parameters;
+import org.apache.sis.util.privy.Constants;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.referencing.operation.projection.NormalizedProjection;
/**
- * The provider for <q>Mollweide</q> (also known as <q>Homalographic</q>)
projection.
- * As of version 9.4 of EPSG geodetic dataset, there is no known EPSG code for
this projection.
+ * The provider for <q>Robinson</q> projection.
*
- * @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*
- * @see <a
href="https://mathworld.wolfram.com/MollweideProjection.html">Mathworld
formulas</a>
- * @see <a href="https://gdal.org/proj_list/mollweide.html">GeoTIFF parameters
for Mollweide</a>
+ * @see <a href="https://gdal.org/proj_list/robinson.html">GeoTIFF parameters
for Robinson</a>
*/
@XmlTransient
-public final class Mollweide extends MapProjection {
+public final class Robinson extends MapProjection {
/**
* For cross-version compatibility.
*/
- private static final long serialVersionUID = -6434031854504431260L;
+ private static final long serialVersionUID = -4027297503846506501L;
/**
- * The operation parameter descriptor for the <cite>Longitude of natural
origin</cite> (λ₀) parameter value.
+ * The operation parameter descriptor for the <cite>Longitude of
projection centre</cite> (λ₀) parameter value.
* Valid values range is [-180 … 180]° and default value is 0°.
*
* <!-- Generated by ParameterNameTableGenerator -->
@@ -92,12 +90,12 @@ public final class Mollweide extends MapProjection {
*/
private static final ParameterDescriptorGroup PARAMETERS;
static {
- PARAMETERS = builder().setCodeSpace(Citations.ESRI, "ESRI")
- .addName("Mollweide")
- .addName(null, "Homalographic")
- .addName(null, "Homolographic")
- .addName(null, "Elliptical")
- .addName(null, "Babinet")
+ PARAMETERS = builder().setCodeSpace(Citations.OGC, Constants.OGC)
+ .addName( "Robinson")
+ .addName(Citations.ESRI, "Robinson")
+ .addName(Citations.GEOTIFF, "CT_Robinson")
+ .addName(Citations.PROJ4, "robin")
+ .addIdentifier(Citations.GEOTIFF, "23")
.createGroupForMapProjection(
CENTRAL_MERIDIAN,
FALSE_EASTING,
@@ -107,17 +105,17 @@ public final class Mollweide extends MapProjection {
/**
* Constructs a new provider.
*/
- public Mollweide() {
+ public Robinson() {
super(PARAMETERS);
}
/**
- * {@inheritDoc}
+ * Creates a map projection on an ellipsoid having a semi-major axis
length of 1.
*
* @return the map projection created from the given parameter values.
*/
@Override
protected NormalizedProjection createProjection(final Parameters
parameters) {
- return new
org.apache.sis.referencing.operation.projection.Mollweide(this, parameters);
+ return new
org.apache.sis.referencing.operation.projection.Robinson(this, parameters);
}
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ContextualParameters.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ContextualParameters.java
index 2e29db0d64..ff10c7f010 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ContextualParameters.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ContextualParameters.java
@@ -282,15 +282,15 @@ public class ContextualParameters extends Parameters
implements Serializable {
values = forward.values;
} else {
final List<GeneralParameterDescriptor> descriptors =
desc.descriptors();
- final ParameterValue<?>[] values = new
ParameterValue<?>[descriptors.size()];
+ final var copy = new ParameterValue<?>[descriptors.size()];
int count = 0;
- for (int i=0; i < values.length; i++) {
- final ContextualParameter<?> p = new
ContextualParameter<>((ParameterDescriptor<?>) descriptors.get(i));
+ for (int i=0; i < copy.length; i++) {
+ final var p = new
ContextualParameter<>((ParameterDescriptor<?>) descriptors.get(i));
if (mapper.test(forward, p)) {
- values[count++] = p;
+ copy[count++] = p;
}
}
- this.values = ArraysExt.resize(values, count);
+ values = ArraysExt.resize(copy, count);
}
isFrozen = true;
}
@@ -402,8 +402,9 @@ public class ContextualParameters extends Parameters
implements Serializable {
* @since 0.7
*/
public final MatrixSIS getMatrix(MatrixRole role) {
- final Matrix fallback;
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final ContextualParameters inverse;
+ final Matrix fallback;
synchronized (this) {
switch (role) {
default: throw new AssertionError(role);
@@ -455,6 +456,7 @@ public class ContextualParameters extends Parameters
implements Serializable {
if (λ0 != 0) {
offset = DoubleDouble.of(-λ0, true).multiply(toRadians);
}
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final MatrixSIS normalize = (MatrixSIS) this.normalize; //
Must be the same instance, not a copy.
normalize.convertBefore(0, toRadians, offset);
normalize.convertBefore(1, toRadians, null);
@@ -479,8 +481,9 @@ public class ContextualParameters extends Parameters
implements Serializable {
*/
public synchronized MatrixSIS denormalizeGeographicOutputs(final double
λ0) {
ensureModifiable();
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ final var denormalize = (MatrixSIS) this.denormalize; //
Must be the same instance, not a copy.
final DoubleDouble toDegrees = DoubleDouble.RADIANS_TO_DEGREES;
- final MatrixSIS denormalize = (MatrixSIS) this.denormalize; //
Must be the same instance, not a copy.
denormalize.convertAfter(0, toDegrees, (λ0 != 0) ? λ0 : null);
denormalize.convertAfter(1, toDegrees, null);
return denormalize;
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/RobinsonTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/RobinsonTest.java
new file mode 100644
index 0000000000..f0a84c4592
--- /dev/null
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/projection/RobinsonTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.parameter.Parameters;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.referencing.privy.Formulas;
+import org.apache.sis.referencing.operation.provider.MapProjection;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+/**
+ * Tests the {@link Robinson} projection.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+public final class RobinsonTest extends MapProjectionTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public RobinsonTest() {
+ final double delta = (100.0 / 60) / 1852; // Approximately 100
metres.
+ derivativeDeltas = new double[] {delta, delta};
+ }
+
+ /**
+ * Returns the provider for the "Robinson" projection.
+ */
+ private static MapProjection provider() {
+ return new org.apache.sis.referencing.operation.provider.Robinson();
+ }
+
+ /**
+ * Tests the first point given in Snyder example which does not involve
interpolation.
+ * Tests also a few simple points at equator and on pole.
+ *
+ * @throws FactoryException if an error occurred while creating the map
projection.
+ * @throws TransformException if an error occurred while projecting a
coordinate.
+ */
+ @Test
+ public void testSimplePoints() throws FactoryException, TransformException
{
+ final MapProjection provider = provider();
+ final Parameters pg =
Parameters.castOrWrap(provider.getParameters().createValue());
+ pg.parameter("semi-major").setValue(1);
+ pg.parameter("semi-minor").setValue(1);
+ transform = provider.createMathTransform(null, pg);
+ tolerance = Formulas.ANGULAR_TOLERANCE;
+
+ final double[] expected = {
+ 0.740630, 0, // 0°N.
+ 0.711005, 0.503055, // 30°N, result from Snyder.
+ 0.591467, 0.993400, // 60°N.
+ 0.394164, 1.352300, // 90°N.
+ };
+ for (int i=0; i <= 3; i++) {
+ double λ = 50;
+ double φ = i * 30;
+ final var p = new DirectPosition2D(λ, φ);
+ assertSame(p, transform.transform(p, p));
+ assertEquals(expected[i*2 ], p.x, 0.000001);
+ assertEquals(expected[i*2 + 1], p.y, 0.000001);
+
+ assertSame(p, transform.inverse().transform(p, p));
+ assertEquals(λ, p.x, Formulas.ANGULAR_TOLERANCE);
+ assertEquals(φ, p.y, Formulas.ANGULAR_TOLERANCE);
+
+ verifyDerivative(λ, φ);
+ }
+ }
+
+ /**
+ * Tests the second point given in Snyder example which involves
interpolation.
+ *
+ * @throws FactoryException if an error occurred while creating the map
projection.
+ * @throws TransformException if an error occurred while projecting a
coordinate.
+ */
+ @Test
+ public void testInterpolation() throws FactoryException,
TransformException {
+ final MapProjection provider = provider();
+ final Parameters pg =
Parameters.castOrWrap(provider.getParameters().createValue());
+ pg.parameter("semi-major").setValue(6370997);
+ pg.parameter("semi-minor").setValue(6370997);
+ transform = provider.createMathTransform(null, pg);
+ tolerance = Formulas.LINEAR_TOLERANCE;
+
+ final double λ = -102;
+ final double φ = -47;
+ final var p = new DirectPosition2D(λ, φ);
+ assertSame(p, transform.transform(p, p));
+ assertEquals(-8521076, p.x, 5);
+ assertEquals(-5009012, p.y, 5);
+
+ assertSame(p, transform.inverse().transform(p, p));
+ assertEquals(λ, p.x, Formulas.ANGULAR_TOLERANCE);
+ assertEquals(φ, p.y, Formulas.ANGULAR_TOLERANCE);
+
+ verifyDerivative(λ, φ);
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
index 72e3e25d60..54746ba1ed 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java
@@ -113,6 +113,7 @@ public final class ProvidersTest extends TestCase {
PseudoSinusoidal.class,
Polyconic.class,
Mollweide.class,
+ Robinson.class,
SouthPoleRotation.class,
NorthPoleRotation.class,
NTv2.class,