From 1295757a7910f13724fe3a5749ea41cf2b8df4e5 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 13 Mar 2015 15:53:16 +0100 Subject: [PATCH 1/9] New - implement geometry interfaces to remove direct reference to JTS --- .../org/h2/api/GeometryParseException.java | 49 +++++ h2/src/main/org/h2/api/IEnvelope.java | 66 +++++++ h2/src/main/org/h2/api/IGeometry.java | 50 +++++ h2/src/main/org/h2/api/IGeometryFactory.java | 51 ++++++ .../main/org/h2/index/SpatialTreeIndex.java | 10 +- h2/src/main/org/h2/jts/H2Envelope.java | 130 +++++++++++++ h2/src/main/org/h2/jts/H2Geometry.java | 166 +++++++++++++++++ h2/src/main/org/h2/jts/H2GeometryFactory.java | 76 ++++++++ .../org/h2/mvstore/db/MVSpatialIndex.java | 14 +- h2/src/main/org/h2/value/DataType.java | 61 +----- h2/src/main/org/h2/value/Value.java | 6 +- h2/src/main/org/h2/value/ValueGeometry.java | 173 ++++++------------ .../services/org.h2.api.IGeometryFactory | 1 + h2/src/test/org/h2/test/db/TestSpatial.java | 59 +++--- 14 files changed, 706 insertions(+), 206 deletions(-) create mode 100644 h2/src/main/org/h2/api/GeometryParseException.java create mode 100644 h2/src/main/org/h2/api/IEnvelope.java create mode 100644 h2/src/main/org/h2/api/IGeometry.java create mode 100644 h2/src/main/org/h2/api/IGeometryFactory.java create mode 100644 h2/src/main/org/h2/jts/H2Envelope.java create mode 100644 h2/src/main/org/h2/jts/H2Geometry.java create mode 100644 h2/src/main/org/h2/jts/H2GeometryFactory.java create mode 100644 h2/src/test/META-INF/services/org.h2.api.IGeometryFactory diff --git a/h2/src/main/org/h2/api/GeometryParseException.java b/h2/src/main/org/h2/api/GeometryParseException.java new file mode 100644 index 000000000..879906b46 --- /dev/null +++ b/h2/src/main/org/h2/api/GeometryParseException.java @@ -0,0 +1,49 @@ +/* + * 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.api; + + +/** + * Signals that an error has been reached unexpectedly while parsing a geometry. + * + * @see java.lang.Exception + * @see IGeometryFactory + */ +public class GeometryParseException extends Exception { + + private static final long serialVersionUID = -3743282683053748926L; + + /** + * 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 (cause==null ? null : cause.toString()) (which typically + * contains the class and detail message of cause). + * + * 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 null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public GeometryParseException(Exception cause) { + super(cause); + } +} diff --git a/h2/src/main/org/h2/api/IEnvelope.java b/h2/src/main/org/h2/api/IEnvelope.java new file mode 100644 index 000000000..4bafbfe85 --- /dev/null +++ b/h2/src/main/org/h2/api/IEnvelope.java @@ -0,0 +1,66 @@ +/* + * 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.api; + +import java.sql.Wrapper; + +/** + * Interface of a envelope type which defines a rectangular region of the 2D + * coordinate plane. + */ +public interface IEnvelope extends Wrapper { + /** + * 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); + + @Override + public boolean isWrapperFor(Class iface); + + @Override + public T unwrap(Class iface); +} diff --git a/h2/src/main/org/h2/api/IGeometry.java b/h2/src/main/org/h2/api/IGeometry.java new file mode 100644 index 000000000..7f0ea6f0a --- /dev/null +++ b/h2/src/main/org/h2/api/IGeometry.java @@ -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.api; + +import java.sql.Wrapper; + +/** + * 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, Cloneable, Wrapper{ + /** + * 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(); + + @Override + public boolean isWrapperFor(Class iface); + + @Override + public T unwrap(Class iface); +} diff --git a/h2/src/main/org/h2/api/IGeometryFactory.java b/h2/src/main/org/h2/api/IGeometryFactory.java new file mode 100644 index 000000000..7f7b1dddd --- /dev/null +++ b/h2/src/main/org/h2/api/IGeometryFactory.java @@ -0,0 +1,51 @@ +/* + * 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.api; + + +/** + * 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); +} diff --git a/h2/src/main/org/h2/index/SpatialTreeIndex.java b/h2/src/main/org/h2/index/SpatialTreeIndex.java index dfff8eade..bd0cd18b1 100644 --- a/h2/src/main/org/h2/index/SpatialTreeIndex.java +++ b/h2/src/main/org/h2/index/SpatialTreeIndex.java @@ -6,7 +6,8 @@ package org.h2.index; import java.util.Iterator; - +import org.h2.api.IEnvelope; +import org.h2.api.IGeometry; import org.h2.engine.Constants; import org.h2.engine.Session; import org.h2.message.DbException; @@ -24,9 +25,6 @@ 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 @@ public void add(Session session, Row row) { 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()); diff --git a/h2/src/main/org/h2/jts/H2Envelope.java b/h2/src/main/org/h2/jts/H2Envelope.java new file mode 100644 index 000000000..56d9cdfbb --- /dev/null +++ b/h2/src/main/org/h2/jts/H2Envelope.java @@ -0,0 +1,130 @@ +package org.h2.jts; + +import java.sql.SQLException; + +import org.h2.api.IEnvelope; +import org.h2.message.DbException; + +import com.vividsolutions.jts.geom.Envelope; + +/** + * Implementation of the {@link IEnvelope} interface which wraps the + * {@link Envelope}. + */ +public class H2Envelope implements IEnvelope { + private final Envelope envelope; + + /** + * Constructs a new {@link H2Envelope} instance by using the given + * parameters. + */ + public H2Envelope(Envelope envelope) { + this.envelope = envelope; + } + + /** + * @see org.h2.value.IEnvelope#getMinX() + */ + @Override + public double getMinX() { + return envelope.getMinX(); + } + + /** + * @see org.h2.value.IEnvelope#getMinY() + */ + @Override + public double getMinY() { + return envelope.getMinY(); + } + + /** + * @see org.h2.value.IEnvelope#getMaxX() + */ + @Override + public double getMaxX() { + return envelope.getMaxX(); + } + + /** + * @see org.h2.value.IEnvelope#getMaxY() + */ + @Override + public double getMaxY() { + return envelope.getMaxY(); + } + + /** + * @see org.h2.value.IEnvelope#intersects(org.h2.value.IEnvelope) + */ + @Override + public boolean intersects(IEnvelope other) { + + //intersects is only supported if the given envelope is wrapper for com.vividsolutions.jts.geom.Envelope + if (!other.isWrapperFor(Envelope.class)) + return false; + + return this.envelope.intersects(other.unwrap(Envelope.class)); + } + + /** + * @see org.h2.value.IEnvelope#getUnion(org.h2.value.IEnvelope) + */ + @Override + public IEnvelope getUnion(IEnvelope other) { + Envelope mergedEnvelope = new Envelope(envelope); + + // union is only supported if the given envelope is wrapper for com.vividsolutions.jts.geom.Envelope + if (other.isWrapperFor(Envelope.class)) + mergedEnvelope.expandToInclude(other.unwrap(Envelope.class)); + + return new H2Envelope(mergedEnvelope); + } + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class iface) { + if (isWrapperFor(iface)) { + return (T) envelope; + } + throw DbException.getInvalidValueException("iface", iface); + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface != null && iface.isAssignableFrom(envelope.getClass()); + } + + @Override + public String toString() { + return envelope.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((envelope == null) ? 0 : envelope.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + H2Envelope other = (H2Envelope) obj; + if (envelope == null) { + if (other.envelope != null) + return false; + } else if (!envelope.equals(other.envelope)) + return false; + return true; + } + + +} diff --git a/h2/src/main/org/h2/jts/H2Geometry.java b/h2/src/main/org/h2/jts/H2Geometry.java new file mode 100644 index 000000000..e7d3ac220 --- /dev/null +++ b/h2/src/main/org/h2/jts/H2Geometry.java @@ -0,0 +1,166 @@ +package org.h2.jts; + +import java.sql.SQLException; + +import org.h2.api.IEnvelope; +import org.h2.api.IGeometry; +import org.h2.message.DbException; + +import com.vividsolutions.jts.geom.CoordinateSequence; +import com.vividsolutions.jts.geom.CoordinateSequenceFilter; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.io.WKBWriter; +import com.vividsolutions.jts.io.WKTWriter; + +/** + * Implementation of the {@link IGeometry} interface which wraps the + * {@link Geometry}. + */ +public class H2Geometry implements IGeometry { + private final Geometry geometry; + + private IEnvelope envelope; + + /** + * Constructs a new {@link H2Geometry} instance by using the given + * parameters. + * + * @param geometry + * geometry to wrap + */ + public H2Geometry(Geometry geometry) { + this.geometry = geometry; + } + + /** + * @see java.lang.Comparable#compareTo(java.lang.Object) + * @throws AssertionError + * if the unwrap of {@link Geometry} is not possible. + */ + @Override + public int compareTo(IGeometry g) throws AssertionError { + if (!g.isWrapperFor(Geometry.class)) + throw new AssertionError( + "Comparision isn't supported if 'com.vividsolutions.jts.geom.Geometry' can't be unwrapped!"); + + return geometry.compareTo(g.unwrap(Geometry.class)); + } + + /** + * @see org.h2.value.IGeometry#getString() + */ + @Override + public String getString() { + return new WKTWriter(3).write(geometry); + } + + /** + * @see org.h2.value.IGeometry#getBytes() + */ + @Override + public byte[] getBytes() { + boolean includeSRID = geometry.getSRID() != 0; + int dimensionCount = getDimensionCount(geometry); + WKBWriter writer = new WKBWriter(dimensionCount, includeSRID); + return writer.write(geometry); + } + + private static int getDimensionCount(Geometry geometry) { + ZVisitor finder = new ZVisitor(); + geometry.apply(finder); + return finder.isFoundZ() ? 3 : 2; + } + + /** + * @see org.h2.value.IGeometry#clone() + */ + @Override + public IGeometry clone() { + return new H2Geometry((Geometry) geometry.clone()); + } + + /** + * @see org.h2.value.IGeometry#getEnvelope() + */ + @Override + public IEnvelope getEnvelope() { + if (envelope != null) { + return envelope; + } + + return envelope = new H2Envelope(geometry.getEnvelopeInternal()); + } + + @SuppressWarnings("unchecked") + @Override + public T unwrap(Class iface) { + if (isWrapperFor(iface)) { + return (T) geometry; + } + throw DbException.getInvalidValueException("iface", iface); + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface != null && iface.isAssignableFrom(geometry.getClass()); + } + + @Override + public String toString() { + return getString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((geometry == null) ? 0 : geometry.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + H2Geometry other = (H2Geometry) obj; + if (geometry == null) { + if (other.geometry != null) + return false; + } else if (!geometry.equals(other.geometry)) + return false; + return true; + } + + /** + * A visitor that checks if there is a Z coordinate. + */ + private 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; + } + } +} \ No newline at end of file diff --git a/h2/src/main/org/h2/jts/H2GeometryFactory.java b/h2/src/main/org/h2/jts/H2GeometryFactory.java new file mode 100644 index 000000000..e2af36624 --- /dev/null +++ b/h2/src/main/org/h2/jts/H2GeometryFactory.java @@ -0,0 +1,76 @@ +package org.h2.jts; + +import org.h2.api.GeometryParseException; +import org.h2.api.IEnvelope; +import org.h2.api.IGeometry; +import org.h2.api.IGeometryFactory; +import org.h2.message.DbException; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.PrecisionModel; +import com.vividsolutions.jts.io.ParseException; +import com.vividsolutions.jts.io.WKBReader; +import com.vividsolutions.jts.io.WKTReader; + +/** + * Implementation of the {@link IGeometryFactory} interface to support the JTS + * Topology Suite. + */ +public class H2GeometryFactory implements IGeometryFactory { + /** + * @see org.h2.value.IGeometryFactory#toGeometry(java.lang.String) + */ + @Override + public IGeometry toGeometry(String s) throws GeometryParseException { + try { + return new H2Geometry(new WKTReader().read(s)); + } catch (ParseException e) { + throw new GeometryParseException(e); + } + } + + /** + * @see org.h2.value.IGeometryFactory#toGeometry(java.lang.String, int) + */ + @Override + public IGeometry toGeometry(String s, int srid) + throws GeometryParseException { + try { + GeometryFactory geometryFactory = new GeometryFactory( + new PrecisionModel(), srid); + Geometry g = new WKTReader(geometryFactory).read(s); + return new H2Geometry(g); + } catch (ParseException e) { + throw new GeometryParseException(e); + } + + } + + /** + * @see org.h2.value.IGeometryFactory#toGeometry(byte[]) + */ + @Override + public IGeometry toGeometry(byte[] bytes) throws GeometryParseException { + try { + return new H2Geometry(new WKBReader().read(bytes)); + } catch (ParseException ex) { + throw DbException.convert(ex); + } + } + + /** + * @see org.h2.value.IGeometryFactory#toGeometry(org.h2.value.IEnvelope) + */ + @Override + public IGeometry toGeometry(IEnvelope envelope) { + if (!H2Envelope.class.isInstance(envelope)) + throw new AssertionError( + "The given envelope must be an instance of H2Envelope!"); + + GeometryFactory geometryFactory = new GeometryFactory(); + return new H2Geometry( + geometryFactory.toGeometry(envelope.unwrap(Envelope.class))); + } +} diff --git a/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java b/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java index caf7b12f9..b37327c84 100644 --- a/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java +++ b/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java @@ -7,8 +7,9 @@ import java.util.Iterator; import java.util.List; - import org.h2.api.ErrorCode; +import org.h2.api.IEnvelope; +import org.h2.api.IGeometry; import org.h2.engine.Database; import org.h2.engine.Session; import org.h2.index.BaseIndex; @@ -33,9 +34,6 @@ 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 @@ private SpatialKey getKey(SearchRow r) { 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 @@ public Cursor findByGeometry(TableFilter filter, SearchRow intersection) { 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()); diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java index 1efa47a2f..b05a1e65a 100644 --- a/h2/src/main/org/h2/value/DataType.java +++ b/h2/src/main/org/h2/value/DataType.java @@ -22,8 +22,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.UUID; - import org.h2.api.ErrorCode; +import org.h2.api.IGeometry; import org.h2.engine.Constants; import org.h2.engine.SessionInterface; import org.h2.engine.SysProperties; @@ -49,15 +49,6 @@ public class DataType { */ 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. @@ -172,17 +163,6 @@ public class DataType { */ 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 @@ public static Value readValue(SessionInterface session, ResultSet rs, } 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 @@ public static String getTypeClassName(int type) { 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 @@ public static int getTypeFromClass(Class x) { } 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,39 +1015,13 @@ public static Value convertToValue(SessionInterface session, Object x, 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()); } } - /** - * 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. * diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java index a78f9c8d3..f1541521e 100644 --- a/h2/src/main/org/h2/value/Value.java +++ b/h2/src/main/org/h2/value/Value.java @@ -18,8 +18,8 @@ import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; - import org.h2.api.ErrorCode; +import org.h2.api.IGeometry; import org.h2.engine.Constants; import org.h2.engine.SysProperties; import org.h2.message.DbException; @@ -818,8 +818,8 @@ public Value convertTo(int targetType) { 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); } } } diff --git a/h2/src/main/org/h2/value/ValueGeometry.java b/h2/src/main/org/h2/value/ValueGeometry.java index 687b861dc..bce9cd5f4 100644 --- a/h2/src/main/org/h2/value/ValueGeometry.java +++ b/h2/src/main/org/h2/value/ValueGeometry.java @@ -8,20 +8,14 @@ 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.api.GeometryParseException; +import org.h2.api.IEnvelope; +import org.h2.api.IGeometry; +import org.h2.api.IGeometryFactory; 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,22 +27,34 @@ 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 geometryFactories = ServiceLoader.load(IGeometryFactory.class); + Iterator 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; 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 +62,7 @@ public class ValueGeometry extends Value { * @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 +71,25 @@ private ValueGeometry(byte[] bytes, Geometry geometry) { /** * 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 +97,15 @@ public static ValueGeometry get(String s) { /** * 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 +126,15 @@ public static ValueGeometry get(byte[] bytes) { * * @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 +150,8 @@ public Geometry getGeometryNoCopy() { */ 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 +161,9 @@ public boolean intersectsBoundingBox(ValueGeometry r) { * @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 +181,13 @@ public String getSQL() { @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 +207,12 @@ public Object getObject() { @Override public byte[] getBytes() { - return getWKB(); + return bytes; } @Override public byte[] getBytesNoCopy() { - return getWKB(); + return bytes; } @Override @@ -237,38 +223,18 @@ public void set(PreparedStatement prep, int parameterIndex) @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()); - } - - /** - * 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; + Arrays.equals(getBytes(), ((ValueGeometry) other).getBytes()); } @Override @@ -278,34 +244,13 @@ public Value convertTo(int targetType) { } return super.convertTo(targetType); } - + /** - * A visitor that checks if there is a Z coordinate. + * Returns true if a IGeometryFactory is available and initialized. + * @return */ - 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; - } - + public static boolean isInitialized() + { + return GEOMETRY_FACTORY!=null; } - } diff --git a/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory b/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory new file mode 100644 index 000000000..54285b021 --- /dev/null +++ b/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory @@ -0,0 +1 @@ +org.h2.jts.H2GeometryFactory \ No newline at end of file diff --git a/h2/src/test/org/h2/test/db/TestSpatial.java b/h2/src/test/org/h2/test/db/TestSpatial.java index fcc8a20a2..fce0ff1eb 100644 --- a/h2/src/test/org/h2/test/db/TestSpatial.java +++ b/h2/src/test/org/h2/test/db/TestSpatial.java @@ -16,18 +16,26 @@ import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.util.AffineTransformation; + import org.h2.api.Aggregate; +import org.h2.api.IEnvelope; +import org.h2.api.IGeometry; +import org.h2.jts.H2Envelope; +import org.h2.jts.H2Geometry; +import org.h2.jts.H2GeometryFactory; import org.h2.test.TestBase; import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleRowSource; import org.h2.value.DataType; import org.h2.value.Value; + import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; + import org.h2.value.ValueGeometry; /** @@ -58,7 +66,7 @@ public void test() throws SQLException { if (config.memory && config.mvcc) { return; } - if (DataType.GEOMETRY_CLASS != null) { + if (ValueGeometry.isInitialized()) { deleteDb("spatial"); url = "spatial"; testSpatial(); @@ -121,7 +129,8 @@ private void testSpatialValues() throws SQLException { new Coordinate(1, 2), new Coordinate(2, 2), new Coordinate(1, 1) }); - assertTrue(polygon.equals(rs.getObject(2))); + + assertTrue(new H2Geometry(polygon).equals(rs.getObject(2))); rs = stat.executeQuery("select * from test where polygon = " + "'POLYGON ((1 1, 1 2, 2 2, 1 1))'"); @@ -567,12 +576,12 @@ public void reset() throws SQLException { * @param srid the projection id * @return Geometry object */ - public static Geometry geomFromText(String text, int srid) throws SQLException { + public static IGeometry geomFromText(String text, int srid) throws SQLException { WKTReader wktReader = new WKTReader(); try { Geometry geom = wktReader.read(text); geom.setSRID(srid); - return geom; + return new H2Geometry(geom); } catch (ParseException ex) { throw new SQLException(ex); } @@ -580,23 +589,27 @@ public static Geometry geomFromText(String text, int srid) throws SQLException { private void testGeometryDataType() { GeometryFactory geometryFactory = new GeometryFactory(); - Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); + H2Geometry geometry = new H2Geometry(geometryFactory.createPoint(new Coordinate(0, 0))); assertEquals(Value.GEOMETRY, DataType.getTypeFromClass(geometry.getClass())); } /** * Test serialization of Z and SRID values. + * @throws SQLException */ - private void testWKB() { + private void testWKB() throws SQLException { ValueGeometry geom3d = ValueGeometry.get( "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))", 27572); ValueGeometry copy = ValueGeometry.get(geom3d.getBytes()); - assertEquals(6, copy.getGeometry().getCoordinates()[0].z); - assertEquals(5, copy.getGeometry().getCoordinates()[1].z); - assertEquals(4, copy.getGeometry().getCoordinates()[2].z); + + Geometry geometry = copy.getGeometry().unwrap(Geometry.class); + + assertEquals(6, geometry.getCoordinates()[0].z); + assertEquals(5, geometry.getCoordinates()[1].z); + assertEquals(4, geometry.getCoordinates()[2].z); // Test SRID copy = ValueGeometry.get(geom3d.getBytes()); - assertEquals(27572, copy.getGeometry().getSRID()); + assertEquals(27572, geometry.getSRID()); } /** @@ -641,12 +654,13 @@ private void testEquals() { GeometryFactory geometryFactory = new GeometryFactory(); Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); geometry.setSRID(27572); + ValueGeometry valueGeometry = - ValueGeometry.getFromGeometry(geometry); + ValueGeometry.get(new H2Geometry(geometry)); Geometry geometry2 = geometryFactory.createPoint(new Coordinate(0, 0)); geometry2.setSRID(5326); ValueGeometry valueGeometry2 = - ValueGeometry.getFromGeometry(geometry2); + ValueGeometry.get(new H2Geometry(geometry2)); assertFalse(valueGeometry.equals(valueGeometry2)); // Check illegal geometry (no WKB representation) try { @@ -709,9 +723,9 @@ private void testAggregateWithGeometry() throws SQLException { assertEquals("geometry", rs.getMetaData(). getColumnTypeName(1).toLowerCase()); assertTrue(rs.next()); - assertTrue(rs.getObject(1) instanceof Geometry); + assertTrue(rs.getObject(1) instanceof IGeometry); assertTrue(new Envelope(1, 10, 1, 5).equals( - ((Geometry) rs.getObject(1)).getEnvelopeInternal())); + ((IGeometry) rs.getObject(1)).getEnvelope().unwrap(Envelope.class))); assertFalse(rs.next()); } finally { conn.close(); @@ -742,18 +756,18 @@ public void init(Connection conn) throws SQLException { @Override public void add(Object value) throws SQLException { - if (value instanceof Geometry) { + if (value instanceof IGeometry) { if (tableEnvelope == null) { - tableEnvelope = ((Geometry) value).getEnvelopeInternal(); + tableEnvelope = ((IGeometry) value).getEnvelope().unwrap(Envelope.class); } else { - tableEnvelope.expandToInclude(((Geometry) value).getEnvelopeInternal()); + tableEnvelope.expandToInclude(((IGeometry) value).getEnvelope().unwrap(Envelope.class)); } } } @Override public Object getResult() throws SQLException { - return new GeometryFactory().toGeometry(tableEnvelope); + return new H2Geometry(new GeometryFactory().toGeometry(tableEnvelope)); } } @@ -802,7 +816,8 @@ private void testValueGeometryScript() throws SQLException { "SELECT " + valueGeometry.getSQL()); assertTrue(rs.next()); Object obj = rs.getObject(1); - ValueGeometry g = ValueGeometry.getFromGeometry(obj); + assertTrue(obj instanceof IGeometry); + ValueGeometry g = ValueGeometry.get((IGeometry) obj); assertTrue("got: " + g + " exp: " + valueGeometry, valueGeometry.equals(g)); } finally { conn.close(); @@ -820,15 +835,15 @@ private void testInPlaceUpdate() throws SQLException { "SELECT 'POINT(1 1)'::geometry"); assertTrue(rs.next()); // Mutate the geometry - ((Geometry) rs.getObject(1)).apply(new AffineTransformation(1, 0, + ((IGeometry) rs.getObject(1)).unwrap(Geometry.class).apply(new AffineTransformation(1, 0, 1, 1, 0, 1)); rs.close(); rs = conn.createStatement().executeQuery( "SELECT 'POINT(1 1)'::geometry"); assertTrue(rs.next()); // Check if the geometry is the one requested - assertEquals(1, ((Point) rs.getObject(1)).getX()); - assertEquals(1, ((Point) rs.getObject(1)).getY()); + assertEquals(1, ((IGeometry) rs.getObject(1)).unwrap(Point.class).getX()); + assertEquals(1, ((IGeometry) rs.getObject(1)).unwrap(Point.class).getY()); rs.close(); } finally { conn.close(); From cc31fb303bf45717ed889e8cc859e62d5c489082 Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Thu, 19 Mar 2015 16:26:36 +0100 Subject: [PATCH 2/9] Make ValueGeometry abstract and use only one factory to remove the direct dependency between JTS and H2 --- h2/src/main/org/h2/api/IEnvelope.java | 66 ------- h2/src/main/org/h2/api/IGeometry.java | 50 ------ h2/src/main/org/h2/api/IGeometryFactory.java | 51 ------ .../org/h2/api/IValueGeometryFactory.java | 72 ++++++++ .../main/org/h2/index/SpatialTreeIndex.java | 12 +- h2/src/main/org/h2/jts/H2Envelope.java | 130 -------------- h2/src/main/org/h2/jts/H2Geometry.java | 166 ------------------ h2/src/main/org/h2/jts/H2GeometryFactory.java | 76 -------- .../org/h2/mvstore/db/MVSpatialIndex.java | 27 +-- .../main/org/h2/mvstore/db/ValueDataType.java | 2 +- h2/src/main/org/h2/value/DataType.java | 13 +- h2/src/main/org/h2/value/H2ValueGeometry.java | 61 +++++++ h2/src/main/org/h2/value/Transfer.java | 4 +- h2/src/main/org/h2/value/Value.java | 42 ++++- h2/src/main/org/h2/value/ValueGeometry.java | 148 +++------------- .../org/h2/value/ValueGeometryFactory.java | 139 +++++++++++++++ .../services/org.h2.api.IGeometryFactory | 1 - .../services/org.h2.api.IValueGeometryFactory | 1 + h2/src/test/org/h2/test/db/TestSpatial.java | 89 +++++----- 19 files changed, 395 insertions(+), 755 deletions(-) delete mode 100644 h2/src/main/org/h2/api/IEnvelope.java delete mode 100644 h2/src/main/org/h2/api/IGeometry.java delete mode 100644 h2/src/main/org/h2/api/IGeometryFactory.java create mode 100644 h2/src/main/org/h2/api/IValueGeometryFactory.java delete mode 100644 h2/src/main/org/h2/jts/H2Envelope.java delete mode 100644 h2/src/main/org/h2/jts/H2Geometry.java delete mode 100644 h2/src/main/org/h2/jts/H2GeometryFactory.java create mode 100644 h2/src/main/org/h2/value/H2ValueGeometry.java create mode 100644 h2/src/main/org/h2/value/ValueGeometryFactory.java delete mode 100644 h2/src/test/META-INF/services/org.h2.api.IGeometryFactory create mode 100644 h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory diff --git a/h2/src/main/org/h2/api/IEnvelope.java b/h2/src/main/org/h2/api/IEnvelope.java deleted file mode 100644 index 4bafbfe85..000000000 --- a/h2/src/main/org/h2/api/IEnvelope.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.api; - -import java.sql.Wrapper; - -/** - * Interface of a envelope type which defines a rectangular region of the 2D - * coordinate plane. - */ -public interface IEnvelope extends Wrapper { - /** - * 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); - - @Override - public boolean isWrapperFor(Class iface); - - @Override - public T unwrap(Class iface); -} diff --git a/h2/src/main/org/h2/api/IGeometry.java b/h2/src/main/org/h2/api/IGeometry.java deleted file mode 100644 index 7f0ea6f0a..000000000 --- a/h2/src/main/org/h2/api/IGeometry.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.api; - -import java.sql.Wrapper; - -/** - * 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, Cloneable, Wrapper{ - /** - * 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(); - - @Override - public boolean isWrapperFor(Class iface); - - @Override - public T unwrap(Class iface); -} diff --git a/h2/src/main/org/h2/api/IGeometryFactory.java b/h2/src/main/org/h2/api/IGeometryFactory.java deleted file mode 100644 index 7f7b1dddd..000000000 --- a/h2/src/main/org/h2/api/IGeometryFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.api; - - -/** - * 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); -} diff --git a/h2/src/main/org/h2/api/IValueGeometryFactory.java b/h2/src/main/org/h2/api/IValueGeometryFactory.java new file mode 100644 index 000000000..00cbede3b --- /dev/null +++ b/h2/src/main/org/h2/api/IValueGeometryFactory.java @@ -0,0 +1,72 @@ +package org.h2.api; + +import org.h2.message.DbException; +import org.h2.value.ValueGeometry; + +import com.vividsolutions.jts.geom.Geometry; + +public interface IValueGeometryFactory, S> { + + /** + * Get or create a geometry value for the given geometry. + * + * @param g + * the geometry object + * @return the value + */ + public T get(Object g); + + public T get(byte[] g); + + public Class getGeometryType(); + + public boolean isGeometryTypeSupported(Object g); + + public S getGeometry(byte[] bytes) throws DbException; + + /** + * 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 S getGeometry(String s) throws DbException; + + + /** + * Get or create a geometry value for the given geometry. + * + * @param s the WKT representation of the geometry + * @param srid the srid of the object + * @return the value + */ + public S getGeometry(String s, int srid) throws DbException; + + /** + * Get or create a geometry value for the given geometry. + * + * @param g + * the geometry object + * @return the value + */ + public T get(Geometry g); + + /** + * Get or create a geometry value for the given geometry. + * + * @param s the WKT representation of the geometry + * @return the value + */ + public T get(String s); + + /** + * Get or create a geometry value for the given geometry. + * + * @param s the WKT representation of the geometry + * @param srid the srid of the object + * @return the value + */ + public T get(String s, int srid); +} diff --git a/h2/src/main/org/h2/index/SpatialTreeIndex.java b/h2/src/main/org/h2/index/SpatialTreeIndex.java index bd0cd18b1..b72072f13 100644 --- a/h2/src/main/org/h2/index/SpatialTreeIndex.java +++ b/h2/src/main/org/h2/index/SpatialTreeIndex.java @@ -6,8 +6,7 @@ package org.h2.index; import java.util.Iterator; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; + import org.h2.engine.Constants; import org.h2.engine.Session; import org.h2.message.DbException; @@ -127,12 +126,11 @@ public void add(Session session, Row row) { } private SpatialKey getEnvelope(SearchRow row) { + if (row == null) { + return null; + } Value v = row.getValue(columnIds[0]); - 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()); + return ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getSpatialKey(row.getKey()); } @Override diff --git a/h2/src/main/org/h2/jts/H2Envelope.java b/h2/src/main/org/h2/jts/H2Envelope.java deleted file mode 100644 index 56d9cdfbb..000000000 --- a/h2/src/main/org/h2/jts/H2Envelope.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.h2.jts; - -import java.sql.SQLException; - -import org.h2.api.IEnvelope; -import org.h2.message.DbException; - -import com.vividsolutions.jts.geom.Envelope; - -/** - * Implementation of the {@link IEnvelope} interface which wraps the - * {@link Envelope}. - */ -public class H2Envelope implements IEnvelope { - private final Envelope envelope; - - /** - * Constructs a new {@link H2Envelope} instance by using the given - * parameters. - */ - public H2Envelope(Envelope envelope) { - this.envelope = envelope; - } - - /** - * @see org.h2.value.IEnvelope#getMinX() - */ - @Override - public double getMinX() { - return envelope.getMinX(); - } - - /** - * @see org.h2.value.IEnvelope#getMinY() - */ - @Override - public double getMinY() { - return envelope.getMinY(); - } - - /** - * @see org.h2.value.IEnvelope#getMaxX() - */ - @Override - public double getMaxX() { - return envelope.getMaxX(); - } - - /** - * @see org.h2.value.IEnvelope#getMaxY() - */ - @Override - public double getMaxY() { - return envelope.getMaxY(); - } - - /** - * @see org.h2.value.IEnvelope#intersects(org.h2.value.IEnvelope) - */ - @Override - public boolean intersects(IEnvelope other) { - - //intersects is only supported if the given envelope is wrapper for com.vividsolutions.jts.geom.Envelope - if (!other.isWrapperFor(Envelope.class)) - return false; - - return this.envelope.intersects(other.unwrap(Envelope.class)); - } - - /** - * @see org.h2.value.IEnvelope#getUnion(org.h2.value.IEnvelope) - */ - @Override - public IEnvelope getUnion(IEnvelope other) { - Envelope mergedEnvelope = new Envelope(envelope); - - // union is only supported if the given envelope is wrapper for com.vividsolutions.jts.geom.Envelope - if (other.isWrapperFor(Envelope.class)) - mergedEnvelope.expandToInclude(other.unwrap(Envelope.class)); - - return new H2Envelope(mergedEnvelope); - } - - @Override - @SuppressWarnings("unchecked") - public T unwrap(Class iface) { - if (isWrapperFor(iface)) { - return (T) envelope; - } - throw DbException.getInvalidValueException("iface", iface); - } - - @Override - public boolean isWrapperFor(Class iface) { - return iface != null && iface.isAssignableFrom(envelope.getClass()); - } - - @Override - public String toString() { - return envelope.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((envelope == null) ? 0 : envelope.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - H2Envelope other = (H2Envelope) obj; - if (envelope == null) { - if (other.envelope != null) - return false; - } else if (!envelope.equals(other.envelope)) - return false; - return true; - } - - -} diff --git a/h2/src/main/org/h2/jts/H2Geometry.java b/h2/src/main/org/h2/jts/H2Geometry.java deleted file mode 100644 index e7d3ac220..000000000 --- a/h2/src/main/org/h2/jts/H2Geometry.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.h2.jts; - -import java.sql.SQLException; - -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.message.DbException; - -import com.vividsolutions.jts.geom.CoordinateSequence; -import com.vividsolutions.jts.geom.CoordinateSequenceFilter; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.io.WKBWriter; -import com.vividsolutions.jts.io.WKTWriter; - -/** - * Implementation of the {@link IGeometry} interface which wraps the - * {@link Geometry}. - */ -public class H2Geometry implements IGeometry { - private final Geometry geometry; - - private IEnvelope envelope; - - /** - * Constructs a new {@link H2Geometry} instance by using the given - * parameters. - * - * @param geometry - * geometry to wrap - */ - public H2Geometry(Geometry geometry) { - this.geometry = geometry; - } - - /** - * @see java.lang.Comparable#compareTo(java.lang.Object) - * @throws AssertionError - * if the unwrap of {@link Geometry} is not possible. - */ - @Override - public int compareTo(IGeometry g) throws AssertionError { - if (!g.isWrapperFor(Geometry.class)) - throw new AssertionError( - "Comparision isn't supported if 'com.vividsolutions.jts.geom.Geometry' can't be unwrapped!"); - - return geometry.compareTo(g.unwrap(Geometry.class)); - } - - /** - * @see org.h2.value.IGeometry#getString() - */ - @Override - public String getString() { - return new WKTWriter(3).write(geometry); - } - - /** - * @see org.h2.value.IGeometry#getBytes() - */ - @Override - public byte[] getBytes() { - boolean includeSRID = geometry.getSRID() != 0; - int dimensionCount = getDimensionCount(geometry); - WKBWriter writer = new WKBWriter(dimensionCount, includeSRID); - return writer.write(geometry); - } - - private static int getDimensionCount(Geometry geometry) { - ZVisitor finder = new ZVisitor(); - geometry.apply(finder); - return finder.isFoundZ() ? 3 : 2; - } - - /** - * @see org.h2.value.IGeometry#clone() - */ - @Override - public IGeometry clone() { - return new H2Geometry((Geometry) geometry.clone()); - } - - /** - * @see org.h2.value.IGeometry#getEnvelope() - */ - @Override - public IEnvelope getEnvelope() { - if (envelope != null) { - return envelope; - } - - return envelope = new H2Envelope(geometry.getEnvelopeInternal()); - } - - @SuppressWarnings("unchecked") - @Override - public T unwrap(Class iface) { - if (isWrapperFor(iface)) { - return (T) geometry; - } - throw DbException.getInvalidValueException("iface", iface); - } - - @Override - public boolean isWrapperFor(Class iface) { - return iface != null && iface.isAssignableFrom(geometry.getClass()); - } - - @Override - public String toString() { - return getString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((geometry == null) ? 0 : geometry.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - H2Geometry other = (H2Geometry) obj; - if (geometry == null) { - if (other.geometry != null) - return false; - } else if (!geometry.equals(other.geometry)) - return false; - return true; - } - - /** - * A visitor that checks if there is a Z coordinate. - */ - private 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; - } - } -} \ No newline at end of file diff --git a/h2/src/main/org/h2/jts/H2GeometryFactory.java b/h2/src/main/org/h2/jts/H2GeometryFactory.java deleted file mode 100644 index e2af36624..000000000 --- a/h2/src/main/org/h2/jts/H2GeometryFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.h2.jts; - -import org.h2.api.GeometryParseException; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.api.IGeometryFactory; -import org.h2.message.DbException; - -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.geom.PrecisionModel; -import com.vividsolutions.jts.io.ParseException; -import com.vividsolutions.jts.io.WKBReader; -import com.vividsolutions.jts.io.WKTReader; - -/** - * Implementation of the {@link IGeometryFactory} interface to support the JTS - * Topology Suite. - */ -public class H2GeometryFactory implements IGeometryFactory { - /** - * @see org.h2.value.IGeometryFactory#toGeometry(java.lang.String) - */ - @Override - public IGeometry toGeometry(String s) throws GeometryParseException { - try { - return new H2Geometry(new WKTReader().read(s)); - } catch (ParseException e) { - throw new GeometryParseException(e); - } - } - - /** - * @see org.h2.value.IGeometryFactory#toGeometry(java.lang.String, int) - */ - @Override - public IGeometry toGeometry(String s, int srid) - throws GeometryParseException { - try { - GeometryFactory geometryFactory = new GeometryFactory( - new PrecisionModel(), srid); - Geometry g = new WKTReader(geometryFactory).read(s); - return new H2Geometry(g); - } catch (ParseException e) { - throw new GeometryParseException(e); - } - - } - - /** - * @see org.h2.value.IGeometryFactory#toGeometry(byte[]) - */ - @Override - public IGeometry toGeometry(byte[] bytes) throws GeometryParseException { - try { - return new H2Geometry(new WKBReader().read(bytes)); - } catch (ParseException ex) { - throw DbException.convert(ex); - } - } - - /** - * @see org.h2.value.IGeometryFactory#toGeometry(org.h2.value.IEnvelope) - */ - @Override - public IGeometry toGeometry(IEnvelope envelope) { - if (!H2Envelope.class.isInstance(envelope)) - throw new AssertionError( - "The given envelope must be an instance of H2Envelope!"); - - GeometryFactory geometryFactory = new GeometryFactory(); - return new H2Geometry( - geometryFactory.toGeometry(envelope.unwrap(Envelope.class))); - } -} diff --git a/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java b/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java index b37327c84..3bc050e27 100644 --- a/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java +++ b/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java @@ -8,8 +8,6 @@ import java.util.Iterator; import java.util.List; import org.h2.api.ErrorCode; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; import org.h2.engine.Database; import org.h2.engine.Session; import org.h2.index.BaseIndex; @@ -120,7 +118,7 @@ public void close(Session session) { @Override public void add(Session session, Row row) { TransactionMap map = getMap(session); - SpatialKey key = getKey(row); + SpatialKey key = getEnvelope(row); if (indexType.isUnique()) { // this will detect committed entries only RTreeCursor cursor = spatialMap.findContainedKeys(key); @@ -159,21 +157,17 @@ public void add(Session session, Row row) { } } - private SpatialKey getKey(SearchRow r) { - if (r == null) { + private SpatialKey getEnvelope(SearchRow row) { + if (row == null) { return null; } - Value v = r.getValue(columnIds[0]); - 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()); + Value v = row.getValue(columnIds[0]); + return ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getSpatialKey(row.getKey()); } @Override public void remove(Session session, Row row) { - SpatialKey key = getKey(row); + SpatialKey key = getEnvelope(row); TransactionMap map = getMap(session); try { Value old = map.remove(key); @@ -217,15 +211,6 @@ public Cursor findByGeometry(TableFilter filter, SearchRow intersection) { return new MVStoreCursor(session, it); } - private SpatialKey getEnvelope(SearchRow row) { - Value v = row.getValue(columnIds[0]); - 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()); - } - /** * Get the row with the given index key. diff --git a/h2/src/main/org/h2/mvstore/db/ValueDataType.java b/h2/src/main/org/h2/mvstore/db/ValueDataType.java index 76bc4ce59..1be63d99c 100644 --- a/h2/src/main/org/h2/mvstore/db/ValueDataType.java +++ b/h2/src/main/org/h2/mvstore/db/ValueDataType.java @@ -576,7 +576,7 @@ private Object readValue(ByteBuffer buff) { int len = readVarInt(buff); byte[] b = DataUtils.newBytes(len); buff.get(b, 0, len); - return ValueGeometry.get(b); + return Value.getGeometryFactory().get(b); } case SPATIAL_KEY_2D: return getSpatialDataType().read(buff); diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java index b05a1e65a..a0580fccd 100644 --- a/h2/src/main/org/h2/value/DataType.java +++ b/h2/src/main/org/h2/value/DataType.java @@ -23,7 +23,6 @@ import java.util.HashMap; import java.util.UUID; import org.h2.api.ErrorCode; -import org.h2.api.IGeometry; import org.h2.engine.Constants; import org.h2.engine.SessionInterface; import org.h2.engine.SysProperties; @@ -637,11 +636,11 @@ public static Value readValue(SessionInterface session, ResultSet rs, } case Value.GEOMETRY: { Object x = rs.getObject(columnIndex); - if (x == null || !(x instanceof IGeometry)) { + if (x == null || !Value.getGeometryFactory().isGeometryTypeSupported(x)) { return ValueNull.INSTANCE; } - return ValueGeometry.get((IGeometry) x); + return Value.getGeometryFactory().get(x); } default: throw DbException.throwInternalError("type="+type); @@ -721,7 +720,7 @@ public static String getTypeClassName(int type) { case Value.RESULT_SET: return ResultSet.class.getName(); case Value.GEOMETRY: - return IGeometry.class.getName(); + return Value.getGeometryFactory().getGeometryType().getName(); default: throw DbException.throwInternalError("type="+type); } @@ -919,7 +918,7 @@ public static int getTypeFromClass(Class x) { } else if (Object[].class.isAssignableFrom(x)) { // this includes String[] and so on return Value.ARRAY; - } else if (IGeometry.class.isAssignableFrom(x)) { + } else if (Value.getGeometryFactory().getGeometryType().isAssignableFrom(x)) { return Value.GEOMETRY; } else { return Value.JAVA_OBJECT; @@ -1015,8 +1014,8 @@ public static Value convertToValue(SessionInterface session, Object x, return ValueArray.get(x.getClass().getComponentType(), v); } else if (x instanceof Character) { return ValueStringFixed.get(((Character) x).toString()); - } else if (x instanceof IGeometry) { - return ValueGeometry.get((IGeometry) x); + } else if (Value.getGeometryFactory().isGeometryTypeSupported(x)) { + return Value.getGeometryFactory().get(x); } else { return ValueJavaObject.getNoCopy(x, null, session.getDataHandler()); } diff --git a/h2/src/main/org/h2/value/H2ValueGeometry.java b/h2/src/main/org/h2/value/H2ValueGeometry.java new file mode 100644 index 000000000..50549e135 --- /dev/null +++ b/h2/src/main/org/h2/value/H2ValueGeometry.java @@ -0,0 +1,61 @@ +package org.h2.value; + +import java.util.Arrays; + +import org.h2.mvstore.rtree.SpatialKey; + +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.io.WKTWriter; + +public class H2ValueGeometry extends ValueGeometry { + + protected H2ValueGeometry(byte[] bytes, Geometry geometry) { + super(bytes, geometry); + } + + @Override + public Geometry getGeometry() { + return (Geometry) getGeometryNoCopy().clone(); + } + + @Override + public boolean intersectsBoundingBox(ValueGeometry r) { + // the Geometry object caches the envelope + return getGeometryNoCopy().getEnvelopeInternal().intersects( + r.getGeometryNoCopy().getEnvelopeInternal()); + } + + public Value getEnvelopeUnion(ValueGeometry r) { + GeometryFactory gf = new GeometryFactory(); + Envelope mergedEnvelope = new Envelope(getGeometryNoCopy().getEnvelopeInternal()); + mergedEnvelope.expandToInclude(r.getGeometryNoCopy().getEnvelopeInternal()); + return getGeometryFactory().get(gf.toGeometry(mergedEnvelope)); + } + + @Override + public boolean equals(Object other) { + return other instanceof ValueGeometry + && Arrays.equals(getBytes(), ((H2ValueGeometry) other).getBytes()); + } + + @Override + protected int compareSecure(Value v, CompareMode mode) { + Geometry g = ((H2ValueGeometry) v).getGeometryNoCopy(); + return getGeometryNoCopy().compareTo(g); + } + + @Override + public String getString() { + return new WKTWriter(3).write(getGeometryNoCopy()); + } + + @Override + public SpatialKey getSpatialKey(long id) { + Envelope env = getGeometryNoCopy().getEnvelopeInternal(); + return new SpatialKey(id, + (float) env.getMinX(), (float) env.getMaxX(), + (float) env.getMinY(), (float) env.getMaxY()); + } +} diff --git a/h2/src/main/org/h2/value/Transfer.java b/h2/src/main/org/h2/value/Transfer.java index ecc8016aa..de40aee21 100644 --- a/h2/src/main/org/h2/value/Transfer.java +++ b/h2/src/main/org/h2/value/Transfer.java @@ -693,9 +693,9 @@ public Value readValue() throws IOException { } case Value.GEOMETRY: if (version >= Constants.TCP_PROTOCOL_VERSION_14) { - return ValueGeometry.get(readBytes()); + return Value.getGeometryFactory().get(readBytes()); } - return ValueGeometry.get(readString()); + return Value.getGeometryFactory().get(readString()); default: throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "type=" + type); } diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java index f1541521e..92b69ef3d 100644 --- a/h2/src/main/org/h2/value/Value.java +++ b/h2/src/main/org/h2/value/Value.java @@ -18,8 +18,11 @@ import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; +import java.util.Iterator; +import java.util.ServiceLoader; + import org.h2.api.ErrorCode; -import org.h2.api.IGeometry; +import org.h2.api.IValueGeometryFactory; import org.h2.engine.Constants; import org.h2.engine.SysProperties; import org.h2.message.DbException; @@ -171,6 +174,20 @@ public abstract class Value { BigDecimal.valueOf(Long.MAX_VALUE); private static final BigDecimal MIN_LONG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE); + + /** + * Factory which provides a couple of methods to create a {@link IGeometry} + * instance. + */ + private static final IValueGeometryFactory,?> GEOMETRY_FACTORY; + + static { + @SuppressWarnings("unchecked") + ServiceLoader> geometryFactories = ServiceLoader.load( + (Class>)IValueGeometryFactory.class); + Iterator> geometryFactoryIterator = geometryFactories.iterator(); + GEOMETRY_FACTORY = geometryFactoryIterator.hasNext() ? geometryFactories.iterator().next() : null; + } /** * Get the SQL expression for this value. @@ -815,11 +832,11 @@ public Value convertTo(int targetType) { case GEOMETRY: switch(getType()) { case BYTES: - return ValueGeometry.get(getBytesNoCopy()); + return GEOMETRY_FACTORY.get(getBytesNoCopy()); case JAVA_OBJECT: Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler()); - if (object instanceof IGeometry) { - return ValueGeometry.get((IGeometry) object); + if (GEOMETRY_FACTORY.isGeometryTypeSupported(object)) { + return GEOMETRY_FACTORY.get(object); } } } @@ -894,7 +911,7 @@ public Value convertTo(int targetType) { case UUID: return ValueUuid.get(s); case GEOMETRY: - return ValueGeometry.get(s); + return GEOMETRY_FACTORY.get(s); default: throw DbException.throwInternalError("type=" + targetType); } @@ -1167,5 +1184,18 @@ public interface ValueClob { public interface ValueBlob { // this is a marker interface } - + + /** + * Returns true if a IGeometryFactory is available and initialized. + * @return + */ + public static boolean isGeometryFactoryInitialized() + { + return GEOMETRY_FACTORY!=null; + } + + public static IValueGeometryFactory, ?> getGeometryFactory() + { + return GEOMETRY_FACTORY; + } } diff --git a/h2/src/main/org/h2/value/ValueGeometry.java b/h2/src/main/org/h2/value/ValueGeometry.java index bce9cd5f4..951f6ba74 100644 --- a/h2/src/main/org/h2/value/ValueGeometry.java +++ b/h2/src/main/org/h2/value/ValueGeometry.java @@ -8,13 +8,8 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Arrays; -import java.util.Iterator; -import java.util.ServiceLoader; -import org.h2.api.GeometryParseException; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.api.IGeometryFactory; -import org.h2.message.DbException; + +import org.h2.mvstore.rtree.SpatialKey; import org.h2.util.StringUtils; /** @@ -24,20 +19,8 @@ * @author Noel Grandin * @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888 */ -public class ValueGeometry extends Value { +public abstract class ValueGeometry extends Value{ - /** - * Factory which provides a couple of methods to create a {@link IGeometry} - * instance. - */ - private static final IGeometryFactory GEOMETRY_FACTORY; - - static { - ServiceLoader geometryFactories = ServiceLoader.load(IGeometryFactory.class); - Iterator 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. @@ -54,7 +37,7 @@ public class ValueGeometry extends Value { * The value. Converted from byte array only on request as conversion * from/to byte array cost a significant amount of CPU cycles. */ - private IGeometry geometry; + private T geometry; /** * Create a new geometry objects. @@ -62,85 +45,28 @@ public class ValueGeometry extends Value { * @param bytes the bytes (always known) * @param geometry the geometry object (may be null) */ - private ValueGeometry(byte[] bytes, IGeometry geometry) { + protected ValueGeometry(byte[] bytes, T geometry) { this.bytes = bytes; this.geometry = geometry; this.hashCode = Arrays.hashCode(bytes); } - /** - * Get or create a geometry value for the given geometry. - * - * @param g the geometry object - * @return the value - */ - public static ValueGeometry get(IGeometry g) { - byte[] bytes = g.getBytes(); - return (ValueGeometry) Value.cache(new ValueGeometry(bytes, g)); - } - - /** - * Get or create a geometry value for the given geometry. - * - * @param s the string representation of the geometry - * @return the value - */ - public static ValueGeometry get(String s) { - try { - IGeometry g = GEOMETRY_FACTORY.toGeometry(s); - return get(g); - } catch (GeometryParseException ex) { - throw DbException.convert(ex); - } - } - - /** - * Get or create a geometry value for the given 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 { - IGeometry g = GEOMETRY_FACTORY.toGeometry(s, srid); - return get(g); - } catch (GeometryParseException ex) { - throw DbException.convert(ex); - } - } - - /** - * Get or create a geometry value for the given geometry. - * - * @param bytes the WKB representation of the geometry - * @return the value - */ - public static ValueGeometry get(byte[] bytes) { - return (ValueGeometry) Value.cache(new ValueGeometry(bytes, null)); - } - /** * Get a copy of geometry object. Geometry object is mutable. The returned * object is therefore copied before returning. * * @return a copy of the geometry object */ - public IGeometry getGeometry() { - return getGeometryNoCopy().clone(); - } - - public IGeometry getGeometryNoCopy() { - if (geometry == null) { - try { - geometry = GEOMETRY_FACTORY.toGeometry(bytes); - } catch (GeometryParseException ex) { - throw DbException.convert(ex); - } - } - return geometry; - } - + public abstract T getGeometry(); + + @SuppressWarnings("unchecked") + public T getGeometryNoCopy() { + if (geometry == null) { + geometry = (T)getGeometryFactory().getGeometry(bytes); + } + return geometry; + } + /** * Test if this geometry envelope intersects with the other geometry * envelope. @@ -148,11 +74,7 @@ public IGeometry getGeometryNoCopy() { * @param r the other geometry * @return true if the two overlap */ - public boolean intersectsBoundingBox(ValueGeometry r) { - // the Geometry object caches the envelope - return getGeometryNoCopy().getEnvelope().intersects( - r.getGeometryNoCopy().getEnvelope()); - } + public abstract boolean intersectsBoundingBox(ValueGeometry r); /** * Get the union. @@ -160,12 +82,9 @@ public boolean intersectsBoundingBox(ValueGeometry r) { * @param r the other geometry * @return the union of this geometry envelope and another geometry envelope */ - public Value getEnvelopeUnion(ValueGeometry r) { - IEnvelope mergedEnvelope = getGeometryNoCopy().getEnvelope().getUnion( - r.getGeometryNoCopy().getEnvelope()); - return get(GEOMETRY_FACTORY.toGeometry(mergedEnvelope)); - } + public abstract Value getEnvelopeUnion(ValueGeometry r); + @Override public int getType() { return Value.GEOMETRY; @@ -179,17 +98,6 @@ public String getSQL() { return "X'" + StringUtils.convertBytesToHex(getBytesNoCopy()) + "'::Geometry"; } - @Override - protected int compareSecure(Value v, CompareMode mode) { - IGeometry g = ((ValueGeometry) v).getGeometryNoCopy(); - return getGeometryNoCopy().compareTo(g); - } - - @Override - public String getString() { - return getGeometryNoCopy().getString(); - } - @Override public long getPrecision() { return 0; @@ -223,19 +131,16 @@ public void set(PreparedStatement prep, int parameterIndex) @Override public int getDisplaySize() { - return getGeometryNoCopy().getString().length(); + return getString().length(); } @Override public int getMemory() { return getBytes().length * 20 + 24; } + + public abstract boolean equals(Object other); - @Override - public boolean equals(Object other) { - return other instanceof ValueGeometry && - Arrays.equals(getBytes(), ((ValueGeometry) other).getBytes()); - } @Override public Value convertTo(int targetType) { @@ -244,13 +149,6 @@ public Value convertTo(int targetType) { } return super.convertTo(targetType); } - - /** - * Returns true if a IGeometryFactory is available and initialized. - * @return - */ - public static boolean isInitialized() - { - return GEOMETRY_FACTORY!=null; - } + + public abstract SpatialKey getSpatialKey(long id); } diff --git a/h2/src/main/org/h2/value/ValueGeometryFactory.java b/h2/src/main/org/h2/value/ValueGeometryFactory.java new file mode 100644 index 000000000..8221f3e6b --- /dev/null +++ b/h2/src/main/org/h2/value/ValueGeometryFactory.java @@ -0,0 +1,139 @@ +package org.h2.value; + +import org.h2.api.IValueGeometryFactory; +import org.h2.message.DbException; + +import com.vividsolutions.jts.geom.CoordinateSequence; +import com.vividsolutions.jts.geom.CoordinateSequenceFilter; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.PrecisionModel; +import com.vividsolutions.jts.io.ParseException; +import com.vividsolutions.jts.io.WKBReader; +import com.vividsolutions.jts.io.WKBWriter; +import com.vividsolutions.jts.io.WKTReader; + +public class ValueGeometryFactory implements IValueGeometryFactory { + + @Override + public Geometry getGeometry(byte[] bytes) throws DbException { + try { + return new WKBReader().read(bytes); + } catch (ParseException ex) { + throw DbException.convert(ex); + } + } + + @Override + public Geometry getGeometry(String s) throws DbException + { + try { + return new WKTReader().read(s); + } catch (ParseException ex) { + throw DbException.convert(ex); + } + } + + @Override + public Geometry getGeometry(String s, int srid) throws DbException { + try { + GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid); + return new WKTReader(geometryFactory).read(s); + }catch(ParseException ex) + { + throw DbException.convert(ex); + } + } + + @Override + public H2ValueGeometry get(Geometry g) { + byte[] bytes = convertToWKB(g); + return (H2ValueGeometry) Value.cache(new H2ValueGeometry(bytes, g)); + } + + + @Override + public H2ValueGeometry get(String s) { + try { + Geometry g = new WKTReader().read(s); + return get(g); + } catch (ParseException ex) { + throw DbException.convert(ex); + } + } + + @Override + public H2ValueGeometry get(String s, int srid) { + try { + GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid); + Geometry g = new WKTReader(geometryFactory).read(s); + return get(g); + } catch (ParseException ex) { + throw DbException.convert(ex); + } + } + + @Override + public H2ValueGeometry get(byte[] bytes) { + return (H2ValueGeometry) Value.cache(new H2ValueGeometry(bytes, null)); + } + + @Override + public H2ValueGeometry get(Object g) { + if(!isGeometryTypeSupported(g)) + throw new RuntimeException("The given object is not compatible with this ValueGeometryFactory instance!"); + + return get((Geometry)g); + } + + @Override + public boolean isGeometryTypeSupported(Object g) { + return g instanceof Geometry; + } + + @Override + public Class getGeometryType() { + return Geometry.class; + } + + 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; + } + + /** + * 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; + } + } +} diff --git a/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory b/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory deleted file mode 100644 index 54285b021..000000000 --- a/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory +++ /dev/null @@ -1 +0,0 @@ -org.h2.jts.H2GeometryFactory \ No newline at end of file diff --git a/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory b/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory new file mode 100644 index 000000000..2d5a602bc --- /dev/null +++ b/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory @@ -0,0 +1 @@ +org.h2.value.ValueGeometryFactory \ No newline at end of file diff --git a/h2/src/test/org/h2/test/db/TestSpatial.java b/h2/src/test/org/h2/test/db/TestSpatial.java index fce0ff1eb..2bcc52e5a 100644 --- a/h2/src/test/org/h2/test/db/TestSpatial.java +++ b/h2/src/test/org/h2/test/db/TestSpatial.java @@ -18,15 +18,12 @@ import com.vividsolutions.jts.geom.util.AffineTransformation; import org.h2.api.Aggregate; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.jts.H2Envelope; -import org.h2.jts.H2Geometry; -import org.h2.jts.H2GeometryFactory; +import org.h2.api.IValueGeometryFactory; import org.h2.test.TestBase; import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleRowSource; import org.h2.value.DataType; +import org.h2.value.H2ValueGeometry; import org.h2.value.Value; import com.vividsolutions.jts.geom.Coordinate; @@ -66,7 +63,7 @@ public void test() throws SQLException { if (config.memory && config.mvcc) { return; } - if (ValueGeometry.isInitialized()) { + if (Value.isGeometryFactoryInitialized()) { deleteDb("spatial"); url = "spatial"; testSpatial(); @@ -100,11 +97,12 @@ private void testSpatial() throws SQLException { } private void testHashCode() { - ValueGeometry geomA = ValueGeometry + + ValueGeometry geomA = Value.getGeometryFactory() .get("POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))"); - ValueGeometry geomB = ValueGeometry + ValueGeometry geomB = Value.getGeometryFactory() .get("POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))"); - ValueGeometry geomC = ValueGeometry + ValueGeometry geomC = Value.getGeometryFactory() .get("POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 5, 67 13 6))"); assertEquals(geomA.hashCode(), geomB.hashCode()); assertFalse(geomA.hashCode() == geomC.hashCode()); @@ -129,8 +127,7 @@ private void testSpatialValues() throws SQLException { new Coordinate(1, 2), new Coordinate(2, 2), new Coordinate(1, 1) }); - - assertTrue(new H2Geometry(polygon).equals(rs.getObject(2))); + assertTrue(polygon.equals(rs.getObject(2))); rs = stat.executeQuery("select * from test where polygon = " + "'POLYGON ((1 1, 1 2, 2 2, 1 1))'"); @@ -576,12 +573,12 @@ public void reset() throws SQLException { * @param srid the projection id * @return Geometry object */ - public static IGeometry geomFromText(String text, int srid) throws SQLException { + public static Geometry geomFromText(String text, int srid) throws SQLException { WKTReader wktReader = new WKTReader(); try { Geometry geom = wktReader.read(text); geom.setSRID(srid); - return new H2Geometry(geom); + return geom; } catch (ParseException ex) { throw new SQLException(ex); } @@ -589,27 +586,23 @@ public static IGeometry geomFromText(String text, int srid) throws SQLException private void testGeometryDataType() { GeometryFactory geometryFactory = new GeometryFactory(); - H2Geometry geometry = new H2Geometry(geometryFactory.createPoint(new Coordinate(0, 0))); + Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); assertEquals(Value.GEOMETRY, DataType.getTypeFromClass(geometry.getClass())); } /** * Test serialization of Z and SRID values. - * @throws SQLException */ - private void testWKB() throws SQLException { - ValueGeometry geom3d = ValueGeometry.get( + private void testWKB() { + H2ValueGeometry geom3d = getValueGeometryFactory().get( "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))", 27572); - ValueGeometry copy = ValueGeometry.get(geom3d.getBytes()); - - Geometry geometry = copy.getGeometry().unwrap(Geometry.class); - - assertEquals(6, geometry.getCoordinates()[0].z); - assertEquals(5, geometry.getCoordinates()[1].z); - assertEquals(4, geometry.getCoordinates()[2].z); + H2ValueGeometry copy = getValueGeometryFactory().get(geom3d.getBytes()); + assertEquals(6, copy.getGeometry().getCoordinates()[0].z); + assertEquals(5, copy.getGeometry().getCoordinates()[1].z); + assertEquals(4, copy.getGeometry().getCoordinates()[2].z); // Test SRID - copy = ValueGeometry.get(geom3d.getBytes()); - assertEquals(27572, geometry.getSRID()); + copy = getValueGeometryFactory().get(geom3d.getBytes()); + assertEquals(27572, copy.getGeometry().getSRID()); } /** @@ -645,26 +638,25 @@ public static String getObjectString(Object object) { */ private void testEquals() { // 3d equality test - ValueGeometry geom3d = ValueGeometry.get( + ValueGeometry geom3d = Value.getGeometryFactory().get( "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))"); - ValueGeometry geom2d = ValueGeometry.get( + ValueGeometry geom2d = Value.getGeometryFactory().get( "POLYGON ((67 13, 67 18, 59 18, 59 13, 67 13))"); assertFalse(geom3d.equals(geom2d)); // SRID equality test GeometryFactory geometryFactory = new GeometryFactory(); Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); geometry.setSRID(27572); - - ValueGeometry valueGeometry = - ValueGeometry.get(new H2Geometry(geometry)); + ValueGeometry valueGeometry = + Value.getGeometryFactory().get(geometry); Geometry geometry2 = geometryFactory.createPoint(new Coordinate(0, 0)); geometry2.setSRID(5326); - ValueGeometry valueGeometry2 = - ValueGeometry.get(new H2Geometry(geometry2)); + ValueGeometry valueGeometry2 = + Value.getGeometryFactory().get(geometry2); assertFalse(valueGeometry.equals(valueGeometry2)); // Check illegal geometry (no WKB representation) try { - ValueGeometry.get("POINT EMPTY"); + Value.getGeometryFactory().get("POINT EMPTY"); fail("expected this to throw IllegalArgumentException"); } catch (IllegalArgumentException ex) { // expected @@ -723,9 +715,9 @@ private void testAggregateWithGeometry() throws SQLException { assertEquals("geometry", rs.getMetaData(). getColumnTypeName(1).toLowerCase()); assertTrue(rs.next()); - assertTrue(rs.getObject(1) instanceof IGeometry); + assertTrue(rs.getObject(1) instanceof Geometry); assertTrue(new Envelope(1, 10, 1, 5).equals( - ((IGeometry) rs.getObject(1)).getEnvelope().unwrap(Envelope.class))); + ((Geometry) rs.getObject(1)).getEnvelopeInternal())); assertFalse(rs.next()); } finally { conn.close(); @@ -756,18 +748,18 @@ public void init(Connection conn) throws SQLException { @Override public void add(Object value) throws SQLException { - if (value instanceof IGeometry) { + if (value instanceof Geometry) { if (tableEnvelope == null) { - tableEnvelope = ((IGeometry) value).getEnvelope().unwrap(Envelope.class); + tableEnvelope = ((Geometry) value).getEnvelopeInternal(); } else { - tableEnvelope.expandToInclude(((IGeometry) value).getEnvelope().unwrap(Envelope.class)); + tableEnvelope.expandToInclude(((Geometry) value).getEnvelopeInternal()); } } } @Override public Object getResult() throws SQLException { - return new H2Geometry(new GeometryFactory().toGeometry(tableEnvelope)); + return new GeometryFactory().toGeometry(tableEnvelope); } } @@ -809,15 +801,14 @@ private void testTableViewSpatialPredicate() throws SQLException { * Check ValueGeometry conversion into SQL script */ private void testValueGeometryScript() throws SQLException { - ValueGeometry valueGeometry = ValueGeometry.get("POINT(1 1 5)"); + ValueGeometry valueGeometry = Value.getGeometryFactory().get("POINT(1 1 5)"); Connection conn = getConnection(url); try { ResultSet rs = conn.createStatement().executeQuery( "SELECT " + valueGeometry.getSQL()); assertTrue(rs.next()); Object obj = rs.getObject(1); - assertTrue(obj instanceof IGeometry); - ValueGeometry g = ValueGeometry.get((IGeometry) obj); + ValueGeometry g = Value.getGeometryFactory().get(obj); assertTrue("got: " + g + " exp: " + valueGeometry, valueGeometry.equals(g)); } finally { conn.close(); @@ -835,15 +826,15 @@ private void testInPlaceUpdate() throws SQLException { "SELECT 'POINT(1 1)'::geometry"); assertTrue(rs.next()); // Mutate the geometry - ((IGeometry) rs.getObject(1)).unwrap(Geometry.class).apply(new AffineTransformation(1, 0, + ((Geometry) rs.getObject(1)).apply(new AffineTransformation(1, 0, 1, 1, 0, 1)); rs.close(); rs = conn.createStatement().executeQuery( "SELECT 'POINT(1 1)'::geometry"); assertTrue(rs.next()); // Check if the geometry is the one requested - assertEquals(1, ((IGeometry) rs.getObject(1)).unwrap(Point.class).getX()); - assertEquals(1, ((IGeometry) rs.getObject(1)).unwrap(Point.class).getY()); + assertEquals(1, ((Point) rs.getObject(1)).getX()); + assertEquals(1, ((Point) rs.getObject(1)).getY()); rs.close(); } finally { conn.close(); @@ -925,4 +916,10 @@ private void testExplainSpatialIndexWithPk() throws SQLException { deleteDb("spatial"); } + @SuppressWarnings("unchecked") + private static IValueGeometryFactory getValueGeometryFactory() + { + return (IValueGeometryFactory)Value.getGeometryFactory(); + } + } From b0261b31cb26b4d1bc83e1e2ad6be292d288f2ae Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Thu, 19 Mar 2015 16:30:00 +0100 Subject: [PATCH 3/9] rename H2ValueGeometry to JTSValueGeometry and ValueGeometryFactory to JTSValueGeometryFactory --- .../org/h2/value/{H2ValueGeometry.java => JTSValueGeometry.java} | 0 .../{ValueGeometryFactory.java => JTSValueGeometryFactory.java} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename h2/src/main/org/h2/value/{H2ValueGeometry.java => JTSValueGeometry.java} (100%) rename h2/src/main/org/h2/value/{ValueGeometryFactory.java => JTSValueGeometryFactory.java} (100%) diff --git a/h2/src/main/org/h2/value/H2ValueGeometry.java b/h2/src/main/org/h2/value/JTSValueGeometry.java similarity index 100% rename from h2/src/main/org/h2/value/H2ValueGeometry.java rename to h2/src/main/org/h2/value/JTSValueGeometry.java diff --git a/h2/src/main/org/h2/value/ValueGeometryFactory.java b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java similarity index 100% rename from h2/src/main/org/h2/value/ValueGeometryFactory.java rename to h2/src/main/org/h2/value/JTSValueGeometryFactory.java From f4e7caf2aaee7736f7cacfaf4eff57c328feb499 Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Thu, 19 Mar 2015 16:31:52 +0100 Subject: [PATCH 4/9] delete GeometryParseException --- .../org/h2/api/GeometryParseException.java | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 h2/src/main/org/h2/api/GeometryParseException.java diff --git a/h2/src/main/org/h2/api/GeometryParseException.java b/h2/src/main/org/h2/api/GeometryParseException.java deleted file mode 100644 index 879906b46..000000000 --- a/h2/src/main/org/h2/api/GeometryParseException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.api; - - -/** - * Signals that an error has been reached unexpectedly while parsing a geometry. - * - * @see java.lang.Exception - * @see IGeometryFactory - */ -public class GeometryParseException extends Exception { - - private static final long serialVersionUID = -3743282683053748926L; - - /** - * 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 (cause==null ? null : cause.toString()) (which typically - * contains the class and detail message of cause). - * - * 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 null value is - * permitted, and indicates that the cause is nonexistent or - * unknown.) - * @since 1.4 - */ - public GeometryParseException(Exception cause) { - super(cause); - } -} From a6e122ee93fcafe0099fafda3c5ba6886bd6bf78 Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Thu, 19 Mar 2015 16:51:21 +0100 Subject: [PATCH 5/9] rename H2ValueGeometry to JTSValueGeometry and ValueGeometryFactory to JTSValueGeometryFactory --- h2/src/main/org/h2/value/JTSValueGeometry.java | 8 ++++---- .../org/h2/value/JTSValueGeometryFactory.java | 16 ++++++++-------- .../services/org.h2.api.IValueGeometryFactory | 2 +- h2/src/test/org/h2/test/db/TestSpatial.java | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/h2/src/main/org/h2/value/JTSValueGeometry.java b/h2/src/main/org/h2/value/JTSValueGeometry.java index 50549e135..442e67f2d 100644 --- a/h2/src/main/org/h2/value/JTSValueGeometry.java +++ b/h2/src/main/org/h2/value/JTSValueGeometry.java @@ -9,9 +9,9 @@ import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.io.WKTWriter; -public class H2ValueGeometry extends ValueGeometry { +public class JTSValueGeometry extends ValueGeometry { - protected H2ValueGeometry(byte[] bytes, Geometry geometry) { + protected JTSValueGeometry(byte[] bytes, Geometry geometry) { super(bytes, geometry); } @@ -37,12 +37,12 @@ public Value getEnvelopeUnion(ValueGeometry r) { @Override public boolean equals(Object other) { return other instanceof ValueGeometry - && Arrays.equals(getBytes(), ((H2ValueGeometry) other).getBytes()); + && Arrays.equals(getBytes(), ((JTSValueGeometry) other).getBytes()); } @Override protected int compareSecure(Value v, CompareMode mode) { - Geometry g = ((H2ValueGeometry) v).getGeometryNoCopy(); + Geometry g = ((JTSValueGeometry) v).getGeometryNoCopy(); return getGeometryNoCopy().compareTo(g); } diff --git a/h2/src/main/org/h2/value/JTSValueGeometryFactory.java b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java index 8221f3e6b..daad8515d 100644 --- a/h2/src/main/org/h2/value/JTSValueGeometryFactory.java +++ b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java @@ -13,7 +13,7 @@ import com.vividsolutions.jts.io.WKBWriter; import com.vividsolutions.jts.io.WKTReader; -public class ValueGeometryFactory implements IValueGeometryFactory { +public class JTSValueGeometryFactory implements IValueGeometryFactory { @Override public Geometry getGeometry(byte[] bytes) throws DbException { @@ -46,14 +46,14 @@ public Geometry getGeometry(String s, int srid) throws DbException { } @Override - public H2ValueGeometry get(Geometry g) { + public JTSValueGeometry get(Geometry g) { byte[] bytes = convertToWKB(g); - return (H2ValueGeometry) Value.cache(new H2ValueGeometry(bytes, g)); + return (JTSValueGeometry) Value.cache(new JTSValueGeometry(bytes, g)); } @Override - public H2ValueGeometry get(String s) { + public JTSValueGeometry get(String s) { try { Geometry g = new WKTReader().read(s); return get(g); @@ -63,7 +63,7 @@ public H2ValueGeometry get(String s) { } @Override - public H2ValueGeometry get(String s, int srid) { + public JTSValueGeometry get(String s, int srid) { try { GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid); Geometry g = new WKTReader(geometryFactory).read(s); @@ -74,12 +74,12 @@ public H2ValueGeometry get(String s, int srid) { } @Override - public H2ValueGeometry get(byte[] bytes) { - return (H2ValueGeometry) Value.cache(new H2ValueGeometry(bytes, null)); + public JTSValueGeometry get(byte[] bytes) { + return (JTSValueGeometry) Value.cache(new JTSValueGeometry(bytes, null)); } @Override - public H2ValueGeometry get(Object g) { + public JTSValueGeometry get(Object g) { if(!isGeometryTypeSupported(g)) throw new RuntimeException("The given object is not compatible with this ValueGeometryFactory instance!"); diff --git a/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory b/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory index 2d5a602bc..f17dd9bd7 100644 --- a/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory +++ b/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory @@ -1 +1 @@ -org.h2.value.ValueGeometryFactory \ No newline at end of file +org.h2.value.JTSValueGeometryFactory \ No newline at end of file diff --git a/h2/src/test/org/h2/test/db/TestSpatial.java b/h2/src/test/org/h2/test/db/TestSpatial.java index 2bcc52e5a..b4edbb3e6 100644 --- a/h2/src/test/org/h2/test/db/TestSpatial.java +++ b/h2/src/test/org/h2/test/db/TestSpatial.java @@ -23,7 +23,7 @@ import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleRowSource; import org.h2.value.DataType; -import org.h2.value.H2ValueGeometry; +import org.h2.value.JTSValueGeometry; import org.h2.value.Value; import com.vividsolutions.jts.geom.Coordinate; @@ -594,9 +594,9 @@ private void testGeometryDataType() { * Test serialization of Z and SRID values. */ private void testWKB() { - H2ValueGeometry geom3d = getValueGeometryFactory().get( + JTSValueGeometry geom3d = getValueGeometryFactory().get( "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))", 27572); - H2ValueGeometry copy = getValueGeometryFactory().get(geom3d.getBytes()); + JTSValueGeometry copy = getValueGeometryFactory().get(geom3d.getBytes()); assertEquals(6, copy.getGeometry().getCoordinates()[0].z); assertEquals(5, copy.getGeometry().getCoordinates()[1].z); assertEquals(4, copy.getGeometry().getCoordinates()[2].z); @@ -917,9 +917,9 @@ private void testExplainSpatialIndexWithPk() throws SQLException { } @SuppressWarnings("unchecked") - private static IValueGeometryFactory getValueGeometryFactory() + private static IValueGeometryFactory getValueGeometryFactory() { - return (IValueGeometryFactory)Value.getGeometryFactory(); + return (IValueGeometryFactory)Value.getGeometryFactory(); } } From 23e5805a5fa937f186d5ec5a7f7c6b501d7faf7a Mon Sep 17 00:00:00 2001 From: shruda Date: Tue, 24 Mar 2015 16:02:35 +0100 Subject: [PATCH 6/9] Revert "Geometry interfaces instead JTS" --- .../org/h2/api/GeometryParseException.java | 49 ----- h2/src/main/org/h2/api/IEnvelope.java | 66 ------- h2/src/main/org/h2/api/IGeometry.java | 50 ----- h2/src/main/org/h2/api/IGeometryFactory.java | 51 ------ .../main/org/h2/index/SpatialTreeIndex.java | 10 +- h2/src/main/org/h2/jts/H2Envelope.java | 130 ------------- h2/src/main/org/h2/jts/H2Geometry.java | 166 ----------------- h2/src/main/org/h2/jts/H2GeometryFactory.java | 76 -------- .../org/h2/mvstore/db/MVSpatialIndex.java | 14 +- h2/src/main/org/h2/value/DataType.java | 61 +++++- h2/src/main/org/h2/value/Value.java | 6 +- h2/src/main/org/h2/value/ValueGeometry.java | 173 ++++++++++++------ .../services/org.h2.api.IGeometryFactory | 1 - h2/src/test/org/h2/test/db/TestSpatial.java | 59 +++--- 14 files changed, 206 insertions(+), 706 deletions(-) delete mode 100644 h2/src/main/org/h2/api/GeometryParseException.java delete mode 100644 h2/src/main/org/h2/api/IEnvelope.java delete mode 100644 h2/src/main/org/h2/api/IGeometry.java delete mode 100644 h2/src/main/org/h2/api/IGeometryFactory.java delete mode 100644 h2/src/main/org/h2/jts/H2Envelope.java delete mode 100644 h2/src/main/org/h2/jts/H2Geometry.java delete mode 100644 h2/src/main/org/h2/jts/H2GeometryFactory.java delete mode 100644 h2/src/test/META-INF/services/org.h2.api.IGeometryFactory diff --git a/h2/src/main/org/h2/api/GeometryParseException.java b/h2/src/main/org/h2/api/GeometryParseException.java deleted file mode 100644 index 879906b46..000000000 --- a/h2/src/main/org/h2/api/GeometryParseException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.api; - - -/** - * Signals that an error has been reached unexpectedly while parsing a geometry. - * - * @see java.lang.Exception - * @see IGeometryFactory - */ -public class GeometryParseException extends Exception { - - private static final long serialVersionUID = -3743282683053748926L; - - /** - * 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 (cause==null ? null : cause.toString()) (which typically - * contains the class and detail message of cause). - * - * 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 null value is - * permitted, and indicates that the cause is nonexistent or - * unknown.) - * @since 1.4 - */ - public GeometryParseException(Exception cause) { - super(cause); - } -} diff --git a/h2/src/main/org/h2/api/IEnvelope.java b/h2/src/main/org/h2/api/IEnvelope.java deleted file mode 100644 index 4bafbfe85..000000000 --- a/h2/src/main/org/h2/api/IEnvelope.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.api; - -import java.sql.Wrapper; - -/** - * Interface of a envelope type which defines a rectangular region of the 2D - * coordinate plane. - */ -public interface IEnvelope extends Wrapper { - /** - * 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); - - @Override - public boolean isWrapperFor(Class iface); - - @Override - public T unwrap(Class iface); -} diff --git a/h2/src/main/org/h2/api/IGeometry.java b/h2/src/main/org/h2/api/IGeometry.java deleted file mode 100644 index 7f0ea6f0a..000000000 --- a/h2/src/main/org/h2/api/IGeometry.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.api; - -import java.sql.Wrapper; - -/** - * 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, Cloneable, Wrapper{ - /** - * 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(); - - @Override - public boolean isWrapperFor(Class iface); - - @Override - public T unwrap(Class iface); -} diff --git a/h2/src/main/org/h2/api/IGeometryFactory.java b/h2/src/main/org/h2/api/IGeometryFactory.java deleted file mode 100644 index 7f7b1dddd..000000000 --- a/h2/src/main/org/h2/api/IGeometryFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.api; - - -/** - * 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); -} diff --git a/h2/src/main/org/h2/index/SpatialTreeIndex.java b/h2/src/main/org/h2/index/SpatialTreeIndex.java index bd0cd18b1..dfff8eade 100644 --- a/h2/src/main/org/h2/index/SpatialTreeIndex.java +++ b/h2/src/main/org/h2/index/SpatialTreeIndex.java @@ -6,8 +6,7 @@ package org.h2.index; import java.util.Iterator; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; + import org.h2.engine.Constants; import org.h2.engine.Session; import org.h2.message.DbException; @@ -25,6 +24,9 @@ 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. * @@ -128,8 +130,8 @@ public void add(Session session, Row row) { private SpatialKey getEnvelope(SearchRow row) { Value v = row.getValue(columnIds[0]); - IGeometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy(); - IEnvelope env = g.getEnvelope(); + Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy(); + Envelope env = g.getEnvelopeInternal(); return new SpatialKey(row.getKey(), (float) env.getMinX(), (float) env.getMaxX(), (float) env.getMinY(), (float) env.getMaxY()); diff --git a/h2/src/main/org/h2/jts/H2Envelope.java b/h2/src/main/org/h2/jts/H2Envelope.java deleted file mode 100644 index 56d9cdfbb..000000000 --- a/h2/src/main/org/h2/jts/H2Envelope.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.h2.jts; - -import java.sql.SQLException; - -import org.h2.api.IEnvelope; -import org.h2.message.DbException; - -import com.vividsolutions.jts.geom.Envelope; - -/** - * Implementation of the {@link IEnvelope} interface which wraps the - * {@link Envelope}. - */ -public class H2Envelope implements IEnvelope { - private final Envelope envelope; - - /** - * Constructs a new {@link H2Envelope} instance by using the given - * parameters. - */ - public H2Envelope(Envelope envelope) { - this.envelope = envelope; - } - - /** - * @see org.h2.value.IEnvelope#getMinX() - */ - @Override - public double getMinX() { - return envelope.getMinX(); - } - - /** - * @see org.h2.value.IEnvelope#getMinY() - */ - @Override - public double getMinY() { - return envelope.getMinY(); - } - - /** - * @see org.h2.value.IEnvelope#getMaxX() - */ - @Override - public double getMaxX() { - return envelope.getMaxX(); - } - - /** - * @see org.h2.value.IEnvelope#getMaxY() - */ - @Override - public double getMaxY() { - return envelope.getMaxY(); - } - - /** - * @see org.h2.value.IEnvelope#intersects(org.h2.value.IEnvelope) - */ - @Override - public boolean intersects(IEnvelope other) { - - //intersects is only supported if the given envelope is wrapper for com.vividsolutions.jts.geom.Envelope - if (!other.isWrapperFor(Envelope.class)) - return false; - - return this.envelope.intersects(other.unwrap(Envelope.class)); - } - - /** - * @see org.h2.value.IEnvelope#getUnion(org.h2.value.IEnvelope) - */ - @Override - public IEnvelope getUnion(IEnvelope other) { - Envelope mergedEnvelope = new Envelope(envelope); - - // union is only supported if the given envelope is wrapper for com.vividsolutions.jts.geom.Envelope - if (other.isWrapperFor(Envelope.class)) - mergedEnvelope.expandToInclude(other.unwrap(Envelope.class)); - - return new H2Envelope(mergedEnvelope); - } - - @Override - @SuppressWarnings("unchecked") - public T unwrap(Class iface) { - if (isWrapperFor(iface)) { - return (T) envelope; - } - throw DbException.getInvalidValueException("iface", iface); - } - - @Override - public boolean isWrapperFor(Class iface) { - return iface != null && iface.isAssignableFrom(envelope.getClass()); - } - - @Override - public String toString() { - return envelope.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((envelope == null) ? 0 : envelope.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - H2Envelope other = (H2Envelope) obj; - if (envelope == null) { - if (other.envelope != null) - return false; - } else if (!envelope.equals(other.envelope)) - return false; - return true; - } - - -} diff --git a/h2/src/main/org/h2/jts/H2Geometry.java b/h2/src/main/org/h2/jts/H2Geometry.java deleted file mode 100644 index e7d3ac220..000000000 --- a/h2/src/main/org/h2/jts/H2Geometry.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.h2.jts; - -import java.sql.SQLException; - -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.message.DbException; - -import com.vividsolutions.jts.geom.CoordinateSequence; -import com.vividsolutions.jts.geom.CoordinateSequenceFilter; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.io.WKBWriter; -import com.vividsolutions.jts.io.WKTWriter; - -/** - * Implementation of the {@link IGeometry} interface which wraps the - * {@link Geometry}. - */ -public class H2Geometry implements IGeometry { - private final Geometry geometry; - - private IEnvelope envelope; - - /** - * Constructs a new {@link H2Geometry} instance by using the given - * parameters. - * - * @param geometry - * geometry to wrap - */ - public H2Geometry(Geometry geometry) { - this.geometry = geometry; - } - - /** - * @see java.lang.Comparable#compareTo(java.lang.Object) - * @throws AssertionError - * if the unwrap of {@link Geometry} is not possible. - */ - @Override - public int compareTo(IGeometry g) throws AssertionError { - if (!g.isWrapperFor(Geometry.class)) - throw new AssertionError( - "Comparision isn't supported if 'com.vividsolutions.jts.geom.Geometry' can't be unwrapped!"); - - return geometry.compareTo(g.unwrap(Geometry.class)); - } - - /** - * @see org.h2.value.IGeometry#getString() - */ - @Override - public String getString() { - return new WKTWriter(3).write(geometry); - } - - /** - * @see org.h2.value.IGeometry#getBytes() - */ - @Override - public byte[] getBytes() { - boolean includeSRID = geometry.getSRID() != 0; - int dimensionCount = getDimensionCount(geometry); - WKBWriter writer = new WKBWriter(dimensionCount, includeSRID); - return writer.write(geometry); - } - - private static int getDimensionCount(Geometry geometry) { - ZVisitor finder = new ZVisitor(); - geometry.apply(finder); - return finder.isFoundZ() ? 3 : 2; - } - - /** - * @see org.h2.value.IGeometry#clone() - */ - @Override - public IGeometry clone() { - return new H2Geometry((Geometry) geometry.clone()); - } - - /** - * @see org.h2.value.IGeometry#getEnvelope() - */ - @Override - public IEnvelope getEnvelope() { - if (envelope != null) { - return envelope; - } - - return envelope = new H2Envelope(geometry.getEnvelopeInternal()); - } - - @SuppressWarnings("unchecked") - @Override - public T unwrap(Class iface) { - if (isWrapperFor(iface)) { - return (T) geometry; - } - throw DbException.getInvalidValueException("iface", iface); - } - - @Override - public boolean isWrapperFor(Class iface) { - return iface != null && iface.isAssignableFrom(geometry.getClass()); - } - - @Override - public String toString() { - return getString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((geometry == null) ? 0 : geometry.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - H2Geometry other = (H2Geometry) obj; - if (geometry == null) { - if (other.geometry != null) - return false; - } else if (!geometry.equals(other.geometry)) - return false; - return true; - } - - /** - * A visitor that checks if there is a Z coordinate. - */ - private 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; - } - } -} \ No newline at end of file diff --git a/h2/src/main/org/h2/jts/H2GeometryFactory.java b/h2/src/main/org/h2/jts/H2GeometryFactory.java deleted file mode 100644 index e2af36624..000000000 --- a/h2/src/main/org/h2/jts/H2GeometryFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.h2.jts; - -import org.h2.api.GeometryParseException; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.api.IGeometryFactory; -import org.h2.message.DbException; - -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.geom.PrecisionModel; -import com.vividsolutions.jts.io.ParseException; -import com.vividsolutions.jts.io.WKBReader; -import com.vividsolutions.jts.io.WKTReader; - -/** - * Implementation of the {@link IGeometryFactory} interface to support the JTS - * Topology Suite. - */ -public class H2GeometryFactory implements IGeometryFactory { - /** - * @see org.h2.value.IGeometryFactory#toGeometry(java.lang.String) - */ - @Override - public IGeometry toGeometry(String s) throws GeometryParseException { - try { - return new H2Geometry(new WKTReader().read(s)); - } catch (ParseException e) { - throw new GeometryParseException(e); - } - } - - /** - * @see org.h2.value.IGeometryFactory#toGeometry(java.lang.String, int) - */ - @Override - public IGeometry toGeometry(String s, int srid) - throws GeometryParseException { - try { - GeometryFactory geometryFactory = new GeometryFactory( - new PrecisionModel(), srid); - Geometry g = new WKTReader(geometryFactory).read(s); - return new H2Geometry(g); - } catch (ParseException e) { - throw new GeometryParseException(e); - } - - } - - /** - * @see org.h2.value.IGeometryFactory#toGeometry(byte[]) - */ - @Override - public IGeometry toGeometry(byte[] bytes) throws GeometryParseException { - try { - return new H2Geometry(new WKBReader().read(bytes)); - } catch (ParseException ex) { - throw DbException.convert(ex); - } - } - - /** - * @see org.h2.value.IGeometryFactory#toGeometry(org.h2.value.IEnvelope) - */ - @Override - public IGeometry toGeometry(IEnvelope envelope) { - if (!H2Envelope.class.isInstance(envelope)) - throw new AssertionError( - "The given envelope must be an instance of H2Envelope!"); - - GeometryFactory geometryFactory = new GeometryFactory(); - return new H2Geometry( - geometryFactory.toGeometry(envelope.unwrap(Envelope.class))); - } -} diff --git a/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java b/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java index b37327c84..caf7b12f9 100644 --- a/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java +++ b/h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java @@ -7,9 +7,8 @@ import java.util.Iterator; import java.util.List; + import org.h2.api.ErrorCode; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; import org.h2.engine.Database; import org.h2.engine.Session; import org.h2.index.BaseIndex; @@ -34,6 +33,9 @@ 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. * @@ -164,8 +166,8 @@ private SpatialKey getKey(SearchRow r) { return null; } Value v = r.getValue(columnIds[0]); - IGeometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy(); - IEnvelope env = g.getEnvelope(); + Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy(); + Envelope env = g.getEnvelopeInternal(); return new SpatialKey(r.getKey(), (float) env.getMinX(), (float) env.getMaxX(), (float) env.getMinY(), (float) env.getMaxY()); @@ -219,8 +221,8 @@ public Cursor findByGeometry(TableFilter filter, SearchRow intersection) { private SpatialKey getEnvelope(SearchRow row) { Value v = row.getValue(columnIds[0]); - IGeometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy(); - IEnvelope env = g.getEnvelope(); + Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy(); + Envelope env = g.getEnvelopeInternal(); return new SpatialKey(row.getKey(), (float) env.getMinX(), (float) env.getMaxX(), (float) env.getMinY(), (float) env.getMaxY()); diff --git a/h2/src/main/org/h2/value/DataType.java b/h2/src/main/org/h2/value/DataType.java index b05a1e65a..1efa47a2f 100644 --- a/h2/src/main/org/h2/value/DataType.java +++ b/h2/src/main/org/h2/value/DataType.java @@ -22,8 +22,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.UUID; + import org.h2.api.ErrorCode; -import org.h2.api.IGeometry; import org.h2.engine.Constants; import org.h2.engine.SessionInterface; import org.h2.engine.SysProperties; @@ -49,6 +49,15 @@ public class DataType { */ 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. @@ -163,6 +172,17 @@ public class DataType { */ 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); @@ -637,11 +657,10 @@ public static Value readValue(SessionInterface session, ResultSet rs, } case Value.GEOMETRY: { Object x = rs.getObject(columnIndex); - if (x == null || !(x instanceof IGeometry)) { + if (x == null) { return ValueNull.INSTANCE; } - - return ValueGeometry.get((IGeometry) x); + return ValueGeometry.getFromGeometry(x); } default: throw DbException.throwInternalError("type="+type); @@ -721,7 +740,7 @@ public static String getTypeClassName(int type) { case Value.RESULT_SET: return ResultSet.class.getName(); case Value.GEOMETRY: - return IGeometry.class.getName(); + return GEOMETRY_CLASS_NAME; default: throw DbException.throwInternalError("type="+type); } @@ -919,7 +938,7 @@ public static int getTypeFromClass(Class x) { } else if (Object[].class.isAssignableFrom(x)) { // this includes String[] and so on return Value.ARRAY; - } else if (IGeometry.class.isAssignableFrom(x)) { + } else if (isGeometryClass(x)) { return Value.GEOMETRY; } else { return Value.JAVA_OBJECT; @@ -1015,13 +1034,39 @@ public static Value convertToValue(SessionInterface session, Object x, return ValueArray.get(x.getClass().getComponentType(), v); } else if (x instanceof Character) { return ValueStringFixed.get(((Character) x).toString()); - } else if (x instanceof IGeometry) { - return ValueGeometry.get((IGeometry) x); + } else if (isGeometry(x)) { + return ValueGeometry.getFromGeometry(x); } else { return ValueJavaObject.getNoCopy(x, null, session.getDataHandler()); } } + /** + * 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. * diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java index f1541521e..a78f9c8d3 100644 --- a/h2/src/main/org/h2/value/Value.java +++ b/h2/src/main/org/h2/value/Value.java @@ -18,8 +18,8 @@ import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; + import org.h2.api.ErrorCode; -import org.h2.api.IGeometry; import org.h2.engine.Constants; import org.h2.engine.SysProperties; import org.h2.message.DbException; @@ -818,8 +818,8 @@ public Value convertTo(int targetType) { return ValueGeometry.get(getBytesNoCopy()); case JAVA_OBJECT: Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler()); - if (object instanceof IGeometry) { - return ValueGeometry.get((IGeometry) object); + if (DataType.isGeometry(object)) { + return ValueGeometry.getFromGeometry(object); } } } diff --git a/h2/src/main/org/h2/value/ValueGeometry.java b/h2/src/main/org/h2/value/ValueGeometry.java index bce9cd5f4..687b861dc 100644 --- a/h2/src/main/org/h2/value/ValueGeometry.java +++ b/h2/src/main/org/h2/value/ValueGeometry.java @@ -8,14 +8,20 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Arrays; -import java.util.Iterator; -import java.util.ServiceLoader; -import org.h2.api.GeometryParseException; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.api.IGeometryFactory; + +import com.vividsolutions.jts.geom.CoordinateSequence; +import com.vividsolutions.jts.geom.CoordinateSequenceFilter; +import com.vividsolutions.jts.geom.PrecisionModel; 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. @@ -27,34 +33,22 @@ public class ValueGeometry extends Value { /** - * Factory which provides a couple of methods to create a {@link IGeometry} - * instance. - */ - private static final IGeometryFactory GEOMETRY_FACTORY; - - static { - ServiceLoader geometryFactories = ServiceLoader.load(IGeometryFactory.class); - Iterator 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. + * As conversion from/to WKB cost a significant amount of CPU cycles, WKB + * are kept in ValueGeometry instance. * - * 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 + * 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 */ private final byte[] bytes; private final int hashCode; /** - * The value. Converted from byte array only on request as conversion - * from/to byte array cost a significant amount of CPU cycles. + * The value. Converted from WKB only on request as conversion from/to WKB + * cost a significant amount of CPU cycles. */ - private IGeometry geometry; + private Geometry geometry; /** * Create a new geometry objects. @@ -62,7 +56,7 @@ public class ValueGeometry extends Value { * @param bytes the bytes (always known) * @param geometry the geometry object (may be null) */ - private ValueGeometry(byte[] bytes, IGeometry geometry) { + private ValueGeometry(byte[] bytes, Geometry geometry) { this.bytes = bytes; this.geometry = geometry; this.hashCode = Arrays.hashCode(bytes); @@ -71,25 +65,43 @@ private ValueGeometry(byte[] bytes, IGeometry geometry) { /** * Get or create a geometry value for the given geometry. * - * @param g the geometry object + * @param o the geometry object (of type + * com.vividsolutions.jts.geom.Geometry) * @return the value */ - public static ValueGeometry get(IGeometry g) { - byte[] bytes = g.getBytes(); + public static ValueGeometry getFromGeometry(Object o) { + return get((Geometry) o); + } + + private static ValueGeometry get(Geometry g) { + byte[] bytes = convertToWKB(g); 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 string representation of the geometry + * @param s the WKT representation of the geometry * @return the value */ public static ValueGeometry get(String s) { try { - IGeometry g = GEOMETRY_FACTORY.toGeometry(s); + Geometry g = new WKTReader().read(s); return get(g); - } catch (GeometryParseException ex) { + } catch (ParseException ex) { throw DbException.convert(ex); } } @@ -97,15 +109,16 @@ public static ValueGeometry get(String s) { /** * Get or create a geometry value for the given geometry. * - * @param s the string representation of the geometry + * @param s the WKT representation of the geometry * @param srid the srid of the object * @return the value */ public static ValueGeometry get(String s, int srid) { try { - IGeometry g = GEOMETRY_FACTORY.toGeometry(s, srid); + GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid); + Geometry g = new WKTReader(geometryFactory).read(s); return get(g); - } catch (GeometryParseException ex) { + } catch (ParseException ex) { throw DbException.convert(ex); } } @@ -126,15 +139,15 @@ public static ValueGeometry get(byte[] bytes) { * * @return a copy of the geometry object */ - public IGeometry getGeometry() { - return getGeometryNoCopy().clone(); + public Geometry getGeometry() { + return (Geometry) getGeometryNoCopy().clone(); } - public IGeometry getGeometryNoCopy() { + public Geometry getGeometryNoCopy() { if (geometry == null) { try { - geometry = GEOMETRY_FACTORY.toGeometry(bytes); - } catch (GeometryParseException ex) { + geometry = new WKBReader().read(bytes); + } catch (ParseException ex) { throw DbException.convert(ex); } } @@ -150,8 +163,8 @@ public IGeometry getGeometryNoCopy() { */ public boolean intersectsBoundingBox(ValueGeometry r) { // the Geometry object caches the envelope - return getGeometryNoCopy().getEnvelope().intersects( - r.getGeometryNoCopy().getEnvelope()); + return getGeometryNoCopy().getEnvelopeInternal().intersects( + r.getGeometryNoCopy().getEnvelopeInternal()); } /** @@ -161,9 +174,10 @@ public boolean intersectsBoundingBox(ValueGeometry r) { * @return the union of this geometry envelope and another geometry envelope */ public Value getEnvelopeUnion(ValueGeometry r) { - IEnvelope mergedEnvelope = getGeometryNoCopy().getEnvelope().getUnion( - r.getGeometryNoCopy().getEnvelope()); - return get(GEOMETRY_FACTORY.toGeometry(mergedEnvelope)); + GeometryFactory gf = new GeometryFactory(); + Envelope mergedEnvelope = new Envelope(getGeometryNoCopy().getEnvelopeInternal()); + mergedEnvelope.expandToInclude(r.getGeometryNoCopy().getEnvelopeInternal()); + return get(gf.toGeometry(mergedEnvelope)); } @Override @@ -181,13 +195,13 @@ public String getSQL() { @Override protected int compareSecure(Value v, CompareMode mode) { - IGeometry g = ((ValueGeometry) v).getGeometryNoCopy(); + Geometry g = ((ValueGeometry) v).getGeometryNoCopy(); return getGeometryNoCopy().compareTo(g); } @Override public String getString() { - return getGeometryNoCopy().getString(); + return getWKT(); } @Override @@ -207,12 +221,12 @@ public Object getObject() { @Override public byte[] getBytes() { - return bytes; + return getWKB(); } @Override public byte[] getBytesNoCopy() { - return bytes; + return getWKB(); } @Override @@ -223,18 +237,38 @@ public void set(PreparedStatement prep, int parameterIndex) @Override public int getDisplaySize() { - return getGeometryNoCopy().getString().length(); + return getWKT().length(); } @Override public int getMemory() { - return getBytes().length * 20 + 24; + return getWKB().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(getBytes(), ((ValueGeometry) other).getBytes()); + Arrays.equals(getWKB(), ((ValueGeometry) other).getWKB()); + } + + /** + * 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 @@ -244,13 +278,34 @@ public Value convertTo(int targetType) { } return super.convertTo(targetType); } - + /** - * Returns true if a IGeometryFactory is available and initialized. - * @return + * A visitor that checks if there is a Z coordinate. */ - public static boolean isInitialized() - { - return GEOMETRY_FACTORY!=null; + 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; + } + } + } diff --git a/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory b/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory deleted file mode 100644 index 54285b021..000000000 --- a/h2/src/test/META-INF/services/org.h2.api.IGeometryFactory +++ /dev/null @@ -1 +0,0 @@ -org.h2.jts.H2GeometryFactory \ No newline at end of file diff --git a/h2/src/test/org/h2/test/db/TestSpatial.java b/h2/src/test/org/h2/test/db/TestSpatial.java index fce0ff1eb..fcc8a20a2 100644 --- a/h2/src/test/org/h2/test/db/TestSpatial.java +++ b/h2/src/test/org/h2/test/db/TestSpatial.java @@ -16,26 +16,18 @@ import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.util.AffineTransformation; - import org.h2.api.Aggregate; -import org.h2.api.IEnvelope; -import org.h2.api.IGeometry; -import org.h2.jts.H2Envelope; -import org.h2.jts.H2Geometry; -import org.h2.jts.H2GeometryFactory; import org.h2.test.TestBase; import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleRowSource; import org.h2.value.DataType; import org.h2.value.Value; - import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; - import org.h2.value.ValueGeometry; /** @@ -66,7 +58,7 @@ public void test() throws SQLException { if (config.memory && config.mvcc) { return; } - if (ValueGeometry.isInitialized()) { + if (DataType.GEOMETRY_CLASS != null) { deleteDb("spatial"); url = "spatial"; testSpatial(); @@ -129,8 +121,7 @@ private void testSpatialValues() throws SQLException { new Coordinate(1, 2), new Coordinate(2, 2), new Coordinate(1, 1) }); - - assertTrue(new H2Geometry(polygon).equals(rs.getObject(2))); + assertTrue(polygon.equals(rs.getObject(2))); rs = stat.executeQuery("select * from test where polygon = " + "'POLYGON ((1 1, 1 2, 2 2, 1 1))'"); @@ -576,12 +567,12 @@ public void reset() throws SQLException { * @param srid the projection id * @return Geometry object */ - public static IGeometry geomFromText(String text, int srid) throws SQLException { + public static Geometry geomFromText(String text, int srid) throws SQLException { WKTReader wktReader = new WKTReader(); try { Geometry geom = wktReader.read(text); geom.setSRID(srid); - return new H2Geometry(geom); + return geom; } catch (ParseException ex) { throw new SQLException(ex); } @@ -589,27 +580,23 @@ public static IGeometry geomFromText(String text, int srid) throws SQLException private void testGeometryDataType() { GeometryFactory geometryFactory = new GeometryFactory(); - H2Geometry geometry = new H2Geometry(geometryFactory.createPoint(new Coordinate(0, 0))); + Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); assertEquals(Value.GEOMETRY, DataType.getTypeFromClass(geometry.getClass())); } /** * Test serialization of Z and SRID values. - * @throws SQLException */ - private void testWKB() throws SQLException { + private void testWKB() { ValueGeometry geom3d = ValueGeometry.get( "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))", 27572); ValueGeometry copy = ValueGeometry.get(geom3d.getBytes()); - - Geometry geometry = copy.getGeometry().unwrap(Geometry.class); - - assertEquals(6, geometry.getCoordinates()[0].z); - assertEquals(5, geometry.getCoordinates()[1].z); - assertEquals(4, geometry.getCoordinates()[2].z); + assertEquals(6, copy.getGeometry().getCoordinates()[0].z); + assertEquals(5, copy.getGeometry().getCoordinates()[1].z); + assertEquals(4, copy.getGeometry().getCoordinates()[2].z); // Test SRID copy = ValueGeometry.get(geom3d.getBytes()); - assertEquals(27572, geometry.getSRID()); + assertEquals(27572, copy.getGeometry().getSRID()); } /** @@ -654,13 +641,12 @@ private void testEquals() { GeometryFactory geometryFactory = new GeometryFactory(); Geometry geometry = geometryFactory.createPoint(new Coordinate(0, 0)); geometry.setSRID(27572); - ValueGeometry valueGeometry = - ValueGeometry.get(new H2Geometry(geometry)); + ValueGeometry.getFromGeometry(geometry); Geometry geometry2 = geometryFactory.createPoint(new Coordinate(0, 0)); geometry2.setSRID(5326); ValueGeometry valueGeometry2 = - ValueGeometry.get(new H2Geometry(geometry2)); + ValueGeometry.getFromGeometry(geometry2); assertFalse(valueGeometry.equals(valueGeometry2)); // Check illegal geometry (no WKB representation) try { @@ -723,9 +709,9 @@ private void testAggregateWithGeometry() throws SQLException { assertEquals("geometry", rs.getMetaData(). getColumnTypeName(1).toLowerCase()); assertTrue(rs.next()); - assertTrue(rs.getObject(1) instanceof IGeometry); + assertTrue(rs.getObject(1) instanceof Geometry); assertTrue(new Envelope(1, 10, 1, 5).equals( - ((IGeometry) rs.getObject(1)).getEnvelope().unwrap(Envelope.class))); + ((Geometry) rs.getObject(1)).getEnvelopeInternal())); assertFalse(rs.next()); } finally { conn.close(); @@ -756,18 +742,18 @@ public void init(Connection conn) throws SQLException { @Override public void add(Object value) throws SQLException { - if (value instanceof IGeometry) { + if (value instanceof Geometry) { if (tableEnvelope == null) { - tableEnvelope = ((IGeometry) value).getEnvelope().unwrap(Envelope.class); + tableEnvelope = ((Geometry) value).getEnvelopeInternal(); } else { - tableEnvelope.expandToInclude(((IGeometry) value).getEnvelope().unwrap(Envelope.class)); + tableEnvelope.expandToInclude(((Geometry) value).getEnvelopeInternal()); } } } @Override public Object getResult() throws SQLException { - return new H2Geometry(new GeometryFactory().toGeometry(tableEnvelope)); + return new GeometryFactory().toGeometry(tableEnvelope); } } @@ -816,8 +802,7 @@ private void testValueGeometryScript() throws SQLException { "SELECT " + valueGeometry.getSQL()); assertTrue(rs.next()); Object obj = rs.getObject(1); - assertTrue(obj instanceof IGeometry); - ValueGeometry g = ValueGeometry.get((IGeometry) obj); + ValueGeometry g = ValueGeometry.getFromGeometry(obj); assertTrue("got: " + g + " exp: " + valueGeometry, valueGeometry.equals(g)); } finally { conn.close(); @@ -835,15 +820,15 @@ private void testInPlaceUpdate() throws SQLException { "SELECT 'POINT(1 1)'::geometry"); assertTrue(rs.next()); // Mutate the geometry - ((IGeometry) rs.getObject(1)).unwrap(Geometry.class).apply(new AffineTransformation(1, 0, + ((Geometry) rs.getObject(1)).apply(new AffineTransformation(1, 0, 1, 1, 0, 1)); rs.close(); rs = conn.createStatement().executeQuery( "SELECT 'POINT(1 1)'::geometry"); assertTrue(rs.next()); // Check if the geometry is the one requested - assertEquals(1, ((IGeometry) rs.getObject(1)).unwrap(Point.class).getX()); - assertEquals(1, ((IGeometry) rs.getObject(1)).unwrap(Point.class).getY()); + assertEquals(1, ((Point) rs.getObject(1)).getX()); + assertEquals(1, ((Point) rs.getObject(1)).getY()); rs.close(); } finally { conn.close(); From c9ab70ce67079adc903cfa5eb8f859e546bda8c4 Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Wed, 15 Apr 2015 18:08:53 +0200 Subject: [PATCH 7/9] Use an additional interface to solve the ServiceLoader problem in case of generic interfaces --- h2/src/main/org/h2/api/ISpatialDriver.java | 7 ++++ .../org/h2/api/IValueGeometryFactory.java | 4 +- h2/src/main/org/h2/expression/Comparison.java | 4 +- h2/src/main/org/h2/index/IndexCursor.java | 9 ++--- h2/src/main/org/h2/store/Data.java | 3 +- .../main/org/h2/value/JTSSpatialDriver.java | 13 +++++++ .../main/org/h2/value/JTSValueGeometry.java | 4 +- .../org/h2/value/JTSValueGeometryFactory.java | 8 ++++ h2/src/main/org/h2/value/Value.java | 9 ++--- h2/src/main/org/h2/value/ValueGeometry.java | 39 +++++++++++++++++-- .../services/org.h2.api.ISpatialDriver | 1 + .../services/org.h2.api.IValueGeometryFactory | 1 - .../org/h2/test/unit/TestValueMemory.java | 4 +- 13 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 h2/src/main/org/h2/api/ISpatialDriver.java create mode 100644 h2/src/main/org/h2/value/JTSSpatialDriver.java create mode 100644 h2/src/test/META-INF/services/org.h2.api.ISpatialDriver delete mode 100644 h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory diff --git a/h2/src/main/org/h2/api/ISpatialDriver.java b/h2/src/main/org/h2/api/ISpatialDriver.java new file mode 100644 index 000000000..f7c42a2e5 --- /dev/null +++ b/h2/src/main/org/h2/api/ISpatialDriver.java @@ -0,0 +1,7 @@ +package org.h2.api; + + +public interface ISpatialDriver { + + public IValueGeometryFactory createGeometryFactory(); +} diff --git a/h2/src/main/org/h2/api/IValueGeometryFactory.java b/h2/src/main/org/h2/api/IValueGeometryFactory.java index 00cbede3b..f62ddaaae 100644 --- a/h2/src/main/org/h2/api/IValueGeometryFactory.java +++ b/h2/src/main/org/h2/api/IValueGeometryFactory.java @@ -1,6 +1,7 @@ package org.h2.api; import org.h2.message.DbException; +import org.h2.value.Value; import org.h2.value.ValueGeometry; import com.vividsolutions.jts.geom.Geometry; @@ -18,6 +19,8 @@ public interface IValueGeometryFactory, S> { public T get(byte[] g); + public T get(Value g); + public Class getGeometryType(); public boolean isGeometryTypeSupported(Object g); @@ -33,7 +36,6 @@ public interface IValueGeometryFactory, S> { * @throws GeometryParseException if a parsing problem occurs */ public S getGeometry(String s) throws DbException; - /** * Get or create a geometry value for the given geometry. diff --git a/h2/src/main/org/h2/expression/Comparison.java b/h2/src/main/org/h2/expression/Comparison.java index 3caaa836f..12503a1c8 100644 --- a/h2/src/main/org/h2/expression/Comparison.java +++ b/h2/src/main/org/h2/expression/Comparison.java @@ -293,9 +293,7 @@ static boolean compareNotNull(Database database, Value l, Value r, result = database.compare(l, r) < 0; break; case SPATIAL_INTERSECTS: { - ValueGeometry lg = (ValueGeometry) l.convertTo(Value.GEOMETRY); - ValueGeometry rg = (ValueGeometry) r.convertTo(Value.GEOMETRY); - result = lg.intersectsBoundingBox(rg); + result = Value.getGeometryFactory().get(l).intersectsBoundingBox(Value.getGeometryFactory().get(r)); break; } default: diff --git a/h2/src/main/org/h2/index/IndexCursor.java b/h2/src/main/org/h2/index/IndexCursor.java index 32783bf48..0ddcd8847 100644 --- a/h2/src/main/org/h2/index/IndexCursor.java +++ b/h2/src/main/org/h2/index/IndexCursor.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.HashSet; + import org.h2.engine.Session; import org.h2.expression.Comparison; import org.h2.message.DbException; @@ -19,7 +20,6 @@ import org.h2.table.Table; import org.h2.table.TableFilter; import org.h2.value.Value; -import org.h2.value.ValueGeometry; import org.h2.value.ValueNull; /** @@ -185,10 +185,9 @@ private SearchRow getSpatialSearchRow(SearchRow row, int columnId, Value v) { // if an object needs to overlap with both a and b, // then it needs to overlap with the the union of a and b // (not the intersection) - ValueGeometry vg = (ValueGeometry) row.getValue(columnId). - convertTo(Value.GEOMETRY); - v = ((ValueGeometry) v.convertTo(Value.GEOMETRY)). - getEnvelopeUnion(vg); + + v= Value.getGeometryFactory().get(v).getEnvelopeUnion( + Value.getGeometryFactory().get(row.getValue(columnId))); } if (columnId < 0) { row.setKey(v.getLong()); diff --git a/h2/src/main/org/h2/store/Data.java b/h2/src/main/org/h2/store/Data.java index 38b869931..7ff9a8bf1 100644 --- a/h2/src/main/org/h2/store/Data.java +++ b/h2/src/main/org/h2/store/Data.java @@ -36,7 +36,6 @@ import org.h2.value.ValueDecimal; import org.h2.value.ValueDouble; import org.h2.value.ValueFloat; -import org.h2.value.ValueGeometry; import org.h2.value.ValueInt; import org.h2.value.ValueJavaObject; import org.h2.value.ValueLob; @@ -773,7 +772,7 @@ public Value readValue() { int len = readVarInt(); byte[] b = DataUtils.newBytes(len); read(b, 0, len); - return ValueGeometry.get(b); + return Value.getGeometryFactory().get(b); } case Value.JAVA_OBJECT: { int len = readVarInt(); diff --git a/h2/src/main/org/h2/value/JTSSpatialDriver.java b/h2/src/main/org/h2/value/JTSSpatialDriver.java new file mode 100644 index 000000000..3a1d8fff8 --- /dev/null +++ b/h2/src/main/org/h2/value/JTSSpatialDriver.java @@ -0,0 +1,13 @@ +package org.h2.value; + +import org.h2.api.ISpatialDriver; +import org.h2.api.IValueGeometryFactory; + + +public class JTSSpatialDriver implements ISpatialDriver{ + + @Override + public IValueGeometryFactory createGeometryFactory() { + return new JTSValueGeometryFactory(); + } +} \ No newline at end of file diff --git a/h2/src/main/org/h2/value/JTSValueGeometry.java b/h2/src/main/org/h2/value/JTSValueGeometry.java index 442e67f2d..116528e7d 100644 --- a/h2/src/main/org/h2/value/JTSValueGeometry.java +++ b/h2/src/main/org/h2/value/JTSValueGeometry.java @@ -21,13 +21,13 @@ public Geometry getGeometry() { } @Override - public boolean intersectsBoundingBox(ValueGeometry r) { + public boolean _intersectsBoundingBox(ValueGeometry r) { // the Geometry object caches the envelope return getGeometryNoCopy().getEnvelopeInternal().intersects( r.getGeometryNoCopy().getEnvelopeInternal()); } - public Value getEnvelopeUnion(ValueGeometry r) { + public Value _getEnvelopeUnion(ValueGeometry r) { GeometryFactory gf = new GeometryFactory(); Envelope mergedEnvelope = new Envelope(getGeometryNoCopy().getEnvelopeInternal()); mergedEnvelope.expandToInclude(r.getGeometryNoCopy().getEnvelopeInternal()); diff --git a/h2/src/main/org/h2/value/JTSValueGeometryFactory.java b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java index daad8515d..d0d964833 100644 --- a/h2/src/main/org/h2/value/JTSValueGeometryFactory.java +++ b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java @@ -85,6 +85,14 @@ public JTSValueGeometry get(Object g) { return get((Geometry)g); } + + @Override + public JTSValueGeometry get(Value g) { + if(!(g instanceof JTSValueGeometry)) + throw new RuntimeException("The given value is not compatible with this ValueGeometryFactory instance!"); + + return (JTSValueGeometry) g; + } @Override public boolean isGeometryTypeSupported(Object g) { diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java index 92b69ef3d..529d8fe37 100644 --- a/h2/src/main/org/h2/value/Value.java +++ b/h2/src/main/org/h2/value/Value.java @@ -22,6 +22,7 @@ import java.util.ServiceLoader; import org.h2.api.ErrorCode; +import org.h2.api.ISpatialDriver; import org.h2.api.IValueGeometryFactory; import org.h2.engine.Constants; import org.h2.engine.SysProperties; @@ -182,11 +183,9 @@ public abstract class Value { private static final IValueGeometryFactory,?> GEOMETRY_FACTORY; static { - @SuppressWarnings("unchecked") - ServiceLoader> geometryFactories = ServiceLoader.load( - (Class>)IValueGeometryFactory.class); - Iterator> geometryFactoryIterator = geometryFactories.iterator(); - GEOMETRY_FACTORY = geometryFactoryIterator.hasNext() ? geometryFactories.iterator().next() : null; + ServiceLoader geometryFactories = ServiceLoader.load(ISpatialDriver.class); + Iterator geometryFactoryIterator = geometryFactories.iterator(); + GEOMETRY_FACTORY = (geometryFactoryIterator.hasNext() ? geometryFactories.iterator().next().createGeometryFactory() : null); } /** diff --git a/h2/src/main/org/h2/value/ValueGeometry.java b/h2/src/main/org/h2/value/ValueGeometry.java index 951f6ba74..9537b03d1 100644 --- a/h2/src/main/org/h2/value/ValueGeometry.java +++ b/h2/src/main/org/h2/value/ValueGeometry.java @@ -74,16 +74,49 @@ public T getGeometryNoCopy() { * @param r the other geometry * @return true if the two overlap */ - public abstract boolean intersectsBoundingBox(ValueGeometry r); + protected abstract boolean _intersectsBoundingBox(ValueGeometry r); + /** + * Test if this geometry envelope intersects with the other geometry + * envelope. + * + * @param r the other geometry + * @return true if the two overlap + */ + @SuppressWarnings("unchecked") + public final boolean intersectsBoundingBox(ValueGeometry r) + { + if(!getClass().isInstance(r)){ + return false; // not supported and should never happen + } + + return _intersectsBoundingBox((ValueGeometry) r); + } + /** * Get the union. * * @param r the other geometry * @return the union of this geometry envelope and another geometry envelope */ - public abstract Value getEnvelopeUnion(ValueGeometry r); - + protected abstract Value _getEnvelopeUnion(ValueGeometry r); + + /** + * Get the union. + * + * @param r the other geometry + * @return the union of this geometry envelope and another geometry envelope + */ + @SuppressWarnings("unchecked") + public final Value getEnvelopeUnion(ValueGeometry r) + { + if(!getClass().isInstance(r)){ + return ValueNull.INSTANCE; // not supported and should never happen + } + + return _getEnvelopeUnion((ValueGeometry) r); + } + @Override public int getType() { diff --git a/h2/src/test/META-INF/services/org.h2.api.ISpatialDriver b/h2/src/test/META-INF/services/org.h2.api.ISpatialDriver new file mode 100644 index 000000000..ead47e333 --- /dev/null +++ b/h2/src/test/META-INF/services/org.h2.api.ISpatialDriver @@ -0,0 +1 @@ +org.h2.value.JTSSpatialDriver \ No newline at end of file diff --git a/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory b/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory deleted file mode 100644 index f17dd9bd7..000000000 --- a/h2/src/test/META-INF/services/org.h2.api.IValueGeometryFactory +++ /dev/null @@ -1 +0,0 @@ -org.h2.value.JTSValueGeometryFactory \ No newline at end of file diff --git a/h2/src/test/org/h2/test/unit/TestValueMemory.java b/h2/src/test/org/h2/test/unit/TestValueMemory.java index 46bcb7c65..55bd1a1ee 100644 --- a/h2/src/test/org/h2/test/unit/TestValueMemory.java +++ b/h2/src/test/org/h2/test/unit/TestValueMemory.java @@ -195,10 +195,10 @@ private Value create(int type) throws SQLException { case Value.STRING_FIXED: return ValueStringFixed.get(randomString(random.nextInt(100))); case Value.GEOMETRY: - if (DataType.GEOMETRY_CLASS == null) { + if(!Value.isGeometryFactoryInitialized()){ return ValueNull.INSTANCE; } - return ValueGeometry.get("POINT (" + random.nextInt(100) + " " + + return Value.getGeometryFactory().get("POINT (" + random.nextInt(100) + " " + random.nextInt(100) + ")"); default: throw new AssertionError("type=" + type); From eff2db37b86baecb62f1f9ea4dfe0716199591fe Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Tue, 8 Mar 2016 18:27:17 +0100 Subject: [PATCH 8/9] Fixes merge conflicts --- h2/src/main/org/h2/api/ISpatialDriver.java | 2 +- .../org/h2/api/IValueGeometryFactory.java | 74 ----------- .../main/org/h2/api/ValueGeometryFactory.java | 120 ++++++++++++++++++ .../main/org/h2/value/JTSSpatialDriver.java | 4 +- .../main/org/h2/value/JTSValueGeometry.java | 13 +- .../org/h2/value/JTSValueGeometryFactory.java | 15 ++- h2/src/main/org/h2/value/Value.java | 10 +- h2/src/main/org/h2/value/ValueGeometry.java | 13 +- h2/src/test/org/h2/test/db/TestSpatial.java | 33 +++-- 9 files changed, 181 insertions(+), 103 deletions(-) delete mode 100644 h2/src/main/org/h2/api/IValueGeometryFactory.java create mode 100644 h2/src/main/org/h2/api/ValueGeometryFactory.java diff --git a/h2/src/main/org/h2/api/ISpatialDriver.java b/h2/src/main/org/h2/api/ISpatialDriver.java index f7c42a2e5..e641cac61 100644 --- a/h2/src/main/org/h2/api/ISpatialDriver.java +++ b/h2/src/main/org/h2/api/ISpatialDriver.java @@ -3,5 +3,5 @@ public interface ISpatialDriver { - public IValueGeometryFactory createGeometryFactory(); + public ValueGeometryFactory createGeometryFactory(); } diff --git a/h2/src/main/org/h2/api/IValueGeometryFactory.java b/h2/src/main/org/h2/api/IValueGeometryFactory.java deleted file mode 100644 index f62ddaaae..000000000 --- a/h2/src/main/org/h2/api/IValueGeometryFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.h2.api; - -import org.h2.message.DbException; -import org.h2.value.Value; -import org.h2.value.ValueGeometry; - -import com.vividsolutions.jts.geom.Geometry; - -public interface IValueGeometryFactory, S> { - - /** - * Get or create a geometry value for the given geometry. - * - * @param g - * the geometry object - * @return the value - */ - public T get(Object g); - - public T get(byte[] g); - - public T get(Value g); - - public Class getGeometryType(); - - public boolean isGeometryTypeSupported(Object g); - - public S getGeometry(byte[] bytes) throws DbException; - - /** - * 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 S getGeometry(String s) throws DbException; - - /** - * Get or create a geometry value for the given geometry. - * - * @param s the WKT representation of the geometry - * @param srid the srid of the object - * @return the value - */ - public S getGeometry(String s, int srid) throws DbException; - - /** - * Get or create a geometry value for the given geometry. - * - * @param g - * the geometry object - * @return the value - */ - public T get(Geometry g); - - /** - * Get or create a geometry value for the given geometry. - * - * @param s the WKT representation of the geometry - * @return the value - */ - public T get(String s); - - /** - * Get or create a geometry value for the given geometry. - * - * @param s the WKT representation of the geometry - * @param srid the srid of the object - * @return the value - */ - public T get(String s, int srid); -} diff --git a/h2/src/main/org/h2/api/ValueGeometryFactory.java b/h2/src/main/org/h2/api/ValueGeometryFactory.java new file mode 100644 index 000000000..9cc3a282c --- /dev/null +++ b/h2/src/main/org/h2/api/ValueGeometryFactory.java @@ -0,0 +1,120 @@ +/* + * Copyright 2004-2015 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.api; + +import org.h2.message.DbException; +import org.h2.value.Value; +import org.h2.value.ValueGeometry; + +import com.vividsolutions.jts.geom.Geometry; + +/** + * Interface of a factory which provides methods for the conversion of a + * geometry object of a framework like JTS into a {@link ValueGeometry}. + * + * @author Steve Hruda + * @param the type of your {@link ValueGeometry} implementation + * @param the type of the frameworks geometry object + */ +public interface ValueGeometryFactory, S> { + + /** + * Get or create a geometry value for the given geometry. + * + * @param g + * the geometry object + * @return the value + */ + public T get(Object g); + + /** + * Get or create a geometry value for the given byte array. + * @param g + * the geometry object as a byte array + * @return the value + */ + public T get(byte[] g); + + /** + * Get or create a geometry value for the given {@link Value}. + * @param g + * the geometry as {@link Value} + * @return the value + */ + public T get(Value g); + + /** + * Returns the type of the used geometry framework. + * @return the type of the used geometry framework. + */ + public Class getGeometryType(); + + /** + * Returns true if the given object is an instance + * of the used geometry framework, false otherwise. + * @param g + * the geometry + * @return true if the given object is an instance + * of the used geometry framework, false otherwise. + */ + public boolean isGeometryTypeSupported(Object g); + + /** + * Creates a geometry instance by using the given byte array + * representation. + * + * @param bytes the byte array representation of the geometry + * @return a new geometry instance + * @throws DbException - if an exception occurs during the creation + */ + public S getGeometry(byte[] bytes) throws DbException; + + /** + * Creates a geometry instance by using the given string + * representation. + * + * @param s the string representation of the geometry + * @return a new geometry instance + * @throws DbException - if an exception occurs during the creation + */ + public S getGeometry(String s) throws DbException; + + /** + * Get or create a geometry value for the given geometry. + * + * @param s the WKT representation of the geometry + * @param srid the srid of the object + * @return the value + * @throws DbException - if an exception occurs during the creation + */ + public S getGeometry(String s, int srid) throws DbException; + + /** + * Get or create a geometry value for the given geometry. + * + * @param g + * the geometry object + * @return the value + */ + public T get(Geometry g); + + /** + * Get or create a geometry value for the given geometry. + * + * @param s the WKT representation of the geometry + * @return the value + */ + public T get(String s); + + /** + * Get or create a geometry value for the given geometry. + * + * @param s the WKT representation of the geometry + * @param srid the srid of the object + * @return the value + */ + public T get(String s, int srid); +} diff --git a/h2/src/main/org/h2/value/JTSSpatialDriver.java b/h2/src/main/org/h2/value/JTSSpatialDriver.java index 3a1d8fff8..143460ffa 100644 --- a/h2/src/main/org/h2/value/JTSSpatialDriver.java +++ b/h2/src/main/org/h2/value/JTSSpatialDriver.java @@ -1,13 +1,13 @@ package org.h2.value; import org.h2.api.ISpatialDriver; -import org.h2.api.IValueGeometryFactory; +import org.h2.api.ValueGeometryFactory; public class JTSSpatialDriver implements ISpatialDriver{ @Override - public IValueGeometryFactory createGeometryFactory() { + public ValueGeometryFactory createGeometryFactory() { return new JTSValueGeometryFactory(); } } \ No newline at end of file diff --git a/h2/src/main/org/h2/value/JTSValueGeometry.java b/h2/src/main/org/h2/value/JTSValueGeometry.java index 116528e7d..aaabe377e 100644 --- a/h2/src/main/org/h2/value/JTSValueGeometry.java +++ b/h2/src/main/org/h2/value/JTSValueGeometry.java @@ -1,3 +1,8 @@ +/* + * 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; import java.util.Arrays; @@ -9,9 +14,14 @@ import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.io.WKTWriter; +/** + * The {@link ValueGeometry} implementation for the JTS geometry framework. + * + * @author Steve Hruda + */ public class JTSValueGeometry extends ValueGeometry { - protected JTSValueGeometry(byte[] bytes, Geometry geometry) { + JTSValueGeometry(byte[] bytes, Geometry geometry) { super(bytes, geometry); } @@ -27,6 +37,7 @@ public boolean _intersectsBoundingBox(ValueGeometry r) { r.getGeometryNoCopy().getEnvelopeInternal()); } + @Override public Value _getEnvelopeUnion(ValueGeometry r) { GeometryFactory gf = new GeometryFactory(); Envelope mergedEnvelope = new Envelope(getGeometryNoCopy().getEnvelopeInternal()); diff --git a/h2/src/main/org/h2/value/JTSValueGeometryFactory.java b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java index d0d964833..41add4d12 100644 --- a/h2/src/main/org/h2/value/JTSValueGeometryFactory.java +++ b/h2/src/main/org/h2/value/JTSValueGeometryFactory.java @@ -1,6 +1,11 @@ +/* + * 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; -import org.h2.api.IValueGeometryFactory; +import org.h2.api.ValueGeometryFactory; import org.h2.message.DbException; import com.vividsolutions.jts.geom.CoordinateSequence; @@ -13,7 +18,13 @@ import com.vividsolutions.jts.io.WKBWriter; import com.vividsolutions.jts.io.WKTReader; -public class JTSValueGeometryFactory implements IValueGeometryFactory { +/** + * The {@link ValueGeometryFactory} implementation for the JTS geometry + * framework. + * + * @author Steve Hruda + */ +public class JTSValueGeometryFactory implements ValueGeometryFactory { @Override public Geometry getGeometry(byte[] bytes) throws DbException { diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java index 457086c13..d3667cc86 100644 --- a/h2/src/main/org/h2/value/Value.java +++ b/h2/src/main/org/h2/value/Value.java @@ -23,7 +23,7 @@ import org.h2.api.ErrorCode; import org.h2.api.ISpatialDriver; -import org.h2.api.IValueGeometryFactory; +import org.h2.api.ValueGeometryFactory; import org.h2.engine.Constants; import org.h2.engine.SysProperties; import org.h2.message.DbException; @@ -180,7 +180,7 @@ public abstract class Value { * Factory which provides a couple of methods to create a {@link IGeometry} * instance. */ - private static final IValueGeometryFactory,?> GEOMETRY_FACTORY; + private static final ValueGeometryFactory,?> GEOMETRY_FACTORY; static { ServiceLoader geometryFactories = ServiceLoader.load(ISpatialDriver.class); @@ -834,8 +834,8 @@ public Value convertTo(int targetType) { return GEOMETRY_FACTORY.get(getBytesNoCopy()); case JAVA_OBJECT: Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler()); - if (object instanceof IGeometry) { - return ValueGeometry.get((IGeometry) object); + if (GEOMETRY_FACTORY.isGeometryTypeSupported(object)) { + return GEOMETRY_FACTORY.get(object); } } } @@ -1193,7 +1193,7 @@ public static boolean isGeometryFactoryInitialized() return GEOMETRY_FACTORY!=null; } - public static IValueGeometryFactory, ?> getGeometryFactory() + public static ValueGeometryFactory, ?> getGeometryFactory() { return GEOMETRY_FACTORY; } diff --git a/h2/src/main/org/h2/value/ValueGeometry.java b/h2/src/main/org/h2/value/ValueGeometry.java index 9537b03d1..62f7da3c0 100644 --- a/h2/src/main/org/h2/value/ValueGeometry.java +++ b/h2/src/main/org/h2/value/ValueGeometry.java @@ -18,6 +18,7 @@ * @author Thomas Mueller * @author Noel Grandin * @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888 + * @param the type of the used framework geometry */ public abstract class ValueGeometry extends Value{ @@ -59,6 +60,10 @@ protected ValueGeometry(byte[] bytes, T geometry) { */ public abstract T getGeometry(); + /** + * Returns the internal geometry instance which should be immutable. + * @return the internal geometry instance which should be immutable + */ @SuppressWarnings("unchecked") public T getGeometryNoCopy() { if (geometry == null) { @@ -172,7 +177,8 @@ public int getMemory() { return getBytes().length * 20 + 24; } - public abstract boolean equals(Object other); + @Override + public abstract boolean equals(Object other); @Override @@ -183,5 +189,10 @@ public Value convertTo(int targetType) { return super.convertTo(targetType); } + /** + * Returns the {@link SpatialKey} for the given row key. + * @param id the row key + * @return the {@link SpatialKey} for the given row key + */ public abstract SpatialKey getSpatialKey(long id); } diff --git a/h2/src/test/org/h2/test/db/TestSpatial.java b/h2/src/test/org/h2/test/db/TestSpatial.java index 033f97746..e0397be4c 100644 --- a/h2/src/test/org/h2/test/db/TestSpatial.java +++ b/h2/src/test/org/h2/test/db/TestSpatial.java @@ -13,27 +13,25 @@ import java.sql.Types; import java.util.Random; -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Point; -import com.vividsolutions.jts.geom.util.AffineTransformation; - import org.h2.api.Aggregate; -import org.h2.api.IValueGeometryFactory; import org.h2.test.TestBase; import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleRowSource; import org.h2.value.DataType; -import org.h2.value.JTSValueGeometry; +import org.h2.value.JTSValueGeometryFactory; import org.h2.value.Value; +import org.h2.value.ValueGeometry; + import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; +import com.vividsolutions.jts.geom.util.AffineTransformation; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; -import org.h2.value.ValueGeometry; - /** * Spatial datatype and index tests. * @@ -594,15 +592,16 @@ private void testGeometryDataType() { * Test serialization of Z and SRID values. */ private void testWKB() { - JTSValueGeometry geom3d = getValueGeometryFactory().get( - "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))", 27572); - JTSValueGeometry copy = getValueGeometryFactory().get(geom3d.getBytes()); - assertEquals(6, copy.getGeometry().getCoordinates()[0].z); - assertEquals(5, copy.getGeometry().getCoordinates()[1].z); - assertEquals(4, copy.getGeometry().getCoordinates()[2].z); - // Test SRID - copy = getValueGeometryFactory().get(geom3d.getBytes()); - assertEquals(27572, copy.getGeometry().getSRID()); + JTSValueGeometryFactory geometryFactory = new JTSValueGeometryFactory(); + ValueGeometry geom3d = geometryFactory.get( + "POLYGON ((67 13 6, 67 18 5, 59 18 4, 59 13 6, 67 13 6))", 27572); + ValueGeometry copy = geometryFactory.get(geom3d.getBytes()); + assertEquals(6, copy.getGeometry().getCoordinates()[0].z); + assertEquals(5, copy.getGeometry().getCoordinates()[1].z); + assertEquals(4, copy.getGeometry().getCoordinates()[2].z); + // Test SRID + copy = geometryFactory.get(geom3d.getBytes()); + assertEquals(27572, copy.getGeometry().getSRID()); } /** From 702e8f14eb9b8080f46da110dd90870fae4bb573 Mon Sep 17 00:00:00 2001 From: "Hruda, Steve" Date: Tue, 8 Mar 2016 18:33:54 +0100 Subject: [PATCH 9/9] Rename ISpatialDriver to SpatialDriver and add missing javadoc --- h2/src/main/org/h2/api/ISpatialDriver.java | 7 ------- h2/src/main/org/h2/api/SpatialDriver.java | 21 +++++++++++++++++++ .../main/org/h2/value/JTSSpatialDriver.java | 18 +++++++++++++--- h2/src/main/org/h2/value/Value.java | 6 +++--- 4 files changed, 39 insertions(+), 13 deletions(-) delete mode 100644 h2/src/main/org/h2/api/ISpatialDriver.java create mode 100644 h2/src/main/org/h2/api/SpatialDriver.java diff --git a/h2/src/main/org/h2/api/ISpatialDriver.java b/h2/src/main/org/h2/api/ISpatialDriver.java deleted file mode 100644 index e641cac61..000000000 --- a/h2/src/main/org/h2/api/ISpatialDriver.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.h2.api; - - -public interface ISpatialDriver { - - public ValueGeometryFactory createGeometryFactory(); -} diff --git a/h2/src/main/org/h2/api/SpatialDriver.java b/h2/src/main/org/h2/api/SpatialDriver.java new file mode 100644 index 000000000..f3cb6d24f --- /dev/null +++ b/h2/src/main/org/h2/api/SpatialDriver.java @@ -0,0 +1,21 @@ +/* + * Copyright 2004-2016 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.api; + +/** + * Interface which will be used by h2 to create the geometry factory. + * + * @author Steve Hruda + */ +public interface SpatialDriver { + + /** + * A new {@link ValueGeometryFactory} instance. + * + * @return a new {@link ValueGeometryFactory} instance + */ + public ValueGeometryFactory createGeometryFactory(); +} diff --git a/h2/src/main/org/h2/value/JTSSpatialDriver.java b/h2/src/main/org/h2/value/JTSSpatialDriver.java index 143460ffa..21920894e 100644 --- a/h2/src/main/org/h2/value/JTSSpatialDriver.java +++ b/h2/src/main/org/h2/value/JTSSpatialDriver.java @@ -1,11 +1,23 @@ +/* + * Copyright 2004-2016 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; -import org.h2.api.ISpatialDriver; +import org.h2.api.SpatialDriver; import org.h2.api.ValueGeometryFactory; +/** + * The {@link SpatialDriver} implementation for the JTS geometry framework. + * + * @author Steve Hruda, 2016 + */ +public class JTSSpatialDriver implements SpatialDriver{ -public class JTSSpatialDriver implements ISpatialDriver{ - + /** + * @see org.h2.api.SpatialDriver#createGeometryFactory() + */ @Override public ValueGeometryFactory createGeometryFactory() { return new JTSValueGeometryFactory(); diff --git a/h2/src/main/org/h2/value/Value.java b/h2/src/main/org/h2/value/Value.java index d3667cc86..9f084ee06 100644 --- a/h2/src/main/org/h2/value/Value.java +++ b/h2/src/main/org/h2/value/Value.java @@ -22,7 +22,7 @@ import java.util.ServiceLoader; import org.h2.api.ErrorCode; -import org.h2.api.ISpatialDriver; +import org.h2.api.SpatialDriver; import org.h2.api.ValueGeometryFactory; import org.h2.engine.Constants; import org.h2.engine.SysProperties; @@ -183,8 +183,8 @@ public abstract class Value { private static final ValueGeometryFactory,?> GEOMETRY_FACTORY; static { - ServiceLoader geometryFactories = ServiceLoader.load(ISpatialDriver.class); - Iterator geometryFactoryIterator = geometryFactories.iterator(); + ServiceLoader geometryFactories = ServiceLoader.load(SpatialDriver.class); + Iterator geometryFactoryIterator = geometryFactories.iterator(); GEOMETRY_FACTORY = (geometryFactoryIterator.hasNext() ? geometryFactories.iterator().next().createGeometryFactory() : null); }