提交 0fd0134f authored 作者: Thomas Mueller's avatar Thomas Mueller

Spatial index (work in progress)

上级 d23ee071
......@@ -18,7 +18,8 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Issue 467: OSGi Class Loader (ability to create reference to class
<ul><li>Improved spatial index and data type.
</li><li>Issue 467: OSGi Class Loader (ability to create reference to class
in other ClassLoader, for example in another OSGi bundle).
</li></ul>
......
......@@ -4,8 +4,8 @@
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*
* N. Fortin, Atelier SIG - IRSTV CNRS 2488:
* Support for the operator "&&" as an alias for SPATIAL_INTERSECTS.
* Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
* Support for the operator "&&" as an alias for SPATIAL_INTERSECTS
*/
package org.h2.command;
......@@ -149,6 +149,10 @@ import org.h2.value.ValueTimestamp;
/**
* The parser is used to convert a SQL statement string to an command object.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class Parser {
......
......@@ -22,6 +22,10 @@ import org.h2.value.ValueNull;
/**
* Example comparison expressions are ID=1, NAME=NAME, NAME IS NULL.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class Comparison extends Condition {
......@@ -102,7 +106,7 @@ public class Comparison extends Condition {
/**
* This is a comparison type that is only used for spatial index
* conditions.
* conditions (operator "&&").
*/
public static final int SPATIAL_INTERSECTS = 11;
......@@ -161,6 +165,8 @@ public class Comparison extends Condition {
return "<>";
case NOT_EQUAL_NULL_SAFE:
return "IS NOT";
case SPATIAL_INTERSECTS:
return "&&";
default:
throw DbException.throwInternalError("compareType=" + compareType);
}
......@@ -285,7 +291,7 @@ public class Comparison extends Condition {
case SPATIAL_INTERSECTS: {
ValueGeometry lg = (ValueGeometry) l.convertTo(Value.GEOMETRY);
ValueGeometry rg = (ValueGeometry) r.convertTo(Value.GEOMETRY);
result = lg.intersects(rg);
result = lg.intersectsBoundingBox(rg);
break;
}
default:
......@@ -300,6 +306,7 @@ public class Comparison extends Condition {
case EQUAL_NULL_SAFE:
case NOT_EQUAL:
case NOT_EQUAL_NULL_SAFE:
case SPATIAL_INTERSECTS:
return type;
case BIGGER_EQUAL:
return SMALLER_EQUAL;
......@@ -314,6 +321,15 @@ public class Comparison extends Condition {
}
}
@Override
public Expression getNotIfPossible(Session session) {
if (compareType == SPATIAL_INTERSECTS) {
return null;
}
int type = getNotCompareType();
return new Comparison(session, type, left, right);
}
private int getNotCompareType() {
switch (compareType) {
case EQUAL:
......@@ -341,12 +357,6 @@ public class Comparison extends Condition {
}
}
@Override
public Expression getNotIfPossible(Session session) {
int type = getNotCompareType();
return new Comparison(session, type, left, right);
}
@Override
public void createIndexConditions(Session session, TableFilter filter) {
ExpressionColumn l = null;
......
......@@ -29,6 +29,10 @@ import org.h2.value.Value;
* A index condition object is made for each condition that can potentially use
* an index. This class does not extend expression, but in general there is one
* expression that maps to each index condition.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class IndexCondition {
......@@ -210,6 +214,9 @@ public class IndexCondition {
buff.append(expressionQuery.getPlanSQL());
buff.append(')');
break;
case Comparison.SPATIAL_INTERSECTS:
buff.append(" && ");
break;
default:
DbException.throwInternalError("type="+compareType);
}
......
......@@ -26,6 +26,10 @@ import org.h2.value.ValueNull;
/**
* The filter used to walk through an index. This class supports IN(..)
* and IN(SELECT ...) optimizations.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class IndexCursor implements Cursor {
......@@ -125,7 +129,7 @@ public class IndexCursor implements Cursor {
end = getSearchRow(end, columnId, v, false);
}
if (isIntersects) {
intersects = getSpatialSearchRow(intersects, columnId, v, true);
intersects = getSpatialSearchRow(intersects, columnId, v);
}
if (isStart || isEnd) {
// an X=? condition will produce less rows than
......@@ -148,8 +152,8 @@ public class IndexCursor implements Cursor {
return;
}
if (!alwaysFalse) {
if (intersects != null && index instanceof SpatialTreeIndex) {
cursor = ((SpatialTreeIndex) index).findByGeometry(tableFilter,
if (intersects != null && index instanceof SpatialIndex) {
cursor = ((SpatialIndex) index).findByGeometry(tableFilter,
intersects);
} else {
cursor = index.find(tableFilter, start, end);
......@@ -174,16 +178,13 @@ public class IndexCursor implements Cursor {
return idxCol == null || idxCol.column == column;
}
private SearchRow getSpatialSearchRow(SearchRow row, int columnId, Value v, boolean isIntersects) {
private SearchRow getSpatialSearchRow(SearchRow row, int columnId, Value v) {
if (row == null) {
row = table.getTemplateRow();
} else {
ValueGeometry vg = (ValueGeometry) row.getValue(columnId);
if (isIntersects) {
v = ((ValueGeometry) v).intersection(vg);
} else {
v = ((ValueGeometry) v).union(vg);
}
} else if (row.getValue(columnId) != null) {
// the intersection of the two envelopes
ValueGeometry vg = (ValueGeometry) row.getValue(columnId).convertTo(Value.GEOMETRY);
v = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeIntersection(vg);
}
if (columnId < 0) {
row.setKey(v.getLong());
......
......@@ -56,6 +56,10 @@ import org.h2.value.ValueUuid;
/**
* This class represents a byte buffer that contains persistent data of a page.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class Data {
......@@ -520,11 +524,13 @@ public class Data {
}
break;
}
case Value.GEOMETRY:
case Value.JAVA_OBJECT: {
writeByte((byte) type);
byte[] b = v.getBytesNoCopy();
writeVarInt(b.length);
write(b, 0, b.length);
int len = b.length;
writeVarInt(len);
write(b, 0, len);
break;
}
case Value.BYTES: {
......@@ -532,11 +538,11 @@ public class Data {
int len = b.length;
if (len < 32) {
writeByte((byte) (BYTES_0_31 + len));
write(b, 0, b.length);
write(b, 0, len);
} else {
writeByte((byte) type);
writeVarInt(b.length);
write(b, 0, b.length);
writeVarInt(len);
write(b, 0, len);
}
break;
}
......@@ -671,14 +677,6 @@ public class Data {
}
break;
}
case Value.GEOMETRY: {
writeByte((byte) type);
byte[] b = v.getBytes();
int len = b.length;
writeVarInt(len);
write(b, 0, len);
break;
}
default:
DbException.throwInternalError("type=" + v.getType());
}
......@@ -764,6 +762,12 @@ public class Data {
read(b, 0, len);
return ValueBytes.getNoCopy(b);
}
case Value.GEOMETRY: {
int len = readVarInt();
byte[] b = DataUtils.newBytes(len);
read(b, 0, len);
return ValueGeometry.get(b);
}
case Value.JAVA_OBJECT: {
int len = readVarInt();
byte[] b = DataUtils.newBytes(len);
......@@ -848,12 +852,6 @@ public class Data {
}
return ValueResultSet.get(rs);
}
case Value.GEOMETRY: {
int len = readVarInt();
byte[] b = DataUtils.newBytes(len);
read(b, 0, len);
return ValueGeometry.get(b);
}
default:
if (type >= INT_0_15 && type < INT_0_15 + 16) {
return ValueInt.get(type - INT_0_15);
......@@ -999,6 +997,7 @@ public class Data {
Timestamp ts = v.getTimestamp();
return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(ts)) + getVarIntLen(ts.getNanos());
}
case Value.GEOMETRY:
case Value.JAVA_OBJECT: {
byte[] b = v.getBytesNoCopy();
return 1 + getVarIntLen(b.length) + b.length;
......@@ -1089,11 +1088,6 @@ public class Data {
}
return len;
}
case Value.GEOMETRY: {
byte[] b = v.getBytesNoCopy();
int len = b.length;
return 1 + getVarIntLen(len) + len;
}
default:
throw DbException.throwInternalError("type=" + v.getType());
}
......
......@@ -228,10 +228,10 @@ public class RegularTable extends TableBase {
if (mainIndexColumn != -1) {
mainIndex.setMainIndexColumn(mainIndexColumn);
index = new PageDelegateIndex(this, indexId, indexName, indexType, mainIndex, create, session);
} else if (!indexType.isSpatial()) {
index = new PageBtreeIndex(this, indexId, indexName, cols, indexType, create, session);
} else if (indexType.isSpatial()) {
index = new SpatialTreeIndex(this, indexId, indexName, cols, indexType, true, create, session);
} else {
throw new UnsupportedOperationException("Spatial index only supported with the MVStore");
index = new PageBtreeIndex(this, indexId, indexName, cols, indexType, create, session);
}
} else {
if (indexType.isHash() && cols.length <= 1) {
......@@ -243,7 +243,7 @@ public class RegularTable extends TableBase {
} else if (!indexType.isSpatial()) {
index = new TreeIndex(this, indexId, indexName, cols, indexType);
} else {
index = new SpatialTreeIndex(this, indexId, indexName, cols, indexType);
index = new SpatialTreeIndex(this, indexId, indexName, cols, indexType, false, true, session);
}
}
if (database.isMultiVersion()) {
......
......@@ -961,7 +961,13 @@ public class DataType {
}
}
private static boolean isGeometry(Object 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 || GEOMETRY_CLASS == null) {
return false;
}
......
......@@ -34,6 +34,10 @@ import org.h2.util.Utils;
/**
* This is the base class for all value classes.
* It provides conversion and comparison methods.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public abstract class Value {
......@@ -793,6 +797,11 @@ public abstract class Value {
switch(getType()) {
case BYTES:
return ValueGeometry.get(getBytesNoCopy());
case JAVA_OBJECT:
Object object = Utils.deserialize(getBytesNoCopy());
if (DataType.isGeometry(object)) {
return ValueGeometry.getFromGeometry(object);
}
}
}
// conversion by parsing the string value
......
......@@ -11,7 +11,9 @@ import java.sql.SQLException;
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;
......@@ -20,6 +22,10 @@ import com.vividsolutions.jts.io.WKTWriter;
/**
* Implementation of the GEOMETRY data type.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class ValueGeometry extends Value {
......@@ -71,35 +77,37 @@ public class ValueGeometry extends Value {
}
/**
* Check whether two values intersect.
*
* @param r the second value
* @return true if they intersect
* Test if this geometry envelope intersects with the other geometry
* envelope.
*
* @param r the other geometry
* @return true if the two envelopes overlaps
*/
public boolean intersects(ValueGeometry r) {
return geometry.intersects(r.getGeometry());
public boolean intersectsBoundingBox(ValueGeometry r) {
// it is useless to cache the envelope as the Geometry object do this already
return geometry.getEnvelopeInternal().intersects(r.getGeometry().getEnvelopeInternal());
}
/**
* Get the intersection of two values.
*
* @param r the second value
* @return the intersection
*/
public Value intersection(ValueGeometry r) {
return get(geometry.intersection(r.geometry));
}
/**
* Get the union of two values.
*
* @param r the second value
* @return the union
* Get the intersection.
*
* @param r the other geometry
* @return the intersection of this geometry envelope and another geometry envelope
*/
public Value union(ValueGeometry r) {
return get(geometry.union(r.geometry));
public ValueGeometry getEnvelopeIntersection(ValueGeometry r) {
Envelope e1 = geometry.getEnvelopeInternal();
Envelope e2 = r.getGeometry().getEnvelopeInternal();
Envelope e3 = e1.intersection(e2);
// try to re-use the object
if (e3 == e1) {
return this;
} else if (e3 == e2) {
return r;
}
GeometryFactory gf = new GeometryFactory();
return get(gf.toGeometry(e3));
}
@Override
public int getType() {
return Value.GEOMETRY;
......@@ -107,7 +115,7 @@ public class ValueGeometry extends Value {
@Override
public String getSQL() {
return StringUtils.quoteStringSQL(toWKT());
return StringUtils.quoteStringSQL(toWKT()) + "'::Geometry";
}
@Override
......@@ -141,6 +149,11 @@ public class ValueGeometry extends Value {
return toWKB();
}
@Override
public byte[] getBytesNoCopy() {
return toWKB();
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setObject(parameterIndex, geometry);
......@@ -167,18 +180,16 @@ public class ValueGeometry extends Value {
* @return the well-known-text
*/
public String toWKT() {
WKTWriter w = new WKTWriter();
return w.write(geometry);
return new WKTWriter().write(geometry);
}
/**
* Convert to value to the Well-Known-Binary format.
* Convert to Well-Known-Binary format.
*
* @return the well-known-binary
*/
public byte[] toWKB() {
WKBWriter w = new WKBWriter();
return w.write(geometry);
return new WKBWriter().write(geometry);
}
/**
......@@ -188,9 +199,8 @@ public class ValueGeometry extends Value {
* @return the Geometry object
*/
private static Geometry fromWKT(String s) {
WKTReader r = new WKTReader();
try {
return r.read(s);
return new WKTReader().read(s);
} catch (ParseException ex) {
throw DbException.convert(ex);
}
......@@ -203,9 +213,8 @@ public class ValueGeometry extends Value {
* @return the Geometry object
*/
private static Geometry fromWKB(byte[] bytes) {
WKBReader r = new WKBReader();
try {
return r.read(bytes);
return new WKBReader().read(bytes);
} catch (ParseException ex) {
throw DbException.convert(ex);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论