sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1807904 - /sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
Date Sat, 09 Sep 2017 13:43:50 GMT
Author: desruisseaux
Date: Sat Sep  9 13:43:50 2017
New Revision: 1807904

URL: http://svn.apache.org/viewvc?rev=1807904&view=rev
Log:
Allow the benchmark to be run from the command line and improve formatting.

Modified:
    sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java

Modified: sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
URL: http://svn.apache.org/viewvc/sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java?rev=1807904&r1=1807903&r2=1807904&view=diff
==============================================================================
--- sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
(original)
+++ sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
Sat Sep  9 13:43:50 2017
@@ -16,9 +16,9 @@
  */
 package org.apache.sis.test.referencing;
 
-import java.io.FileWriter;
+import java.io.Flushable;
 import java.io.IOException;
-import java.io.PrintWriter;
+import java.io.PrintStream;
 import java.util.Random;
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.extent.GeographicBoundingBox;
@@ -28,7 +28,6 @@ import org.opengis.referencing.operation
 import org.opengis.util.FactoryException;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.math.Statistics;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
@@ -38,13 +37,21 @@ import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.referencing.crs.AbstractCRS;
 import org.apache.sis.referencing.cs.AxesConvention;
-import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
+import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.io.TableAppender;
 import org.apache.sis.storage.gdal.Proj4;
 
 
 /**
  * Compares coordinate operations performed with Apache SIS and {@literal Proj.4}.
+ * This class is designed for being run from the command line, potentially with output redirected
to a file.
+ * Example:
+ *
+ * <blockquote>{@code java org.apache.sis.test.referencing.CoordinateOperationComparator
1 --tabs > result.txt}</blockquote>
+ *
+ * The {@code --tabs} option is for using tabulations as column separator instead than formatted
outputs.
+ * This make importation in spreadsheets easier.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
@@ -52,26 +59,31 @@ import org.apache.sis.storage.gdal.Proj4
  * @module
  */
 public final class CoordinateOperationComparator {
-    public static void main(String[] args) throws Exception {
-        for (int n = 0; ; n++) {
-            final CoordinateOperationComparator c;
-            switch (n) {
-                 case  0: c = new CoordinateOperationComparator("Cylindrical Aqual Area (Spherical)",
4053, 3410); break;
-                 case  1: c = new CoordinateOperationComparator("Cylindrical Aqual Area",
            4326, 6933); break;
-                 case  2: c = new CoordinateOperationComparator("Pseudo-Mercator",      
             4326, 3857); break;
-                 case  3: c = new CoordinateOperationComparator("Mercator",             
             4326, 3395); break;
-                 case  4: c = new CoordinateOperationComparator("Lambert Conic Conformal",
           4269, 3978); break;
-                 case  5: c = new CoordinateOperationComparator("Polar stereographic",  
             4326, 3031); break;
-                 case  6: c = new CoordinateOperationComparator("Albert Equal Area",    
             4269, 5070); break;
-                 case  7: c = new CoordinateOperationComparator("Mercator 41 to Mercator",
           3994, 3395); break;
-                 case  8: c = new CoordinateOperationComparator("Tokyo to JGD2000",     
             4301, 4612); break;
-                 case  9: c = new CoordinateOperationComparator("Tokyo to JGD2000 in UTM
zone 54",    3095, 3100); break;
-                 case 10: c = new CoordinateOperationComparator("OSGB 1936 to ED50 (UKOOA)",
         4277, 4230); break;
-                 case 11: c = new CoordinateOperationComparator("Martinique 1938 to RGAF09",
         4625, 5489); break;
-                 case 12: c = new CoordinateOperationComparator("Stereographic to stereographic",
    2986, 7082); break;
-                 default: return;
-            }
-            c.run();
+    /**
+     * Creates one of the predefined tests identified by the given sequential number. All
pre-defined tests known
+     * to the {@link #main(String[]) method} are listed here. This list may be expanded in
any future version.
+     *
+     * @param  n  sequential number of the test to create.
+     * @return the pre-defined test identified by the given number, or {@code null} if none.
+     */
+    private static CoordinateOperationComparator create(final int n)
+            throws FactoryException, TransformException, IOException
+    {
+        switch (n) {
+             case  1: return new CoordinateOperationComparator("Cylindrical Equal Area (Spherical)",
4053, 3410);
+             case  2: return new CoordinateOperationComparator("Cylindrical Equal Area",
            4326, 6933);
+             case  3: return new CoordinateOperationComparator("Pseudo-Mercator",       
            4326, 3857);
+             case  4: return new CoordinateOperationComparator("Mercator",              
            4326, 3395);
+             case  5: return new CoordinateOperationComparator("Lambert Conic Conformal",
           4269, 3978);
+             case  6: return new CoordinateOperationComparator("Polar stereographic",   
            4326, 3031);
+             case  7: return new CoordinateOperationComparator("Albert Equal Area",     
            4269, 5070);
+             case  8: return new CoordinateOperationComparator("Mercator 41 to Mercator",
           3994, 3395);
+             case  9: return new CoordinateOperationComparator("Tokyo to JGD2000",      
            4301, 4612);
+             case 10: return new CoordinateOperationComparator("Tokyo to JGD2000 in UTM zone
54",    3095, 3100);
+             case 11: return new CoordinateOperationComparator("OSGB 1936 to ED50 (UKOOA)",
         4277, 4230);
+             case 12: return new CoordinateOperationComparator("Martinique 1938 to RGAF09",
         4625, 5489);
+             case 13: return new CoordinateOperationComparator("Stereographic to stereographic",
    2986, 7082);
+             default: return null;
         }
     }
 
@@ -100,7 +112,7 @@ public final class CoordinateOperationCo
 
     /**
      * The coordinates to transform as (x₀,y₀), (x₁,y₁), (x₂,y₂),
(x₃,y₃), <i>etc.</i> tupples.
-     * This array will be initialized by {@link #randomCoordinates()}, then never modified.
+     * This array will be initialized by the constructor, then never modified.
      */
     private final double[] inputs;
 
@@ -114,7 +126,7 @@ public final class CoordinateOperationCo
      * {@code true} if the CRS is geographic, or {@code false} if the CRS is projected.
      * If {@code true}, then distances will be estimated using the nautical mile as an approximation.
      *
-     * @see #distance(boolean, double, double)
+     * @see #distance(boolean, double[], double[], int)
      */
     private final boolean isSourceGeographic, isTargetGeographic;
 
@@ -134,27 +146,50 @@ public final class CoordinateOperationCo
     private final CoordinateOperation[] normalized;
 
     /**
+     * A title for the test to be run, for information purpose only.
+     */
+    private final String title;
+
+    /**
+     * Names of the implementation being tested, for information purpose only.
+     */
+    private final String[] implementationNames;
+
+    /**
      * Where to write benchmark results.
      */
-    private final PrintWriter out;
+    @SuppressWarnings("UseOfSystemOutOrSystemErr")
+    private static final PrintStream out = System.out;
+
+    /**
+     * {@code true} for using tabulations instead than formatting in tables.
+     * This is enabled by the {@code --tabs}} flag on the command-line.
+     */
+    private static boolean useTabulations;
+
+    /**
+     * {@code true} for flushing the output stream after each line when writing a table.
+     * This allows more immediate feedback to the user, but sometime break the table layout.
+     * We disable the immediate mode when writing to a file since the user would not see
it anyway.
+     */
+    private static boolean immediate;
 
     /**
      * Creates a comparator for coordinate operations between the given pair of EPSG codes.
      * Current implementations creates operations for Apache SIS and Proj.4 libraries,
      * but this list may be expanded in any future version.
      *
-     * @param  outputFile  name of the file where to write results, or {@code null} for
standard output.
-     *                     If non-null, a {@code ".txt"} extension will be added.
+     * @param  title       a title for this benchmark.
      * @param  source      EPSG code of source CRS.
      * @param  target      EPSG code of target CRS.
-     * @throws IOException         if an error occurred while creating the output file.
-     * @throws FactoryException    if an error occurred while instantiating CRS or coordinate
operation.
-     * @throws TransformException  if an error occurred while transforming coordinates.
+     * @throws FactoryException   if an error occurred while instantiating CRS or coordinate
operation.
+     * @throws TransformException if an error occurred while transforming coordinates.
+     * @throws IOException        if an error occurred while writing results.
      */
-    public CoordinateOperationComparator(final String outputFile, final int source, final
int target)
-            throws IOException, FactoryException, TransformException
+    public CoordinateOperationComparator(final String title, final int source, final int
target)
+            throws FactoryException, TransformException, IOException
     {
-        this(outputFile,
+        this(title, new String[] {"Apache SIS", "Proj.4"},
              CRS.findOperation(    CRS.forCode(          "EPSG:" + source),
                                    CRS.forCode(          "EPSG:" + target), null),
              Proj4.createOperation(Proj4.createCRS("+init=epsg:" + source + " +over", 2),
@@ -169,14 +204,16 @@ public final class CoordinateOperationCo
      * All operations except the first (authoritative) one shall use the (<var>longitude</var>,
<var>latitude</var>)
      * axis order, for simpler comparisons with Proj.4 and similar libraries.
      *
-     * @param  operations          the operations to compare. The first operation shall be
the authoritative one.
-     * @throws FactoryException    if an error occurred while creating a coordinate operation.
-     * @throws TransformException  if an error occurred while computing the domain of validity.
+     * @param  operations           the operations to compare. The first operation shall
be the authoritative one.
+     * @param  implementationNames  names of the implementation being tested, for information
purpose only.
+     * @throws FactoryException     if an error occurred while creating a coordinate operation.
+     * @throws TransformException   if an error occurred while computing the domain of validity.
      */
-    @SuppressWarnings("UseOfSystemOutOrSystemErr")
-    private CoordinateOperationComparator(final String outputFile, CoordinateOperation...
operations)
-            throws IOException, FactoryException, TransformException
+    private CoordinateOperationComparator(final String title, final String[] implementationNames,
+            CoordinateOperation... operations) throws FactoryException, TransformException,
IOException
     {
+        this.title = title;
+        this.implementationNames = implementationNames.clone();
         operations = operations.clone();
         authoritative = operations[0];
         final AbstractCRS sourceCRS = AbstractCRS.castOrCopy(authoritative.getSourceCRS());
@@ -194,12 +231,13 @@ public final class CoordinateOperationCo
         GeographicBoundingBox bbox = CRS.getGeographicBoundingBox(authoritative);
         bbox = Extents.intersection(bbox, new DefaultGeographicBoundingBox(-179, 179, -89,
89));
         final Envelope domain = Envelopes.transform(new GeneralEnvelope(bbox), normalizedSource);
-        out = (outputFile != null) ? new PrintWriter(new FileWriter(outputFile + ".txt"))
: new PrintWriter(System.out);
-        print("Source CRS", sourceCRS);
-        print("Target CRS", targetCRS);
-        print("Operation", authoritative);
-        print("Method", method(authoritative));
-        out.println("Domain:\t" + domain);
+        final Appendable table = newTable();
+        printIdentification(table, "Source CRS", sourceCRS);
+        printIdentification(table, "Target CRS", targetCRS);
+        printIdentification(table, "Operation", authoritative);
+        printIdentification(table, "Method", method(authoritative));
+        table.append(String.format("Domain:\t\t%s%n", domain));
+        flush(table);
         /*
          * Fills the input array with random coordinates.
          * This array shall not be modified after construction.
@@ -231,16 +269,6 @@ public final class CoordinateOperationCo
     }
 
     /**
-     * Prints the name of the given identified objects.
-     *
-     * @param label   label to write before the identified object.
-     * @param object  object for which to write the name.
-     */
-    private void print(final String label, final IdentifiedObject object) {
-        out.printf("%s:\t%s\t%s%n", label, IdentifiedObjects.toString(IdentifiedObjects.getIdentifier(object,
Citations.EPSG)), object.getName().getCode());
-    }
-
-    /**
      * Get the operation method of the given coordinate operation.
      * This is used for information purpose only.
      */
@@ -258,17 +286,42 @@ public final class CoordinateOperationCo
     }
 
     /**
+     * Compares the coordinate operation results of Apache SIS with other implementations.
+     * This comparisons is performed before to execute the actual benchmark, so it already
+     * causes some JVM warmup. For all comparisons, Apache SIS is taked as the reference.
+     */
+    private void compareOperationResults() throws TransformException, IOException {
+        out.printf("Difference in forward operation results between %s and other implementations:%n",
implementationNames[0]);
+        final Statistics stats = new Statistics("Difference");
+        final Appendable table = newTable();
+        table.append(String.format("Implementation\tDifference (m)\tStd. dev.%n"));
+        final int numPts = inputs.length / DIM;
+        normalized[0].getMathTransform().transform(inputs, 0, outputs, 0, numPts);
+        final double[] intermediate = new double[inputs.length];
+        for (int j=1; j<normalized.length; j++) {
+            normalized[j].getMathTransform().transform(inputs, 0, intermediate, 0, numPts);
+            for (int i = intermediate.length; (i -= DIM) >= 0;) {
+                stats.accept(distance(isTargetGeographic, outputs, intermediate, i));
+            }
+            table.append(String.format("%s\t%g\t%g%n", implementationNames[j], stats.mean(),
stats.standardDeviation(false)));
+            stats.reset();
+        }
+        flush(table);
+    }
+
+    /**
      * Runs the benchmarks. First, this method compare operation results without measuring
performance.
      * Then, this method performs "forward operation" followed by "inverse operation" one
hundred times,
      * measuring performances and drifts at each iteration.
      *
      * @throws TransformException if an error occurred while transforming coordinates.
-     * @throws IOException if an error occurred while writing results.
+     * @throws IOException        if an error occurred while writing results.
      */
     public void run() throws TransformException, IOException {
         compareOperationResults();
-        for (final CoordinateOperation op : normalized) {
-            measure(op);
+        for (int j=0; j<normalized.length; j++) {
+            out.printf("Testing %s with %s implementation%n", title, implementationNames[j]);
+            measure(normalized[j]);
         }
         if (out.checkError()) {
             throw new IOException("Error while writing results file.");
@@ -276,55 +329,24 @@ public final class CoordinateOperationCo
     }
 
     /**
-     * Compares the coordinate operation results of Apache SIS with other implementations.
-     */
-    private void compareOperationResults() throws TransformException {
-        final int numPts = inputs.length / DIM;
-        normalized[0].getMathTransform().transform(inputs, 0, outputs, 0, numPts);
-        final Statistics[] stats = new Statistics[normalized.length - 1];
-        for (int i=0; i<stats.length; i++) {
-            stats[i] = new Statistics("Difference");
-        }
-        out.println();
-        final double[] intermediate = new double[inputs.length];
-        for (int j=0; j<stats.length; j++) {
-            final Statistics s = stats[j];
-            normalized[j+1].getMathTransform().transform(inputs, 0, intermediate, 0, numPts);
-            for (int i = intermediate.length; (i -= DIM) >= 0;) {
-                s.accept(distance(isTargetGeographic, outputs, intermediate, i));
-            }
-        }
-        out.print("Difference in projection results (metres):");
-        for (final Statistics s : stats) {
-            out.printf("\t%g\t±%g", s.mean(), s.standardDeviation(false));
-        }
-        out.println();
-    }
-
-    /**
      * Measures performance and drift of the given coordinate operation.
      */
-    private void measure(final CoordinateOperation op) throws TransformException {
+    private void measure(final CoordinateOperation op) throws TransformException, IOException
{
         System.arraycopy(inputs, 0, outputs, 0, inputs.length);
         final int numPts                  = inputs.length / DIM;
         final MathTransform tr            = op.getMathTransform();
         final MathTransform inverse       = tr.inverse();
-        final Statistics    drift         = new Statistics("Drift");
+        final Statistics    drift         = new Statistics("Drift (m)");
         final Statistics    forwardTime   = new Statistics("Forward time (ms)");
         final Statistics    inverseTime   = new Statistics("Inverse time (ms)");
         final Statistics    cumulatedTime = new Statistics("Cumulated time (ms)");
-        out.println();
-        out.println("────────────────────────────────────────────────────────────────────────────────");
-        out.println("Measuring drift after " + NUM_LOOPS + " iterations for:");
+        out.println("Implementation defines the MathTransform as below:");
         out.println();
         out.println(tr);
-        if (tr instanceof AbstractMathTransform) {
-            out.println();
-            out.println("Internal:");
-            out.println(((AbstractMathTransform) tr).toString(Convention.INTERNAL));
-        }
         out.println();
-        out.println("Mean\tStd. dev.\tMinimum\tMaximum\tForward time (ms)\tInverse time (ms)");
+        out.println("Drift after up to " + NUM_LOOPS + " iterations:");
+        Appendable table = newTable();
+        table.append(String.format("Mean (m)\tStd. dev.\tMinimum (m)\tMaximum (m)\tForward
time (ms)\tInverse time (ms)%n"));
         for (int n=0; n<NUM_LOOPS; n++) {
             final long t0 = System.nanoTime();
             tr.transform(outputs, 0, outputs, 0, numPts);
@@ -335,7 +357,10 @@ public final class CoordinateOperationCo
             final double tf = (t1 - t0) / (double) StandardDateFormat.NANOS_PER_MILLISECOND;
             final double ti = (t2 - t1) / (double) StandardDateFormat.NANOS_PER_MILLISECOND;
             final double tc = (t2 - t0) / (double) StandardDateFormat.NANOS_PER_MILLISECOND;
-            out.printf("%g\t%g\t%g\t%g\t%g\t%g%n", drift.mean(), drift.standardDeviation(false),
drift.minimum(), drift.maximum(), tf, ti);
+
+            if (n != 0 && immediate) ((Flushable) table).flush();
+            table.append(String.format("%g\t%g\t%g\t%g\t%g\t%g%n",
+                    drift.mean(), drift.standardDeviation(false), drift.minimum(), drift.maximum(),
tf, ti));
             if (n >= WARMUP_LOOPS) {
                 forwardTime  .accept(tf);
                 inverseTime  .accept(ti);
@@ -343,12 +368,23 @@ public final class CoordinateOperationCo
             }
             drift.reset();
         }
-        out.printf("Average execution time (forward):\t%g\t± %g%n",     forwardTime.mean(),
  inverseTime.standardDeviation(false));
-        out.printf("Average execution time (inverse):\t%g\t± %g%n",     inverseTime.mean(),
  inverseTime.standardDeviation(false));
-        out.printf("Average execution time (cumulated):\t%g\t± %g%n", cumulatedTime.mean(),
cumulatedTime.standardDeviation(false));
-        out.println();
-        out.println("Performance (ms):");
-        out.println("Block size\tForward\tStd. dev.\tInverse\tStd. dev.\tCumulated\tStd.
dev.");
+        flush(table);
+        out.println("Average execution time:");
+        table = newTable();
+        table.append(String.format("\tMean (ms)\tStd. dev%n"));
+        table.append(String.format("Forward:\t%g\t%g%n",   forwardTime.mean(),   forwardTime.standardDeviation(false)));
+        table.append(String.format("Inverse:\t%g\t%g%n",   inverseTime.mean(),   inverseTime.standardDeviation(false)));
+        table.append(String.format("Cumulated:\t%g\t%g%n", cumulatedTime.mean(), cumulatedTime.standardDeviation(false)));
+        flush(table);
+        /*
+         * Test with decreasing amount of coordinates transformed in one method call.
+         * This increase the cost of determining which operation to execute.
+         * We measure performance degradation caused by this increasing cost.
+         */
+        out.println("Performance with decreasing amount of coordinates processed in one method
call:");
+        table = newTable();
+        table.append(String.format("Block size\tForward time (ms)\tStd. dev.\tInverse time
(ms)\tStd. dev.\tCumulated (ms)\tStd. dev.%n"));
+        boolean first = true;
         for (int size = inputs.length / DIM; size >= 1; size /= 2) {
             forwardTime.reset();
             inverseTime.reset();
@@ -370,13 +406,19 @@ public final class CoordinateOperationCo
                 inverseTime  .accept(ti / (double) StandardDateFormat.NANOS_PER_MILLISECOND);
                 cumulatedTime.accept(tc / (double) StandardDateFormat.NANOS_PER_MILLISECOND);
             }
-            out.printf("%d\t%g\t%g\t%g\t%g\t%g\t%g\n", size,
+            if (!first && immediate) ((Flushable) table).flush();
+            table.append(String.format("%d\t%g\t%g\t%g\t%g\t%g\t%g\n", size,
                     forwardTime.mean(),   forwardTime.standardDeviation(false),
                     inverseTime.mean(),   inverseTime.standardDeviation(false),
-                    cumulatedTime.mean(), cumulatedTime.standardDeviation(false));
+                    cumulatedTime.mean(), cumulatedTime.standardDeviation(false)));
             collectDifferences(drift);
+            first = false;
+        }
+        flush(table);
+        if (drift.maximum() > Formulas.LINEAR_TOLERANCE) {
+            out.println("WARNING: large drift in above performance check");
+            out.println(drift);
         }
-        out.printf("Verification: average difference = %g and maximal difference = %g%n",
drift.mean(), drift.maximum());
     }
 
     /**
@@ -409,4 +451,117 @@ public final class CoordinateOperationCo
         }
         return d;
     }
+
+    /**
+     * Returns the destination where to write next tabular data.
+     */
+    private static Appendable newTable() {
+        if (useTabulations) return out;
+        TableAppender table = new TableAppender(out);
+        table.appendHorizontalSeparator();
+        return table;
+    }
+
+    /**
+     * Flushes the output created by {@link #newTable()} and append a new line.
+     * After this method call, the table should not be used anymore.
+     */
+    private static void flush(final Appendable table) throws IOException {
+        if (table instanceof TableAppender) {
+            ((TableAppender) table).appendHorizontalSeparator();
+        }
+        ((Flushable) table).flush();
+        out.println();
+    }
+
+    /**
+     * Prints the name of the given identified objects.
+     *
+     * @param label   label to write before the identified object.
+     * @param object  object for which to write the name.
+     */
+    private static void printIdentification(final Appendable table, final String label, final
IdentifiedObject object) throws IOException {
+        String id = IdentifiedObjects.toString(IdentifiedObjects.getIdentifier(object, Citations.EPSG));
+        table.append(String.format("%s:\t%s\t%s%n", label, (id != null) ? id : "", object.getName().getCode()));
+    }
+
+    /**
+     * Runs the benchmark identified by the given number. Each number identifies a particular
pair of source
+     * and target CRS. For example the test #1 applies the "Cylindrical Equal Area (Spherical)"
projection.
+     * See source code of this class for the list of available tests.
+     *
+     * @param  args  number of the test to execute.
+     * @throws FactoryException   if an error occurred while instantiating CRS or coordinate
operation.
+     * @throws TransformException if an error occurred while transforming coordinates.
+     * @throws IOException        if an error occurred while writing results.
+     */
+    public static void main(final String[] args) throws FactoryException, TransformException,
IOException {
+        /*
+         * Where to write error messages or information. While this stream is called "the
error stream", it is actually
+         * also used for printing information (for example progress) that we don't want to
include in the result file.
+         * Note that this is also the stream used by {@code java.util.logging}.
+         */
+        @SuppressWarnings("UseOfSystemOutOrSystemErr")
+        final PrintStream info = System.err;
+        immediate = (System.console() != null);
+
+        String error = null;
+        int sourceCRS = 0, targetCRS = 0;
+        for (final String p : args) {
+            if (p.equals("--tabs")) {
+                useTabulations = true;
+            } else if (p.startsWith("--")) {
+                error = String.format("Unrecognized option: %s", p);
+                break;
+            } else {
+                final int n;
+                try {
+                    n = Integer.parseInt(args[0]);
+                } catch (NumberFormatException e) {
+                    error = String.format("Invalid test number or EPSG code: %s", e.getLocalizedMessage());
+                    break;
+                }
+                if (n <= 0) {
+                    error = String.format("Invalid test number or EPSG code: %d", n);
+                    break;
+                }
+                if (sourceCRS == 0) {
+                    sourceCRS = n;
+                } else if (targetCRS == 0) {
+                    targetCRS = n;
+                } else {
+                    error = "Too many EPSG codes (expected 2).";
+                    break;
+                }
+            }
+        }
+        /*
+         * If there is two numbers, they are interpreted as EPSG codes of source and target
CRS respectively.
+         * But if there is only one number (targetCRS == 0), then the source CRS is interpreted
as the number
+         * of a pre-defined test.
+         */
+        CoordinateOperationComparator c = null;
+        if (error == null) {
+            if (sourceCRS == 0) {
+                error = String.format("Usage: one or two numbers%n"
+                        + "  [sequential number of pre-defined test]%n"
+                        + "  [EPSG code of source CRS] [EPSG code of targetCRS]");
+            } else if (targetCRS == 0) {
+                info.printf("Initializing pre-defined test #%d...%n", sourceCRS);
+                c = create(sourceCRS);
+                if (c == null) {
+                    error = "Error: no such pre-defined test.";
+                }
+            } else {
+                String name = String.format("EPSG:%d to EPSG:%d", sourceCRS, targetCRS);
+                info.printf("Initializing test for %s...%n", name);
+                c = new CoordinateOperationComparator(name, sourceCRS, targetCRS);
+            }
+        }
+        if (c == null) {
+            info.println(error);
+            System.exit(1);
+        }
+        c.run();
+    }
 }



Mime
View raw message