sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: More complete parsing of "grid_mapping" parameters specified by CF-conventions.
Date Fri, 26 Apr 2019 16:08:59 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new b638306  More complete parsing of "grid_mapping" parameters specified by CF-conventions.
b638306 is described below

commit b638306b84aaa1dea6dbbf942160697b668bcb94
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Apr 26 18:08:30 2019 +0200

    More complete parsing of "grid_mapping" parameters specified by CF-conventions.
---
 .../apache/sis/internal/earth/netcdf/GCOM_C.java   |   3 +-
 .../org/apache/sis/internal/netcdf/Convention.java |  28 ++--
 .../apache/sis/internal/netcdf/GridMapping.java    | 175 ++++++++++++++++++---
 .../org/apache/sis/internal/netcdf/Resources.java  |   6 +
 .../sis/internal/netcdf/Resources.properties       |   1 +
 .../sis/internal/netcdf/Resources_fr.properties    |   1 +
 6 files changed, 186 insertions(+), 28 deletions(-)

diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
index 0a88e7e..0993fc0 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
@@ -373,7 +373,7 @@ public final class GCOM_C extends Convention {
      * }
      *
      * @param  node  the group of variables from which to read attributes.
-     * @return the map projection definition, or {@code null} if none.
+     * @return the map projection definition as a modifiable map, or {@code null} if none.
      */
     @Override
     public Map<String,Object> projection(final Node node) {
@@ -396,6 +396,7 @@ public final class GCOM_C extends Convention {
         final Map<String,Object> definition = new HashMap<>(4);
         definition.put(BASE_CRS, CommonCRS.SPHERE.geographic());
         definition.put("grid_mapping_name", method);
+        definition.put("conversion_name", name);
         return definition;
     }
 
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
index b1aaeae..3a5939f 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
@@ -496,6 +496,8 @@ public class Convention {
 
     /**
      * Key associated to {@link GeographicCRS} value in the map returned by {@link #projection(Node)}.
+     * The geographic CRS associated to this key <strong>must</strong> have (latitude,
longitude) axes
+     * in degrees.
      */
     protected static final String BASE_CRS = "base_crs";
 
@@ -526,25 +528,28 @@ public class Convention {
      *     <td>{@code "*_name"}</td>
      *     <td>{@link String}</td>
      *     <td>Name of a component (datum, base CRS, …)</td>
-     *     <td>Attributes found on grid mapping variable.</td>
+     *     <td>Attribute values found on grid mapping variable.</td>
      *   </tr><tr>
      *     <td>(projection-dependent)</td>
      *     <td>{@link Number} or {@code double[]}</td>
      *     <td>Map projection parameter values</td>
-     *     <td>Attributes found on grid mapping variable.</td>
+     *     <td>Attribute values found on grid mapping variable.</td>
      *   </tr><tr>
      *     <td>{@code "towgs84"}</td>
      *     <td>{@link BursaWolfParameters}</td>
      *     <td>Datum shift information.</td>
-     *     <td>Attributes found on grid mapping variable.</td>
+     *     <td>Built from {@code "towgs84"} attribute values.</td>
      *   </tr>
      * </table>
      *
-     * Subclasses can override this method for example in order to override the {@value #BASE_CRS}
attribute
+     * The {@value CF#GRID_MAPPING_NAME} entry is mandatory. All other entries are optional.
+     *
+     * <p>Subclasses can override this method for example in order to override the
{@value #BASE_CRS} attribute
      * if they know that a particular product is based on "World Geodetic System 1984" or
other datum.
+     * The returned map must be modifiable for allowing callers to modify its content.</p>
      *
      * @param  node  the {@value CF#GRID_MAPPING} variable (preferred) or the data variable
(as a fallback) from which to read attributes.
-     * @return the map projection definition, or {@code null} if none.
+     * @return the map projection definition as a modifiable map, or {@code null} if none.
      *
      * @see <a href="http://cfconventions.org/cf-conventions/cf-conventions.html#grid-mappings-and-projections">CF-conventions</a>
      */
@@ -555,7 +560,6 @@ public class Convention {
         }
         final Map<String,Object> definition = new HashMap<>();
         definition.put(CF.GRID_MAPPING_NAME, method);
-        definition.put(BASE_CRS, CRSBuilder.DEFAULT.geographic());
         for (final String name : node.getAttributeNames()) {
             final String ln = name.toLowerCase(Locale.US);
             final Object value;
@@ -587,9 +591,15 @@ public class Convention {
                     continue;
                 }
                 default: {
-                    final double n = node.getAttributeAsNumber(name);
-                    if (Double.isNaN(n)) continue;
-                    value = n;
+                    /*
+                     * Assume that all map projection parameters in netCDF files are numbers
or array of numbers.
+                     */
+                    final Object[] values = node.getAttributeValues(name, true);
+                    switch (values.length) {
+                        case 0:  continue;                       // Attribute not found or
not numeric.
+                        case 1:  value = values[0]; break;       // This is the usual case.
+                        default: value = Vector.create(values, false).doubleValues(); break;
+                    }
                     break;
                 }
             }
diff --git 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
index 7778b2a..e7d28bc 100644
--- 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
@@ -18,14 +18,18 @@ package org.apache.sis.internal.netcdf;
 
 import java.util.Map;
 import java.util.List;
+import java.util.HashMap;
+import java.util.Collections;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.text.ParseException;
-import java.util.Collections;
 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.CRSFactory;
 import org.opengis.referencing.crs.ProjectedCRS;
 import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -34,12 +38,18 @@ 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.CommonCRS;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.crs.AbstractCRS;
 import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.referencing.datum.BursaWolfParameters;
+import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.TransformSeparator;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
@@ -49,11 +59,13 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.util.Constants;
+import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.CharSequences;
 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;
 
 
@@ -63,12 +75,10 @@ import ucar.nc2.constants.CF;
  * (e.g. GDAL or ESRI conventions). This class uses a different approach than {@link CRSBuilder},
  * which creates Coordinate Reference Systems by inspecting coordinate system axes.
  *
- * <p>Current implementation does not yet parse CF-convention attributes.
- * Only some GDAL and ESRI custom attributes are currently supported.</p>
- *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
  *
+ * @see <a href="http://cfconventions.org/cf-conventions/cf-conventions.html#grid-mappings-and-projections">CF-conventions</a>
  * @see <a href="https://www.unidata.ucar.edu/software/thredds/current/netcdf-java/reference/StandardCoordinateTransforms.html">UCAR
projections</a>
  *
  * @since 1.0
@@ -147,22 +157,121 @@ final class GridMapping {
 
     /**
      * If the netCDF variable defines explicitly the map projection method and its parameters,
returns those parameters.
-     * Otherwise returns {@code null}.
+     * 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, depend 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);
+        final Map<String,Object> definition = node.decoder.convention().projection(node);
         if (definition != null) try {
-            final CoordinateOperationFactory factory = node.decoder.getCoordinateOperationFactory();
-            final OperationMethod method = factory.getOperationMethod((String) definition.get(CF.GRID_MAPPING_NAME));
+            /*
+             * 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("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 OperationMethod method = opFactory.getOperationMethod((String) definition.get(CF.GRID_MAPPING_NAME));
             final ParameterValueGroup parameters = method.getParameters().createValue();
-            // TODO: set parameter values.
-            final Map<String,?> name = Collections.singletonMap(Conversion.NAME_KEY,
"NetCDF projection");      // TODO: find a better name.
-            final Conversion conversion = factory.createDefiningConversion(name, method,
parameters);
-
-            final GeographicCRS baseCRS = (GeographicCRS) definition.get(Convention.BASE_CRS);
+            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 CRSFactory crsFactory = node.decoder.getCRSFactory();
+            GeographicCRS baseCRS = (GeographicCRS) definition.get(Convention.BASE_CRS);
+            if (baseCRS == null) {
+                final DatumFactory datumFactory = node.decoder.getDatumFactory();
+                final CommonCRS defaultDefinitions = CRSBuilder.DEFAULT;
+                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, "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, "reference_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("towgs84");
+                final GeodeticDatum datum;
+                if (isSpecified | bursaWolf != null) {
+                    Map<String,Object> properties = properties(definition, "horizontal_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 form all above properties.
+                 */
+                if (isSpecified) {
+                    final Map<String,?> properties = properties(definition, "geographic_coordinate_system_name",
datum);
+                    baseCRS = crsFactory.createGeographicCRS(properties, datum, defaultDefinitions.geographic().getCoordinateSystem());
+                } else {
+                    baseCRS = defaultDefinitions.geographic();
+                }
+            }
+            /*
+             * Create defining conversion from the parameters and the projected CRS.
+             */
+            Map<String,?> properties = properties(definition, "conversion_name", node.getName());
+            final Conversion conversion = opFactory.createDefiningConversion(properties,
method, parameters);
             final CartesianCS cs = CommonCRS.WGS84.universal(0,0).getCoordinateSystem();
                    // TODO
-            final ProjectedCRS crs = node.decoder.getCRSFactory().createProjectedCRS(name,
baseCRS, conversion, cs);
-
+            properties = properties(definition, "projected_coordinate_system_name", conversion);
+            final ProjectedCRS crs = crsFactory.createProjectedCRS(properties, baseCRS, conversion,
cs);
+            /*
+             * Build the "grid to CRS" if present. This is not present in CF-convention,
+             * but may be present in some non-CF conventions.
+             */
             final MathTransform gridToCRS = node.decoder.convention().gridToCRS(node, crs);
             return new GridMapping(crs, gridToCRS, false);
         } catch (ClassCastException | IllegalArgumentException | FactoryException | TransformException
e) {
@@ -172,6 +281,30 @@ final class GridMapping {
     }
 
     /**
+     * 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
@@ -280,8 +413,14 @@ final class GridMapping {
      * @param  ex   the exception that occurred while creating the CRS or grid geometry.
      */
     private static void canNotCreate(final Node node, final short key, final Exception ex)
{
-        NamedElement.warning(node.decoder.listeners, Variable.class, "getGridGeometry", ex,
null,
-                key, node.decoder.getFilename(), node.getName(), ex.getLocalizedMessage());
+        warning(node, ex, key, node.decoder.getFilename(), node.getName(), ex.getLocalizedMessage());
+    }
+
+    /**
+     * Logs a warning, presuming that {@link GridMapping} are invoked (indirectly) from {@link
Variable#getGridGeometry()}.
+     */
+    private static void warning(final Node node, final Exception ex, final short key, final
Object... arguments) {
+        NamedElement.warning(node.decoder.listeners, Variable.class, "getGridGeometry", ex,
null, key, arguments);
     }
 
     /**
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
index f24dab9..4baff51 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
@@ -92,6 +92,12 @@ public final class Resources extends IndexedResourceBundle {
         public static final short CanNotRender_2 = 14;
 
         /**
+         * Can not set map projection parameter “{1}​:{2}” = {3} in the “{0}” netCDF
file. The reason
+         * is: {4}
+         */
+        public static final short CanNotSetProjectionParameter_5 = 20;
+
+        /**
          * Can not use axis “{0}” in a grid geometry.
          */
         public static final short CanNotUseAxis_1 = 18;
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
index 7a05771..82f6080 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
@@ -25,6 +25,7 @@ CanNotCreateCRS_3                 = Can not create the Coordinate Reference
Syst
 CanNotCreateGridGeometry_3        = Can not create the grid geometry \u201c{1}\u201d in the
\u201c{0}\u201d netCDF file. The reason is: {2}
 CanNotRelateVariableDimension_3   = Can not relate dimension \u201c{2}\u201d of variable
\u201c{1}\u201d to a coordinate system dimension in netCDF file \u201c{0}\u201d.
 CanNotRender_2                    = Can not render an image for \u201c{0}\u201d. The reason
is: {1}
+CanNotSetProjectionParameter_5    = Can not set map projection parameter \u201c{1}\u200b:{2}\u201d
= {3} in the \u201c{0}\u201d netCDF file. The reason is: {4}
 CanNotUseAxis_1                   = Can not use axis \u201c{0}\u201d in a grid geometry.
 CanNotUseUCAR                     = Can not use UCAR library for netCDF format. Fallback
on Apache SIS implementation.
 DimensionNotFound_3               = Dimension \u201c{2}\u201d declared by attribute \u201c{1}\u201d
is not found in the \u201c{0}\u201d file.
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
index b32eff7..79e59f4 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
@@ -30,6 +30,7 @@ CanNotCreateCRS_3                 = Ne peut pas cr\u00e9er le syst\u00e8me
de r\
 CanNotCreateGridGeometry_3        = Ne peut pas cr\u00e9er la g\u00e9om\u00e9trie de grille
\u00ab\u202f{1}\u202f\u00bb dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb. La raison
est\u2008: {2}
 CanNotRelateVariableDimension_3   = Ne peut pas relier la dimension \u00ab\u202f{2}\u202f\u00bb
de la variable \u00ab\u202f{1}\u202f\u00bb \u00e0 une dimension d\u2019un syst\u00e8me de
coordonn\u00e9es du fichier netCDF \u00ab\u202f{0}\u202f\u00bb.
 CanNotRender_2                    = Ne peut pas produire une image pour \u00ab\u202f{0}\u202f\u00bb.
La raison est\u2008: {1}
+CanNotSetProjectionParameter_5    = Ne peut pas d\u00e9finir le param\u00e8tre de projection
\u00ab\u202f{1}\u200b:{2}\u202f\u00bb = {3} dans le fichier netCDF \u00ab\u202f{0}\u202f\u00bb.
La raison est\u2008: {4}
 CanNotUseAxis_1                   = Ne peut pas utiliser l\u2019axe \u00ab\u202f{0}\u202f\u00bb
pour une g\u00e9om\u00e9trie de grille.
 CanNotUseUCAR                     = Ne peut pas utiliser la biblioth\u00e8que de l\u2019UCAR
pour le format netCDF. L\u2019impl\u00e9mentation de Apache SIS sera utilis\u00e9e \u00e0
la place.
 DimensionNotFound_3               = La dimension \u00ab\u202f{2}\u202f\u00bb d\u00e9clar\u00e9e
par l\u2019attribut \u00ab\u202f{1}\u202f\u00bb n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans
le fichier \u00ab\u202f{0}\u202f\u00bb.


Mime
View raw message