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
commit 4d757f3b9d12e7ee6a103aca7d6df63b2530c767 Author: Martin Desruisseaux <[email protected]> AuthorDate: Wed Mar 13 20:42:42 2019 +0100 Add netCDF convention for Global Change Observation Mission - Climate (GCOM-C). --- ide-project/NetBeans/build.xml | 7 + storage/sis-earth-observation/pom.xml | 6 + .../apache/sis/internal/earth/netcdf/GCOM_C.java | 309 +++++++++++++++++++++ .../apache/sis/internal/earth/netcdf/GCOM_W.java | 207 ++++++++++++++ .../sis/internal/earth/netcdf/package-info.java | 28 ++ .../org.apache.sis.internal.netcdf.Convention | 2 + 6 files changed, 559 insertions(+) diff --git a/ide-project/NetBeans/build.xml b/ide-project/NetBeans/build.xml index 8c66df3..ea5a0f7 100644 --- a/ide-project/NetBeans/build.xml +++ b/ide-project/NetBeans/build.xml @@ -244,6 +244,13 @@ <include name="*/*/src/main/resources/META-INF/services/org.apache.sis.storage.DataStoreProvider"/> </fileset> </concat> + + <!-- NetCDF conventions to be loaded by ServiceLoader. --> + <concat destfile="${build.classes.dir}/META-INF/services/org.apache.sis.internal.netcdf.Convention" encoding="UTF-8" fixlastline="yes"> + <fileset dir="${project.root}"> + <include name="*/*/src/main/resources/META-INF/services/org.apache.sis.internal.netcdf.Convention"/> + </fileset> + </concat> </target> <!-- diff --git a/storage/sis-earth-observation/pom.xml b/storage/sis-earth-observation/pom.xml index 33f1b0d..927b4fc 100644 --- a/storage/sis-earth-observation/pom.xml +++ b/storage/sis-earth-observation/pom.xml @@ -125,6 +125,12 @@ <artifactId>sis-storage</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.sis.storage</groupId> + <artifactId>sis-netcdf</artifactId> + <version>${project.version}</version> + <optional>true</optional> + </dependency> </dependencies> </project> 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 new file mode 100644 index 0000000..a10fda0 --- /dev/null +++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java @@ -0,0 +1,309 @@ +/* + * 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.earth.netcdf; + +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Pattern; +import org.apache.sis.storage.netcdf.AttributeNames; +import org.apache.sis.internal.netcdf.Convention; +import org.apache.sis.internal.netcdf.Decoder; +import org.apache.sis.internal.netcdf.Variable; +import org.apache.sis.internal.netcdf.VariableRole; +import org.apache.sis.referencing.operation.transform.TransferFunction; +import org.apache.sis.measure.NumberRange; + + +/** + * Global Change Observation Mission - Climate (GCOM-C) conventions. This class provides customizations to the netCDF reader + * for decoding <cite>Shikisai</cite> GCOM-C files produced by Japan Aerospace Exploration Agency (JAXA), version 1.00. + * The file format is HDF5 and variables are like below (simplified): + * + * {@preformat text + * group: Geometry_data { + * variables: + * float Latitude(161, 126) + * string Unit = "degree" + * string Dim0 = "Line grids" + * string Dim1 = "Pixel grids" + * int Resampling_interval = 10 + * float Longitude(161, 126) + * string Unit = "degree" + * string Dim0 = "Line grids" + * string Dim1 = "Pixel grids" + * int Resampling_interval = 10 + * } + * group: Image_data { + * variables: + * ushort SST(1599, 1250) // Note: different size than (latitude, longitude) variables. + * string dim0 = "Line grids" + * string dim1 = "Piexl grids" // Note: typo in "Pixel" + * ushort Error_DN = 65535 + * ushort Land_DN = 65534 + * ushort Cloud_error_DN = 65533 + * ushort Retrieval_error_DN = 65532 + * ushort Maximum_valid_DN = 65531 + * ushort Minimum_valid_DN = 0 + * float Slope = 0.0012 + * float Offset = -10 + * string Unit = "degree" + * } + * group: Global_attributes { + * string :Algorithm_developer = "Japan Aerospace Exploration Agency (JAXA)" + * string :Dataset_description = "Sea surface temperatures determined by using the TIR 1 and 2 data of SGLI" + * string :Satellite = "Global Change Observation Mission - Climate (GCOM-C)" + * string :Scene_start_time = "20181201 09:14:16.797" + * string :Scene_end_time = "20181201 09:18:11.980" + * } + * group: Processing_attributes { + * string :Contact_point = "JAXA/Earth Observation Research Center (EORC)" + * string :Processing_organization = "JAXA/GCOM-C science project" + * string :Processing_UT = "20181202 04:42:09" + * } + * } + * + * Observations: + * <ul class="verbose"> + * <li>There is no {@code convention} attribute, so we have to rely on something else for detecting this convention.</li> + * <li>The size of latitude and longitude variables is not the same than the size of image data. + * This particularity is handled by {@link #gridToDataIndices(Variable)}.</li> + * <li>The {@code dim0} and {@code dim1} attribute names in image data have a different case + * than the attributes in longitude and latitude variables. Furthermore a value contains a typo. + * This particularity is handled by {@link #nameOfDimension(Variable, int)}.</li> + * <li>The {@code Slope} and {@code Offset} attribute names are different than the names defined in CF-Convention. + * This particularity is handled by {@link #transferFunction(Variable)}.</li> + * <li>There is more than one sentinel values for missing values. + * All attribute names for missing values have {@code "_DN"} suffix. + * This particularity is handled by {@link #nodataValues(Variable)}.</li> + * <li>The global attributes have different names than CF-Convention. + * This particularity is handled by {@link #mapAttributeName(String)}.</li> + * </ul> + * + * @author Alexis Manin (Geomatys) + * @author Martin Desruisseaux (Geomatys) + * @version 1.0 + * + * @see <a href="http://global.jaxa.jp/projects/sat/gcom_c/">SHIKISAI (GCOM-C) on JAXA</a> + * @see <a href="https://en.wikipedia.org/wiki/Global_Change_Observation_Mission">GCOM on Wikipedia</a> + * + * @since 1.0 + * @module + */ +public final class GCOM_C extends Convention { + /** + * Sentinel value to search in the {@code "Satellite"} attribute for determining if GCOM-C conventions apply. + */ + private static final Pattern SENTINEL_VALUE = Pattern.compile(".*\\bGCOM-C\\b.*"); + + /** + * Mapping from ACDD or CF-Convention attribute names to names of attributes used by GCOM-C. + */ + private static final Map<String,String> ATTRIBUTES; + static { + final Map<String,String> m = new HashMap<>(); + m.put(AttributeNames.TITLE, "Product_name"); // identificationInfo / citation / title + m.put(AttributeNames.PRODUCT_VERSION, "Product_version"); // identificationInfo / citation / edition + m.put(AttributeNames.IDENTIFIER.TEXT, "Product_file_name"); // identificationInfo / citation / identifier / code + m.put(AttributeNames.DATE_CREATED, "Processing_UT"); // identificationInfo / citation / date + m.put(AttributeNames.CREATOR.INSTITUTION, "Processing_organization"); // identificationInfo / citation / citedResponsibleParty + m.put(AttributeNames.SUMMARY, "Dataset_description"); // identificationInfo / abstract + m.put(AttributeNames.PLATFORM.TEXT, "Satellite"); // acquisitionInformation / platform / identifier + m.put(AttributeNames.INSTRUMENT.TEXT, "Sensor"); // acquisitionInformation / platform / instrument / identifier + m.put(AttributeNames.PROCESSING_LEVEL, "Product_level"); // contentInfo / processingLevelCode + m.put(AttributeNames.SOURCE, "Input_files"); // dataQualityInfo / lineage / source / description + m.put(AttributeNames.TIME.MINIMUM, "Scene_start_time"); // identificationInfo / extent / temporalElement / extent + m.put(AttributeNames.TIME.MAXIMUM, "Scene_end_time"); // identificationInfo / extent / temporalElement / extent + ATTRIBUTES = m; + } + + /** + * Names of attributes for sample values having "no-data" meaning. + * All those names have {@value #SUFFIX} suffix. + */ + private static final String[] NO_DATA = { + "Error_DN", + "Land_DN", + "Cloud_error_DN", + "Retrieval_error_DN" + }; + + /** + * Suffix of all attribute names enumerated in {@link #NO_DATA}. + */ + private static final String SUFFIX = "_DN"; + + /** + * Creates a new instance of GCOM-C conventions. + */ + public GCOM_C() { + } + + /** + * Detects if this set of conventions applies to the given netCDF file. + * This methods fetches the {@code "Global_attributes/Satellite"} attribute, + * which is expected to contain the following value: + * + * <blockquote>Global Change Observation Mission - Climate (GCOM-C)</blockquote> + * + * We test only for presence of {@code "GCOM-C"} in order to allow + * for some variations in exact text content. + * + * @param decoder the netCDF file to test. + * @return {@code true} if this set of conventions can apply. + */ + @Override + protected boolean isApplicableTo(final Decoder decoder) { + final String[] path = decoder.getSearchPath(); + decoder.setSearchPath("Global_attributes"); + final String s = decoder.stringValue("Satellite"); + decoder.setSearchPath(path); // Must reset the decoder in its original state. + return (s != null) && SENTINEL_VALUE.matcher(s).matches(); + } + + /** + * Specifies a list of groups where to search for named attributes, in preference order. + * This is used for ISO 19115 metadata. The {@code null} name stands for the root group. + * + * @return name of groups where to search for global attributes, in preference order. + */ + @Override + public String[] getSearchPath() { + return new String[] {"Global_attributes", null, "Processing_attributes"}; + } + + /** + * Returns the name of an attribute in this convention which is equivalent to the attribute of given name in CF-Convention. + * The given parameter is a name from <cite>CF conventions</cite> or from <cite>Attribute Convention for Dataset Discovery + * (ACDD)</cite>. + * + * @param name an attribute name from CF or ACDD convention. + * @return the attribute name expected to be found in a netCDF file for GCOM-C, or {@code name} if unknown. + */ + @Override + public String mapAttributeName(final String name) { + return ATTRIBUTES.getOrDefault(name, name); + } + + /** + * Returns the attribute-specified name of the dimension at the given index, or {@code null} if unspecified. + * See {@link Convention#nameOfDimension(Variable, int)} for a more detailed explanation of this information. + * The implementation in this class fixes a typo found in some {@code "Dim1"} attribute values and generates + * the values when they are known to be missing. + * + * @param dataOrAxis the variable for which to get the attribute-specified name of the dimension. + * @param index zero-based index of the dimension for which to get the name. + * @return dimension name as specified by attributes, or {@code null} if none. + */ + @Override + public String nameOfDimension(final Variable dataOrAxis, final int index) { + String name = super.nameOfDimension(dataOrAxis, index); + if (name == null) { + if ("QA_flag".equals(dataOrAxis.getName())) { // Missing Dim0 and Dim1 for this variable in GCOM-C version 1.00. + switch (index) { + case 0: name = "Line grids"; break; + case 1: name = "Pixel grids"; break; + } + } + } else if ("Piexl grids".equalsIgnoreCase(name)) { // Typo in GCOM-C version 1.00. + name = "Pixel grids"; + } + return name; + } + + /** + * Returns whether the given variable is used as a coordinate system axis, a coverage or something else. + * + * @param variable the variable for which to get the role, or {@code null}. + * @return role of the given variable, or {@code null} if the given variable was null. + */ + @Override + public VariableRole roleOf(final Variable variable) { + VariableRole role = super.roleOf(variable); + if (role == VariableRole.COVERAGE) { + /* + * Exclude (for now) some variables associated to longitude and latitude: Obs_time, Sensor_zenith, Solar_zenith. + * In a future version we should probably keep them but store them in their own resource aggregate. + */ + final String group = variable.getGroupName(); + if ("Geometry_data".equalsIgnoreCase(group)) { + role = VariableRole.OTHER; + } + } + return role; + } + + /** + * Returns the range of valid values, or {@code null} if unknown. + * + * @param data the variable to get valid range of values for. + * @return the range of valid values, or {@code null} if unknown. + */ + @Override + public NumberRange<?> validRange(final Variable data) { + NumberRange<?> range = super.validRange(data); + if (range == null) { + final double min = data.getAttributeAsNumber("Minimum_valid_DN"); + final double max = data.getAttributeAsNumber("Maximum_valid_DN"); + if (Double.isFinite(min) && Double.isFinite(max)) { + range = NumberRange.createBestFit(min, true, max, true); + } + } + return range; + } + + /** + * Returns all no-data values declared for the given variable, or an empty map if none. + * The map keys are the no-data values (pad sample values or missing sample values). + * The map values are {@link String} instances containing the description of the no-data value. + * + * @param data the variable for which to get no-data values. + * @return no-data values with textual descriptions. + */ + @Override + public Map<Number,Object> nodataValues(final Variable data) { + final Map<Number, Object> pads = super.nodataValues(data); + for (String name : NO_DATA) { + final double value = data.getAttributeAsNumber(name); + if (Double.isFinite(value)) { + if (name.endsWith(SUFFIX)) { + name = name.substring(0, name.length() - SUFFIX.length()); + } + pads.put(value, name.replace('_', ' ')); + } + } + return pads; + } + + /** + * Builds the function converting values from their packed formats in the variable to "real" values. + * This method is invoked only if {@link #validRange(Variable)} returned a non-null value. + * + * @param data the variable from which to determine the transfer function. + * @return a transfer function built from the attributes defined in the given variable. + */ + @Override + public TransferFunction transferFunction(final Variable data) { + final TransferFunction tr = super.transferFunction(data); + if (tr.isIdentity()) { + final double slope = data.getAttributeAsNumber("Slope"); + final double offset = data.getAttributeAsNumber("Offset"); + if (Double.isFinite(slope)) tr.setScale (slope); + if (Double.isFinite(offset)) tr.setOffset(offset); + } + return tr; + } +} diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java new file mode 100644 index 0000000..90177bb --- /dev/null +++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java @@ -0,0 +1,207 @@ +/* + * 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.earth.netcdf; + +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Pattern; +import org.apache.sis.storage.netcdf.AttributeNames; +import org.apache.sis.internal.netcdf.Convention; +import org.apache.sis.internal.netcdf.Decoder; +import org.apache.sis.internal.netcdf.Variable; +import org.apache.sis.internal.netcdf.VariableRole; +import org.apache.sis.referencing.operation.transform.TransferFunction; + + +/** + * Global Change Observation Mission - Water (GCOM-W) conventions. This class provides customizations to the netCDF reader + * for decoding <cite>Shizuku</cite> GCOM-W files produced by Japan Aerospace Exploration Agency (JAXA), version 3. + * The file format is HDF5 and variables are like below (simplified): + * + * {@preformat text + * variables: + * short "Geophysical Data"(1976, 243, 2) + * float SCALE FACTOR = 0.01 + * string UNIT = "C" + * float "Latitude of Observation Point"(1976, 243) + * string UNIT = "deg" + * float "Longitude of Observation Point"(1976, 243) + * string UNIT = "deg" + * + * // global attributes: + * string :GeophysicalName = "Sea Surface Temperature" + * string :ProductName = "AMSR2-L2" + * string :ProductVersion = "3" + * string :ObservationStartDateTime = "2018-11-01T00:08:02.028Z" + * string :ObservationEndDateTime = "2018-11-01T00:57:24.247Z" + * string :PlatformShortName = "GCOM-W1" ; + * string :SensorShortName = "AMSR2" ; + * } + * + * Observations: + * <ul class="verbose"> + * <li>There is no {@code convention} attribute, so we have to rely on something else for detecting this convention.</li> + * <li>The attributes do not specify the "no data" value. A look in sample files suggest that -32768 is used. + * This particularity is handled by {@link #nodataValues(Variable)}.</li> + * <li>The global attributes have different names than CF-Convention. + * This particularity is handled by {@link #mapAttributeName(String)}.</li> + * </ul> + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.0 + * + * @see <a href="http://global.jaxa.jp/projects/sat/gcom_w/">SHIZUKU (GCOM-W) on JAXA</a> + * @see <a href="https://en.wikipedia.org/wiki/Global_Change_Observation_Mission">GCOM on Wikipedia</a> + * + * @since 1.0 + * @module + */ +public final class GCOM_W extends Convention { + /** + * Sentinel value to search in the {@code "PlatformShortName"} attribute for determining if GCOM-W conventions apply. + */ + private static final Pattern SENTINEL_VALUE = Pattern.compile(".*\\bGCOM-W1\\b.*"); + + /** + * Mapping from ACDD or CF-Convention attribute names to names of attributes used by GCOM-W. + */ + private static final Map<String,String> ATTRIBUTES; + static { + final Map<String,String> m = new HashMap<>(); + m.put(AttributeNames.TITLE, "ProductName"); // identificationInfo / citation / title + m.put(AttributeNames.PRODUCT_VERSION, "ProductVersion"); // identificationInfo / citation / edition + m.put(AttributeNames.IDENTIFIER.TEXT, "GranuleID"); // identificationInfo / citation / identifier / code + m.put(AttributeNames.DATE_CREATED, "ProductionDateTime"); // identificationInfo / citation / date + m.put(AttributeNames.TIME.MINIMUM, "ObservationStartDateTime"); // identificationInfo / extent / temporalElement / extent + m.put(AttributeNames.TIME.MAXIMUM, "ObservationEndDateTime"); // identificationInfo / extent / temporalElement / extent + m.put(AttributeNames.CREATOR.INSTITUTION, "ProcessingCenter"); // identificationInfo / citation / citedResponsibleParty + m.put(AttributeNames.SUMMARY, "GeophysicalName"); // identificationInfo / abstract + m.put(AttributeNames.PLATFORM.TEXT, "PlatformShortName"); // acquisitionInformation / platform / identifier + m.put(AttributeNames.INSTRUMENT.TEXT, "SensorShortName"); // acquisitionInformation / platform / instrument / identifier + m.put(AttributeNames.SOURCE, "InputFileName"); // dataQualityInfo / lineage / source / description + ATTRIBUTES = m; + } + + /** + * Names of variables to use as axes (first word only). + */ + private static final String[] AXES = { + "Latitude", + "Longitude" + }; + + /** + * "No data" value to use when not specified in the file. + * This value has been determined by looking at the content of a sample file. + */ + private static final short NO_DATA = -32768; + + /** + * Creates a new instance of GCOM-W conventions. + */ + public GCOM_W() { + } + + /** + * Detects if this set of conventions applies to the given netCDF file. + * This methods fetches the {@code "PlatformShortName"} attribute, + * which is expected to contain the following value: + * + * <blockquote>GCOM-W1</blockquote> + * + * @param decoder the netCDF file to test. + * @return {@code true} if this set of conventions can apply. + */ + @Override + protected boolean isApplicableTo(final Decoder decoder) { + final String s = decoder.stringValue("PlatformShortName"); + return (s != null) && SENTINEL_VALUE.matcher(s).matches(); + } + + /** + * Returns the name of an attribute in this convention which is equivalent to the attribute of given name in CF-Convention. + * The given parameter is a name from <cite>CF conventions</cite> or from <cite>Attribute Convention for Dataset Discovery + * (ACDD)</cite>. + * + * @param name an attribute name from CF or ACDD convention. + * @return the attribute name expected to be found in a netCDF file for GCOM-W, or {@code name} if unknown. + */ + @Override + public String mapAttributeName(final String name) { + return ATTRIBUTES.getOrDefault(name, name); + } + + /** + * Returns whether the given variable is used as a coordinate system axis, a coverage or something else. + * + * @param variable the variable for which to get the role, or {@code null}. + * @return role of the given variable, or {@code null} if the given variable was null. + */ + @Override + public VariableRole roleOf(final Variable variable) { + VariableRole role = super.roleOf(variable); + if (role == VariableRole.COVERAGE) { + final String name = variable.getName(); + for (final String c : AXES) { + if (name.regionMatches(true, 0, c, 0, c.length())) { + role = VariableRole.AXIS; + break; + } + } + } + return role; + } + + /** + * Returns no-data values declared for the given variable. + * + * @param data the variable for which to get no-data values. + * @return no-data values with a flag specifying their role. + */ + @Override + public Map<Number,Object> nodataValues(final Variable data) { + final Map<Number, Object> pads = super.nodataValues(data); + if (pads.isEmpty() && roleOf(data) == VariableRole.COVERAGE) { + pads.put(NO_DATA, 3); + /* + * Value 3 stands for: + * - bit 0 set: NO_DATA is a pad value (can be used as background). + * - bit 1 set: NO_DATA is a missing value. + */ + } + return pads; + } + + /** + * Builds the function converting values from their packed formats in the variable to "real" values. + * This method is invoked only if {@link #validRange(Variable)} returned a non-null value. + * + * @param data the variable from which to determine the transfer function. + * @return a transfer function built from the attributes defined in the given variable. + */ + @Override + public TransferFunction transferFunction(final Variable data) { + final TransferFunction tr = super.transferFunction(data); + if (tr.isIdentity()) { + final double slope = data.getAttributeAsNumber("SCALE FACTOR"); + final double offset = data.getAttributeAsNumber("OFFSET"); + if (Double.isFinite(slope)) tr.setScale (slope); + if (Double.isFinite(offset)) tr.setOffset(offset); + } + return tr; + } +} diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java new file mode 100644 index 0000000..f353184 --- /dev/null +++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/** + * Extensions to netCDF conventions applied on a case-by-case basis depending on the product. + * Classes defined in this packages are declared as {@link org.apache.sis.internal.netcdf.Convention} services. + * + * @author Alexis Manin (Geomatys) + * @author Martin Desruisseaux (Geomatys) + * @version 1.0 + * @since 1.0 + * @module + */ +package org.apache.sis.internal.earth.netcdf; diff --git a/storage/sis-earth-observation/src/main/resources/META-INF/services/org.apache.sis.internal.netcdf.Convention b/storage/sis-earth-observation/src/main/resources/META-INF/services/org.apache.sis.internal.netcdf.Convention new file mode 100644 index 0000000..04d3c1d --- /dev/null +++ b/storage/sis-earth-observation/src/main/resources/META-INF/services/org.apache.sis.internal.netcdf.Convention @@ -0,0 +1,2 @@ +org.apache.sis.internal.earth.netcdf.GCOM_C +org.apache.sis.internal.earth.netcdf.GCOM_W
