sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1799773 - in /sis/branches/JDK8: core/sis-feature/ core/sis-feature/src/main/java/org/apache/sis/internal/feature/ core/sis-utility/src/main/java/org/apache/sis/math/ ide-project/NetBeans/nbproject/ storage/sis-shapefile/ storage/sis-stora...
Date Sat, 24 Jun 2017 13:55:52 GMT
Author: desruisseaux
Date: Sat Jun 24 13:55:51 2017
New Revision: 1799773

URL: http://svn.apache.org/viewvc?rev=1799773&view=rev
Log:
Avoid direct dependencies to ESRI Geometry API. Leave the choice to user between ESRI, JTS and Java2D.

Added:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
      - copied, changed from r1799451, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
      - copied, changed from r1799451, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java
      - copied, changed from r1799451, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
Removed:
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/GroupPointsAsPolylineOperation.java
Modified:
    sis/branches/JDK8/core/sis-feature/pom.xml
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
    sis/branches/JDK8/ide-project/NetBeans/nbproject/genfiles.properties
    sis/branches/JDK8/ide-project/NetBeans/nbproject/project.xml
    sis/branches/JDK8/storage/sis-shapefile/pom.xml
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/TimeEncoding.java
    sis/branches/JDK8/storage/sis-xmlstore/pom.xml
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/GroupAsPolylineOperation.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Reader.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java

Modified: sis/branches/JDK8/core/sis-feature/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/pom.xml?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/pom.xml (original)
+++ sis/branches/JDK8/core/sis-feature/pom.xml Sat Jun 24 13:55:51 2017
@@ -122,6 +122,7 @@ Representations of geographic features.
     <dependency>
       <groupId>com.esri.geometry</groupId>
       <artifactId>esri-geometry-api</artifactId>
+      <scope>provided</scope>
     </dependency>
 
     <!-- Test dependencies -->

