You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/04/30 14:35:16 UTC
[sis] 01/01: Merge branch 'geoapi-3.1'. Contains work on netCDF
reader and addition of two map projections.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 4610eabf6db804db9aa428d9977f8140f8e76e52
Merge: e55b128 2751173
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Apr 30 16:34:42 2019 +0200
Merge branch 'geoapi-3.1'. Contains work on netCDF reader and addition of two map projections.
.../sis/internal/metadata/ReferencingServices.java | 2 +-
.../sis/internal/simple/SimpleIdentifier.java | 31 +-
.../apache/sis/metadata/PropertyInformation.java | 5 +-
.../org/apache/sis/metadata/TreeTableView.java | 2 +-
.../iso/extent/DefaultSpatialTemporalExtent.java | 4 +-
.../sis/internal/simple/SimpleIdentifierTest.java | 12 -
.../org/apache/sis/internal/map/MapContext.java | 2 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 32 +-
.../apache/sis/coverage/grid/PixelTranslation.java | 24 +-
.../apache/sis/coverage/grid/GridGeometryTest.java | 33 +-
.../internal/referencing/ReferencingUtilities.java | 18 +
.../referencing/provider/MercatorSpherical.java | 2 +-
.../internal/referencing/provider/Polyconic.java | 117 +++++++
.../referencing/provider/PseudoPlateCarree.java | 13 +-
.../internal/referencing/provider/Sinusoidal.java | 103 ++++++
.../org/apache/sis/parameter/ParameterFormat.java | 2 +-
.../java/org/apache/sis/referencing/CommonCRS.java | 46 +--
.../sis/referencing/EPSGFactoryFallback.java | 186 +++++++---
.../sis/referencing/StandardDefinitions.java | 85 +++--
.../java/org/apache/sis/referencing/cs/Codes.java | 3 +-
.../referencing/factory/sql/EPSGDataAccess.java | 2 +-
.../operation/CoordinateOperationContext.java | 12 +-
.../operation/CoordinateOperationRegistry.java | 2 +-
.../referencing/operation/matrix/package-info.java | 4 +-
.../operation/projection/AlbersEqualArea.java | 4 +-
.../operation/projection/ConformalProjection.java | 200 ++++-------
.../operation/projection/CylindricalEqualArea.java | 6 +-
.../operation/projection/EqualAreaProjection.java | 206 +++++------
.../operation/projection/Initializer.java | 12 +-
.../projection/LambertConicConformal.java | 4 +-
.../referencing/operation/projection/Mercator.java | 4 +-
.../operation/projection/MeridianArcBased.java | 222 ++++++++++++
.../operation/projection/NormalizedProjection.java | 2 +-
.../operation/projection/ObliqueMercator.java | 1 -
.../operation/projection/ObliqueStereographic.java | 3 +-
.../operation/projection/PolarStereographic.java | 4 +-
.../operation/projection/Polyconic.java | 382 +++++++++++++++++++++
.../operation/projection/Sinusoidal.java | 240 +++++++++++++
.../operation/projection/TransverseMercator.java | 183 ++++++----
.../operation/transform/MathTransforms.java | 36 +-
.../operation/transform/TranslationTransform.java | 9 +
...g.opengis.referencing.operation.OperationMethod | 2 +
.../org/apache/sis/geometry/TransformTestCase.java | 2 +-
.../referencing/provider/ProvidersTest.java | 4 +-
.../java/org/apache/sis/referencing/CRSTest.java | 4 +-
.../sis/referencing/EPSGFactoryFallbackTest.java | 38 +-
.../sis/referencing/StandardDefinitionsTest.java | 33 +-
.../sis/referencing/cs/DefaultCartesianCSTest.java | 7 +-
.../operation/CoordinateOperationFinderTest.java | 4 +-
.../operation/projection/AlbersEqualAreaTest.java | 10 +-
.../projection/ConformalProjectionTest.java | 117 +++----
.../projection/EqualAreaProjectionTest.java | 127 +++++++
.../projection/LambertConicConformalTest.java | 8 +-
.../projection/MapProjectionTestCase.java | 27 +-
.../projection/MercatorMethodComparison.java | 107 ++----
.../operation/projection/MercatorTest.java | 8 +-
.../operation/projection/MeridianArcTest.java | 263 ++++++++++++++
.../sis/referencing/operation/projection/NoOp.java | 1 -
.../projection/ObliqueStereographicTest.java | 2 +-
.../operation/projection/PolyconicTest.java | 143 ++++++++
.../operation/projection/SinusoidalTest.java | 131 +++++++
.../projection/TransverseMercatorTest.java | 6 +-
.../operation/projection/ZonedGridSystemTest.java | 6 +-
.../sis/test/suite/ReferencingTestSuite.java | 4 +
.../org/apache/sis/internal/util/Constants.java | 6 -
.../sis/internal/util/StandardDateFormat.java | 2 +-
.../java/org/apache/sis/util/ArgumentChecks.java | 2 +-
.../java/org/apache/sis/util/collection/Cache.java | 2 +-
.../java/org/apache/sis/util/package-info.java | 2 +-
ide-project/NetBeans/nbproject/genfiles.properties | 2 +-
ide-project/NetBeans/nbproject/project.xml | 1 +
profiles/sis-french-profile/pom.xml | 2 +-
.../apache/sis/internal/earth/netcdf/GCOM_C.java | 266 +++++++++++---
.../apache/sis/internal/earth/netcdf/GCOM_W.java | 36 +-
.../org/apache/sis/storage/geotiff/CRSBuilder.java | 2 +-
.../org/apache/sis/internal/netcdf/CRSBuilder.java | 64 ++--
.../org/apache/sis/internal/netcdf/Convention.java | 253 +++++++++++++-
.../org/apache/sis/internal/netcdf/Decoder.java | 8 +
.../java/org/apache/sis/internal/netcdf/Grid.java | 2 +-
.../apache/sis/internal/netcdf/GridMapping.java | 245 +++++++++++--
.../apache/sis/internal/netcdf/NamedElement.java | 2 +-
.../java/org/apache/sis/internal/netcdf/Node.java | 190 ++++++++++
.../apache/sis/internal/netcdf/RasterResource.java | 41 ++-
.../org/apache/sis/internal/netcdf/Resources.java | 6 +
.../sis/internal/netcdf/Resources.properties | 1 +
.../sis/internal/netcdf/Resources_fr.properties | 1 +
.../org/apache/sis/internal/netcdf/Variable.java | 153 +--------
.../sis/internal/netcdf/impl/ChannelDecoder.java | 14 +-
.../sis/internal/netcdf/impl/VariableInfo.java | 18 +-
.../sis/internal/netcdf/ucar/DecoderWrapper.java | 17 +
.../sis/internal/netcdf/ucar/GroupWrapper.java | 78 +++++
.../sis/internal/netcdf/ucar/VariableWrapper.java | 26 +-
.../sis/internal/storage/AbstractResource.java | 4 +-
.../sis/internal/storage/StoreUtilities.java | 4 +-
.../org/apache/sis/internal/storage/csv/Store.java | 6 +-
.../sis/internal/storage/xml/package-info.java | 2 +-
.../main/java/org/apache/sis/storage/DataSet.java | 4 +-
.../apache/sis/storage/GridCoverageResource.java | 12 +
.../main/java/org/apache/sis/storage/Resource.java | 2 +-
.../org/apache/sis/internal/storage/gpx/Store.java | 4 +-
100 files changed, 3705 insertions(+), 1089 deletions(-)
diff --cc core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConformalProjectionTest.java
index 76bb7b7,651a031..edc64d7
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConformalProjectionTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConformalProjectionTest.java
@@@ -195,9 -191,16 +191,9 @@@ public final strictfp class ConformalPr
}
@Override public double derivative(final double φ) {
final double sinφ = sin(φ);
- return projection.dy_dφ(sinφ, cos(φ)) * expOfNorthing(projection, φ);
+ return projection.dy_dφ(sinφ, cos(φ)) * transform(φ);
}
};
- isInverseTransformSupported = false;
- derivativeDeltas = new double[] {2E-8};
- tolerance = 1E-7;
- verifyInDomain(new double[] {-89 * (PI/180)}, // Minimal value to test.
- new double[] {+89 * (PI/180)}, // Maximal value to test.
- new int[] {100}, // Number of points to test.
- TestUtilities.createRandomNumberGenerator());
}
/**
diff --cc core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/PolyconicTest.java
index 0000000,7ffe0d5..51a8849
mode 000000,100644..100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/PolyconicTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/PolyconicTest.java
@@@ -1,0 -1,155 +1,143 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.referencing.operation.projection;
+
+ import org.opengis.util.FactoryException;
+ import org.opengis.referencing.operation.TransformException;
+ import org.apache.sis.internal.referencing.Formulas;
+ import org.apache.sis.test.DependsOnMethod;
+ import org.apache.sis.test.DependsOn;
+ import org.junit.Test;
+
+
+ /**
+ * Tests the {@link Polyconic} class.
+ *
+ * @author Simon Reynard (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Rémi Maréchal (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ @DependsOn(MeridianArcTest.class)
+ public final strictfp class PolyconicTest extends MapProjectionTestCase {
+ /**
+ * Creates a new instance of {@link Polyconic} concatenated with the (de)normalization matrices.
+ * The new instance is stored in the inherited {@link #transform} field.
+ *
+ * @param ellipsoidal {@code false} for a sphere, or {@code true} for WGS84 ellipsoid.
+ */
+ private void createProjection(final boolean ellipsoidal) throws FactoryException {
+ createCompleteProjection(new org.apache.sis.internal.referencing.provider.Polyconic(),
+ ellipsoidal ? CLARKE_A : RADIUS, // Semi-major axis (Clarke 1866)
+ ellipsoidal ? CLARKE_B : RADIUS, // Semi-minor axis (Clarke 1866)
+ -96, // Central meridian
+ 30, // Latitude of origin
+ 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. The first point in this test is provided
+ * by Snyder at page 304. The Snyder example gives intermediate values at different step,
+ * which may be verified by executing this code in the debugger.
+ *
+ * @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);
+ verifyTransform(
+ new double[] { // (λ,φ) coordinates in degrees to project.
+ -75, 40, // Snyder example is relative to λ₀ = 96°W.
+ -75, 0
+ },
+ new double[] { // Expected (x,y) results in metres.
+ 1780350.84, 1327706.12, // Values derived from Snyder page 303 with R=6400000 metres.
+ 2345722.51, -3351032.16
+ });
+ }
+
+ /**
+ * Tests the projection of a few points on an ellipsoid. The first point in this test is provided
+ * by Snyder at page 304. The Snyder example gives intermediate values at different step, which may
+ * be verified by executing this code in the debugger. In particular during inverse projection,
+ * values of φ should be as below during each iteration steps:
+ *
+ * <ol>
+ * <li>0.6967280</li>
+ * <li>0.6981286</li>
+ * <li>0.6981317</li>
+ * </ol>
+ *
+ * @throws FactoryException if an error occurred while creating the map projection.
+ * @throws TransformException if an error occurred while projecting a point.
+ */
+ @Test
+ public void testEllipsoidal() throws FactoryException, TransformException {
+ createProjection(true);
+ verifyTransform(
+ new double[] { // (λ,φ) coordinates in degrees to project.
+ -75, 40, // Snyder example is relative to λ₀ = 96°W.
+ -75, 0
+ },
+ new double[] { // Expected (x,y) results in metres.
+ 1776774.54, 1319657.78, // Values derived from Snyder page 304.
+ 2337734.74, -3319933.30
+ });
+ }
+
+ /**
+ * Tests the derivatives at a few points on a sphere. This method compares the derivatives computed
+ * by the projection with an estimation of derivatives computed by the finite differences method.
+ *
+ * @throws FactoryException if an error occurred while creating the map projection.
+ * @throws TransformException if an error occurred while projecting a point.
+ */
+ @Test
+ @DependsOnMethod("testInverseDerivative")
+ public void testDerivativeOnSphere() throws FactoryException, TransformException {
+ createProjection(false);
+ final double delta = (1.0 / 60) / 1852; // Approximatively 1 metre.
+ derivativeDeltas = new double[] {delta, delta};
+ tolerance = Formulas.LINEAR_TOLERANCE / 10;
+ verifyDerivative(-100, 3);
+ verifyDerivative( -56, 50);
+ verifyDerivative( -20, 47);
+ }
+
+ /**
+ * Tests the derivatives at a few points on an ellipsoid. 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 testDerivativeOnEllipsoid() throws FactoryException, TransformException {
+ createProjection(true);
+ final double delta = (1.0 / 60) / 1852; // Approximatively 1 metre.
+ derivativeDeltas = new double[] {delta, delta};
+ tolerance = Formulas.LINEAR_TOLERANCE / 10;
+ verifyDerivative(-100, 3);
+ verifyDerivative( -56, 50);
+ verifyDerivative( -20, 47);
+ }
-
- /**
- * Runs the test defined in the GeoAPI-conformance module.
- *
- * @throws FactoryException if the transform can not be created.
- * @throws TransformException if an error occurred while projecting a point.
- */
- @Test
- @DependsOnMethod("testEllipsoidal")
- public void runGeoapiTest() throws FactoryException, TransformException {
- createGeoApiTest(new org.apache.sis.internal.referencing.provider.Polyconic()).testPolyconic();
- }
+ }
diff --cc ide-project/NetBeans/nbproject/genfiles.properties
index fd1d952,cb2fa3c..adb45e3
--- a/ide-project/NetBeans/nbproject/genfiles.properties
+++ b/ide-project/NetBeans/nbproject/genfiles.properties
@@@ -3,6 -3,6 +3,6 @@@
build.xml.data.CRC32=58e6b21c
build.xml.script.CRC32=462eaba0
build.xml.stylesheet.CRC32=28e38971@1.53.1.46
- nbproject/build-impl.xml.data.CRC32=5c88b452
-nbproject/build-impl.xml.data.CRC32=86faa61f
-nbproject/build-impl.xml.script.CRC32=aa8f5386
++nbproject/build-impl.xml.data.CRC32=d72b1d8e
+nbproject/build-impl.xml.script.CRC32=3a1dc6ad
nbproject/build-impl.xml.stylesheet.CRC32=3a2fa800@1.89.1.48
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
index ea7a691,b6b9d6f..5ba3333
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridMapping.java
@@@ -22,9 -24,23 +24,22 @@@ import java.util.logging.Level
import java.util.logging.LogRecord;
import java.text.ParseException;
import org.opengis.util.FactoryException;
+ import org.opengis.parameter.ParameterValueGroup;
+ import org.opengis.parameter.ParameterNotFoundException;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.cs.CoordinateSystem;
+ import org.opengis.referencing.crs.ProjectedCRS;
+ import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.opengis.referencing.operation.TransformException;
-import org.opengis.referencing.operation.CoordinateOperationFactory;
+ import org.opengis.referencing.operation.OperationMethod;
import org.opengis.referencing.operation.MathTransform;
+ import org.opengis.referencing.operation.Conversion;
+ import org.opengis.referencing.datum.DatumFactory;
+ import org.opengis.referencing.datum.GeodeticDatum;
+ import org.opengis.referencing.datum.PrimeMeridian;
+ import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.datum.PixelInCell;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.CRS;
@@@ -44,8 -67,9 +66,12 @@@ import org.apache.sis.util.CharSequence
import org.apache.sis.util.ArraysExt;
import org.apache.sis.io.wkt.WKTFormat;
import org.apache.sis.io.wkt.Warnings;
+ import org.apache.sis.measure.Units;
import ucar.nc2.constants.CF;
++// Branch-dependent imports
++import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
++
/**
* Temporary objects for creating a {@link GridGeometry} instance defined by attributes on a variable.
@@@ -132,6 -158,169 +160,169 @@@ final class GridMapping
}
/**
+ * If the netCDF variable defines explicitly the map projection method and its parameters, returns those parameters.
+ * Otherwise returns {@code null}. The given {@code node} argument is typically a dummy variable referenced by value
+ * of the {@value CF#GRID_MAPPING} attribute on the real data variable (as required by CF-conventions), but may also
+ * be something else (the data variable itself, or a group, <i>etc.</i>). That node, together with the attributes to
+ * be parsed, depends on the {@link Convention} instance.
+ *
+ * @see <a href="http://cfconventions.org/cf-conventions/cf-conventions.html#grid-mappings-and-projections">CF-conventions</a>
+ */
+ private static GridMapping parseProjectionParameters(final Node node) {
+ final Map<String,Object> definition = node.decoder.convention().projection(node);
+ if (definition != null) try {
+ /*
+ * Fetch now numerical values that are not map projection parameters.
+ * This step needs to be done before to try to set parameter values.
+ */
+ final Object greenwichLongitude = definition.remove(Convention.LONGITUDE_OF_PRIME_MERIDIAN);
+ /*
+ * Prepare the block of projection parameters. The set of legal parameter depends on the map projection.
+ * We assume that all numerical values are map projection parameters; character sequences (assumed to be
+ * component names) are handled later. The CF-conventions use parameter names that are slightly different
+ * than OGC names, but Apache SIS implementations of map projections know how to handle them, including
+ * the redundant parameters like "inverse_flattening" and "earth_radius".
+ */
- final CoordinateOperationFactory opFactory = node.decoder.getCoordinateOperationFactory();
++ final DefaultCoordinateOperationFactory opFactory = node.decoder.getCoordinateOperationFactory();
+ final OperationMethod method = opFactory.getOperationMethod((String) definition.remove(CF.GRID_MAPPING_NAME));
+ final ParameterValueGroup parameters = method.getParameters().createValue();
+ for (final Map.Entry<String,Object> entry : definition.entrySet()) {
+ final String name = entry.getKey();
+ final Object value = entry.getValue();
+ if (value instanceof Number || value instanceof double[]) try {
+ parameters.parameter(name).setValue(value);
+ } catch (IllegalArgumentException ex) {
+ warning(node, ex, Resources.Keys.CanNotSetProjectionParameter_5, node.decoder.getFilename(),
+ node.getName(), name, value, ex.getLocalizedMessage());
+ }
+ }
+ /*
+ * In principle, projection parameters do not include the semi-major and semi-minor axis lengths.
+ * But if those information are provided, then we use them for building the geodetic reference frame.
+ * Otherwise a default reference frame will be used.
+ */
+ final GeographicCRS baseCRS = createBaseCRS(node.decoder, parameters, definition, greenwichLongitude);
+ final MathTransform baseToCRS;
+ final CoordinateReferenceSystem crs;
+ if (method instanceof PseudoPlateCarree) {
+ // Only swap axis order from (latitude, longitude) to (longitude, latitude).
+ baseToCRS = MathTransforms.linear(new Matrix3(0, 1, 0, 1, 0, 0, 0, 0, 1));
+ crs = baseCRS;
+ } else {
+ Map<String,?> properties = properties(definition, Convention.CONVERSION_NAME, node.getName());
+ final Conversion conversion = opFactory.createDefiningConversion(properties, method, parameters);
+ final CartesianCS cs = ReferencingUtilities.standardProjectedCS(node.decoder.getCSAuthorityFactory());
+ properties = properties(definition, Convention.PROJECTED_CRS_NAME, conversion);
+ final ProjectedCRS p = node.decoder.getCRSFactory().createProjectedCRS(properties, baseCRS, conversion, cs);
+ baseToCRS = p.getConversionFromBase().getMathTransform();
+ crs = p;
+ }
+ /*
+ * Build the "grid to CRS" if present. This is not defined by CF-convention,
+ * but may be present in some non-CF conventions.
+ */
+ final MathTransform gridToCRS = node.decoder.convention().gridToCRS(node, baseToCRS);
+ return new GridMapping(crs, gridToCRS, false);
+ } catch (ClassCastException | IllegalArgumentException | FactoryException | TransformException e) {
+ canNotCreate(node, Resources.Keys.CanNotCreateCRS_3, e);
+ }
+ return null;
+ }
+
+ /**
+ * Creates the geographic CRS from axis length specified in the given map projection parameters.
+ * The returned CRS will always have (latitude, longitude) axes in that order and in degrees.
+ */
+ private static GeographicCRS createBaseCRS(final Decoder decoder, final ParameterValueGroup parameters,
+ final Map<String,Object> definition, final Object greenwichLongitude) throws FactoryException
+ {
+ final DatumFactory datumFactory = decoder.getDatumFactory();
+ final CommonCRS defaultDefinitions = decoder.convention().defaultHorizontalCRS(false);
+ boolean isSpecified = false;
+ /*
+ * Prime meridian built from "longitude_of_prime_meridian".
+ */
+ final PrimeMeridian meridian;
+ if (greenwichLongitude instanceof Number) {
+ final double longitude = ((Number) greenwichLongitude).doubleValue();
+ final Map<String,?> properties = properties(definition, Convention.PRIME_MERIDIAN_NAME, null);
+ meridian = datumFactory.createPrimeMeridian(properties, longitude, Units.DEGREE);
+ isSpecified = true;
+ } else {
+ meridian = defaultDefinitions.primeMeridian();
+ }
+ /*
+ * Ellipsoid built from "semi_major_axis", "semi_minor_axis", etc.
+ */
+ Ellipsoid ellipsoid;
+ try {
+ final double semiMajor = parameters.parameter(Constants.SEMI_MAJOR).doubleValue();
+ final Map<String,?> properties = properties(definition, Convention.ELLIPSOID_NAME, null);
+ if (parameters.parameter(Constants.IS_IVF_DEFINITIVE).booleanValue()) {
+ final double ivf = parameters.parameter(Constants.INVERSE_FLATTENING).doubleValue();
+ ellipsoid = datumFactory.createFlattenedSphere(properties, semiMajor, ivf, Units.METRE);
+ } else {
+ final double semiMinor = parameters.parameter(Constants.SEMI_MINOR).doubleValue();
+ ellipsoid = datumFactory.createEllipsoid(properties, semiMajor, semiMinor, Units.METRE);
+ }
+ isSpecified = true;
+ } catch (ParameterNotFoundException | IllegalStateException e) {
+ // Ignore - may be normal if the map projection is not an Apache SIS implementation.
+ ellipsoid = defaultDefinitions.ellipsoid();
+ }
+ /*
+ * Geodetic datum built from "towgs84" and above properties.
+ */
+ final Object bursaWolf = definition.remove(Convention.TOWGS84);
+ final GeodeticDatum datum;
+ if (isSpecified | bursaWolf != null) {
+ Map<String,Object> properties = properties(definition, Convention.GEODETIC_DATUM_NAME, ellipsoid);
+ if (bursaWolf instanceof BursaWolfParameters) {
+ properties = new HashMap<>(properties);
+ properties.put(DefaultGeodeticDatum.BURSA_WOLF_KEY, bursaWolf);
+ isSpecified = true;
+ }
+ datum = datumFactory.createGeodeticDatum(properties, ellipsoid, meridian);
+ } else {
+ datum = defaultDefinitions.datum();
+ }
+ /*
+ * Geographic CRS from all above properties.
+ */
+ if (isSpecified) {
+ final Map<String,?> properties = properties(definition, Convention.GEOGRAPHIC_CRS_NAME, datum);
+ return decoder.getCRSFactory().createGeographicCRS(properties, datum,
+ defaultDefinitions.geographic().getCoordinateSystem());
+ } else {
+ return defaultDefinitions.geographic();
+ }
+ }
+
+ /**
+ * Returns the {@code properties} argument value to give to the factory methods of geodetic objects.
+ * The returned map contains at least an entry for {@value IdentifiedObject#NAME_KEY} with the name
+ * fetched from the value of the attribute named {@code nameAttribute}.
+ *
+ * @param definition map containing the attribute values.
+ * @param nameAttribute name of the attribute from which to get the name.
+ * @param fallback fallback as an {@link IdentifiedObject} (from which the name will be copied),
+ * or a character sequence, or {@code null} for "Unnamed" localized string.
+ */
+ private static Map<String,Object> properties(final Map<String,Object> definition, final String nameAttribute, final Object fallback) {
+ Object name = definition.remove(nameAttribute);
+ if (name == null) {
+ if (fallback instanceof IdentifiedObject) {
+ name = ((IdentifiedObject) fallback).getName();
+ } else if (fallback != null) {
+ name = fallback;
+ } else {
+ name = Vocabulary.formatInternational(Vocabulary.Keys.Unnamed);
+ }
+ }
+ return Collections.singletonMap(IdentifiedObject.NAME_KEY, name);
+ }
+
+ /**
* Tries to parse a CRS and affine transform from GDAL GeoTransform coefficients.
* Those coefficients are not in the usual order expected by matrix, affine
* transforms or TFW files. The relationship from pixel/line (P,L) coordinates