提交 1196666c authored 作者: noelgrandin's avatar noelgrandin

Add support for spatial datatype GEOMETRY.

上级 9a883bad
...@@ -2335,6 +2335,18 @@ and ""ResultSet.getObject(..)"" or ""ResultSet.getArray(..)"" to retrieve the va ...@@ -2335,6 +2335,18 @@ and ""ResultSet.getObject(..)"" or ""ResultSet.getArray(..)"" to retrieve the va
ARRAY ARRAY
" "
"Data Types","GEOMETRY Type","
GEOMETRY
","
A spatial geometry type, based on the com.vividsolutions.jts library.
Normally represented in textual format using the WKT (Well Known Text) format.
Use a quoted string containing a KWB formatted string or ""PreparedStatement.setObject()"" to store values,
and ""ResultSet.getObject(..)"" or ""ResultSet.getString(..)"" to retrieve the values.
","
ARRAY
"
"Functions (Aggregate)","AVG"," "Functions (Aggregate)","AVG","
AVG ( [ DISTINCT ] { numeric } ) AVG ( [ DISTINCT ] { numeric } )
"," ","
......
...@@ -42,6 +42,7 @@ Change Log ...@@ -42,6 +42,7 @@ Change Log
</li><li>Issue 479: Support for SUBSTRING without a FROM condition, patch from Andrew Franklin. </li><li>Issue 479: Support for SUBSTRING without a FROM condition, patch from Andrew Franklin.
</li><li>Issue 472: PgServer does not work with any recent Postgres JDBC driver, patch from Andrew Franklin. </li><li>Issue 472: PgServer does not work with any recent Postgres JDBC driver, patch from Andrew Franklin.
</li><li>Add syntax for passing additional parameters into custom TableEngine implementations. </li><li>Add syntax for passing additional parameters into custom TableEngine implementations.
</li><li>Add support for spatial datatype GEOMETRY.
</li></ul> </li></ul>
<h2>Version 1.3.172 (2013-05-25)</h2> <h2>Version 1.3.172 (2013-05-25)</h2>
......
...@@ -38,6 +38,7 @@ import org.h2.value.ValueDate; ...@@ -38,6 +38,7 @@ import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal; import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble; import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat; import org.h2.value.ValueFloat;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
import org.h2.value.ValueJavaObject; import org.h2.value.ValueJavaObject;
import org.h2.value.ValueLob; import org.h2.value.ValueLob;
...@@ -670,6 +671,20 @@ public class Data { ...@@ -670,6 +671,20 @@ public class Data {
} }
break; break;
} }
case Value.GEOMETRY: {
writeByte((byte) type);
byte[] b = v.getBytes();
int len = b.length;
if (len < 32) {
writeByte((byte) (BYTES_0_31 + len));
write(b, 0, b.length);
} else {
writeByte((byte) type);
writeVarInt(b.length);
write(b, 0, b.length);
}
break;
}
default: default:
DbException.throwInternalError("type=" + v.getType()); DbException.throwInternalError("type=" + v.getType());
} }
...@@ -1074,6 +1089,14 @@ public class Data { ...@@ -1074,6 +1089,14 @@ public class Data {
} }
return len; return len;
} }
case Value.GEOMETRY: {
byte[] b = v.getBytesNoCopy();
int len = b.length;
if (len < 32) {
return 1 + b.length;
}
return 1 + getVarIntLen(b.length) + b.length;
}
default: default:
throw DbException.throwInternalError("type=" + v.getType()); throw DbException.throwInternalError("type=" + v.getType());
} }
......
...@@ -22,7 +22,6 @@ import java.sql.Types; ...@@ -22,7 +22,6 @@ import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.UUID; import java.util.UUID;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
...@@ -323,6 +322,11 @@ public class DataType { ...@@ -323,6 +322,11 @@ public class DataType {
// 80 for ValueLob, 24 for String // 80 for ValueLob, 24 for String
104 104
); );
add(Value.GEOMETRY, Types.OTHER, "Geometry",
createString(false),
new String[]{"GEOMETRY"},
32
);
DataType dataType = new DataType(); DataType dataType = new DataType();
dataType.prefix = "("; dataType.prefix = "(";
dataType.suffix = "')"; dataType.suffix = "')";
...@@ -594,6 +598,13 @@ public class DataType { ...@@ -594,6 +598,13 @@ public class DataType {
} }
return ValueResultSet.get(rs); return ValueResultSet.get(rs);
} }
case Value.GEOMETRY: {
com.vividsolutions.jts.geom.Geometry x = (com.vividsolutions.jts.geom.Geometry) rs.getObject(columnIndex);
if (x == null) {
return ValueNull.INSTANCE;
}
return ValueGeometry.get(x);
}
default: default:
throw DbException.throwInternalError("type="+type); throw DbException.throwInternalError("type="+type);
} }
...@@ -671,6 +682,8 @@ public class DataType { ...@@ -671,6 +682,8 @@ public class DataType {
return Array.class.getName(); return Array.class.getName();
case Value.RESULT_SET: case Value.RESULT_SET:
return ResultSet.class.getName(); return ResultSet.class.getName();
case Value.GEOMETRY:
return com.vividsolutions.jts.geom.Geometry.class.getName();
default: default:
throw DbException.throwInternalError("type="+type); throw DbException.throwInternalError("type="+type);
} }
...@@ -832,6 +845,8 @@ public class DataType { ...@@ -832,6 +845,8 @@ public class DataType {
} else if (Object[].class.isAssignableFrom(x)) { } else if (Object[].class.isAssignableFrom(x)) {
// this includes String[] and so on // this includes String[] and so on
return Value.ARRAY; return Value.ARRAY;
} else if (com.vividsolutions.jts.geom.Geometry.class.isAssignableFrom(x)) {
return Value.GEOMETRY;
} else { } else {
return Value.JAVA_OBJECT; return Value.JAVA_OBJECT;
} }
...@@ -920,6 +935,8 @@ public class DataType { ...@@ -920,6 +935,8 @@ public class DataType {
return ValueArray.get(x.getClass().getComponentType(), v); return ValueArray.get(x.getClass().getComponentType(), v);
} else if (x instanceof Character) { } else if (x instanceof Character) {
return ValueStringFixed.get(((Character) x).toString()); return ValueStringFixed.get(((Character) x).toString());
} else if (x instanceof com.vividsolutions.jts.geom.Geometry) {
return ValueGeometry.get((com.vividsolutions.jts.geom.Geometry) x);
} else { } else {
return ValueJavaObject.getNoCopy(x, null); return ValueJavaObject.getNoCopy(x, null);
} }
......
...@@ -151,10 +151,15 @@ public abstract class Value { ...@@ -151,10 +151,15 @@ public abstract class Value {
*/ */
public static final int STRING_FIXED = 21; public static final int STRING_FIXED = 21;
/**
* The value type for string values with a fixed size.
*/
public static final int GEOMETRY = 22;
/** /**
* The number of value types. * The number of value types.
*/ */
public static final int TYPE_COUNT = STRING_FIXED + 1; public static final int TYPE_COUNT = GEOMETRY + 1;
private static SoftReference<Value[]> softCache = new SoftReference<Value[]>(null); private static SoftReference<Value[]> softCache = new SoftReference<Value[]>(null);
private static final BigDecimal MAX_LONG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE); private static final BigDecimal MAX_LONG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE);
...@@ -294,6 +299,8 @@ public abstract class Value { ...@@ -294,6 +299,8 @@ public abstract class Value {
return 42; return 42;
case JAVA_OBJECT: case JAVA_OBJECT:
return 43; return 43;
case GEOMETRY:
return 44;
case ARRAY: case ARRAY:
return 50; return 50;
case RESULT_SET: case RESULT_SET:
...@@ -725,6 +732,7 @@ public abstract class Value { ...@@ -725,6 +732,7 @@ public abstract class Value {
case BLOB: case BLOB:
return ValueBytes.getNoCopy(getBytesNoCopy()); return ValueBytes.getNoCopy(getBytesNoCopy());
case UUID: case UUID:
case GEOMETRY:
return ValueBytes.getNoCopy(getBytes()); return ValueBytes.getNoCopy(getBytes());
case BYTE: case BYTE:
return ValueBytes.getNoCopy(new byte[]{getByte()}); return ValueBytes.getNoCopy(new byte[]{getByte()});
...@@ -781,6 +789,11 @@ public abstract class Value { ...@@ -781,6 +789,11 @@ public abstract class Value {
return ValueUuid.get(getBytesNoCopy()); return ValueUuid.get(getBytesNoCopy());
} }
} }
case GEOMETRY:
switch(getType()) {
case BYTES:
return ValueGeometry.get(getBytesNoCopy());
}
} }
// conversion by parsing the string value // conversion by parsing the string value
String s = getString(); String s = getString();
...@@ -847,6 +860,8 @@ public abstract class Value { ...@@ -847,6 +860,8 @@ public abstract class Value {
} }
case UUID: case UUID:
return ValueUuid.get(s); return ValueUuid.get(s);
case GEOMETRY:
return ValueGeometry.get(s);
default: default:
throw DbException.throwInternalError("type=" + targetType); throw DbException.throwInternalError("type=" + targetType);
} }
......
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.message.DbException;
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.
*/
public class ValueGeometry extends Value {
/**
* The value.
*/
private final com.vividsolutions.jts.geom.Geometry geometry;
private ValueGeometry(com.vividsolutions.jts.geom.Geometry geometry) {
this.geometry = geometry;
}
/**
* Get or create a geometry value for the given geometry.
*
* @param g the geometry
* @return the value
*/
public static ValueGeometry get(com.vividsolutions.jts.geom.Geometry g) {
return (ValueGeometry) Value.cache(new ValueGeometry(g));
}
/**
* Get or create a geometry value for the given geometry.
*
* @param s the WKT representation of the geometry
* @return the value
*/
public static ValueGeometry get(String s) {
return (ValueGeometry) Value.cache(new ValueGeometry(fromWKT(s)));
}
/**
* 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(fromWKB(bytes)));
}
@Override
public int getType() {
return Value.GEOMETRY;
}
@Override
public String getSQL() {
return "'" + toWKT() + "'";
}
@Override
protected int compareSecure(Value v, CompareMode mode) {
com.vividsolutions.jts.geom.Geometry g = ((ValueGeometry) v).geometry;
return this.geometry.compareTo(g);
}
@Override
public String getString() {
return toWKT();
}
@Override
public long getPrecision() {
return toWKT().length();
}
@Override
public int hashCode() {
return this.geometry.hashCode();
}
@Override
public Object getObject() {
return geometry;
}
@Override
public byte[] getBytes() {
return toWKB();
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setObject(parameterIndex, geometry);
}
@Override
public int getDisplaySize() {
return toWKT().length();
}
@Override
public int getMemory() {
return toWKB().length + 24;
}
@Override
public boolean equals(Object other) {
return other instanceof ValueGeometry && geometry.equals(((ValueGeometry) other).geometry);
}
/**
* Convert to Well-Known-Text format.
*/
public String toWKT() {
WKTWriter w = new WKTWriter();
return w.write(this.geometry);
}
/**
* Convert to Well-Known-Binary format.
*/
public byte[] toWKB() {
WKBWriter w = new WKBWriter();
return w.write(this.geometry);
}
/**
* Convert from Well-Known-Text format.
*/
private static com.vividsolutions.jts.geom.Geometry fromWKT(String s) {
WKTReader r = new WKTReader();
try {
return r.read(s);
} catch (ParseException ex) {
throw DbException.convert(ex);
}
}
/**
* Convert from Well-Known-Binary format.
*/
private static com.vividsolutions.jts.geom.Geometry fromWKB(byte[] bytes) {
WKBReader r = new WKBReader();
try {
return r.read(bytes);
} catch (ParseException ex) {
throw DbException.convert(ex);
}
}
}
...@@ -63,6 +63,7 @@ import org.h2.test.db.TestSequence; ...@@ -63,6 +63,7 @@ import org.h2.test.db.TestSequence;
import org.h2.test.db.TestSessionsLocks; import org.h2.test.db.TestSessionsLocks;
import org.h2.test.db.TestShow; import org.h2.test.db.TestShow;
import org.h2.test.db.TestSpaceReuse; import org.h2.test.db.TestSpaceReuse;
import org.h2.test.db.TestSpatial;
import org.h2.test.db.TestSpeed; import org.h2.test.db.TestSpeed;
import org.h2.test.db.TestTableEngines; import org.h2.test.db.TestTableEngines;
import org.h2.test.db.TestTempTables; import org.h2.test.db.TestTempTables;
...@@ -106,16 +107,16 @@ import org.h2.test.mvcc.TestMvcc3; ...@@ -106,16 +107,16 @@ import org.h2.test.mvcc.TestMvcc3;
import org.h2.test.mvcc.TestMvccMultiThreaded; import org.h2.test.mvcc.TestMvccMultiThreaded;
import org.h2.test.rowlock.TestRowLocks; import org.h2.test.rowlock.TestRowLocks;
import org.h2.test.server.TestAutoServer; import org.h2.test.server.TestAutoServer;
import org.h2.test.server.TestInit;
import org.h2.test.server.TestNestedLoop; import org.h2.test.server.TestNestedLoop;
import org.h2.test.server.TestWeb; import org.h2.test.server.TestWeb;
import org.h2.test.server.TestInit;
import org.h2.test.store.TestCacheConcurrentLIRS; import org.h2.test.store.TestCacheConcurrentLIRS;
import org.h2.test.store.TestCacheLIRS; import org.h2.test.store.TestCacheLIRS;
import org.h2.test.store.TestCacheLongKeyLIRS; import org.h2.test.store.TestCacheLongKeyLIRS;
import org.h2.test.store.TestConcurrent; import org.h2.test.store.TestConcurrent;
import org.h2.test.store.TestDataUtils; import org.h2.test.store.TestDataUtils;
import org.h2.test.store.TestMVStore;
import org.h2.test.store.TestMVRTree; import org.h2.test.store.TestMVRTree;
import org.h2.test.store.TestMVStore;
import org.h2.test.store.TestMVTableEngine; import org.h2.test.store.TestMVTableEngine;
import org.h2.test.store.TestObjectDataType; import org.h2.test.store.TestObjectDataType;
import org.h2.test.store.TestSpinLock; import org.h2.test.store.TestSpinLock;
...@@ -159,12 +160,10 @@ import org.h2.test.unit.TestFtp; ...@@ -159,12 +160,10 @@ import org.h2.test.unit.TestFtp;
import org.h2.test.unit.TestIntArray; import org.h2.test.unit.TestIntArray;
import org.h2.test.unit.TestIntIntHashMap; import org.h2.test.unit.TestIntIntHashMap;
import org.h2.test.unit.TestJmx; import org.h2.test.unit.TestJmx;
import org.h2.test.unit.TestModifyOnWrite;
import org.h2.test.unit.TestObjectDeserialization;
import org.h2.test.unit.TestSort;
import org.h2.test.unit.TestTraceSystem;
import org.h2.test.unit.TestMathUtils; import org.h2.test.unit.TestMathUtils;
import org.h2.test.unit.TestModifyOnWrite;
import org.h2.test.unit.TestNetUtils; import org.h2.test.unit.TestNetUtils;
import org.h2.test.unit.TestObjectDeserialization;
import org.h2.test.unit.TestOldVersion; import org.h2.test.unit.TestOldVersion;
import org.h2.test.unit.TestOverflow; import org.h2.test.unit.TestOverflow;
import org.h2.test.unit.TestPageStore; import org.h2.test.unit.TestPageStore;
...@@ -178,10 +177,12 @@ import org.h2.test.unit.TestSampleApps; ...@@ -178,10 +177,12 @@ import org.h2.test.unit.TestSampleApps;
import org.h2.test.unit.TestScriptReader; import org.h2.test.unit.TestScriptReader;
import org.h2.test.unit.TestSecurity; import org.h2.test.unit.TestSecurity;
import org.h2.test.unit.TestShell; import org.h2.test.unit.TestShell;
import org.h2.test.unit.TestSort;
import org.h2.test.unit.TestStreams; import org.h2.test.unit.TestStreams;
import org.h2.test.unit.TestStringCache; import org.h2.test.unit.TestStringCache;
import org.h2.test.unit.TestStringUtils; import org.h2.test.unit.TestStringUtils;
import org.h2.test.unit.TestTools; import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestTraceSystem;
import org.h2.test.unit.TestUtils; import org.h2.test.unit.TestUtils;
import org.h2.test.unit.TestValue; import org.h2.test.unit.TestValue;
import org.h2.test.unit.TestValueHashMap; import org.h2.test.unit.TestValueHashMap;
...@@ -191,8 +192,8 @@ import org.h2.test.utils.SelfDestructor; ...@@ -191,8 +192,8 @@ import org.h2.test.utils.SelfDestructor;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server; import org.h2.tools.Server;
import org.h2.util.Profiler; import org.h2.util.Profiler;
import org.h2.util.Utils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils;
/** /**
* The main test application. JUnit is not used because loops are easier to * The main test application. JUnit is not used because loops are easier to
...@@ -621,6 +622,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -621,6 +622,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestSequence().runTest(this); new TestSequence().runTest(this);
new TestShow().runTest(this); new TestShow().runTest(this);
new TestSpaceReuse().runTest(this); new TestSpaceReuse().runTest(this);
new TestSpatial().runTest(this);
new TestSpeed().runTest(this); new TestSpeed().runTest(this);
new TestTableEngines().runTest(this); new TestTableEngines().runTest(this);
new TestTempTables().runTest(this); new TestTempTables().runTest(this);
......
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.test.TestBase;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Polygon;
/**
* Spatial datatype and index tests.
*/
public class TestSpatial extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws SQLException {
deleteDb("spatial");
testSpatialValues();
deleteDb("spatial");
}
private void testSpatialValues() throws SQLException {
deleteDb("spatial");
Connection conn = getConnection("spatial");
Statement stat = conn.createStatement();
stat.execute("create memory table test(id int primary key, poly geometry)");
stat.execute("insert into test values(1, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
ResultSet rs = stat.executeQuery("select * from test");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertEquals("POLYGON ((1 1, 1 2, 2 2, 1 1))", rs.getString(2));
GeometryFactory f = new GeometryFactory();
Polygon poly = f.createPolygon(new Coordinate[] { new Coordinate(1,1), new Coordinate(1,2), new Coordinate(2,2), new Coordinate(1, 1) });
assertTrue(poly.equals(rs.getObject(2)));
rs = stat.executeQuery("select * from test where poly = 'POLYGON ((1 1, 1 2, 2 2, 1 1))'");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
stat.executeQuery("select * from test where poly > 'POLYGON ((1 1, 1 2, 2 2, 1 1))'");
stat.executeQuery("select * from test where poly < 'POLYGON ((1 1, 1 2, 2 2, 1 1))'");
stat.execute("drop table test");
conn.close();
deleteDb("spatial");
}
}
...@@ -273,6 +273,8 @@ public class Build extends BuildBase { ...@@ -273,6 +273,8 @@ public class Build extends BuildBase {
"66ab449ff3aa5c4adfc82c89025cc983b422eb95", offline); "66ab449ff3aa5c4adfc82c89025cc983b422eb95", offline);
downloadOrVerify("ext/org.osgi.enterprise-4.2.0.jar", "org/osgi", "org.osgi.enterprise", "4.2.0", downloadOrVerify("ext/org.osgi.enterprise-4.2.0.jar", "org/osgi", "org.osgi.enterprise", "4.2.0",
"8634dcb0fc62196e820ed0f1062993c377f74972", offline); "8634dcb0fc62196e820ed0f1062993c377f74972", offline);
downloadOrVerify("ext/jts-1.13.jar", "com.vividsolutions", "jts", "1.13",
"3ccfb9b60f04d71add996a666ceb8902904fd805", offline);
} }
private void downloadOrVerify(String target, String group, String artifact, private void downloadOrVerify(String target, String group, String artifact,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论