sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1807432 - in /sis/release-test/maven/src/main/java/org/apache/sis/test: Integration.java referencing/ referencing/CoordinateOperationComparator.java referencing/package-info.java
Date Wed, 06 Sep 2017 00:15:06 GMT
Author: desruisseaux
Date: Wed Sep  6 00:15:06 2017
New Revision: 1807432

URL: http://svn.apache.org/viewvc?rev=1807432&view=rev
Log:
Initial commit of the code we used for benchmarking Apache SIS and Proj.4.

Added:
    sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/
    sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
      - copied, changed from r1807431, sis/release-test/maven/src/main/java/org/apache/sis/test/Integration.java
    sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java
  (with props)
Removed:
    sis/release-test/maven/src/main/java/org/apache/sis/test/Integration.java

Copied: sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
(from r1807431, sis/release-test/maven/src/main/java/org/apache/sis/test/Integration.java)
URL: http://svn.apache.org/viewvc/sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java?p2=sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java&p1=sis/release-test/maven/src/main/java/org/apache/sis/test/Integration.java&r1=1807431&r2=1807432&rev=1807432&view=diff
==============================================================================
--- sis/release-test/maven/src/main/java/org/apache/sis/test/Integration.java (original)
+++ sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/CoordinateOperationComparator.java
Wed Sep  6 00:15:06 2017
@@ -14,16 +14,399 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.test;
+package org.apache.sis.test.referencing;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Random;
+import org.opengis.geometry.Envelope;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.crs.GeographicCRS;
+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;
+import org.apache.sis.metadata.iso.extent.Extents;
+import org.apache.sis.internal.util.StandardDateFormat;
+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.metadata.ReferencingServices;
+import org.apache.sis.storage.gdal.Proj4;
 
 
 /**
- * Integration tests (empty for now).
+ * Compares coordinate operations performed with Apache SIS and {@literal Proj.4}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
  */