Copied: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java (from r1799451, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java?p2=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java&p1=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java&r1=1799451&r2=1799773&rev=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -16,23 +16,20 @@
  */
 package org.apache.sis.internal.feature;
 
-import java.util.logging.Level;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.util.Iterator;
 import com.esri.core.geometry.Geometry;
 import com.esri.core.geometry.Envelope2D;
+import com.esri.core.geometry.MultiPath;
+import com.esri.core.geometry.Polyline;
+import com.esri.core.geometry.Point;
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.util.Static;
-import org.apache.sis.util.logging.Logging;
-import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.math.Vector;
 
 
 /**
- * Utility methods on geometric objects defined in libraries outside Apache SIS.
+ * Centralizes some usages of ESRI geometry API by Apache SIS.
  * We use this class for isolating dependencies from the {@code org.apache.feature} package
  * to ESRI's API or to Java Topology Suite (JTS) API.
- * This gives us a single place to review if we want to support different geometry libraries,
- * or if Apache SIS come with its own implementation.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -40,110 +37,130 @@ import org.apache.sis.internal.system.Lo
  * @since   0.7
  * @module
  */
-public final class Geometries extends Static {
+final class ESRI extends Geometries {
     /**
-     * The geometry object from Java Topology Suite (JTS),
-     * or {@code null} if the JTS library is not on the classpath.
+     * Creates the singleton instance.
      */
-    private static final Class<?> JTS;
+    ESRI() {
+        super(Geometry.class, Point.class);
+    }
 
     /**
-     * Getter methods on JTS envelopes, or {@code null} if the JTS library is not on the classpath.
-     * Each methods take no argument and return a {@code double} value.
+     * If the given object is an ESRI geometry and its envelope is non-empty, returns
+     * that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
+     *
+     * @param  geometry  the geometry from which to get the envelope, or {@code null}.
+     * @return the envelope of the given object, or {@code null} if the object is not
+     *         a recognized geometry or its envelope is empty.
      */
-    private static final Method INTERNAL, MIN_X, MIN_Y, MAX_X, MAX_Y;
+    @Override
+    final GeneralEnvelope tryGetEnvelope(final Object geometry) {
+        if (geometry instanceof Geometry) {
+            final Envelope2D bounds = new Envelope2D();
+            ((Geometry) geometry).queryEnvelope2D(bounds);
+            if (!bounds.isEmpty()) {                                     // Test if there is NaN values.
+                final GeneralEnvelope env = new GeneralEnvelope(2);
+                env.setRange(0, bounds.xmin, bounds.xmax);
+                env.setRange(1, bounds.ymin, bounds.ymax);
+                return env;
+            }
+        }
+        return null;
+    }
 
-    static {
-        Class<?> type;
-        Method genv, xmin, ymin, xmax, ymax;
-        try {
-            final Class<?> envt;
-            type = Class.forName("com.vividsolutions.jts.geom.Geometry");
-            genv = type.getMethod("getEnvelopeInternal", (Class[]) null);
-            envt = genv.getReturnType();
-            xmin = envt.getMethod("getMinX", (Class[]) null);
-            ymin = envt.getMethod("getMinY", (Class[]) null);
-            xmax = envt.getMethod("getMaxX", (Class[]) null);
-            ymax = envt.getMethod("getMaxY", (Class[]) null);
-        } catch (ClassNotFoundException | NoSuchMethodException e) {
-            Logging.getLogger(Loggers.GEOMETRY).log(Level.CONFIG, e.toString());
-            type = null;
-            genv = null;
-            xmin = null;
-            xmax = null;
-            ymin = null;
-            ymax = null;
+    /**
+     * If the given point is an implementation of this library, returns its coordinate.
+     * Otherwise returns {@code null}. If non-null, the returned array may have a length of 2 or 3.
+     */
+    @Override
+    final double[] tryGetCoordinate(final Object point) {
+        if (point instanceof Point) {
+            final Point pt = (Point) point;
+            final double z = pt.getZ();
+            final double[] coord;
+            if (Double.isNaN(z)) {
+                coord = new double[2];
+            } else {
+                coord = new double[3];
+                coord[2] = z;
+            }
+            coord[1] = pt.getY();
+            coord[0] = pt.getX();
+            return coord;
         }
-        JTS = type;
-        INTERNAL = genv;
-        MIN_X = xmin;
-        MIN_Y = ymin;
-        MAX_X = xmax;
-        MAX_Y = ymax;
+        return null;
     }
 
     /**
-     * Do not allow instantiation of this class.
+     * Creates a two-dimensional point from the given coordinate.
      */
-    private Geometries() {
+    @Override
+    public Object createPoint(double x, double y) {
+        return new Point(x, y, Double.NaN);
     }
 
     /**
-     * Returns {@code true} if the given type is one of the type known to Apache SIS.
-     *
-     * @param  type  the type to verify.
-     * @return {@code true} if the given type is one of the geometry type known to SIS.
+     * Creates a polyline from the given ordinate values.
+     * Each {@link Double#NaN} ordinate value start a new path.
+     * The implementation returned by this method must be an instance of {@link #rootClass}.
      */
-    public static boolean isKnownType(final Class<?> type) {
-        return Geometry.class.isAssignableFrom(type) || (JTS != null && JTS.isAssignableFrom(type));
+    @Override
+    public Object createPolyline(final int dimension, final Vector ordinates) {
+        if (dimension != 2) {
+            throw unsupported(dimension);
+        }
+        final Polyline path = new Polyline();
+        final int size = ordinates.size();
+        boolean lineTo = false;
+        for (int i=0; i<size;) {
+            final double x = ordinates.doubleValue(i++);
+            final double y = ordinates.doubleValue(i++);
+            if (Double.isNaN(x) || Double.isNaN(y)) {
+                lineTo = false;
+            } else if (lineTo) {
+                path.lineTo(x, y);
+            } else {
+                path.startPath(x, y);
+                lineTo = true;
+            }
+        }
+        return path;
     }
 
     /**
-     * If the given object is one of the recognized type and its envelope is non-empty,
-     * returns that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
+     * Merges a sequence of points or paths if the first instance is an implementation of this library.
      *
-     * @param  geometry  the geometry from which to get the envelope, or {@code null}.
-     * @return the envelope of the given object, or {@code null} if the object is not
-     *         a recognized geometry or its envelope is empty.
+     * @throws ClassCastException if an element in the iterator is not a JTS geometry.
      */
-    public static GeneralEnvelope getEnvelope(final Object geometry) {
-        final double xmin, ymin, xmax, ymax;
-        if (geometry instanceof Geometry) {
-            final Envelope2D bounds = new Envelope2D();
-            ((Geometry) geometry).queryEnvelope2D(bounds);
-            if (bounds.isEmpty()) {                                     // Test if there is NaN values.
-                return null;
-            }
-            xmin = bounds.xmin;
-            ymin = bounds.ymin;
-            xmax = bounds.xmax;
-            ymax = bounds.ymax;
-        } else if (JTS != null && JTS.isInstance(geometry)) {
-            try {
-                final Object env = INTERNAL.invoke(geometry, (Object[]) null);
-                xmin = (Double) MIN_X.invoke(env, (Object[]) null);
-                ymin = (Double) MIN_Y.invoke(env, (Object[]) null);
-                xmax = (Double) MAX_X.invoke(env, (Object[]) null);
-                ymax = (Double) MAX_Y.invoke(env, (Object[]) null);
-            } catch (ReflectiveOperationException e) {
-                if (e instanceof InvocationTargetException) {
-                    final Throwable cause = e.getCause();
-                    if (cause instanceof RuntimeException) {
-                        throw (RuntimeException) cause;
-                    }
-                    if (cause instanceof Error) {
-                        throw (Error) cause;
+    @Override
+    final Object tryMergePolylines(Object next, final Iterator<?> polylines) {
+        if (!(next instanceof MultiPath || next instanceof Point)) {
+            return null;
+        }
+        final Polyline path = new Polyline();
+        boolean lineTo = false;
+        for (;; next = polylines.next()) {
+            if (next != null) {
+                if (next instanceof Point) {
+                    final double x = ((Point) next).getX();
+                    final double y = ((Point) next).getY();
+                    if (Double.isNaN(x) || Double.isNaN(y)) {
+                        lineTo = false;
+                    } else if (lineTo) {
+                        path.lineTo(x, y);
+                    } else {
+                        path.startPath(x, y);
+                        lineTo = true;
                     }
+                } else {
+                    path.add((MultiPath) next, false);
+                    lineTo = false;
                 }
-                // Should never happen unless JTS's API changed.
-                throw (Error) new IncompatibleClassChangeError(e.toString()).initCause(e);
             }
-        } else {
-            return null;
+            if (!polylines.hasNext()) {         // Should be part of the 'for' instruction, but we need
+                break;                          // to skip this condition during the first iteration.
+            }
         }
-        final GeneralEnvelope env = new GeneralEnvelope(2);
-        env.setRange(0, xmin, xmax);
-        env.setRange(1, ymin, ymax);
-        return env;
+        return path;
     }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -16,15 +16,13 @@
  */
 package org.apache.sis.internal.feature;
 
+import java.util.Iterator;
 import java.util.logging.Level;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import com.esri.core.geometry.Geometry;
-import com.esri.core.geometry.Envelope2D;
-import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.util.Static;
+import java.util.logging.LogRecord;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.math.Vector;
 
 
 /**
@@ -40,110 +38,200 @@ import org.apache.sis.internal.system.Lo
  * @since   0.7
  * @module
  */
-public final class Geometries extends Static {
+public abstract class Geometries {
+    /*
+     * Registers all supported library implementations. Those libraries are optional
+     * (users will typically put at most one on their classpath).
+     */
+    static {
+        register("Java2D");
+        register("JTS");
+        register("ESRI");       // Default implementation if other libraries are also present.
+    }
+
     /**
-     * The geometry object from Java Topology Suite (JTS),
-     * or {@code null} if the JTS library is not on the classpath.
+     * The root geometry class.
      */
-    private static final Class<?> JTS;
+    public final Class<?> rootClass;
 
     /**
-     * Getter methods on JTS envelopes, or {@code null} if the JTS library is not on the classpath.
-     * Each methods take no argument and return a {@code double} value.
+     * The class for a point.
      */
-    private static final Method INTERNAL, MIN_X, MIN_Y, MAX_X, MAX_Y;
+    public final Class<?> pointClass;
 
-    static {
-        Class<?> type;
-        Method genv, xmin, ymin, xmax, ymax;
+    /**
+     * The default geometry implementation to use. Unmodifiable after class initialization.
+     */
+    private static Geometries implementation;
+
+    /**
+     * The fallback implementation to use if the default one is not available.
+     */
+    private final Geometries fallback;
+
+    /**
+     * Creates a new adapter for the given root geometry class.
+     */
+    Geometries(final Class<?> rootClass, final Class<?> pointClass) {
+        this.rootClass  = rootClass;
+        this.pointClass = pointClass;
+        fallback = implementation;
+    }
+
+    /**
+     * Registers the library implementation of the given name (JTS or ESRI) if present; ignore otherwise.
+     * The given name shall be the simple name of a {@code Geometries} subclass in the same package.
+     * The last registered library will be the default implementation.
+     */
+    private static void register(final String name) {
+        String classname = Geometries.class.getName();
+        classname = classname.substring(0, classname.lastIndexOf('.')+1).concat(name);
         try {
-            final Class<?> envt;
-            type = Class.forName("com.vividsolutions.jts.geom.Geometry");
-            genv = type.getMethod("getEnvelopeInternal", (Class[]) null);
-            envt = genv.getReturnType();
-            xmin = envt.getMethod("getMinX", (Class[]) null);
-            ymin = envt.getMethod("getMinY", (Class[]) null);
-            xmax = envt.getMethod("getMaxX", (Class[]) null);
-            ymax = envt.getMethod("getMaxY", (Class[]) null);
-        } catch (ClassNotFoundException | NoSuchMethodException e) {
-            Logging.getLogger(Loggers.GEOMETRY).log(Level.CONFIG, e.toString());
-            type = null;
-            genv = null;
-            xmin = null;
-            xmax = null;
-            ymin = null;
-            ymax = null;
+            implementation = (Geometries) Class.forName(classname).newInstance();
+        } catch (ReflectiveOperationException | LinkageError e) {
+            LogRecord record = Resources.forLocale(null).getLogRecord(Level.CONFIG,
+                    Resources.Keys.OptionalLibraryNotFound_2, name, e.toString());
+            record.setLoggerName(Loggers.GEOMETRY);
+            Logging.log(Geometries.class, "register", record);
         }
-        JTS = type;
-        INTERNAL = genv;
-        MIN_X = xmin;
-        MIN_Y = ymin;
-        MAX_X = xmax;
-        MAX_Y = ymax;
     }
 
     /**
-     * Do not allow instantiation of this class.
+     * Returns an accessor to the default geometry library implementation in use.
+     *
+     * @return the default geometry implementation.
      */
-    private Geometries() {
+    public static Geometries implementation() {
+        return implementation;
     }
 
     /**
-     * Returns {@code true} if the given type is one of the type known to Apache SIS.
+     * Returns {@code true} if the given type is one of the types known to Apache SIS.
      *
      * @param  type  the type to verify.
      * @return {@code true} if the given type is one of the geometry type known to SIS.
      */
     public static boolean isKnownType(final Class<?> type) {
-        return Geometry.class.isAssignableFrom(type) || (JTS != null && JTS.isAssignableFrom(type));
+        for (Geometries g = implementation; g != null; g = g.fallback) {
+            if (g.rootClass.isAssignableFrom(type)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * If the given point is an implementation of this library, returns its coordinate.
+     * Otherwise returns {@code null}.
+     */
+    abstract double[] tryGetCoordinate(Object point);
+
+    /**
+     * If the given object is one of the recognized point implementation, returns its coordinate.
+     * Otherwise returns {@code null}. If non-null, the returned array may have a length of 2 or 3.
+     * If the CRS is geographic, then the (x,y) values should be (longitude, latitude) for compliance
+     * with usage in ESRI and JTS libraries.
+     *
+     * @param  point  the point from which to get the coordinate, or {@code null}.
+     * @return the coordinate of the given point as an array of length 2 or 3,
+     *         or {@code null} if the given object is not a recognized implementation.
+     *
+     * @see #createPoint(double, double)
+     */
+    public static double[] getCoordinate(final Object point) {
+        for (Geometries g = implementation; g != null; g = g.fallback) {
+            double[] coord = g.tryGetCoordinate(point);
+            if (coord != null) return coord;
+        }
+        return null;
     }
 
     /**
+     * If the given geometry is the type supported by this {@code Geometries} instance,
+     * returns its envelope if non-empty. Otherwise returns {@code null}. We currently
+     * do not distinguish the reasons why this method may return null.
+     */
+    abstract GeneralEnvelope tryGetEnvelope(Object geometry);
+
+    /**
      * If the given object is one of the recognized type and its envelope is non-empty,
      * returns that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
      *
      * @param  geometry  the geometry from which to get the envelope, or {@code null}.
-     * @return the envelope of the given object, or {@code null} if the object is not
-     *         a recognized geometry or its envelope is empty.
+     * @return the envelope of the given geometry, or {@code null} if the given object
+     *         is not a recognized geometry or its envelope is empty.
      */
     public static GeneralEnvelope getEnvelope(final Object geometry) {
-        final double xmin, ymin, xmax, ymax;
-        if (geometry instanceof Geometry) {
-            final Envelope2D bounds = new Envelope2D();
-            ((Geometry) geometry).queryEnvelope2D(bounds);
-            if (bounds.isEmpty()) {                                     // Test if there is NaN values.
-                return null;
-            }
-            xmin = bounds.xmin;
-            ymin = bounds.ymin;
-            xmax = bounds.xmax;
-            ymax = bounds.ymax;
-        } else if (JTS != null && JTS.isInstance(geometry)) {
-            try {
-                final Object env = INTERNAL.invoke(geometry, (Object[]) null);
-                xmin = (Double) MIN_X.invoke(env, (Object[]) null);
-                ymin = (Double) MIN_Y.invoke(env, (Object[]) null);
-                xmax = (Double) MAX_X.invoke(env, (Object[]) null);
-                ymax = (Double) MAX_Y.invoke(env, (Object[]) null);
-            } catch (ReflectiveOperationException e) {
-                if (e instanceof InvocationTargetException) {
-                    final Throwable cause = e.getCause();
-                    if (cause instanceof RuntimeException) {
-                        throw (RuntimeException) cause;
-                    }
-                    if (cause instanceof Error) {
-                        throw (Error) cause;
+        for (Geometries g = implementation; g != null; g = g.fallback) {
+            GeneralEnvelope env = g.tryGetEnvelope(geometry);
+            if (env != null) return env;
+        }
+        return null;
+    }
+
+    /**
+     * Creates a two-dimensional point from the given coordinate. If the CRS is geographic, then the
+     * (x,y) values should be (longitude, latitude) for compliance with usage in ESRI and JTS libraries.
+     *
+     * @param  x  the first ordinate value.
+     * @param  y  the second ordinate value.
+     * @return the point for the given ordinate values.
+     *
+     * @see #getCoordinate(Object)
+     */
+    public abstract Object createPoint(double x, double y);
+
+    /**
+     * Creates a path or polyline from the given ordinate values.
+     * Each {@link Double#NaN} ordinate value start a new path.
+     * The implementation returned by this method is an instance of {@link #rootClass}.
+     *
+     * @param  dimension  the number of dimensions (2 or 3).
+     * @param  ordinates  sequence of (x,y) or (x,y,z) tuples.
+     * @return the geometric object for the given points.
+     * @throws UnsupportedOperationException if the geometry library can not create the requested path.
+     */
+    public abstract Object createPolyline(int dimension, Vector ordinates);
+
+    /**
+     * Merges a sequence of polyline instances if the first instance is an implementation of this library.
+     *
+     * @param  first      the first instance to merge.
+     * @param  polylines  the second and subsequent instances to merge.
+     * @return the merged polyline, or {@code null} if the first instance is not an implementation of this library.
+     * @throws ClassCastException if an element in the iterator is not an implementation of this library.
+     */
+    abstract Object tryMergePolylines(Object first, Iterator<?> polylines);
+
+    /**
+     * Merges a sequence of points or polylines into a single polyline instances.
+     * Each previous polyline will be a separated path in the new polyline instances.
+     * The implementation returned by this method is an instance of {@link #rootClass}.
+     *
+     * @param  paths  the points or polylines to merge in a single polyline object.
+     * @return the merged polyline, or {@code null} if the given iterator has no element.
+     * @throws ClassCastException if not all elements in the given iterator are instances of the same library.
+     */
+    public static Object mergePolylines(final Iterator<?> paths) {
+        while (paths.hasNext()) {
+            final Object first = paths.next();
+            if (first != null) {
+                for (Geometries g = implementation; g != null; g = g.fallback) {
+                    final Object merged = g.tryMergePolylines(first, paths);
+                    if (merged != null) {
+                        return merged;
                     }
                 }
-                // Should never happen unless JTS's API changed.
-                throw (Error) new IncompatibleClassChangeError(e.toString()).initCause(e);
+                throw unsupported(2);
             }
-        } else {
-            return null;
         }
-        final GeneralEnvelope env = new GeneralEnvelope(2);
-        env.setRange(0, xmin, xmax);
-        env.setRange(1, ymin, ymax);
-        return env;
+        return null;
+    }
+
+    /**
+     * Returns an error message for an unsupported geometry object.
+     *
+     * @param  dimension  number of dimensions (2 or 3) requested for the geometry object.
+     */
+    static UnsupportedOperationException unsupported(final int dimension) {
+        return new UnsupportedOperationException(Resources.format(Resources.Keys.UnsupportedGeometryObject_1, dimension));
     }
 }

Copied: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java (from r1799451, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java?p2=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java&p1=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java&r1=1799451&r2=1799773&rev=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -16,115 +16,66 @@
  */
 package org.apache.sis.internal.feature;
 
-import java.util.logging.Level;
-import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
 import java.lang.reflect.Method;
-import com.esri.core.geometry.Geometry;
-import com.esri.core.geometry.Envelope2D;
+import java.lang.reflect.InvocationTargetException;
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.util.Static;
-import org.apache.sis.util.logging.Logging;
-import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.math.Vector;
 
 
 /**
- * Utility methods on geometric objects defined in libraries outside Apache SIS.
+ * Centralizes some usages of JTS geometry API by Apache SIS.
  * We use this class for isolating dependencies from the {@code org.apache.feature} package
  * to ESRI's API or to Java Topology Suite (JTS) API.
- * This gives us a single place to review if we want to support different geometry libraries,
- * or if Apache SIS come with its own implementation.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
  * @since   0.7
  * @module
+ *
+ * @todo avoid use of reflection and use JTS API directly after JTS released
+ *       a new version of the library under BSD-like license.
  */
-public final class Geometries extends Static {
-    /**
-     * The geometry object from Java Topology Suite (JTS),
-     * or {@code null} if the JTS library is not on the classpath.
-     */
-    private static final Class<?> JTS;
-
+final class JTS extends Geometries {
     /**
-     * Getter methods on JTS envelopes, or {@code null} if the JTS library is not on the classpath.
+     * Getter methods on JTS envelopes.
      * Each methods take no argument and return a {@code double} value.
      */
-    private static final Method INTERNAL, MIN_X, MIN_Y, MAX_X, MAX_Y;
-
-    static {
-        Class<?> type;
-        Method genv, xmin, ymin, xmax, ymax;
-        try {
-            final Class<?> envt;
-            type = Class.forName("com.vividsolutions.jts.geom.Geometry");
-            genv = type.getMethod("getEnvelopeInternal", (Class[]) null);
-            envt = genv.getReturnType();
-            xmin = envt.getMethod("getMinX", (Class[]) null);
-            ymin = envt.getMethod("getMinY", (Class[]) null);
-            xmax = envt.getMethod("getMaxX", (Class[]) null);
-            ymax = envt.getMethod("getMaxY", (Class[]) null);
-        } catch (ClassNotFoundException | NoSuchMethodException e) {
-            Logging.getLogger(Loggers.GEOMETRY).log(Level.CONFIG, e.toString());
-            type = null;
-            genv = null;
-            xmin = null;
-            xmax = null;
-            ymin = null;
-            ymax = null;
-        }
-        JTS = type;
-        INTERNAL = genv;
-        MIN_X = xmin;
-        MIN_Y = ymin;
-        MAX_X = xmax;
-        MAX_Y = ymax;
-    }
-
-    /**
-     * Do not allow instantiation of this class.
-     */
-    private Geometries() {
-    }
+    private final Method getEnvelopeInternal, getMinX, getMinY, getMaxX, getMaxY;
 
     /**
-     * Returns {@code true} if the given type is one of the type known to Apache SIS.
-     *
-     * @param  type  the type to verify.
-     * @return {@code true} if the given type is one of the geometry type known to SIS.
+     * Creates the singleton instance.
      */
-    public static boolean isKnownType(final Class<?> type) {
-        return Geometry.class.isAssignableFrom(type) || (JTS != null && JTS.isAssignableFrom(type));
+    JTS() throws ClassNotFoundException, NoSuchMethodException {
+        super(Class.forName("com.vividsolutions.jts.geom.Geometry"),
+              Class.forName("com.vividsolutions.jts.geom.Point"));
+        getEnvelopeInternal = rootClass.getMethod("getEnvelopeInternal", (Class[]) null);
+        final Class<?> envt = getEnvelopeInternal.getReturnType();
+        getMinX = envt.getMethod("getMinX", (Class[]) null);
+        getMinY = envt.getMethod("getMinY", (Class[]) null);
+        getMaxX = envt.getMethod("getMaxX", (Class[]) null);
+        getMaxY = envt.getMethod("getMaxY", (Class[]) null);
     }
 
     /**
-     * If the given object is one of the recognized type and its envelope is non-empty,
-     * returns that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
+     * If the given object is a JTS geometry and its envelope is non-empty, returns
+     * that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
      *
      * @param  geometry  the geometry from which to get the envelope, or {@code null}.
      * @return the envelope of the given object, or {@code null} if the object is not
      *         a recognized geometry or its envelope is empty.
      */
-    public static GeneralEnvelope getEnvelope(final Object geometry) {
+    @Override
+    final GeneralEnvelope tryGetEnvelope(final Object geometry) {
         final double xmin, ymin, xmax, ymax;
-        if (geometry instanceof Geometry) {
-            final Envelope2D bounds = new Envelope2D();
-            ((Geometry) geometry).queryEnvelope2D(bounds);
-            if (bounds.isEmpty()) {                                     // Test if there is NaN values.
-                return null;
-            }
-            xmin = bounds.xmin;
-            ymin = bounds.ymin;
-            xmax = bounds.xmax;
-            ymax = bounds.ymax;
-        } else if (JTS != null && JTS.isInstance(geometry)) {
+        if (rootClass.isInstance(geometry)) {
             try {
-                final Object env = INTERNAL.invoke(geometry, (Object[]) null);
-                xmin = (Double) MIN_X.invoke(env, (Object[]) null);
-                ymin = (Double) MIN_Y.invoke(env, (Object[]) null);
-                xmax = (Double) MAX_X.invoke(env, (Object[]) null);
-                ymax = (Double) MAX_Y.invoke(env, (Object[]) null);
+                final Object env = getEnvelopeInternal.invoke(geometry, (Object[]) null);
+                xmin = (Double) getMinX.invoke(env, (Object[]) null);
+                ymin = (Double) getMinY.invoke(env, (Object[]) null);
+                xmax = (Double) getMaxX.invoke(env, (Object[]) null);
+                ymax = (Double) getMaxY.invoke(env, (Object[]) null);
             } catch (ReflectiveOperationException e) {
                 if (e instanceof InvocationTargetException) {
                     final Throwable cause = e.getCause();
@@ -138,12 +89,49 @@ public final class Geometries extends St
                 // Should never happen unless JTS's API changed.
                 throw (Error) new IncompatibleClassChangeError(e.toString()).initCause(e);
             }
-        } else {
-            return null;
+            final GeneralEnvelope env = new GeneralEnvelope(2);
+            env.setRange(0, xmin, xmax);
+            env.setRange(1, ymin, ymax);
+            return env;
         }
-        final GeneralEnvelope env = new GeneralEnvelope(2);
-        env.setRange(0, xmin, xmax);
-        env.setRange(1, ymin, ymax);
-        return env;
+        return null;
+    }
+
+    /**
+     * If the given point is an implementation of this library, returns its coordinate.
+     * Otherwise returns {@code null}. If non-null, the returned array may have a length of 2 or 3.
+     */
+    @Override
+    final double[] tryGetCoordinate(final Object point) {
+        return null;   // TODO - see class javadoc
+    }
+
+    /**
+     * Creates a two-dimensional point from the given coordinate.
+     */
+    @Override
+    public Object createPoint(double x, double y) {
+        throw unsupported(2);   // TODO - see class javadoc
+    }
+
+    /**
+     * Creates a polyline from the given ordinate values.
+     * Each {@link Double#NaN} ordinate value start a new path.
+     * The implementation returned by this method must be an instance of {@link #rootClass}.
+     */
+    @Override
+    public Object createPolyline(final int dimension, final Vector ordinates) {
+        // TODO - see class javadoc
+        throw unsupported(dimension);
+    }
+
+    /**
+     * Merges a sequence of points or paths if the first instance is an implementation of this library.
+     *
+     * @throws ClassCastException if an element in the iterator is not a JTS geometry.
+     */
+    @Override
+    final Object tryMergePolylines(final Object first, final Iterator<?> polylines) {
+        throw unsupported(2);   // TODO - see class javadoc
     }
 }

Copied: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java (from r1799451, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java?p2=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java&p1=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java&r1=1799451&r2=1799773&rev=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -16,23 +16,21 @@
  */
 package org.apache.sis.internal.feature;
 
-import java.util.logging.Level;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import com.esri.core.geometry.Geometry;
-import com.esri.core.geometry.Envelope2D;
+import java.util.Iterator;
+import java.awt.Shape;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.util.Static;
-import org.apache.sis.util.logging.Logging;
-import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.referencing.j2d.ShapeUtilities;
+import org.apache.sis.math.Vector;
 
 
 /**
- * Utility methods on geometric objects defined in libraries outside Apache SIS.
+ * Centralizes usages of some (not all) Java2D geometry API by Apache SIS.
  * We use this class for isolating dependencies from the {@code org.apache.feature} package
  * to ESRI's API or to Java Topology Suite (JTS) API.
- * This gives us a single place to review if we want to support different geometry libraries,
- * or if Apache SIS come with its own implementation.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -40,110 +38,155 @@ import org.apache.sis.internal.system.Lo
  * @since   0.7
  * @module
  */
-public final class Geometries extends Static {
+final class Java2D extends Geometries {
     /**
-     * The geometry object from Java Topology Suite (JTS),
-     * or {@code null} if the JTS library is not on the classpath.
+     * Creates the singleton instance.
      */
-    private static final Class<?> JTS;
+    Java2D() {
+        super(Shape.class, Point2D.class);
+    }
 
     /**
-     * Getter methods on JTS envelopes, or {@code null} if the JTS library is not on the classpath.
-     * Each methods take no argument and return a {@code double} value.
+     * If the given object is a Java2D geometry and its envelope is non-empty, returns
+     * that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
+     *
+     * @param  geometry  the geometry from which to get the envelope, or {@code null}.
+     * @return the envelope of the given object, or {@code null} if the object is not
+     *         a recognized geometry or its envelope is empty.
      */
-    private static final Method INTERNAL, MIN_X, MIN_Y, MAX_X, MAX_Y;
+    @Override
+    final GeneralEnvelope tryGetEnvelope(final Object geometry) {
+        if (geometry instanceof Shape) {
+            final Rectangle2D bounds = ((Shape) geometry).getBounds2D();
+            if (!bounds.isEmpty()) {                                     // Test if there is NaN values.
+                final GeneralEnvelope env = new GeneralEnvelope(2);
+                env.setRange(0, bounds.getMinX(), bounds.getMaxX());
+                env.setRange(1, bounds.getMinY(), bounds.getMaxY());
+                return env;
+            }
+        }
+        return null;
+    }
 
-    static {
-        Class<?> type;
-        Method genv, xmin, ymin, xmax, ymax;
-        try {
-            final Class<?> envt;
-            type = Class.forName("com.vividsolutions.jts.geom.Geometry");
-            genv = type.getMethod("getEnvelopeInternal", (Class[]) null);
-            envt = genv.getReturnType();
-            xmin = envt.getMethod("getMinX", (Class[]) null);
-            ymin = envt.getMethod("getMinY", (Class[]) null);
-            xmax = envt.getMethod("getMaxX", (Class[]) null);
-            ymax = envt.getMethod("getMaxY", (Class[]) null);
-        } catch (ClassNotFoundException | NoSuchMethodException e) {
-            Logging.getLogger(Loggers.GEOMETRY).log(Level.CONFIG, e.toString());
-            type = null;
-            genv = null;
-            xmin = null;
-            xmax = null;
-            ymin = null;
-            ymax = null;
+    /**
+     * If the given point is an implementation of this library, returns its coordinate.
+     * Otherwise returns {@code null}.
+     */
+    @Override
+    final double[] tryGetCoordinate(final Object point) {
+        if (point instanceof Point2D) {
+            final Point2D pt = (Point2D) point;
+            return new double[] {
+                pt.getX(),
+                pt.getY()
+            };
         }
-        JTS = type;
-        INTERNAL = genv;
-        MIN_X = xmin;
-        MIN_Y = ymin;
-        MAX_X = xmax;
-        MAX_Y = ymax;
+        return null;
     }
 
     /**
-     * Do not allow instantiation of this class.
+     * Creates a two-dimensional point from the given coordinate.
      */
-    private Geometries() {
+    @Override
+    public Object createPoint(double x, double y) {
+        return new Point2D.Double(x, y);
     }
 
     /**
-     * Returns {@code true} if the given type is one of the type known to Apache SIS.
+     * Returns {@code true} if all values in the given vector can be casted to {@code float} without precision lost.
      *
-     * @param  type  the type to verify.
-     * @return {@code true} if the given type is one of the geometry type known to SIS.
+     * @param  data  the data to test.
+     * @return whether all the given data can be casted to {@code float} type.
      */
-    public static boolean isKnownType(final Class<?> type) {
-        return Geometry.class.isAssignableFrom(type) || (JTS != null && JTS.isAssignableFrom(type));
+    private static boolean isConvertibleToFloats(final Vector data) {
+        for (int i=data.size(); --i >= 0;) {
+            final double value = data.doubleValue(i);
+            if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits((float) value)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
-     * If the given object is one of the recognized type and its envelope is non-empty,
-     * returns that envelope as an Apache SIS implementation. Otherwise returns {@code null}.
+     * Creates a path from the given ordinate values.
+     * Each {@link Double#NaN} ordinate value start a new path.
+     * The implementation returned by this method must be an instance of {@link #rootClass}.
+     */
+    @Override
+    public Object createPolyline(final int dimension, final Vector ordinates) {
+        if (dimension != 2) {
+            throw unsupported(dimension);
+        }
+        final boolean isFloat = isConvertibleToFloats(ordinates);
+        final int size = ordinates.size();
+        /*
+         * Note: Point2D is not an instance of Shape, so we can not make a special case for it.
+         */
+        if (size == 4) {
+            final double x1, y1, x2, y2;
+            if (!Double.isNaN(x1 = ordinates.doubleValue(0)) &&
+                !Double.isNaN(y1 = ordinates.doubleValue(1)) &&
+                !Double.isNaN(x2 = ordinates.doubleValue(2)) &&
+                !Double.isNaN(y2 = ordinates.doubleValue(3)))
+            {
+                final Line2D path = isFloat ? new Line2D.Float() : new Line2D.Double();
+                path.setLine(x1, y1, x2, y2);
+                return path;
+            }
+        }
+        final Path2D path = isFloat ? new Path2D.Float (Path2D.WIND_NON_ZERO, size/2)
+                                    : new Path2D.Double(Path2D.WIND_NON_ZERO, size/2);
+        boolean lineTo = false;
+        for (int i=0; i<size;) {
+            final double x = ordinates.doubleValue(i++);
+            final double y = ordinates.doubleValue(i++);
+            if (Double.isNaN(x) || Double.isNaN(y)) {
+                lineTo = false;
+            } else if (lineTo) {
+                path.lineTo(x, y);
+            } else {
+                path.moveTo(x, y);
+                lineTo = true;
+            }
+        }
+        return path;
+    }
+
+    /**
+     * Merges a sequence of points or paths if the first instance is an implementation of this library.
      *
-     * @param  geometry  the geometry from which to get the envelope, or {@code null}.
-     * @return the envelope of the given object, or {@code null} if the object is not
-     *         a recognized geometry or its envelope is empty.
+     * @throws ClassCastException if an element in the iterator is not a {@link Shape} or a {@link Point2D}.
      */
-    public static GeneralEnvelope getEnvelope(final Object geometry) {
-        final double xmin, ymin, xmax, ymax;
-        if (geometry instanceof Geometry) {
-            final Envelope2D bounds = new Envelope2D();
-            ((Geometry) geometry).queryEnvelope2D(bounds);
-            if (bounds.isEmpty()) {                                     // Test if there is NaN values.
-                return null;
-            }
-            xmin = bounds.xmin;
-            ymin = bounds.ymin;
-            xmax = bounds.xmax;
-            ymax = bounds.ymax;
-        } else if (JTS != null && JTS.isInstance(geometry)) {
-            try {
-                final Object env = INTERNAL.invoke(geometry, (Object[]) null);
-                xmin = (Double) MIN_X.invoke(env, (Object[]) null);
-                ymin = (Double) MIN_Y.invoke(env, (Object[]) null);
-                xmax = (Double) MAX_X.invoke(env, (Object[]) null);
-                ymax = (Double) MAX_Y.invoke(env, (Object[]) null);
-            } catch (ReflectiveOperationException e) {
-                if (e instanceof InvocationTargetException) {
-                    final Throwable cause = e.getCause();
-                    if (cause instanceof RuntimeException) {
-                        throw (RuntimeException) cause;
-                    }
-                    if (cause instanceof Error) {
-                        throw (Error) cause;
+    @Override
+    final Object tryMergePolylines(Object next, final Iterator<?> polylines) {
+        if (!(next instanceof Shape || next instanceof Point2D)) {
+            return null;
+        }
+        final Path2D path = new Path2D.Double();
+        boolean lineTo = false;
+        for (;; next = polylines.next()) {
+            if (next != null) {
+                if (next instanceof Point2D) {
+                    final double x = ((Point2D) next).getX();
+                    final double y = ((Point2D) next).getY();
+                    if (Double.isNaN(x) || Double.isNaN(y)) {
+                        lineTo = false;
+                    } else if (lineTo) {
+                        path.lineTo(x, y);
+                    } else {
+                        path.moveTo(x, y);
+                        lineTo = true;
                     }
+                } else {
+                    path.append((Shape) next, false);
+                    lineTo = false;
                 }
-                // Should never happen unless JTS's API changed.
-                throw (Error) new IncompatibleClassChangeError(e.toString()).initCause(e);
             }
-        } else {
-            return null;
+            if (!polylines.hasNext()) {         // Should be part of the 'for' instruction, but we need
+                break;                          // to skip this condition during the first iteration.
+            }
         }
-        final GeneralEnvelope env = new GeneralEnvelope(2);
-        env.setRange(0, xmin, xmax);
-        env.setRange(1, ymin, ymax);
-        return env;
+        return ShapeUtilities.toPrimitive(path);
     }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -133,6 +133,12 @@ public final class Resources extends Ind
         public static final short NotASingleton_1 = 14;
 
         /**
+         * The {0} optional library is not available. Geometric operations will ignore that library.
+         * Cause is {1}.
+         */
+        public static final short OptionalLibraryNotFound_2 = 19;
+
+        /**
          * Property “{1}” already exists in feature “{0}”.
          */
         public static final short PropertyAlreadyExists_2 = 15;
@@ -152,6 +158,11 @@ public final class Resources extends Ind
          * Feature named “{0}” has not yet been resolved.
          */
         public static final short UnresolvedFeatureName_1 = 18;
+
+        /**
+         * Unsupported geometry {0}D object.
+         */
+        public static final short UnsupportedGeometryObject_1 = 20;
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties [ISO-8859-1] Sat Jun 24 13:55:51 2017
@@ -33,7 +33,9 @@ IllegalPropertyValueClass_3       = Prop
 MismatchedPropertyType_1          = Mismatched type for \u201c{0}\u201d property.
 MismatchedValueClass_3            = An attribute for \u2018{1}\u2019 values where expected, but the \u201c{0}\u201d attribute specifies values of type \u2018{2}\u2019.
 NotASingleton_1                   = Property \u201c{0}\u201d contains more than one value.
+OptionalLibraryNotFound_2         = The {0} optional library is not available. Geometric operations will ignore that library.\nCause is {1}.
 PropertyAlreadyExists_2           = Property \u201c{1}\u201d already exists in feature \u201c{0}\u201d.
 PropertyNotFound_2                = No property named \u201c{1}\u201d has been found in \u201c{0}\u201d feature.
 UnexpectedNumberOfComponents_4    = The \u201c{1}\u201d value given to \u201c{0}\u201d property should be separable in {2} components, but we got {3}.
 UnresolvedFeatureName_1           = Feature named \u201c{0}\u201d has not yet been resolved.
+UnsupportedGeometryObject_1       = Unsupported geometry {0}D object.

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties [ISO-8859-1] Sat Jun 24 13:55:51 2017
@@ -38,7 +38,9 @@ IllegalPropertyValueClass_3       = La p
 MismatchedPropertyType_1          = Le type de la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb ne correspond pas.
 MismatchedValueClass_3            = Un attribut pour des valeurs de type \u2018{1}\u2019 \u00e9tait attendu, mais l\u2019attribut \u00ab\u202f{0}\u202f\u00bb sp\u00e9cifie des valeurs de type \u2018{2}\u2019.
 NotASingleton_1                   = La propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb contient plus de une valeur.
+OptionalLibraryNotFound_2         = La biblioth\u00e8que optionnelle {0} n\u2019est pas disponible. Les op\u00e9rations g\u00e9om\u00e9triques ignoreront cette biblioth\u00e8que.\nLa cause est {1}.
 PropertyNotFound_2                = Aucune propri\u00e9t\u00e9 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb n\u2019a \u00e9t\u00e9 trouv\u00e9e dans l\u2019entit\u00e9 \u00ab\u202f{0}\u202f\u00bb.
 PropertyAlreadyExists_2           = La propri\u00e9t\u00e9 \u00ab\u202f{1}\u202f\u00bb existe d\u00e9j\u00e0 dans l\u2019entit\u00e9 \u00ab\u202f{0}\u202f\u00bb.
 UnexpectedNumberOfComponents_4    = La valeur \u00ab\u202f{1}\u202f\u00bb donn\u00e9e \u00e0 la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb devrait \u00eatre s\u00e9parable en {2} composantes, mais on en a obtenus {3}.
 UnresolvedFeatureName_1           = L\u2019entit\u00e9 nomm\u00e9e \u00ab\u202f{0}\u202f\u00bb n\u2019a pas encore \u00e9t\u00e9 r\u00e9solue.
+UnsupportedGeometryObject_1       = Object g\u00e9om\u00e9trique {0}D non-support\u00e9.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -202,6 +202,20 @@ public abstract class Vector extends Abs
      * is backed by an array of type {@code float[]}, then this method returns {@code Float.class},
      * not {@link Float#TYPE}.
      *
+     * <p>The information returned by this method is only indicative; it is not guaranteed to specify accurately
+     * this kind of objects returned by the {@link #get(int)} method. There is various situation where the types
+     * may not match:</p>
+     *
+     * <ul>
+     *   <li>If this vector {@linkplan #isUnsigned() is unsigned}, then the values returned by {@code get(int)}
+     *       may be instances of a type wider than the type used by this vector for storing the values.</li>
+     *   <li>If this vector has been {@linkplain #createForDecimal(float[]) created for decimal numbers},
+     *       then the values returned by {@code get(int)} will use double-precision even if this vector
+     *       stores the values as single-precision floating point numbers.</li>
+     *   <li>If this vector {@linkplain #compress(double) has been compressed}, then the type returned by this
+     *       method does not describe accurately the range of values that this vector can store.</li>
+     * </ul>
+     *
      * <p>Users of the {@link #doubleValue(int)} method do not need to care about this information since
      * {@code Vector} will perform automatically the type conversion. Users of other methods may want to
      * verify this information for avoiding {@link ArithmeticException}.</p>
@@ -425,14 +439,16 @@ public abstract class Vector extends Abs
     /**
      * Sets the number at the given index.
      * The given number should be an instance of the same type than the number returned by {@link #get(int)}.
+     * If not, the stored value may lost precision as a result of the cast.
      *
      * @param  index  the index in the [0 … {@linkplain #size() size}-1] range.
      * @param  value  the value to set at the given index.
      * @return the value previously stored at the given index.
+     * @throws UnsupportedOperationException if this vector is read-only.
      * @throws IndexOutOfBoundsException if the given index is out of bounds.
      * @throws NumberFormatException if the previous value was stored as a {@code String} and can not be parsed.
-     * @throws ClassCastException if the given value can not be converted to the type expected by this vector.
-     * @throws ArrayStoreException if the given value can not be stored in this vector.
+     * @throws ArithmeticException if this vector uses some {@linkplain #compress(double) compression} technic
+     *         and the given value is out of range for that compression.
      */
     @Override
     public abstract Number set(int index, Number value);

Modified: sis/branches/JDK8/ide-project/NetBeans/nbproject/genfiles.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/ide-project/NetBeans/nbproject/genfiles.properties?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/ide-project/NetBeans/nbproject/genfiles.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/ide-project/NetBeans/nbproject/genfiles.properties [ISO-8859-1] Sat Jun 24 13:55:51 2017
@@ -3,6 +3,6 @@
 build.xml.data.CRC32=58e6b21c
 build.xml.script.CRC32=462eaba0
 build.xml.stylesheet.CRC32=28e38971@1.53.1.46
-nbproject/build-impl.xml.data.CRC32=2b076fc2
+nbproject/build-impl.xml.data.CRC32=f7767470
 nbproject/build-impl.xml.script.CRC32=254cb1ce
 nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48

Modified: sis/branches/JDK8/ide-project/NetBeans/nbproject/project.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/ide-project/NetBeans/nbproject/project.xml?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/ide-project/NetBeans/nbproject/project.xml (original)
+++ sis/branches/JDK8/ide-project/NetBeans/nbproject/project.xml Sat Jun 24 13:55:51 2017
@@ -94,6 +94,8 @@
             <word>namespaces</word>
             <word>orthodromic</word>
             <word>parsable</word>
+            <word>polyline</word>
+            <word>polylines</word>
             <word>spliterator</word>
             <word>timezone</word>
             <word>Unicode</word>

Modified: sis/branches/JDK8/storage/sis-shapefile/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-shapefile/pom.xml?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-shapefile/pom.xml (original)
+++ sis/branches/JDK8/storage/sis-shapefile/pom.xml Sat Jun 24 13:55:51 2017
@@ -121,6 +121,11 @@ Read and write files in the Shapefile fo
       <artifactId>sis-storage</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.esri.geometry</groupId>
+      <artifactId>esri-geometry-api</artifactId>
+      <scope>compile</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/TimeEncoding.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/TimeEncoding.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/TimeEncoding.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/TimeEncoding.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -100,8 +100,8 @@ class TimeEncoding extends SurjectiveCon
         return Instant.ofEpochMilli(millis + origin)
                       .plusNanos(Math.round((value - millis) * StandardDateFormat.NANOS_PER_MILLISECOND));
         /*
-         * Performance note: the call to .plusNano(…) will usually return 'this'
-         * since the time granularity is rarely finer than milliseconds.
+         * Performance note: the call to .plusNano(…) will usually return the same 'Instant' instance
+         * (without creating new object) since the time granularity is rarely finer than milliseconds.
          */
     }
 

Modified: sis/branches/JDK8/storage/sis-xmlstore/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/pom.xml?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/pom.xml (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/pom.xml Sat Jun 24 13:55:51 2017
@@ -112,6 +112,11 @@ Read and write files in the GPX format.
       <artifactId>sis-storage</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.esri.geometry</groupId>
+      <artifactId>esri-geometry-api</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/GroupAsPolylineOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/GroupAsPolylineOperation.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/GroupAsPolylineOperation.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/GroupAsPolylineOperation.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -17,9 +17,9 @@
 package org.apache.sis.internal.storage.gpx;
 
 import java.util.Map;
+import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
-import com.esri.core.geometry.Polyline;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.feature.AbstractAttribute;
@@ -27,6 +27,7 @@ import org.apache.sis.feature.AbstractOp
 import org.apache.sis.feature.DefaultAttributeType;
 import org.apache.sis.internal.feature.AttributeConvention;
 import org.apache.sis.internal.feature.FeatureUtilities;
+import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.util.resources.Errors;
 
 // Branch-dependent imports
@@ -38,22 +39,30 @@ import org.opengis.feature.AttributeType
 
 /**
  * Creates a single (Multi){@code Polyline} instance from a sequence of points or polylines stored in another property.
- * This base class expects a sequence of {@link Polyline} as input, but subclass will expect other kind of geometries.
+ * This base class expects a sequence of {@code Point} or {@code Polyline} instances as input.
  * The single (Multi){@code Polyline} instance is re-computed every time this property is requested.
  *
- * <div class="note"><b>Example:</b>
+ * <div class="note"><b>Examples:</b>
+ * <p><i>Polylines created from points:</i>
+ * a boat that record it's position every hour.
+ * The list of all positions is stored in an attribute with [0 … ∞] cardinality.
+ * This class will extract each position and create a line as a new attribute.
+ * Any change applied to the positions will be visible on the line.</p>
+ *
+ * <p><i>Polylines created from other polylines:</i>
  * a boat that record track every hour.
  * The list of all tracks is stored in an attribute with [0 … ∞] cardinality.
  * This class will extract each track and create a polyline as a new attribute.
- * Any change applied to the tracks will be visible on the polyline.
+ * Any change applied to the tracks will be visible on the polyline.</p>
  * </div>
  *
  * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
  * @since   0.8
  * @module
  */
-class GroupAsPolylineOperation extends AbstractOperation {
+final class GroupAsPolylineOperation extends AbstractOperation {
     /**
      * For cross-version compatibility.
      */
@@ -65,27 +74,38 @@ class GroupAsPolylineOperation extends A
     private static final ParameterDescriptorGroup EMPTY_PARAMS = FeatureUtilities.parameters("GroupPolylines");
 
     /**
-     * The type of the values computed by this operation. The name of this type presumes
-     * that the result will be assigned to the "geometry" attribute of the feature type.
+     * Name of the property to follow in order to get the geometries to add to a polyline.
+     * This property shall be a feature association, usually with [0 … ∞] cardinality.
      */
-    static final AttributeType<Polyline> RESULT_TYPE = new DefaultAttributeType<>(
-            Collections.singletonMap(NAME_KEY, AttributeConvention.ENVELOPE_PROPERTY), Polyline.class, 1, 1, null);
+    private final String association;
 
     /**
-     * Name of the property to follow in order to get the geometries to add to a polyline.
-     * This property shall be a feature association, usually with [0 … ∞] cardinality.
+     * The expected result type to be returned by {@link #getResult()}.
      */
-    final String association;
+    private final AttributeType<?> result;
 
     /**
      * Creates a new operation which will look for geometries in the given feature association.
      *
      * @param  identification  name and other information to be given to this operation.
      * @param  association     name of the property to follow in order to get the geometries to add to a polyline.
+     * @param  result          the expected result type to be returned by {@link #getResult()}.
      */
-    GroupAsPolylineOperation(final Map<String,?> identification, final String association) {
+    GroupAsPolylineOperation(final Map<String,?> identification, final String association, final AttributeType<?> result) {
         super(identification);
         this.association = association;
+        this.result = result;
+    }
+
+    /**
+     * Creates the {@code result} argument for the constructor. This creation is provided in a separated method
+     * because the same instance will be shared by many {@code GroupAsPolylineOperation} instances.
+     *
+     * @param  geometries  accessor to the geometry implementation in use (Java2D, ESRI or JTS).
+     */
+    static AttributeType<?> getResult(final Geometries geometries) {
+        return new DefaultAttributeType<>(Collections.singletonMap(NAME_KEY, AttributeConvention.ENVELOPE_PROPERTY),
+                geometries.rootClass, 1, 1, null);
     }
 
     /**
@@ -100,8 +120,8 @@ class GroupAsPolylineOperation extends A
      * Returns the expected result type.
      */
     @Override
-    public final AttributeType<Polyline> getResult() {
-        return RESULT_TYPE;
+    public final AttributeType<?> getResult() {
+        return result;
     }
 
     /**
@@ -110,19 +130,9 @@ class GroupAsPolylineOperation extends A
      * the result will be recomputed.
      */
     @Override
+    @SuppressWarnings({"rawtypes", "unchecked"})
     public final Property apply(Feature feature, ParameterValueGroup parameters) {
-        return new Result(feature);
-    }
-
-    /**
-     * Invoked for every geometric objects to put in a single polyline.
-     *
-     * @param addTo     where to add the geometry object.
-     * @param geometry  the point or polyline to add to {@code addTo}.
-     * @param isFirst   whether {@code geometry} is the first object added to the given polyline.
-     */
-    void addGeometry(final Polyline addTo, final Object geometry, final boolean isFirst) {
-        addTo.add((Polyline) geometry, false);
+        return new Result(feature, association, result);
     }
 
 
@@ -132,8 +142,10 @@ class GroupAsPolylineOperation extends A
      * Note that the cache is not used when {@link #apply(Feature, ParameterValueGroup)} is invoked,
      * causing a new value to be computed again. The intend is to behave as if the operation has been
      * executed at {@code apply(…)} invocation time, even if we deferred the actual execution.
+     *
+     * @param  <G>  the root geometry class (implementation-dependent).
      */
-    private final class Result extends AbstractAttribute<Polyline> {
+    private static final class Result<G> extends AbstractAttribute<G> {
         /**
          * For cross-version compatibility.
          */
@@ -145,31 +157,43 @@ class GroupAsPolylineOperation extends A
         private final Feature feature;
 
         /**
+         * Name of the property to follow in order to get the geometries to add to a polyline.
+         * This property shall be a feature association, usually with [0 … ∞] cardinality.
+         */
+        private final String association;
+
+        /**
          * The result, computed when first needed.
          */
-        private transient Polyline geometry;
+        private transient G geometry;
 
         /**
          * Creates a new result for an execution on the given feature.
          * The actual computation is deferred to the first call of {@link #getValue()}.
          */
-        Result(final Feature feature) {
-            super(RESULT_TYPE);
+        Result(final Feature feature, final String association, final AttributeType<G> result) {
+            super(result);
             this.feature = feature;
+            this.association = association;
         }
 
         /**
-         * Computes the geometry from all points of polylines found in the associated feature.
+         * Computes the geometry from all points or polylines found in the associated feature.
          */
         @Override
-        public Polyline getValue() {
+        public G getValue() {
             if (geometry == null) {
-                boolean isFirst = true;
-                geometry = new Polyline();
-                for (final Object child : (Collection<?>) feature.getPropertyValue(association)) {
-                    addGeometry(geometry, ((Feature) child).getPropertyValue("sis:geometry"), isFirst);
-                    isFirst = false;
-                }
+                final Iterator<?> it = ((Collection<?>) feature.getPropertyValue(association)).iterator();
+                final Object geom = Geometries.mergePolylines(new Iterator<Object>() {
+                    @Override public boolean hasNext() {
+                        return it.hasNext();
+                    }
+
+                    @Override public Object next() {
+                        return ((Feature) it.next()).getPropertyValue("sis:geometry");
+                    }
+                });
+                geometry = getType().getValueClass().cast(geom);
             }
             return geometry;
         }
@@ -178,7 +202,7 @@ class GroupAsPolylineOperation extends A
          * Does not allow modification of this attribute.
          */
         @Override
-        public void setValue(Polyline value) {
+        public void setValue(G value) {
             throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, Attribute.class));
         }
     }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Reader.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Reader.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Reader.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Reader.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -25,7 +25,6 @@ import java.net.URISyntaxException;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamReader;
 import javax.xml.bind.JAXBException;
-import com.esri.core.geometry.Point;
 import org.apache.sis.storage.gps.Fix;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
@@ -408,9 +407,10 @@ parse:  while (reader.hasNext()) {
             throw new DataStoreContentException(errors().getString(Errors.Keys.MandatoryAttribute_2,
                     (lat == null) ? Attributes.LATITUDE : Attributes.LONGITUDE, tagName));
         }
-        final Feature feature = ((Store) owner).types.wayPoint.newInstance();
+        final Types types = ((Store) owner).types;
+        final Feature feature = types.wayPoint.newInstance();
         feature.setPropertyValue("sis:identifier", index);
-        feature.setPropertyValue("sis:geometry", new Point(parseDouble(lon), parseDouble(lat)));
+        feature.setPropertyValue("sis:geometry", types.geometries.createPoint(parseDouble(lon), parseDouble(lat)));
         List<Link> links = null;
         while (true) {
             /*

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -21,7 +21,6 @@ import java.util.Collections;
 import java.util.Locale;
 import java.util.Map;
 import java.util.HashMap;
-import com.esri.core.geometry.Point;
 import org.opengis.util.ScopedName;
 import org.opengis.util.GenericName;
 import org.opengis.util.NameFactory;
@@ -39,14 +38,15 @@ import org.apache.sis.feature.builder.Fe
 import org.apache.sis.feature.builder.PropertyTypeBuilder;
 import org.apache.sis.feature.builder.AttributeRole;
 import org.apache.sis.internal.feature.AttributeConvention;
+import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.internal.storage.FeatureCatalogBuilder;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.util.iso.ResourceInternationalString;
 import org.apache.sis.util.iso.DefaultNameFactory;
-import org.apache.sis.util.Static;
 
 // Branch-dependent imports
 import java.time.temporal.Temporal;
+import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
 
 
@@ -60,7 +60,7 @@ import org.opengis.feature.FeatureType;
  * @since   0.8
  * @module
  */
-final class Types extends Static {
+final class Types {
     /**
      * Way point GPX feature type.
      */
@@ -93,6 +93,11 @@ final class Types extends Static {
     final FeatureNaming<FeatureType> names;
 
     /**
+     * Accessor to the geometry implementation in use (Java2D, ESRI or JTS).
+     */
+    final Geometries geometries;
+
+    /**
      * A system-wide instance for {@code FeatureType} instances created using the {@link DefaultNameFactory}.
      * This is normally the only instance used in an application.
      */
@@ -113,6 +118,7 @@ final class Types extends Static {
      * @throws FactoryException if an error occurred while creating an "envelope bounds" operation.
      */
     Types(final NameFactory factory, final Locale locale) throws FactoryException, IllegalNameException {
+        geometries = Geometries.implementation();
         final Map<String,InternationalString[]> resources = new HashMap<>();
         final ScopedName    geomName = AttributeConvention.GEOMETRY_PROPERTY;
         final Map<String,?> geomInfo = Collections.singletonMap(AbstractIdentifiedType.NAME_KEY, geomName);
@@ -163,7 +169,7 @@ final class Types extends Static {
          */
         builder = new FeatureTypeBuilder(null, factory, locale).setSuperTypes(parent);
         builder.setNameSpace(Tags.PREFIX).setName("WayPoint");
-        builder.addAttribute(Point.class).setName(geomName)
+        builder.addAttribute(geometries.pointClass).setName(geomName)
                 .setCRS(CommonCRS.WGS84.normalizedGeographic())
                 .addRole(AttributeRole.DEFAULT_GEOMETRY);
         builder.setDefaultCardinality(0, 1);
@@ -204,7 +210,8 @@ final class Types extends Static {
          * │ rtept          │ WayPoint       │ gpx:wptType            │   [0 … ∞]   │
          * └────────────────┴────────────────┴────────────────────────┴─────────────┘
          */
-        GroupAsPolylineOperation groupOp = new GroupPointsAsPolylineOperation(geomInfo, Tags.ROUTE_POINTS);
+        final AttributeType<?> groupResult = GroupAsPolylineOperation.getResult(geometries);
+        GroupAsPolylineOperation groupOp = new GroupAsPolylineOperation(geomInfo, Tags.ROUTE_POINTS, groupResult);
         builder = new FeatureTypeBuilder(null, factory, locale).setSuperTypes(parent);
         builder.setNameSpace(Tags.PREFIX).setName("Route");
         builder.addProperty(groupOp);
@@ -230,7 +237,7 @@ final class Types extends Static {
          * │ trkpt          │ WayPoint │ gpx:wptType │   [0 … ∞]   │
          * └────────────────┴──────────┴─────────────┴─────────────┘
          */
-        groupOp = new GroupPointsAsPolylineOperation(geomInfo, Tags.TRACK_POINTS);
+        groupOp = new GroupAsPolylineOperation(geomInfo, Tags.TRACK_POINTS, groupResult);
         builder = new FeatureTypeBuilder(null, factory, locale).setSuperTypes(parent);
         builder.setNameSpace(Tags.PREFIX).setName("TrackSegment");
         builder.addProperty(groupOp);
@@ -256,7 +263,7 @@ final class Types extends Static {
          * │ trkseg         │ TrackSegment   │ gpx:trksegType         │   [0 … ∞]   │
          * └────────────────┴────────────────┴────────────────────────┴─────────────┘
          */
-        groupOp = new GroupAsPolylineOperation(geomInfo, Tags.TRACK_SEGMENTS);
+        groupOp = new GroupAsPolylineOperation(geomInfo, Tags.TRACK_SEGMENTS, groupResult);
         builder = new FeatureTypeBuilder(null, factory, locale).setSuperTypes(parent);
         builder.setNameSpace(Tags.PREFIX).setName("Track");
         builder.addProperty(groupOp);

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java?rev=1799773&r1=1799772&r2=1799773&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java [UTF-8] Sat Jun 24 13:55:51 2017
@@ -20,11 +20,11 @@ import java.io.IOException;
 import java.util.Collection;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.bind.JAXBException;
-import com.esri.core.geometry.Point;
 import org.apache.sis.storage.gps.Fix;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.IllegalFeatureTypeException;
 import org.apache.sis.internal.storage.xml.stream.StaxStreamWriter;
+import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.util.Version;
 
 // Branch-dependent imports
@@ -191,31 +191,33 @@ final class Writer extends StaxStreamWri
      */
     private void writeWayPoint(final Feature feature, final String tagName) throws XMLStreamException, JAXBException {
         if (feature != null) {
-            final Point pt = (Point) feature.getPropertyValue("sis:geometry");
-            writer.writeStartElement(tagName);
-            writer.writeAttribute(Attributes.LATITUDE, Double.toString(pt.getY()));
-            writer.writeAttribute(Attributes.LONGITUDE, Double.toString(pt.getX()));
-
-            writeSingleValue(Tags.ELEVATION,       feature.getPropertyValue(Tags.ELEVATION));
-            writeSingleValue(Tags.TIME,            feature.getPropertyValue(Tags.TIME));
-            writeSingleValue(Tags.MAGNETIC_VAR,    feature.getPropertyValue(Tags.MAGNETIC_VAR));
-            writeSingleValue(Tags.GEOID_HEIGHT,    feature.getPropertyValue(Tags.GEOID_HEIGHT));
-            writeSingleValue(Tags.NAME,            feature.getPropertyValue(Tags.NAME));
-            writeSingleValue(Tags.COMMENT,         feature.getPropertyValue(Tags.COMMENT));
-            writeSingleValue(Tags.DESCRIPTION,     feature.getPropertyValue(Tags.DESCRIPTION));
-            writeSingleValue(Tags.SOURCE,          feature.getPropertyValue(Tags.SOURCE));
-            writeLinks((Collection<?>)             feature.getPropertyValue(Tags.LINK));
-            writeSingleValue(Tags.SYMBOL,          feature.getPropertyValue(Tags.SYMBOL));
-            writeSingleValue(Tags.TYPE,            feature.getPropertyValue(Tags.TYPE));
-            writeSingle((Fix)                      feature.getPropertyValue(Tags.FIX));
-            writeSingleValue(Tags.SATELITTES,      feature.getPropertyValue(Tags.SATELITTES));
-            writeSingleValue(Tags.HDOP,            feature.getPropertyValue(Tags.HDOP));
-            writeSingleValue(Tags.VDOP,            feature.getPropertyValue(Tags.VDOP));
-            writeSingleValue(Tags.PDOP,            feature.getPropertyValue(Tags.PDOP));
-            writeSingleValue(Tags.AGE_OF_GPS_DATA, feature.getPropertyValue(Tags.AGE_OF_GPS_DATA));
-            writeSingleValue(Tags.DGPS_ID,         feature.getPropertyValue(Tags.DGPS_ID));
+            final double[] pt = Geometries.getCoordinate(feature.getPropertyValue("sis:geometry"));
+            if (pt != null && pt.length >= 2) {
+                writer.writeStartElement(tagName);
+                writer.writeAttribute(Attributes.LATITUDE,  Double.toString(pt[1]));
+                writer.writeAttribute(Attributes.LONGITUDE, Double.toString(pt[0]));
+
+                writeSingleValue(Tags.ELEVATION,       feature.getPropertyValue(Tags.ELEVATION));
+                writeSingleValue(Tags.TIME,            feature.getPropertyValue(Tags.TIME));
+                writeSingleValue(Tags.MAGNETIC_VAR,    feature.getPropertyValue(Tags.MAGNETIC_VAR));
+                writeSingleValue(Tags.GEOID_HEIGHT,    feature.getPropertyValue(Tags.GEOID_HEIGHT));
+                writeSingleValue(Tags.NAME,            feature.getPropertyValue(Tags.NAME));
+                writeSingleValue(Tags.COMMENT,         feature.getPropertyValue(Tags.COMMENT));
+                writeSingleValue(Tags.DESCRIPTION,     feature.getPropertyValue(Tags.DESCRIPTION));
+                writeSingleValue(Tags.SOURCE,          feature.getPropertyValue(Tags.SOURCE));
+                writeLinks((Collection<?>)             feature.getPropertyValue(Tags.LINK));
+                writeSingleValue(Tags.SYMBOL,          feature.getPropertyValue(Tags.SYMBOL));
+                writeSingleValue(Tags.TYPE,            feature.getPropertyValue(Tags.TYPE));
+                writeSingle((Fix)                      feature.getPropertyValue(Tags.FIX));
+                writeSingleValue(Tags.SATELITTES,      feature.getPropertyValue(Tags.SATELITTES));
+                writeSingleValue(Tags.HDOP,            feature.getPropertyValue(Tags.HDOP));
+                writeSingleValue(Tags.VDOP,            feature.getPropertyValue(Tags.VDOP));
+                writeSingleValue(Tags.PDOP,            feature.getPropertyValue(Tags.PDOP));
+                writeSingleValue(Tags.AGE_OF_GPS_DATA, feature.getPropertyValue(Tags.AGE_OF_GPS_DATA));
+                writeSingleValue(Tags.DGPS_ID,         feature.getPropertyValue(Tags.DGPS_ID));
 
-            writer.writeEndElement();
+                writer.writeEndElement();
+            }
         }
     }
 



Mime
View raw message