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 603c5713cc6d28e0317af3a313006cb719b59688
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed Nov 22 14:57:37 2023 +0100

    Add a "translate" sub-command to the "sis" command line.
    This sub-command rewrites a raster in another format.
---
 .../main/org/apache/sis/console/Command.java       |   1 +
 .../main/org/apache/sis/console/CommandRunner.java |  16 ++++
 .../org/apache/sis/console/Commands.properties     |   1 +
 .../org/apache/sis/console/Commands_fr.properties  |  17 ++--
 .../main/org/apache/sis/console/HelpCommand.java   |   3 +-
 .../main/org/apache/sis/console/Option.java        |   8 +-
 .../main/org/apache/sis/console/Options.properties |   3 +-
 .../org/apache/sis/console/Options_fr.properties   |  23 ++---
 .../org/apache/sis/console/TransformCommand.java   |   6 +-
 .../org/apache/sis/console/TranslateCommand.java   | 105 +++++++++++++++++++++
 optional/src/org.apache.sis.gui/bundle/bin/sis     |   2 +-
 optional/src/org.apache.sis.gui/bundle/bin/sisfx   |   2 +-
 12 files changed, 158 insertions(+), 29 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
index 06024bf7fc..6a6f4c9111 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
@@ -157,6 +157,7 @@ public final class Command {
                 case "crs":        command = new CRSCommand       
(commandIndex, args); break;
                 case "identifier": command = new 
IdentifierCommand(commandIndex, args); break;
                 case "transform":  command = new TransformCommand 
(commandIndex, args); break;
+                case "translate":  command = new TranslateCommand 
(commandIndex, args); break;
                 default: throw new InvalidCommandException(Errors.format(
                             Errors.Keys.UnknownCommand_1, commandName), 
commandName);
             }
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
index 647a88fd7b..e76c930c70 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
@@ -248,6 +248,22 @@ abstract class CommandRunner {
         }
     }
 
+    /**
+     * Return the value of a mandatory option.
+     *
+     * @param  option  the option to fetch.
+     * @return the option value, never {@code null}.
+     * @throws InvalidOptionException if the option is missing.
+     */
+    final String getMandatoryOption(final Option option) throws 
InvalidOptionException {
+        final String value = options.get(option);
+        if (value == null) {
+            final String name = option.label();
+            throw new 
InvalidOptionException(Errors.format(Errors.Keys.MissingValueForOption_1, 
name), name);
+        }
+        return value;
+    }
+
     /**
      * Checks if the user-provided {@linkplain #options} contains mutually 
exclusive options.
      * If an inconsistency is found, then this method prints an error message 
to {@link #err}
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands.properties
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands.properties
index cfff01933b..14e23b348f 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands.properties
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands.properties
@@ -10,3 +10,4 @@ metadata=Show metadata information for the given file.
 crs=Show Coordinate Reference System (CRS) information for the given file.
 identifier=Show identifiers for metadata and referencing systems in the given 
file.
 transform=Convert or transform coordinates from given source CRS to target CRS.
+translate=Rewrite a data file in another format.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands_fr.properties
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands_fr.properties
index 3388ed7e52..65747e839d 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands_fr.properties
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Commands_fr.properties
@@ -1,12 +1,13 @@
 # Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements;
 # and to You under the Apache License, Version 2.0.
-SIS=Syst�me d\u2019informations spatiales
+SIS=Syst\u00e8me d\u2019informations spatiales
 Usage=Usage: sis <commande> [options] [fichiers]
 
-help=Affiche un �cran d\u2019aide.
-about=Affiche des informations � propos de Apache SIS et de la configuration 
du syst�me.
-mime-type=Affiche le type MIME du fichier sp�cifi�.
-metadata=Affiche les m�ta-donn�es du fichier sp�cifi�.
-crs=Affiche le syst�me de r�f�rence des coordonn�es du fichier sp�cifi�.
-identifier=Affiche les identifiants des m�ta-donn�es et des syst�mes de 
r�f�rences du fichier sp�cifi�.
-transform=Transforme des coordonn�es du syst�me de r�f�rence source vers le 
syst�me destination donn�.
+help=Affiche un \u00e9cran d\u2019aide.
+about=Affiche des informations \u00e0 propos de Apache SIS et de la 
configuration du syst\u00e8me.
+mime-type=Affiche le type MIME du fichier sp\u00e9cifi\u00e9.
+metadata=Affiche les m\u00e9ta-donn\u00e9es du fichier sp\u00e9cifi\u00e9.
+crs=Affiche le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es du 
fichier sp\u00e9cifi\u00e9.
+identifier=Affiche les identifiants des m\u00e9ta-donn\u00e9es et des 
syst\u00e8mes de r\u00e9f\u00e9rences du fichier sp\u00e9cifi\u00e9.
+transform=Transforme des coordonn\u00e9es du syst\u00e8me de 
r\u00e9f\u00e9rence source vers le syst\u00e8me destination donn\u00e9.
+translate=R\u00e9\u00e9crit un fichier de donn\u00e9es dans un autre format.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/HelpCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/HelpCommand.java
index 00c1a08bdb..bbcee10ed0 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/HelpCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/HelpCommand.java
@@ -39,7 +39,8 @@ final class HelpCommand extends CommandRunner {
         "metadata",
         "crs",
         "identifier",
-        "transform"
+        "transform",
+        "translate"
     };
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java
index 5104792a46..2e9f7537d9 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java
@@ -21,7 +21,8 @@ import org.apache.sis.util.resources.Errors;
 
 
 /**
- * A command-line option.
+ * All command-line options allowed by the SIS command line. The name used on 
the command-line
+ * is the lower-cases variant of the enumeration name, except for a few cases 
with camel cases.
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
@@ -36,6 +37,11 @@ enum Option {
      */
     TARGET_CRS(true),
 