-public final class Integration {
+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();
+        }
+    }
+
+    /**
+     * Number of dimensions in all coordinates to be used for the tests.
+     */
+    private static final int DIM = 2;
+
+    /**
+     * Number of test coordinates along <var>x</var> and <var>y</var>
axes (must be a power of 2).
+     * The total number of coordinates used in each iteration will the the square of this
value.
+     */
+    private static final int NUM_PTS = 256;
+
+    /**
+     * Number of "transform projection - inverse projection" cycles to do.
+     * GIGS (Geospatial Integrity of Geoscience Applications) recommends 100 iterations.
+     */
+    private static final int NUM_LOOPS = 100;
+
+    /**
+     * Number of cycles to skip (for HotSpot warmup) before to collect statistics about performance.
+     * Note that statistics about accuracy are collected for all iterations, regardless this
value.
+     */
+    private static final int WARMUP_LOOPS = 10;
+
+    /**
+     * 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.
+     */
+    private final double[] inputs;
+
+    /**
+     * The array where to store results of coordinate operations.
+     * This array will be overwritten at each iteration.
+     */
+    private final double[] outputs;
+
+    /**
+     * {@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)
+     */
+    private final boolean isSourceGeographic, isTargetGeographic;
+
+    /**
+     * The coordinate operation defined by EPSG as provided by Apache SIS. This is the most
authoritative
+     * source of information that we have, since Apache SIS follows more closely EPSG definitions
than many
+     * other map projection libraries. However, axis order may be different than the order
expected by other
+     * implementations like Proj.4.
+     */
+    private final CoordinateOperation authoritative;
+
+    /**
+     * The coordinate operations to compare. The first operation should be the same than
{@link #authoritative},
+     * but with (<var>longitude</var>, <var>latitude</var>) axis
order. We use that axis order for avoiding
+     * complications with the use of Proj.4 or similar libraries.
+     */
+    private final CoordinateOperation[] normalized;
+
+    /**
+     * Where to write benchmark results.
+     */
+    private final PrintWriter out;
+
+    /**
+     * 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  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.
+     */
+    public CoordinateOperationComparator(final String outputFile, final int source, final
int target)
+            throws IOException, FactoryException, TransformException
+    {
+        this(outputFile,
+             CRS.findOperation(    CRS.forCode(          "EPSG:" + source),
+                                   CRS.forCode(          "EPSG:" + target), null),
+             Proj4.createOperation(Proj4.createCRS("+init=epsg:" + source + " +over", 2),
+                                   Proj4.createCRS("+init=epsg:" + target + " +over", 2),
true));
+    }
+
+    /**
+     * Creates a comparator for all the given coordinate operations.
+     * The first coordinate operation shall be the authoritative one as defined by EPSG,
+     * including <cite>domain of validity</cite> and <cite>positional accuracy</cite>
metadata.
+     * This is usually the operation created by Apache SIS. All other operations are implemented
by other libraries.
+     * 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.
+     */
+    @SuppressWarnings("UseOfSystemOutOrSystemErr")
+    private CoordinateOperationComparator(final String outputFile, CoordinateOperation...
operations)
+            throws IOException, FactoryException, TransformException
+    {
+        operations = operations.clone();
+        authoritative = operations[0];
+        final AbstractCRS sourceCRS = AbstractCRS.castOrCopy(authoritative.getSourceCRS());
+        final AbstractCRS targetCRS = AbstractCRS.castOrCopy(authoritative.getTargetCRS());
+        final AbstractCRS normalizedSource = sourceCRS.forConvention(AxesConvention.NORMALIZED);
+        final AbstractCRS normalizedTarget = targetCRS.forConvention(AxesConvention.NORMALIZED);
+        operations[0]      = CRS.findOperation(normalizedSource, normalizedTarget, null);
+        isSourceGeographic = (normalizedSource instanceof GeographicCRS);
+        isTargetGeographic = (normalizedTarget instanceof GeographicCRS);
+        normalized         = operations;
+        /*
+         * Get the operation domain of validity in the units of source CRS,
+         * then print a summary about the comparison we are about to do.
+         */
+        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);
+        /*
+         * Fills the input array with random coordinates.
+         * This array shall not be modified after construction.
+         */
+        final Random random = new Random();
+        inputs = new double[NUM_PTS * NUM_PTS * DIM];
+        final double minX   = domain.getMinimum(0);
+        final double minY   = domain.getMinimum(1);
+        final double scaleX = domain.getSpan(0) / NUM_PTS;
+        final double scaleY = domain.getSpan(1) / NUM_PTS;
+        int n = 0;
+        for (int j=0; j<NUM_PTS; j++) {
+            for (int i=0; i<NUM_PTS; i++) {
+                inputs[n++] = minX + scaleX * (i + random.nextDouble() - 0.5);
+                inputs[n++] = minY + scaleY * (j + random.nextDouble() - 0.5);
+            }
+        }
+        assert n == inputs.length : n;
+        for (int i = inputs.length; (i -= DIM) > 0;) {
+            final int j = random.nextInt(i) & ~1;
+            double x    = inputs[i  ];
+            double y    = inputs[i+1];
+            inputs[i  ] = inputs[j  ];
+            inputs[i+1] = inputs[j+1];
+            inputs[j  ] = x;
+            inputs[j+1] = y;
+        }
+        outputs = new double[inputs.length];
+    }
+
+    /**
+     * 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.
+     */
+    private static OperationMethod method(final CoordinateOperation op) {
+        if (op instanceof SingleOperation) {
+            return ((SingleOperation) op).getMethod();
+        }
+        if (op instanceof ConcatenatedOperation) {
+            for (CoordinateOperation c : ((ConcatenatedOperation) op).getOperations()) {
+                OperationMethod method = method(c);
+                if (method != null) return method;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 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.
+     */
+    public void run() throws TransformException, IOException {
+        compareOperationResults();
+        for (final CoordinateOperation op : normalized) {
+            measure(op);
+        }
+        if (out.checkError()) {
+            throw new IOException("Error while writing results file.");
+        }
+    }
+
+    /**
+     * 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 {
+        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    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();
+        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)");
+        for (int n=0; n<NUM_LOOPS; n++) {
+            final long t0 = System.nanoTime();
+            tr.transform(outputs, 0, outputs, 0, numPts);
+            final long t1 = System.nanoTime();
+            inverse.transform(outputs, 0, outputs, 0, numPts);
+            final long t2 = System.nanoTime();
+            collectDifferences(drift);
+            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 >= WARMUP_LOOPS) {
+                forwardTime  .accept(tf);
+                inverseTime  .accept(ti);
+                cumulatedTime.accept(tc);
+            }
+            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.");
+        for (int size = inputs.length / DIM; size >= 1; size /= 2) {
+            forwardTime.reset();
+            inverseTime.reset();
+            cumulatedTime.reset();
+            for (int i=0; i<10; i++) {
+                System.arraycopy(inputs, 0, outputs, 0, inputs.length);
+                long tf = 0, ti = 0, tc = 0;
+                for (int offset = 0; offset < inputs.length; offset += size * DIM) {
+                    final long t0 = System.nanoTime();
+                    tr.transform(outputs, offset, outputs, offset, size);
+                    final long t1 = System.nanoTime();
+                    inverse.transform(outputs, offset, outputs, offset, size);
+                    final long t2 = System.nanoTime();
+                    tf += (t1 - t0);
+                    ti += (t2 - t1);
+                    tc += (t2 - t0);
+                }
+                forwardTime  .accept(tf / (double) StandardDateFormat.NANOS_PER_MILLISECOND);
+                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,
+                    forwardTime.mean(),   forwardTime.standardDeviation(false),
+                    inverseTime.mean(),   inverseTime.standardDeviation(false),
+                    cumulatedTime.mean(), cumulatedTime.standardDeviation(false));
+            collectDifferences(drift);
+        }
+        out.printf("Verification: average difference = %g and maximal difference = %g%n",
drift.mean(), drift.maximum());
+    }
+
+    /**
+     * Adds distances between {@link #inputs} (expected coordinates) and {@link #outputs} (actual
coordinates) to
+     * the given statistics. This method is invoked after a full "forward operation - inverse
operation" cycle.
+     *
+     * @param  drift  where to add statistics about distances between expected and actual
coordinates.
+     */
+    private void collectDifferences(final Statistics drift) {
+        for (int i = inputs.length; (i -= DIM) >= 0;) {
+            drift.accept(distance(isSourceGeographic, inputs, outputs, i));
+        }
+    }
+
     /**
-     * Do not allow instantiation of this class.
+     * Returns an estimation of the distance between expected and actual coordinates.
+     *
+     * <p><strong>NOTE:</strong> this method assumes that {@link #DIM}
is equal to 2.
+     * If this is no longer the case, replace {@link Math#hypot(double, double)} call
+     * by {@link org.apache.sis.math.MathFunctions#magnitude(double...)}.</p>
+     *
+     * @param  expected  array of expected coordinates.
+     * @param  actual    array of actual coordinates.
+     * @param  i         index of the first ordinal value in {@code expected} and {@code
actual} arrays.
      */
-    private Integration() {
+    private static double distance(final boolean isGeographic, final double[] expected, final
double[] actual, int i) {
+        double d = Math.hypot(actual[i] - expected[i], actual[++i] - expected[i]);
+        if (isGeographic) {
+            d *= ReferencingServices.NAUTICAL_MILE * 60;
+        }
+        return d;
     }
 }

Added: sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java
URL: http://svn.apache.org/viewvc/sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java?rev=1807432&view=auto
==============================================================================
--- sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java
(added)
+++ sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java
[UTF-8] Wed Sep  6 00:15:06 2017
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Compares Apache SIS with other map projection libraries.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+package org.apache.sis.test.referencing;

Propchange: sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/release-test/maven/src/main/java/org/apache/sis/test/referencing/package-info.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message