Hi guys,
I currently working on a patch which removes the direct*
com.vividsolutions.jts* dependency from H2. In addition I will post a
pull-request to H2GIS so that they can easily support that in their next
version.
The code for the H2 patch is ready. My next step is to get the *TestSpatial
*running. The problem what I have is the following:
My patch is based on three interfaces which has to be implemented by
"Geometry Framework" and will be loaded by the *java.util.ServiceLoader :*
-
*IEnvelope *
-
*IGeometry *
- *IGeometryFactory*
All that interfaces are reduced to a minimum set of methods, which will be
needed by H2 database.
>From my point of view it's ok that *TestSpatial *is based on a
*com.vividsolutions.jts* implementation. The big question is where should I
place such an implementation, because I want to create the possibility to
replace such an implementation.
I think the best way would to place this implementation in H2GIS or place
it in H2 and frameworks like H2GIS have to define the necessary META-INF
file for Java's ServiceLoader.
Attached you can find the first version of my patch.
What's your opinion?
Regards,
Steve
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.
Index: src/main/org/h2/index/SpatialTreeIndex.java
===================================================================
--- src/main/org/h2/index/SpatialTreeIndex.java (revision 6028)
+++ src/main/org/h2/index/SpatialTreeIndex.java (working copy)
@@ -6,7 +6,6 @@
package org.h2.index;
import java.util.Iterator;
-
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.message.DbException;
@@ -21,12 +20,11 @@
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
+import org.h2.value.IEnvelope;
+import org.h2.value.IGeometry;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
/**
* This is an index based on a MVR-TreeMap.
*
@@ -130,8 +128,8 @@
private SpatialKey getEnvelope(SearchRow row) {
Value v = row.getValue(columnIds[0]);
- Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
- Envelope env = g.getEnvelopeInternal();
+ IGeometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
+ IEnvelope env = g.getEnvelope();
return new SpatialKey(row.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
Index: src/main/org/h2/mvstore/db/MVSpatialIndex.java
===================================================================
--- src/main/org/h2/mvstore/db/MVSpatialIndex.java (revision 6028)
+++ src/main/org/h2/mvstore/db/MVSpatialIndex.java (working copy)
@@ -7,7 +7,6 @@
import java.util.Iterator;
import java.util.List;
-
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
@@ -29,13 +28,12 @@
import org.h2.result.SortOrder;
import org.h2.table.IndexColumn;
import org.h2.table.TableFilter;
+import org.h2.value.IEnvelope;
+import org.h2.value.IGeometry;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueLong;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-
/**
* This is an index based on a MVRTreeMap.
*
@@ -166,8 +164,8 @@
return null;
}
Value v = r.getValue(columnIds[0]);
- Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
- Envelope env = g.getEnvelopeInternal();
+ IGeometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
+ IEnvelope env = g.getEnvelope();
return new SpatialKey(r.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
@@ -221,8 +219,8 @@
private SpatialKey getEnvelope(SearchRow row) {
Value v = row.getValue(columnIds[0]);
- Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
- Envelope env = g.getEnvelopeInternal();
+ IGeometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
+ IEnvelope env = g.getEnvelope();
return new SpatialKey(row.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
Index: src/main/org/h2/value/DataType.java
===================================================================
--- src/main/org/h2/value/DataType.java (revision 6028)
+++ src/main/org/h2/value/DataType.java (working copy)
@@ -50,15 +50,6 @@
public static final int TYPE_RESULT_SET = -10;
/**
- * The Geometry class. This object is null if the jts jar file is not in the
- * classpath.
- */
- public static final Class<?> GEOMETRY_CLASS;
-
- private static final String GEOMETRY_CLASS_NAME =
- "com.vividsolutions.jts.geom.Geometry";
-
- /**
* The list of types. An ArrayList so that Tomcat doesn't set it to null
* when clearing references.
*/
@@ -173,17 +164,6 @@
public int memory;
static {
- Class<?> g;
- try {
- g = JdbcUtils.loadUserClass(GEOMETRY_CLASS_NAME);
- } catch (Exception e) {
- // class is not in the classpath - ignore
- g = null;
- }
- GEOMETRY_CLASS = g;
- }
-
- static {
for (int i = 0; i < Value.TYPE_COUNT; i++) {
TYPES_BY_VALUE_TYPE.add(null);
}
@@ -657,10 +637,11 @@
}
case Value.GEOMETRY: {
Object x = rs.getObject(columnIndex);
- if (x == null) {
+ if (x == null || !(x instanceof IGeometry)) {
return ValueNull.INSTANCE;
}
- return ValueGeometry.getFromGeometry(x);
+
+ return ValueGeometry.get((IGeometry) x);
}
default:
throw DbException.throwInternalError("type="+type);
@@ -740,7 +721,7 @@
case Value.RESULT_SET:
return ResultSet.class.getName();
case Value.GEOMETRY:
- return GEOMETRY_CLASS_NAME;
+ return IGeometry.class.getName();
default:
throw DbException.throwInternalError("type="+type);
}
@@ -938,7 +919,7 @@
} else if (Object[].class.isAssignableFrom(x)) {
// this includes String[] and so on
return Value.ARRAY;
- } else if (isGeometryClass(x)) {
+ } else if (IGeometry.class.isAssignableFrom(x)) {
return Value.GEOMETRY;
} else {
return Value.JAVA_OBJECT;
@@ -1034,8 +1015,8 @@
return ValueArray.get(x.getClass().getComponentType(), v);
} else if (x instanceof Character) {
return ValueStringFixed.get(((Character) x).toString());
- } else if (isGeometry(x)) {
- return ValueGeometry.getFromGeometry(x);
+ } else if (x instanceof IGeometry) {
+ return ValueGeometry.get((IGeometry) x);
} else {
return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
}
@@ -1042,32 +1023,6 @@
}
/**
- * Check whether a given class matches the Geometry class.
- *
- * @param x the class
- * @return true if it is a Geometry class
- */
- public static boolean isGeometryClass(Class<?> x) {
- if (x == null || GEOMETRY_CLASS == null) {
- return false;
- }
- return GEOMETRY_CLASS.isAssignableFrom(x);
- }
-
- /**
- * Check whether a given object is a Geometry object.
- *
- * @param x the the object
- * @return true if it is a Geometry object
- */
- public static boolean isGeometry(Object x) {
- if (x == null) {
- return false;
- }
- return isGeometryClass(x.getClass());
- }
-
- /**
* Get a data type object from a type name.
*
* @param s the type name
Index: src/main/org/h2/value/GeometryParseException.java
===================================================================
--- src/main/org/h2/value/GeometryParseException.java (revision 0)
+++ src/main/org/h2/value/GeometryParseException.java (revision 0)
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
+ * and the EPL 1.0 (http://h2database.com/html/license.html).
+ * Initial Developer: H2 Group
+ */
+package org.h2.value;
+
+/**
+ * Signals that an error has been reached unexpectedly while parsing a geometry.
+ *
+ * @see java.lang.Exception
+ * @see IGeometryFactory
+ */
+public class GeometryParseException extends Exception {
+ /**
+ * Constructs a new exception with the specified detail message. The cause
+ * is not initialized, and may subsequently be initialized by a call to
+ * {@link #initCause}.
+ *
+ * @param message the detail message. The detail message is saved for later
+ * retrieval by the {@link #getMessage()} method.
+ */
+ public GeometryParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified cause and a detail message
+ * of <tt>(cause==null ? null : cause.toString())</tt> (which typically
+ * contains the class and detail message of <tt>cause</tt>).
+ *
+ * This constructor is useful for exceptions that are little more than
+ * wrappers for other throwables (for example,
+ * {@link java.security.PrivilegedActionException}).
+ *
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A <tt>null</tt> value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ * @since 1.4
+ */
+ public GeometryParseException(Exception cause) {
+ super(cause);
+ }
+}
Index: src/main/org/h2/value/IEnvelope.java
===================================================================
--- src/main/org/h2/value/IEnvelope.java (revision 0)
+++ src/main/org/h2/value/IEnvelope.java (revision 0)
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
+ * and the EPL 1.0 (http://h2database.com/html/license.html).
+ * Initial Developer: H2 Group
+ */
+package org.h2.value;
+
+/**
+ * Interface of a envelope type which defines a rectangular region of the 2D
+ * coordinate plane.
+ */
+public interface IEnvelope {
+ /**
+ * Returns the Envelopes minimum x-value.
+ *
+ * @return the Envelopes minimum x-value
+ */
+ public double getMinX();
+
+ /**
+ * Returns the Envelopes minimum y-value.
+ *
+ * @return the Envelopes minimum y-value
+ */
+ public double getMinY();
+
+ /**
+ * Returns the Envelopes maximum x-value.
+ *
+ * @return the Envelopes maximum x-value
+ */
+ public double getMaxX();
+
+ /**
+ * Returns the Envelopes maximum y-value.
+ *
+ * @return the Envelopes maximum y-value
+ */
+ public double getMaxY();
+
+ /**
+ * Check if the region defined by other overlaps (intersects) the region of
+ * this Envelope.
+ *
+ * @param envelope
+ * @return {@code true} if the given envelope overlaps the region of this
+ * envelope, {@code false} otherwise
+ */
+ public boolean intersects(IEnvelope envelope);
+
+ /**
+ * Creates the union of this and the given envelope.
+ *
+ * @param other the other envelope
+ * @return the union of this envelope and another envelope
+ */
+ public IEnvelope getUnion(IEnvelope other);
+}
Index: src/main/org/h2/value/IGeometry.java
===================================================================
--- src/main/org/h2/value/IGeometry.java (revision 0)
+++ src/main/org/h2/value/IGeometry.java (revision 0)
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
+ * and the EPL 1.0 (http://h2database.com/html/license.html).
+ * Initial Developer: H2 Group
+ */
+package org.h2.value;
+
+/**
+ * Interface of a geometry type which provides all necessary information for the
+ * H2 database and it's spatial index.
+ *
+ * @author Steve Hruda, 2014
+ */
+public interface IGeometry extends Comparable<IGeometry>, Cloneable {
+ /**
+ * Returns the string representation of the geometry.
+ *
+ * @return the string representation of the geometry
+ */
+ public String getString();
+
+ /**
+ * Returns the binary representation of the geometry.
+ *
+ * @return the binary representation of the geometry
+ */
+ public byte[] getBytes();
+
+ /**
+ * Returns a full copy of this {@link IGeometry} object.
+ *
+ * @return a full copy of this {@link IGeometry} object
+ */
+ public IGeometry clone();
+
+ /**
+ * Returns the geometries bounding box.
+ *
+ * @return the bounding box
+ */
+ public IEnvelope getEnvelope();
+}
Index: src/main/org/h2/value/IGeometryFactory.java
===================================================================
--- src/main/org/h2/value/IGeometryFactory.java (revision 0)
+++ src/main/org/h2/value/IGeometryFactory.java (revision 0)
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
+ * and the EPL 1.0 (http://h2database.com/html/license.html).
+ * Initial Developer: H2 Group
+ */
+package org.h2.value;
+
+/**
+ * Interface of a factory which provides a couple of methods to create a
+ * {@link IGeometry} instance.
+ */
+public interface IGeometryFactory {
+ /**
+ * Creates a {@link IGeometry} instance by using the given string
+ * representation.
+ *
+ * @param s the string representation of the geometry
+ * @return a new {@link IGeometry} instance
+ * @throws GeometryParseException if a parsing problem occurs
+ */
+ public IGeometry toGeometry(String s) throws GeometryParseException;
+
+ /**
+ * Creates a {@link IGeometry} instance by using the given parameters.
+ *
+ * @param s the string representation of the geometry
+ * @param srid the srid of the object
+ * @return a new {@link IGeometry} instance
+ * @throws GeometryParseException if a parsing problem occurs
+ */
+ public IGeometry toGeometry(String s, int srid) throws GeometryParseException;
+
+ /**
+ * Creates a {@link IGeometry} instance by using the given binary
+ * representation.
+ *
+ * @param bytes the binary representation of the geometry
+ * @return a new {@link IGeometry} instance
+ * @throws GeometryParseException if a parsing problem occurs
+ */
+ public IGeometry toGeometry(byte[] bytes) throws GeometryParseException;
+
+ /**
+ * Creates a {@link IGeometry} instance by using the given parameters.
+ *
+ * @param envelope the envelope
+ * @return a new {@link IGeometry} instance
+ */
+ public IGeometry toGeometry(IEnvelope envelope);
+}
Index: src/main/org/h2/value/Value.java
===================================================================
--- src/main/org/h2/value/Value.java (revision 6028)
+++ src/main/org/h2/value/Value.java (working copy)
@@ -818,8 +818,8 @@
return ValueGeometry.get(getBytesNoCopy());
case JAVA_OBJECT:
Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler());
- if (DataType.isGeometry(object)) {
- return ValueGeometry.getFromGeometry(object);
+ if (object instanceof IGeometry) {
+ return ValueGeometry.get((IGeometry) object);
}
}
}
Index: src/main/org/h2/value/ValueGeometry.java
===================================================================
--- src/main/org/h2/value/ValueGeometry.java (revision 6028)
+++ src/main/org/h2/value/ValueGeometry.java (working copy)
@@ -8,20 +8,10 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
-
-import com.vividsolutions.jts.geom.CoordinateSequence;
-import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
-import com.vividsolutions.jts.geom.PrecisionModel;
+import java.util.Iterator;
+import java.util.ServiceLoader;
import org.h2.message.DbException;
import org.h2.util.StringUtils;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
-import com.vividsolutions.jts.geom.GeometryFactory;
-import com.vividsolutions.jts.io.ParseException;
-import com.vividsolutions.jts.io.WKBReader;
-import com.vividsolutions.jts.io.WKBWriter;
-import com.vividsolutions.jts.io.WKTReader;
-import com.vividsolutions.jts.io.WKTWriter;
/**
* Implementation of the GEOMETRY data type.
@@ -33,12 +23,25 @@
public class ValueGeometry extends Value {
/**
- * As conversion from/to WKB cost a significant amount of CPU cycles, WKB
- * are kept in ValueGeometry instance.
+ * Factory which provides a couple of methods to create a {@link IGeometry}
+ * instance.
+ */
+ private static final IGeometryFactory GEOMETRY_FACTORY;
+
+ static {
+ ServiceLoader<IGeometryFactory> geometryFactories = ServiceLoader.load(IGeometryFactory.class);
+ Iterator<IGeometryFactory> geometryFactoryIterator = geometryFactories.iterator();
+ GEOMETRY_FACTORY = geometryFactoryIterator.hasNext() ? geometryFactories.iterator().next() : null;
+ }
+
+
+ /**
+ * As conversion from/to byte array cost a significant amount of CPU cycles,
+ * byte array are kept in ValueGeometry instance.
*
- * We always calculate the WKB, because not all WKT values can be
- * represented in WKB, but since we persist it in WKB format, it has to be
- * valid in WKB
+ * We always calculate the byte array, because not all geometry string
+ * representation values can be represented in byte array, but since we
+ * persist it in binary format, it has to be valid in byte array
*/
private final byte[] bytes;
@@ -45,10 +48,10 @@
private final int hashCode;
/**
- * The value. Converted from WKB only on request as conversion from/to WKB
- * cost a significant amount of CPU cycles.
+ * The value. Converted from byte array only on request as conversion
+ * from/to byte array cost a significant amount of CPU cycles.
*/
- private Geometry geometry;
+ private IGeometry geometry;
/**
* Create a new geometry objects.
@@ -56,7 +59,7 @@
* @param bytes the bytes (always known)
* @param geometry the geometry object (may be null)
*/
- private ValueGeometry(byte[] bytes, Geometry geometry) {
+ private ValueGeometry(byte[] bytes, IGeometry geometry) {
this.bytes = bytes;
this.geometry = geometry;
this.hashCode = Arrays.hashCode(bytes);
@@ -65,43 +68,25 @@
/**
* Get or create a geometry value for the given geometry.
*
- * @param o the geometry object (of type
- * com.vividsolutions.jts.geom.Geometry)
+ * @param g the geometry object
* @return the value
*/
- public static ValueGeometry getFromGeometry(Object o) {
- return get((Geometry) o);
- }
-
- private static ValueGeometry get(Geometry g) {
- byte[] bytes = convertToWKB(g);
+ public static ValueGeometry get(IGeometry g) {
+ byte[] bytes = g.getBytes();
return (ValueGeometry) Value.cache(new ValueGeometry(bytes, g));
}
- private static byte[] convertToWKB(Geometry g) {
- boolean includeSRID = g.getSRID() != 0;
- int dimensionCount = getDimensionCount(g);
- WKBWriter writer = new WKBWriter(dimensionCount, includeSRID);
- return writer.write(g);
- }
-
- private static int getDimensionCount(Geometry geometry) {
- ZVisitor finder = new ZVisitor();
- geometry.apply(finder);
- return finder.isFoundZ() ? 3 : 2;
- }
-
/**
* Get or create a geometry value for the given geometry.
*
- * @param s the WKT representation of the geometry
+ * @param s the string representation of the geometry
* @return the value
*/
public static ValueGeometry get(String s) {
try {
- Geometry g = new WKTReader().read(s);
+ IGeometry g = GEOMETRY_FACTORY.toGeometry(s);
return get(g);
- } catch (ParseException ex) {
+ } catch (GeometryParseException ex) {
throw DbException.convert(ex);
}
}
@@ -109,16 +94,15 @@
/**
* Get or create a geometry value for the given geometry.
*
- * @param s the WKT representation of the geometry
+ * @param s the string representation of the geometry
* @param srid the srid of the object
* @return the value
*/
public static ValueGeometry get(String s, int srid) {
try {
- GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid);
- Geometry g = new WKTReader(geometryFactory).read(s);
+ IGeometry g = GEOMETRY_FACTORY.toGeometry(s, srid);
return get(g);
- } catch (ParseException ex) {
+ } catch (GeometryParseException ex) {
throw DbException.convert(ex);
}
}
@@ -139,15 +123,15 @@
*
* @return a copy of the geometry object
*/
- public Geometry getGeometry() {
- return (Geometry) getGeometryNoCopy().clone();
+ public IGeometry getGeometry() {
+ return getGeometryNoCopy().clone();
}
- public Geometry getGeometryNoCopy() {
+ public IGeometry getGeometryNoCopy() {
if (geometry == null) {
try {
- geometry = new WKBReader().read(bytes);
- } catch (ParseException ex) {
+ geometry = GEOMETRY_FACTORY.toGeometry(bytes);
+ } catch (GeometryParseException ex) {
throw DbException.convert(ex);
}
}
@@ -163,8 +147,8 @@
*/
public boolean intersectsBoundingBox(ValueGeometry r) {
// the Geometry object caches the envelope
- return getGeometryNoCopy().getEnvelopeInternal().intersects(
- r.getGeometryNoCopy().getEnvelopeInternal());
+ return getGeometryNoCopy().getEnvelope().intersects(
+ r.getGeometryNoCopy().getEnvelope());
}
/**
@@ -174,10 +158,9 @@
* @return the union of this geometry envelope and another geometry envelope
*/
public Value getEnvelopeUnion(ValueGeometry r) {
- GeometryFactory gf = new GeometryFactory();
- Envelope mergedEnvelope = new Envelope(getGeometryNoCopy().getEnvelopeInternal());
- mergedEnvelope.expandToInclude(r.getGeometryNoCopy().getEnvelopeInternal());
- return get(gf.toGeometry(mergedEnvelope));
+ IEnvelope mergedEnvelope = getGeometryNoCopy().getEnvelope().getUnion(
+ r.getGeometryNoCopy().getEnvelope());
+ return get(GEOMETRY_FACTORY.toGeometry(mergedEnvelope));
}
@Override
@@ -195,13 +178,13 @@
@Override
protected int compareSecure(Value v, CompareMode mode) {
- Geometry g = ((ValueGeometry) v).getGeometryNoCopy();
+ IGeometry g = ((ValueGeometry) v).getGeometryNoCopy();
return getGeometryNoCopy().compareTo(g);
}
@Override
public String getString() {
- return getWKT();
+ return getGeometryNoCopy().getString();
}
@Override
@@ -221,12 +204,12 @@
@Override
public byte[] getBytes() {
- return getWKB();
+ return bytes;
}
@Override
public byte[] getBytesNoCopy() {
- return getWKB();
+ return bytes;
}
@Override
@@ -237,40 +220,20 @@
@Override
public int getDisplaySize() {
- return getWKT().length();
+ return getGeometryNoCopy().getString().length();
}
@Override
public int getMemory() {
- return getWKB().length * 20 + 24;
+ return getBytes().length * 20 + 24;
}
@Override
public boolean equals(Object other) {
- // The JTS library only does half-way support for 3D coordinates, so
- // their equals method only checks the first two coordinates.
return other instanceof ValueGeometry &&
- Arrays.equals(getWKB(), ((ValueGeometry) other).getWKB());
+ Arrays.equals(getBytes(), ((ValueGeometry) other).getBytes());
}
- /**
- * Get the value in Well-Known-Text format.
- *
- * @return the well-known-text
- */
- public String getWKT() {
- return new WKTWriter(3).write(getGeometryNoCopy());
- }
-
- /**
- * Get the value in Well-Known-Binary format.
- *
- * @return the well-known-binary
- */
- public byte[] getWKB() {
- return bytes;
- }
-
@Override
public Value convertTo(int targetType) {
if (targetType == Value.JAVA_OBJECT) {
@@ -278,34 +241,4 @@
}
return super.convertTo(targetType);
}
-
- /**
- * A visitor that checks if there is a Z coordinate.
- */
- static class ZVisitor implements CoordinateSequenceFilter {
- boolean foundZ;
-
- public boolean isFoundZ() {
- return foundZ;
- }
-
- @Override
- public void filter(CoordinateSequence coordinateSequence, int i) {
- if (!Double.isNaN(coordinateSequence.getOrdinate(i, 2))) {
- foundZ = true;
- }
- }
-
- @Override
- public boolean isDone() {
- return foundZ;
- }
-
- @Override
- public boolean isGeometryChanged() {
- return false;
- }
-
- }
-
}