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

Spatial index (work in progress)

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