Initial implementation of Orthographic projection (spherical formulas) implemented from Wikipedia and Snyder equations.
https://issues.apache.org/jira/browse/SIS-223 - not yet completed because missing ellipsoidal formulas.
This work implied revisiting the way we copy names and identifiers from an existing ParameterDescriptor for creating a new ParameterDescriptor.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java
index 4ee8e02..fcb6d03 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java
@@ -170,8 +170,9 @@
* GeoTIFF: NatOriginLat
*/
LATITUDE_OF_FALSE_ORIGIN = createLatitude(
- renameAlias(LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN, Citations.GEOTIFF, Mercator1SP.LATITUDE_OF_ORIGIN, builder)
- .rename(Citations.OGC, "latitude_of_center"), true);
+ renameAlias(builder, LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN, // Copy from this parameter…
+ Citations.GEOTIFF, Mercator1SP.LATITUDE_OF_ORIGIN) // … except for this name.
+ .rename(Citations.OGC, "latitude_of_center"), true);
/*
* EPSG: Longitude of false origin
* OGC: longitude_of_center
@@ -180,8 +181,9 @@
* GeoTIFF: NatOriginLong
*/
LONGITUDE_OF_FALSE_ORIGIN = createLongitude(
- renameAlias(LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN, Citations.GEOTIFF, Mercator1SP.LONGITUDE_OF_ORIGIN, builder)
- .rename(Citations.OGC, "longitude_of_center"));
+ renameAlias(builder, LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN,
+ Citations.GEOTIFF, Mercator1SP.LONGITUDE_OF_ORIGIN)
+ .rename(Citations.OGC, "longitude_of_center"));
/*
* EPSG: Latitude of 1st standard parallel
* OGC: standard_parallel_1
@@ -210,7 +212,8 @@
* GeoTIFF: FalseEasting
*/
EASTING_AT_FALSE_ORIGIN = createShift(
- renameAlias(LambertConformal2SP.EASTING_AT_FALSE_ORIGIN, Citations.GEOTIFF, LambertConformal1SP.FALSE_EASTING, builder));
+ renameAlias(builder, LambertConformal2SP.EASTING_AT_FALSE_ORIGIN,
+ Citations.GEOTIFF, LambertConformal1SP.FALSE_EASTING));
/*
* EPSG: Northing at false origin
* OGC: false_northing
@@ -219,7 +222,8 @@
* GeoTIFF: FalseNorthing
*/
NORTHING_AT_FALSE_ORIGIN = createShift(
- renameAlias(LambertConformal2SP.NORTHING_AT_FALSE_ORIGIN, Citations.GEOTIFF, LambertConformal1SP.FALSE_NORTHING, builder));
+ renameAlias(builder, LambertConformal2SP.NORTHING_AT_FALSE_ORIGIN,
+ Citations.GEOTIFF, LambertConformal1SP.FALSE_NORTHING));
PARAMETERS = builder
.addIdentifier( "9822")
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
index 70c45d0..3ee4617 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
@@ -145,8 +145,8 @@
* @return the given {@code builder}, for method call chaining.
*/
static ParameterBuilder copyNames(final ParameterBuilder builder, final ParameterDescriptor<Double> template) {
- return builder.addName(MapProjection.sameNameAs(Citations.ESRI, template))
- .addName(MapProjection.sameNameAs(Citations.OGC, template))
- .addName(MapProjection.sameNameAs(Citations.PROJ4, template));
+ return builder.addNameAndIdentifier(Citations.ESRI, template)
+ .addNameAndIdentifier(Citations.OGC, template)
+ .addNameAndIdentifier(Citations.PROJ4, template);
}
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
index 2b7f8eb..5d895ed 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
@@ -237,7 +237,7 @@
.addName(Citations.NETCDF, "LambertConformal")
.addName(Citations.GEOTIFF, "CT_LambertConfConic_2SP")
.addName(Citations.GEOTIFF, "CT_LambertConfConic")
- .addName(sameNameAs(Citations.PROJ4, LambertConformal1SP.PARAMETERS))
+ .addNameAndIdentifier(Citations.PROJ4, LambertConformal1SP.PARAMETERS)
.addIdentifier(Citations.GEOTIFF, "8")
.addIdentifier(Citations.MAP_INFO, "3")
.addIdentifier(Citations.S57, "6")
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
index d1675b6..3c4df75 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
@@ -308,8 +308,12 @@
/**
* Returns the name of the given authority declared in the given parameter descriptor.
* This method is used only as a way to avoid creating many instances of the same name.
+ *
+ * @param authority the authority for which to get the name.
+ * @param parameters where to get name for the given authority.
+ * @throws NoSuchElementException if the given authority has not been found.
*/
- static GenericName sameNameAs(final Citation authority, final GeneralParameterDescriptor parameters) {
+ private static GenericName sameNameAs(final Citation authority, final GeneralParameterDescriptor parameters) {
for (final GenericName candidate : parameters.getAlias()) {
if (candidate instanceof Identifier && ((Identifier) candidate).getAuthority() == authority) {
return candidate;
@@ -322,37 +326,80 @@
* Copies name, aliases and identifiers of the given {@code template}, except the alias and identifiers of the
* given authority which are replaced by the alias and identifiers of the same authority in {@code replacement}.
*
+ * @param builder an initially clean builder where to add the names and identifiers.
* @param template the parameter from which to copy names and identifiers.
* @param toRename authority of the alias to rename.
* @param replacement the parameter from which to get the new name for the alias to rename.
- * @param builder an initially clean builder where to add the names and identifiers.
* @return the given {@code builder}, for method call chaining.
*
* @since 0.8
*/
- static ParameterBuilder renameAlias(final ParameterDescriptor<Double> template, final Citation toRename,
- final ParameterDescriptor<Double> replacement, final ParameterBuilder builder)
+ static ParameterBuilder renameAlias(final ParameterBuilder builder, final ParameterDescriptor<Double> template,
+ final Citation toRename, final ParameterDescriptor<Double> replacement)
+ {
+ renameAliases(builder, template, new Citation[] {toRename}, new ParameterDescriptor<?>[] {replacement});
+ return builder;
+ }
+
+ /**
+ * Same as above {@link #renameAlias(ParameterBuilder, ParameterDescriptor, Citation, ParameterDescriptor)
+ * renameAlias(…)} but with two aliases to rename.
+ *
+ * @param builder an initially clean builder where to add the names and identifiers.
+ * @param template the parameter from which to copy names and identifiers.
+ * @param s1 authority of the first alias to rename.
+ * @param r1 the parameter from which to get the new name for the first alias to rename.
+ * @param s2 authority of the second alias to rename.
+ * @param r2 the parameter from which to get the new name for the second alias to rename.
+ * @return the given {@code builder}, for method call chaining.
+ *
+ * @since 1.1
+ */
+ static ParameterBuilder renameAlias(final ParameterBuilder builder, final ParameterDescriptor<Double> template,
+ final Citation s1, final ParameterDescriptor<Double> r1,
+ final Citation s2, final ParameterDescriptor<Double> r2)
+ {
+ renameAliases(builder, template, new Citation[] {s1, s2}, new ParameterDescriptor<?>[] {r1, r2});
+ return builder;
+ }
+
+ /**
+ * Implementation of {@code renameAlias(…)} methods.
+ */
+ private static void renameAliases(final ParameterBuilder builder, final ParameterDescriptor<Double> template,
+ final Citation[] toRename, final ParameterDescriptor<?>[] replacement)
{
builder.addName(template.getName());
- GenericName newName = sameNameAs(toRename, replacement);
- Identifier newCode = IdentifiedObjects.getIdentifier(replacement, toRename);
+ final GenericName[] newNames = new GenericName[toRename.length];
+ final Identifier[] newCodes = new Identifier [toRename.length];
+ for (int i=0; i<toRename.length; i++) {
+ newNames[i] = sameNameAs(toRename[i], replacement[i]);
+ newCodes[i] = IdentifiedObjects.getIdentifier(replacement[i], toRename[i]);
+ }
for (GenericName alias : template.getAlias()) {
- if (((Identifier) alias).getAuthority() == toRename) {
- if (newName == null) continue;
- alias = newName;
- newName = null;
+ final Citation authority = ((Identifier) alias).getAuthority();
+ for (int i=0; i<toRename.length; i++) {
+ if (authority == toRename[i]) {
+ if (newNames[i] == null) continue;
+ alias = newNames[i];
+ newNames[i] = null;
+ break;
+ }
}
builder.addName(alias);
}
for (Identifier id : template.getIdentifiers()) {
- if (id.getAuthority() == toRename) {
- if (newCode == null) continue;
- id = newCode;
- newCode = null;
+ final Citation authority = id.getAuthority();
+ for (int i=0; i<toRename.length; i++) {
+ if (authority == toRename[i]) {
+ if (newCodes[i] == null) continue;
+ id = newCodes[i];
+ newCodes[i] = null;
+ break;
+ }
}
builder.addIdentifier(id);
}
- return builder;
}
/**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
index 18e79c2..5529538 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
@@ -132,7 +132,7 @@
.addName(Citations.OGC, "Mercator_2SP")
.addName(Citations.ESRI, "Mercator")
.addName(Citations.NETCDF, "Mercator")
- .addName(sameNameAs(Citations.PROJ4, Mercator1SP.PARAMETERS))
+ .addNameAndIdentifier(Citations.PROJ4, Mercator1SP.PARAMETERS)
.addIdentifier(Citations.MAP_INFO, "26") // MapInfo names this projection "Regional Mercator".
.addIdentifier(Citations.S57, "8")
.createGroupForMapProjection(
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 64cd5f5..8ef6651 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
@@ -67,7 +67,7 @@
PARAMETERS = addNameAndLegacy(addIdentifierAndLegacy(builder, IDENTIFIER, "9841"),
"Mercator (Spherical)", "Mercator (1SP) (Spherical)") // "Mercator (Spherical)" starting from EPSG version 7.6
- .addName(sameNameAs(Citations.PROJ4, Mercator1SP.PARAMETERS))
+ .addNameAndIdentifier(Citations.PROJ4, Mercator1SP.PARAMETERS)
.createGroupForMapProjection(
Mercator1SP.LATITUDE_OF_ORIGIN,
Mercator1SP.LONGITUDE_OF_ORIGIN,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java
index 9215a92..323cddd 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java
@@ -33,7 +33,7 @@
*
* @author Rueben Schulz (UBC)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
*
* @see <a href="http://geotiff.maptools.org/proj_list/hotine_oblique_mercator.html">GeoTIFF parameters for Hotine Oblique Mercator</a>
*
@@ -62,10 +62,9 @@
* <caption>Parameter names</caption>
* <tr><td> EPSG: </td><td> Latitude of projection centre </td></tr>
* <tr><td> OGC: </td><td> latitude_of_center </td></tr>
- * <tr><td> ESRI: </td><td> Latitude_Of_Origin </td></tr>
- * <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
- * <tr><td> Proj4: </td><td> lat_0 </td></tr>
* <tr><td> ESRI: </td><td> Latitude_Of_Center </td></tr>
+ * <tr><td> GeoTIFF: </td><td> CenterLat </td></tr>
+ * <tr><td> Proj4: </td><td> lat_0 </td></tr>
* </table>
* <b>Notes:</b>
* <ul>
@@ -83,10 +82,9 @@
* <caption>Parameter names</caption>
* <tr><td> EPSG: </td><td> Longitude of projection centre </td></tr>
* <tr><td> OGC: </td><td> longitude_of_center </td></tr>
- * <tr><td> ESRI: </td><td> Central_Meridian </td></tr>
- * <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
- * <tr><td> Proj4: </td><td> lonc </td></tr>
* <tr><td> ESRI: </td><td> Longitude_Of_Center </td></tr>
+ * <tr><td> GeoTIFF: </td><td> CenterLong </td></tr>
+ * <tr><td> Proj4: </td><td> lonc </td></tr>
* </table>
*/
public static final ParameterDescriptor<Double> LONGITUDE_OF_CENTRE;
@@ -156,18 +154,21 @@
static {
final ParameterBuilder builder = builder();
- LATITUDE_OF_CENTRE = createLatitude(builder.addNamesAndIdentifiers(AlbersEqualArea.LATITUDE_OF_FALSE_ORIGIN)
- .reidentify(Citations.EPSG, "8811")
- .rename (Citations.EPSG, "Latitude of projection centre")
- .addName (Citations.ESRI, "Latitude_Of_Center")
- .rename (Citations.NETCDF), false); // Remove the netCDF name.
+ LATITUDE_OF_CENTRE = createLatitude(builder
+ .addIdentifier("8811")
+ .addName("Latitude of projection centre")
+ .addNameAndIdentifier(Citations.OGC, AlbersEqualArea.LATITUDE_OF_FALSE_ORIGIN)
+ .addName (Citations.ESRI, "Latitude_Of_Center")
+ .addNameAndIdentifier(Citations.GEOTIFF, Equirectangular.LATITUDE_OF_ORIGIN)
+ .addNameAndIdentifier(Citations.PROJ4, Equirectangular.LATITUDE_OF_ORIGIN), false);
- LONGITUDE_OF_CENTRE = createLongitude(builder.addNamesAndIdentifiers(AlbersEqualArea.LONGITUDE_OF_FALSE_ORIGIN)
- .reidentify(Citations.EPSG, "8812")
- .rename (Citations.EPSG, "Longitude of projection centre")
- .addName (Citations.ESRI, "Longitude_Of_Center")
- .rename (Citations.PROJ4, "lonc")
- .rename (Citations.NETCDF)); // Remove the netCDF name.
+ LONGITUDE_OF_CENTRE = createLongitude(builder
+ .addIdentifier("8812")
+ .addName("Longitude of projection centre")
+ .addNameAndIdentifier(Citations.OGC, AlbersEqualArea.LONGITUDE_OF_FALSE_ORIGIN)
+ .addName (Citations.ESRI, "Longitude_Of_Center")
+ .addNameAndIdentifier(Citations.GEOTIFF, Equirectangular.LONGITUDE_OF_ORIGIN)
+ .addName (Citations.PROJ4, "lonc"));
AZIMUTH = builder
.addIdentifier("8813")
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Orthographic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Orthographic.java
new file mode 100644
index 0000000..41fe49c
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Orthographic.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+import static org.apache.sis.internal.referencing.provider.AbstractProvider.builder;
+
+
+/**
+ * The provider for <cite>"Orthographic"</cite> projection (EPSG:9840).
+ *
+ * @author Rueben Schulz (UBC)
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @see <a href="http://geotiff.maptools.org/proj_list/orthographic.html">GeoTIFF parameters for Orthographic</a>
+ *
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+@XmlTransient
+public class Orthographic extends MapProjection {
+ /**
+ * For compatibility with different versions during deserialization.
+ */
+ private static final long serialVersionUID = -8669271590570783071L;
+
+ /**
+ * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
+ * Valid values can be -90° or 90° only. There is no default value.
+ *
+ * <!-- 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> ESRI: </td><td> Latitude_Of_Center </td></tr>
+ * <tr><td> NetCDF: </td><td> latitude_of_projection_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> ESRI: </td><td> Longitude_Of_Center </td></tr>
+ * <tr><td> NetCDF: </td><td> longitude_of_projection_origin </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>Scale factor at natural origin</cite> (k₀) parameter value.
+ * Valid values range is (0 … ∞) and default value is 1. This is not formally a parameter of this projection.
+ *
+ * <!-- Generated by ParameterNameTableGenerator -->
+ * <table class="sis">
+ * <caption>Parameter names</caption>
+ * <tr><td> OGC: </td><td> scale_factor </td></tr>
+ * <tr><td> ESRI: </td><td> Scale_Factor </td></tr>
+ * <tr><td> Proj4: </td><td> k </td></tr>
+ * </table>
+ * <b>Notes:</b>
+ * <ul>
+ * <li>Optional</li>
+ * </ul>
+ */
+ public static final ParameterDescriptor<Double> SCALE_FACTOR = Mercator2SP.SCALE_FACTOR;
+
+ /**
+ * 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> ESRI: </td><td> False_Easting </td></tr>
+ * <tr><td> NetCDF: </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 = LambertConformal1SP.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> ESRI: </td><td> False_Northing </td></tr>
+ * <tr><td> NetCDF: </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 = LambertConformal1SP.FALSE_NORTHING;
+
+ /**
+ * The group of all parameters expected by this coordinate operation.
+ */
+ static final ParameterDescriptorGroup PARAMETERS;
+ static {
+ final ParameterBuilder builder = builder();
+
+ LATITUDE_OF_ORIGIN = createLatitude(
+ renameAlias(builder, LambertConformal1SP.LATITUDE_OF_ORIGIN, // Copy from this parameter…
+ Citations.ESRI, ObliqueMercator.LATITUDE_OF_CENTRE, // … except for this name.
+ Citations.GEOTIFF, ObliqueMercator.LATITUDE_OF_CENTRE), true);
+
+ LONGITUDE_OF_ORIGIN = createLongitude(
+ renameAlias(builder, LambertConformal1SP.LONGITUDE_OF_ORIGIN,
+ Citations.ESRI, ObliqueMercator.LONGITUDE_OF_CENTRE,
+ Citations.GEOTIFF, ObliqueMercator.LONGITUDE_OF_CENTRE));
+
+ PARAMETERS = builder.addIdentifier("9840")
+ .addName( "Orthographic")
+ .addName(Citations.OGC, "Orthographic")
+ .addName(Citations.ESRI, "Orthographic")
+ .addName(Citations.NETCDF, "Orthographic")
+ .addName(Citations.GEOTIFF, "CT_Orthographic")
+ .addName(Citations.S57, "Orthographic")
+ .addName(Citations.S57, "ORT")
+ .addName(Citations.PROJ4, "ortho")
+ .addIdentifier(Citations.GEOTIFF, "21")
+ .addIdentifier(Citations.S57, "10")
+ .createGroupForMapProjection(
+ LATITUDE_OF_ORIGIN,
+ LONGITUDE_OF_ORIGIN,
+ SCALE_FACTOR,
+ FALSE_EASTING,
+ FALSE_NORTHING);
+ }
+
+ /**
+ * Constructs a new provider.
+ */
+ public Orthographic() {
+ 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.Orthographic(this, parameters);
+ }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
index d254585..4d17593 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
@@ -100,6 +100,10 @@
* <tr><td> ESRI: </td><td> Scale_Factor </td></tr>
* <tr><td> Proj4: </td><td> k </td></tr>
* </table>
+ * <b>Notes:</b>
+ * <ul>
+ * <li>Optional</li>
+ * </ul>
*/
static final ParameterDescriptor<Double> SCALE_FACTOR;
@@ -120,7 +124,8 @@
SCALE_FACTOR = createScale(builder
.addNamesAndIdentifiers(Mercator2SP.SCALE_FACTOR)
- .setRemarks(notFormalParameter("Polar Stereographic (variant A)")).setDeprecated(true));
+ .setRemarks(notFormalParameter("Polar Stereographic (variant A)"))
+ .setRequired(false).setDeprecated(true));
PARAMETERS = builder
.addIdentifier(IDENTIFIER)
@@ -128,7 +133,7 @@
.addName(Citations.S57, "Polar stereographic")
.addName(Citations.S57, "PST")
.addIdentifier(Citations.S57, "11")
- .addName(sameNameAs(Citations.PROJ4, PolarStereographicA.PARAMETERS))
+ .addNameAndIdentifier(Citations.PROJ4, PolarStereographicA.PARAMETERS)
.createGroupForMapProjection(
STANDARD_PARALLEL,
LONGITUDE_OF_ORIGIN,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
index 6b440b9..3671f3c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
@@ -116,8 +116,8 @@
LATITUDE_OF_ORIGIN = createLatitude(builder
.addNamesAndIdentifiers(Mercator1SP.LATITUDE_OF_ORIGIN), true);
- LONGITUDE_OF_ORIGIN = createLongitude(renameAlias(Mercator1SP.LONGITUDE_OF_ORIGIN,
- Citations.NETCDF, LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN, builder));
+ LONGITUDE_OF_ORIGIN = createLongitude(renameAlias(builder, Mercator1SP.LONGITUDE_OF_ORIGIN, // Copy from this parameter…
+ Citations.NETCDF, LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN)); // … except for this name.
SCALE_FACTOR = createScale(builder
.addNamesAndIdentifiers(Mercator1SP.SCALE_FACTOR)
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
index 0c71bf6..aefc1d5 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
@@ -168,7 +168,7 @@
* </div>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
*
* @param <B> the builder subclass.
*
@@ -681,12 +681,12 @@
/**
- * Returns {@code true} if the given name or identifier is deprecated.
+ * Returns {@code true} if the given name or identifier is non-null and non-deprecated.
*
* @see #isDeprecated()
*/
- private static boolean isDeprecated(final Object object) {
- return (object instanceof Deprecable) && ((Deprecable) object).isDeprecated();
+ private static boolean isValid(final Object object) {
+ return (object != null) && !((object instanceof Deprecable) && ((Deprecable) object).isDeprecated());
}
/**
@@ -696,7 +696,7 @@
* <p>This is a convenience method for using an existing object as a template, before to modify
* some names by calls to {@link #rename(Citation, CharSequence[])}.</p>
*
- * @param object the object from which to copy the references to names and identifiers.
+ * @param object the object from which to copy the names and identifiers.
* @return {@code this}, for method call chaining.
*
* @since 0.6
@@ -704,16 +704,46 @@
public B addNamesAndIdentifiers(final IdentifiedObject object) {
ensureNonNull("object", object);
for (final Identifier id : object.getIdentifiers()) {
- if (!isDeprecated(id)) {
+ if (isValid(id)) {
addIdentifier(id);
}
}
Identifier id = object.getName();
- if (!isDeprecated(id)) {
+ if (isValid(id)) {
addName(id);
}
for (final GenericName alias : object.getAlias()) {
- if (!isDeprecated(alias)) {
+ if (isValid(alias)) {
+ addName(alias);
+ }
+ }
+ return self();
+ }
+
+ /**
+ * Adds the non-deprecated names and identifiers from the given object for the specified authority.
+ * This is a convenience method for reusing name and identifier already declared for another object.
+ *
+ * @param authority the authority for which to copy the name and identifier.
+ * @param object the object from which to copy the name and identifier.
+ * @return {@code this}, for method call chaining.
+ *
+ * @since 1.1
+ */
+ public B addNameAndIdentifier(final Citation authority, final IdentifiedObject object) {
+ ensureNonNull("authority", authority);
+ ensureNonNull("object", object);
+ for (final Identifier id : object.getIdentifiers()) {
+ if (isValid(id) && authority.equals(id.getAuthority())) {
+ addIdentifier(id);
+ }
+ }
+ Identifier id = object.getName();
+ if (isValid(id) && authority.equals(id.getAuthority())) {
+ addName(id);
+ }
+ for (final GenericName alias : object.getAlias()) {
+ if (isValid(alias) && (alias instanceof Identifier) && authority.equals(((Identifier) alias).getAuthority())) {
addName(alias);
}
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Orthographic.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Orthographic.java
new file mode 100644
index 0000000..0601b48
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Orthographic.java
@@ -0,0 +1,184 @@
+/*
+ * 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.apache.sis.internal.util.Numerics;
+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.matrix.Matrix2;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.Workaround;
+
+import static java.lang.Math.*;
+import static org.apache.sis.internal.referencing.provider.Orthographic.*;
+
+
+/**
+ * <cite>Orthographic</cite> projection (EPSG:9840). See "Orthographic projection" on
+ * <a href="https://en.wikipedia.org/wiki/Orthographic_projection_in_cartography">Wikipedia</a> and
+ * <a href="http://mathworld.wolfram.com/OrthographicProjection.html">MathWorld</a> for an overview.
+ *
+ * <h2>Description</h2>
+ * This is a perspective azimuthal (planar) projection that is neither conformal nor equal-area.
+ * It resembles a globe viewed from a point of perspective at infinite distance.
+ * Only one hemisphere can be seen at a time.
+ * While not useful for accurate measurements, this projection is useful for pictorial views of the world.
+ *
+ * @author Rueben Schulz (UBC)
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Rémi Maréchal (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public class Orthographic extends NormalizedProjection {
+ /**
+ * For compatibility with different versions during deserialization.
+ */
+ private static final long serialVersionUID = -321305496803338120L;
+
+ /**
+ * Sine and cosine of latitude of origin.
+ */
+ private 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.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, Initializer.AUTHALIC_RADIUS);
+ }
+
+ /**
+ * Creates an orthographic projection from the given parameters.
+ * The {@code method} argument can be the description of one of the following:
+ *
+ * <ul>
+ * <li><cite>"Orthographic"</cite>.</li>
+ * </ul>
+ *
+ * @param method description of the projection parameters.
+ * @param parameters the parameter values of the projection to create.
+ */
+ public Orthographic(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 Orthographic(final Initializer initializer) {
+ super(initializer);
+ final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));
+ sinφ0 = sin(φ0);
+ cosφ0 = cos(φ0);
+ }
+
+ /**
+ * Converts the specified (λ,φ) coordinate and stores the (<var>x</var>,<var>y</var>) result in {@code dstPts}.
+ * The units of measurement are implementation-specific (see subclass javadoc).
+ *
+ * @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 cosφ_cosλ = cosφ*cosλ;
+ final double sinφ0_sinφ = sinφ0*sinφ; // Note: φ₀ here is φ₁ in Snyder.
+ final double cosc = sinφ0_sinφ + cosφ0*cosφ_cosλ; // Snyder (5-3) page 149
+ final double x, y;
+ /*
+ * c is the distance from the center of orthographic projection.
+ * If that distance is greater than 90° (identied by cos(c) < 0)
+ * then the point is on the opposite hemisphere and should be clipped.
+ */
+ if (cosc >= 0) {
+ x = cosφ * sinλ; // Snyder (20-3)
+ y = cosφ0*sinφ - sinφ0*cosφ_cosλ; // Snyder (20-4)
+ } else {
+ x = Double.NaN;
+ y = Double.NaN;
+ }
+ if (dstPts != null) {
+ dstPts[dstOff ] = x;
+ dstPts[dstOff+1] = y;
+ }
+ if (!derivate) {
+ return null;
+ }
+ return new Matrix2(cosφ_cosλ,
+ -sinφ*sinλ,
+ sinφ0 * x,
+ cosφ0 * cosφ + sinφ0_sinφ*cosλ);
+ }
+
+ /**
+ * 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
+ {
+ /*
+ * Note: Synder said that equations become undetermined if ρ=0.
+ * But setting the radius R to 1 allows simplifications to emerge.
+ * In particular sin(c) = ρ, so terms like sin(c)/ρ disappear.
+ */
+ final double x = srcPts[srcOff ];
+ final double y = srcPts[srcOff+1];
+ final double ρ2 = x*x + y*y; // sin(c) = ρ/R, but in this method R=1.
+ final double cosc = sqrt(1 - ρ2); // NaN if ρ > 1.
+ dstPts[dstOff ] = atan2(x, cosc*cosφ0 - y*sinφ0); // Synder (20-15) with ρ = sin(c)
+ dstPts[dstOff+1] = asin(cosc*sinφ0 + y*cosφ0); // Synder (20-14) where y⋅sin(c)/ρ = y/R with R=1.
+ }
+
+ /**
+ * Compares the given object with this transform for equivalence.
+ */
+ @Override
+ public boolean equals(final Object object, final ComparisonMode mode) {
+ if (super.equals(object, mode)) {
+ final Orthographic that = (Orthographic) object;
+ return Numerics.epsilonEqual(sinφ0, that.sinφ0, mode) &&
+ Numerics.epsilonEqual(cosφ0, that.cosφ0, mode);
+ }
+ return false;
+ }
+}
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 26726f5..5aa20e9 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
@@ -54,6 +54,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.Orthographic
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 a78648b..c6e497d 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
@@ -103,6 +103,7 @@
ObliqueMercatorCenter.class,
ObliqueMercatorTwoPoints.class,
ObliqueMercatorTwoPointsCenter.class,
+ Orthographic.class,
ZonedTransverseMercator.class,
SatelliteTracking.class,
Sinusoidal.class,
@@ -153,7 +154,7 @@
final Map<GeneralParameterDescriptor, GeneralParameterDescriptor> parameters = new HashMap<>();
final Map<Object, Object> namesAndIdentifiers = new HashMap<>();
for (final Class<?> c : methods()) {
- final OperationMethod method = (OperationMethod) c.newInstance();
+ final OperationMethod method = (OperationMethod) c.getConstructor((Class[]) null).newInstance((Object[]) null);
final ParameterDescriptorGroup group = method.getParameters();
final String operationName = group.getName().getCode();
for (final GeneralParameterDescriptor param : group.descriptors()) {
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/OrthographicTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/OrthographicTest.java
new file mode 100644
index 0000000..2f83695
--- /dev/null
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/OrthographicTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.apache.sis.referencing.operation.transform.CoordinateDomain;
+import org.apache.sis.internal.referencing.provider.MapProjection;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.test.DependsOn;
+import org.junit.*;
+
+import static java.lang.StrictMath.*;
+
+
+/**
+ * Tests the {@link Orthographic} class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Rémi Maréchal (Geomatys)
+ *
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+@DependsOn(NormalizedProjectionTest.class)
+public final strictfp class OrthographicTest extends MapProjectionTestCase {
+ /**
+ * Creates a new instance of {@link Orthographic}.
+ *
+ * @param φ0 latitude of projection centre.
+ */
+ private void create(final double φ0) {
+ final MapProjection provider = new org.apache.sis.internal.referencing.provider.Orthographic();
+ final Parameters parameters = parameters(provider, false);
+ parameters.parameter("latitude_of_origin").setValue(φ0);
+ transform = new Orthographic(provider, parameters);
+ final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres.
+ derivativeDeltas = new double[] {delta, delta};
+ validate();
+ }
+
+ /**
+ * Tests the equatorial projection on a sphere.
+ * This method uses points from Snyder table 22.
+ *
+ * @throws TransformException should never happen.
+ */
+ @Test
+ public void testEquatorial() throws TransformException {
+ create(0);
+ tolerance = 1E-4; // Accuracy of numbers provided in Snyder tables.
+ verifyTransform(
+ new double[] { // (λ,φ) coordinates in radians to project.
+ toRadians( 0), toRadians( 0),
+ toRadians(20), toRadians( 0),
+ toRadians( 0), toRadians(40),
+ toRadians(30), toRadians(20),
+ toRadians(20), toRadians(80)
+ },
+ new double[] { // Expected (x,y) results.
+ 0.0, 0.0,
+ 0.3420, 0.0,
+ 0.0, 0.6428,
+ 0.4698, 0.3420,
+ 0.0594, 0.9848
+ });
+
+ tolerance = NORMALIZED_TOLERANCE;
+ verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS_HALF_λ, 209067359);
+ verifyDerivative(toRadians(5), toRadians(3));
+ }
+
+ /**
+ * Tests the oblique projection on a sphere.
+ * This method uses points from Snyder table 23.
+ *
+ * @throws TransformException should never happen.
+ */
+ @Test
+ public void testOblique() throws TransformException {
+ create(40);
+ tolerance = 1E-4; // Accuracy of numbers provided in Snyder tables.
+ verifyTransform(
+ new double[] { // (λ,φ) coordinates in radians to project.
+ toRadians( 0), toRadians(40),
+ toRadians( 0), toRadians( 0),
+ toRadians(20), toRadians( 0),
+ toRadians( 0), toRadians(50),
+ toRadians(30), toRadians(20),
+ toRadians(20), toRadians(80)
+ },
+ new double[] { // Expected (x,y) results.
+ 0.0, 0.0,
+ 0.0, -0.6428,
+ 0.3420, -0.6040,
+ 0.0, 0.1736,
+ 0.4698, -0.2611,
+ 0.0594, 0.6495
+ });
+
+ tolerance = NORMALIZED_TOLERANCE;
+ verifyDerivative(toRadians(5), toRadians(30));
+ }
+
+ /**
+ * Tests the polar projection on a sphere.
+ * There is no reference points for this method;
+ * we just test consistency between forward and inverse methods.
+ *
+ * @throws TransformException should never happen.
+ */
+ @Test
+ public void testPolarNorth() throws TransformException {
+ create(+90);
+ tolerance = NORMALIZED_TOLERANCE;
+ verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS_NORTH, 753524735);
+ verifyDerivative(toRadians(5), toRadians(85));
+ }
+
+ /**
+ * Tests the polar projection on a sphere.
+ * There is no reference points for this method;
+ * we just test consistency between forward and inverse methods.
+ *
+ * @throws TransformException should never happen.
+ */
+ @Test
+ public void testPolarSouth() throws TransformException {
+ create(-90);
+ tolerance = NORMALIZED_TOLERANCE;
+ verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS_SOUTH, 753524735);
+ verifyDerivative(toRadians(5), toRadians(-85));
+ }
+}
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 8020457..5e26fe4 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
@@ -191,6 +191,7 @@
org.apache.sis.referencing.operation.projection.SinusoidalTest.class,
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.SatelliteTrackingTest.class,
// Coordinate operation and derived Coordinate Reference Systems (cyclic dependency).