+    /**
+     * The file to write.
+     */
+    OUTPUT(true),
+
     /**
      * The output format. Examples: {@code "xml"}, {@code "text"}.
      */
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
index ac3b077027..e8c984cb5d 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
@@ -2,7 +2,8 @@
 # and to You under the Apache License, Version 2.0.
 sourceCRS=The Coordinate Reference System of input data.
 targetCRS=The Coordinate Reference System of output data.
-format=The output format: xml, wkt, wkt1 or text.
+output=The output file.
+format=The output format. Examples: xml, wkt, wkt1 or text.
 locale=The locale to use for the command output.
 timezone=The timezone for the dates to be formatted.
 encoding=The encoding to use for the command outputs and some inputs.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
index 265511713f..1828e7be7d 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
@@ -1,13 +1,14 @@
 # Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements;
 # and to You under the Apache License, Version 2.0.
-sourceCRS=Le syst�me de r�f�rence des coordonn�es source.
-targetCRS=Le syst�me de r�f�rence des coordonn�es destination.
-format=Le format de sortie: xml, wkt, wkt1 ou text.
-locale=Les param�tres r�gionaux � utiliser pour la sortie de la commande.
-timezone=Le fuseau horaire des dates � �crire.
-encoding=L\u2019encodage des caract�res � utiliser pour la sortie de la 
commande et certaines entr�es.
-colors=Indique si l\u2019affichage peut �tre en couleurs.
-brief=Indique que la sortie de la commande ne doit contenir que de br�ves 
informations.
-verbose=Indique que la sortie de la commande doit contenir des informations 
plus d�taill�es.
-debug=Affiche la trace compl�te de l\u2019exception en cas d\u2019�chec.
-help=Liste les options disponibles pour une commande sp�cifique.
+sourceCRS=Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es source.
+targetCRS=Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es 
destination.
+output=Le fichier de sortie.
+format=Le format de sortie. Exemples: xml, wkt, wkt1 ou text.
+locale=Les param\u00e8tres r\u00e9gionaux \u00e0 utiliser pour la sortie de la 
commande.
+timezone=Le fuseau horaire des dates \u00e0 \u00e9crire.
+encoding=L\u2019encodage des caract\u00e8res \u00e0 utiliser pour la sortie de 
la commande et certaines entr\u00e9es.
+colors=Indique si l\u2019affichage peut \u00eatre en couleurs.
+brief=Indique que la sortie de la commande ne doit contenir que de br\u00e8ves 
informations.
+verbose=Indique que la sortie de la commande doit contenir des informations 
plus d\u00e9taill\u00e9es.
+debug=Affiche la trace compl\u00e8te de l\u2019exception en cas 
d\u2019\u00e9chec.
+help=Liste les options disponibles pour une commande sp\u00e9cifique.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
index 0e635fc037..d43128dc62 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
@@ -162,11 +162,7 @@ final class TransformCommand extends 
FormattedOutputCommand {
      * @throws FactoryException if the operation failed for another reason.
      */
     private CoordinateReferenceSystem fetchCRS(final Option option) throws 
InvalidOptionException, FactoryException, DataStoreException {
-        final String identifier = options.get(option);
-        if (identifier == null) {
-            final String name = option.label();
-            throw new 
InvalidOptionException(Errors.format(Errors.Keys.MissingValueForOption_1, 
name), name);
-        }
+        final String identifier = getMandatoryOption(option);
         if (CodeType.guess(identifier).isCRS) try {
             return CRS.forCode(identifier);
         } catch (NoSuchAuthorityCodeException e) {
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
new file mode 100644
index 0000000000..7d3deb6a5d
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
@@ -0,0 +1,105 @@
+/*
+ * 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.console;
+
+import java.util.EnumSet;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import org.apache.sis.setup.OptionKey;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStores;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.WritableGridCoverageResource;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.WritableAggregate;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.Classes;
+
+
+/**
+ * The "translate" sub-command.
+ * This command reads resources and rewrites them in another format.
+ * If more than one source file is specified, then all those files are 
aggregated in the output file.
+ * This is possible only if the output format supports the storage of an 
arbitrary number of resources.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+final class TranslateCommand extends CommandRunner {
+    /**
+     * Creates the {@code "translate"} sub-command.
+     *
+     * @param  commandIndex  index of the {@code arguments} element containing 
the {@code "translate"} command name, or -1 if none.
+     * @param  arguments     the command-line arguments provided by the user.
+     * @throws InvalidOptionException if an illegal option has been provided, 
or the option has an illegal value.
+     */
+    TranslateCommand(final int commandIndex, final String... arguments) throws 
InvalidOptionException {
+        super(commandIndex, arguments, EnumSet.of(Option.OUTPUT, 
Option.FORMAT, Option.HELP, Option.DEBUG));
+    }
+
+    /**
+     * Translates a file.
+     *
+     * @return 0 on success, or an exit code if the command failed for a 
reason other than an uncaught Java exception.
+     * @throws Exception if an error occurred while executing the sub-command.
+     */
+    @Override
+    public int run() throws Exception {
+        if (hasUnexpectedFileCount(1, Integer.MAX_VALUE)) {
+            return Command.INVALID_ARGUMENT_EXIT_CODE;
+        }
+        final String output = getMandatoryOption(Option.OUTPUT);
+        final String format = options.get(Option.FORMAT);
+        final var connector = new StorageConnector(Path.of(output));
+        connector.setOption(OptionKey.OPEN_OPTIONS, new StandardOpenOption[] {
+            StandardOpenOption.CREATE_NEW,
+            StandardOpenOption.WRITE
+        });
+        try (DataStore target = DataStores.openWritable(connector, format)) {
+            for (final String file : files) {
+                try (DataStore source = DataStores.open(file)) {
+                    if (target instanceof WritableAggregate) {
+                        ((WritableAggregate) target).add(source);
+                    } else if (target instanceof WritableGridCoverageResource) 
{
+                        write(source, (WritableGridCoverageResource) target);
+                    }
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Writes the given source to the given target.
+     * This method invokes itself recursively if the source is an aggregate.
+     */
+    private void write(final Resource source, final 
WritableGridCoverageResource target) throws DataStoreException {
+        if (source instanceof GridCoverageResource) {
+            target.write(((GridCoverageResource) source).read(null, null));
+        } else if (source instanceof Aggregate) {
+            for (final Resource component : ((Aggregate) source).components()) 
{
+                write(component, target);
+            }
+        } else {
+            Object id = source.getIdentifier().orElse(null);
+            if (id == null) id = Classes.getShortClassName(source);
+            err.println(Errors.format(Errors.Keys.CanNotCopy_1, id));
+        }
+    }
+}
diff --git a/optional/src/org.apache.sis.gui/bundle/bin/sis 
b/optional/src/org.apache.sis.gui/bundle/bin/sis
index 07f7afd957..1caa9c6359 100755
--- a/optional/src/org.apache.sis.gui/bundle/bin/sis
+++ b/optional/src/org.apache.sis.gui/bundle/bin/sis
@@ -18,7 +18,7 @@
 
 set -o errexit
 
-BASE_DIR="`dirname $0`/.."
+BASE_DIR="`dirname "$( readlink -e "$0"; )";`/.."
 SIS_DATA="${SIS_DATA:-$BASE_DIR/data}"
 export SIS_DATA
 
diff --git a/optional/src/org.apache.sis.gui/bundle/bin/sisfx 
b/optional/src/org.apache.sis.gui/bundle/bin/sisfx
index 5a8cda7447..4bbdfcd5b5 100755
--- a/optional/src/org.apache.sis.gui/bundle/bin/sisfx
+++ b/optional/src/org.apache.sis.gui/bundle/bin/sisfx
@@ -18,7 +18,7 @@
 
 set -o errexit
 
-BASE_DIR="`dirname $0`/.."
+BASE_DIR="`dirname "$( readlink -e "$0"; )";`/.."
 . "$BASE_DIR/conf/setenv.sh"
 
 SIS_DATA="${SIS_DATA:-$BASE_DIR/data}"

Reply via email to