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 <[email protected]> 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 [email protected] - 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/[email protected] 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
