提交 99655cf1 authored 作者: noelgrandin's avatar noelgrandin

Add support for in-memory spatial index.

上级 18cfcf6c
...@@ -42,9 +42,10 @@ Change Log ...@@ -42,9 +42,10 @@ 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><li>Issue 480: Bugfix post issue #475, #477, patch from Andrew Franklin. </li><li>Issue 480: Bugfix post issue #475, #477, patch from Andrew Franklin.
</li><li>Issue 481: Further extensions to PgServer to support better support PG JDBC, patch from Andrew Franklin. </li><li>Issue 481: Further extensions to PgServer to support better support PG JDBC, patch from Andrew Franklin.
</li><li>Add support for spatial datatype GEOMETRY.
</li><li>Add support for in-memory spatial index.
</li></ul> </li></ul>
<h2>Version 1.3.172 (2013-05-25)</h2> <h2>Version 1.3.172 (2013-05-25)</h2>
......
...@@ -1889,6 +1889,14 @@ public class Parser { ...@@ -1889,6 +1889,14 @@ public class Parser {
read(")"); read(")");
return new ConditionExists(query); return new ConditionExists(query);
} }
if (readIf("INTERSECTS")) {
read("(");
Expression r1 = readConcat();
read(",");
Expression r2 = readConcat();
read(")");
return new Comparison(session, Comparison.SPATIAL_INTERSECTS, r1, r2);
}
Expression r = readConcat(); Expression r = readConcat();
while (true) { while (true) {
// special case: NOT NULL is not part of an expression (as in CREATE // special case: NOT NULL is not part of an expression (as in CREATE
...@@ -3863,7 +3871,7 @@ public class Parser { ...@@ -3863,7 +3871,7 @@ public class Parser {
} }
return parseCreateTable(false, false, cached); return parseCreateTable(false, false, cached);
} else { } else {
boolean hash = false, primaryKey = false, unique = false; boolean hash = false, primaryKey = false, unique = false, spatial = false;;
String indexName = null; String indexName = null;
Schema oldSchema = null; Schema oldSchema = null;
boolean ifNotExists = false; boolean ifNotExists = false;
...@@ -3885,6 +3893,9 @@ public class Parser { ...@@ -3885,6 +3893,9 @@ public class Parser {
if (readIf("HASH")) { if (readIf("HASH")) {
hash = true; hash = true;
} }
if (readIf("SPATIAL")) {
spatial = true;
}
if (readIf("INDEX")) { if (readIf("INDEX")) {
if (!isToken("ON")) { if (!isToken("ON")) {
ifNotExists = readIfNoExists(); ifNotExists = readIfNoExists();
...@@ -3901,6 +3912,7 @@ public class Parser { ...@@ -3901,6 +3912,7 @@ public class Parser {
CreateIndex command = new CreateIndex(session, getSchema()); CreateIndex command = new CreateIndex(session, getSchema());
command.setIfNotExists(ifNotExists); command.setIfNotExists(ifNotExists);
command.setHash(hash); command.setHash(hash);
command.setSpatial(spatial);
command.setPrimaryKey(primaryKey); command.setPrimaryKey(primaryKey);
command.setTableName(tableName); command.setTableName(tableName);
command.setUnique(unique); command.setUnique(unique);
......
...@@ -27,7 +27,7 @@ public class CreateIndex extends SchemaCommand { ...@@ -27,7 +27,7 @@ public class CreateIndex extends SchemaCommand {
private String tableName; private String tableName;
private String indexName; private String indexName;
private IndexColumn[] indexColumns; private IndexColumn[] indexColumns;
private boolean primaryKey, unique, hash; private boolean primaryKey, unique, hash, spatial;
private boolean ifNotExists; private boolean ifNotExists;
private String comment; private String comment;
...@@ -87,7 +87,7 @@ public class CreateIndex extends SchemaCommand { ...@@ -87,7 +87,7 @@ public class CreateIndex extends SchemaCommand {
} else if (unique) { } else if (unique) {
indexType = IndexType.createUnique(persistent, hash); indexType = IndexType.createUnique(persistent, hash);
} else { } else {
indexType = IndexType.createNonUnique(persistent, hash); indexType = IndexType.createNonUnique(persistent, hash, spatial);
} }
IndexColumn.mapColumns(indexColumns, table); IndexColumn.mapColumns(indexColumns, table);
table.addIndex(session, indexName, id, indexColumns, indexType, create, comment); table.addIndex(session, indexName, id, indexColumns, indexType, create, comment);
...@@ -106,6 +106,10 @@ public class CreateIndex extends SchemaCommand { ...@@ -106,6 +106,10 @@ public class CreateIndex extends SchemaCommand {
this.hash = b; this.hash = b;
} }
public void setSpatial(boolean b) {
this.spatial = b;
}
public void setComment(String comment) { public void setComment(String comment) {
this.comment = comment; this.comment = comment;
} }
......
...@@ -17,6 +17,7 @@ import org.h2.table.TableFilter; ...@@ -17,6 +17,7 @@ import org.h2.table.TableFilter;
import org.h2.util.New; import org.h2.util.New;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueBoolean; import org.h2.value.ValueBoolean;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
/** /**
...@@ -99,6 +100,11 @@ public class Comparison extends Condition { ...@@ -99,6 +100,11 @@ public class Comparison extends Condition {
*/ */
public static final int IN_QUERY = 10; public static final int IN_QUERY = 10;
/**
* This is a pseudo comparison type that is only used for spatial index conditions.
*/
public static final int SPATIAL_INTERSECTS = 11;
private final Database database; private final Database database;
private int compareType; private int compareType;
private Expression left; private Expression left;
...@@ -121,6 +127,9 @@ public class Comparison extends Condition { ...@@ -121,6 +127,9 @@ public class Comparison extends Condition {
case IS_NOT_NULL: case IS_NOT_NULL:
sql = left.getSQL() + " IS NOT NULL"; sql = left.getSQL() + " IS NOT NULL";
break; break;
case SPATIAL_INTERSECTS:
sql = "INTERSECTS(" + left.getSQL() + ", " + right.getSQL() + ")";
break;
default: default:
sql = left.getSQL() + " " + getCompareOperator(compareType) + " " + right.getSQL(); sql = left.getSQL() + " " + getCompareOperator(compareType) + " " + right.getSQL();
} }
...@@ -272,6 +281,12 @@ public class Comparison extends Condition { ...@@ -272,6 +281,12 @@ public class Comparison extends Condition {
case SMALLER: case SMALLER:
result = database.compare(l, r) < 0; result = database.compare(l, r) < 0;
break; break;
case SPATIAL_INTERSECTS: {
ValueGeometry lg = (ValueGeometry) l.convertTo(Value.GEOMETRY);
ValueGeometry rg = (ValueGeometry) r.convertTo(Value.GEOMETRY);
result = lg.intersects(rg);
break;
}
default: default:
throw DbException.throwInternalError("type=" + compareType); throw DbException.throwInternalError("type=" + compareType);
} }
...@@ -392,6 +407,7 @@ public class Comparison extends Condition { ...@@ -392,6 +407,7 @@ public class Comparison extends Condition {
case BIGGER_EQUAL: case BIGGER_EQUAL:
case SMALLER_EQUAL: case SMALLER_EQUAL:
case SMALLER: case SMALLER:
case SPATIAL_INTERSECTS:
addIndex = true; addIndex = true;
break; break;
default: default:
......
...@@ -57,6 +57,11 @@ public class IndexCondition { ...@@ -57,6 +57,11 @@ public class IndexCondition {
*/ */
public static final int ALWAYS_FALSE = 8; public static final int ALWAYS_FALSE = 8;
/**
* A bit of a search mask meaning 'spatial intersection'.
*/
public static final int SPATIAL_INTERSECTS = 16;
private final Column column; private final Column column;
/** /**
* see constants in {@link Comparison} * see constants in {@link Comparison}
...@@ -249,6 +254,8 @@ public class IndexCondition { ...@@ -249,6 +254,8 @@ public class IndexCondition {
case Comparison.SMALLER_EQUAL: case Comparison.SMALLER_EQUAL:
case Comparison.SMALLER: case Comparison.SMALLER:
return END; return END;
case Comparison.SPATIAL_INTERSECTS:
return SPATIAL_INTERSECTS;
default: default:
throw DbException.throwInternalError("type=" + compareType); throw DbException.throwInternalError("type=" + compareType);
} }
...@@ -299,6 +306,20 @@ public class IndexCondition { ...@@ -299,6 +306,20 @@ public class IndexCondition {
} }
} }
/**
* Check if this index condition is of the type spatial column intersects value.
*
* @return true if this is a spatial intersects condition
*/
public boolean isSpatialIntersects() {
switch (compareType) {
case Comparison.SPATIAL_INTERSECTS:
return true;
default:
return false;
}
}
public int getCompareType() { public int getCompareType() {
return compareType; return compareType;
} }
......
...@@ -20,6 +20,7 @@ import org.h2.table.IndexColumn; ...@@ -20,6 +20,7 @@ import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
/** /**
...@@ -35,7 +36,7 @@ public class IndexCursor implements Cursor { ...@@ -35,7 +36,7 @@ public class IndexCursor implements Cursor {
private IndexColumn[] indexColumns; private IndexColumn[] indexColumns;
private boolean alwaysFalse; private boolean alwaysFalse;
private SearchRow start, end; private SearchRow start, end, intersects;
private Cursor cursor; private Cursor cursor;
private Column inColumn; private Column inColumn;
private int inListIndex; private int inListIndex;
...@@ -104,6 +105,7 @@ public class IndexCursor implements Cursor { ...@@ -104,6 +105,7 @@ public class IndexCursor implements Cursor {
Value v = condition.getCurrentValue(s); Value v = condition.getCurrentValue(s);
boolean isStart = condition.isStart(); boolean isStart = condition.isStart();
boolean isEnd = condition.isEnd(); boolean isEnd = condition.isEnd();
boolean isIntersects = condition.isSpatialIntersects();
int columnId = column.getColumnId(); int columnId = column.getColumnId();
if (columnId >= 0) { if (columnId >= 0) {
IndexColumn idxCol = indexColumns[columnId]; IndexColumn idxCol = indexColumns[columnId];
...@@ -121,6 +123,9 @@ public class IndexCursor implements Cursor { ...@@ -121,6 +123,9 @@ public class IndexCursor implements Cursor {
if (isEnd) { if (isEnd) {
end = getSearchRow(end, columnId, v, false); end = getSearchRow(end, columnId, v, false);
} }
if (isIntersects) {
intersects = getSpatialSearchRow(intersects, columnId, v, true);
}
if (isStart || isEnd) { if (isStart || isEnd) {
// an X=? condition will produce less rows than // an X=? condition will produce less rows than
// an X IN(..) condition // an X IN(..) condition
...@@ -142,7 +147,11 @@ public class IndexCursor implements Cursor { ...@@ -142,7 +147,11 @@ public class IndexCursor implements Cursor {
return; return;
} }
if (!alwaysFalse) { if (!alwaysFalse) {
cursor = index.find(tableFilter, start, end); if (intersects != null && index instanceof SpatialTreeIndex) {
cursor = ((SpatialTreeIndex)index).findByGeometry(tableFilter, intersects);
} else {
cursor = index.find(tableFilter, start, end);
}
} }
} }
...@@ -163,6 +172,25 @@ public class IndexCursor implements Cursor { ...@@ -163,6 +172,25 @@ 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) {
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);
}
}
if (columnId < 0) {
row.setKey(v.getLong());
} else {
row.setValue(columnId, v);
}
return row;
}
private SearchRow getSearchRow(SearchRow row, int columnId, Value v, boolean max) { private SearchRow getSearchRow(SearchRow row, int columnId, Value v, boolean max) {
if (row == null) { if (row == null) {
row = table.getTemplateRow(); row = table.getTemplateRow();
......
...@@ -11,7 +11,7 @@ package org.h2.index; ...@@ -11,7 +11,7 @@ package org.h2.index;
*/ */
public class IndexType { public class IndexType {
private boolean primaryKey, persistent, unique, hash, scan; private boolean primaryKey, persistent, unique, hash, scan, spatial;
private boolean belongsToConstraint; private boolean belongsToConstraint;
/** /**
...@@ -52,7 +52,7 @@ public class IndexType { ...@@ -52,7 +52,7 @@ public class IndexType {
* @return the index type * @return the index type
*/ */
public static IndexType createNonUnique(boolean persistent) { public static IndexType createNonUnique(boolean persistent) {
return createNonUnique(persistent, false); return createNonUnique(persistent, false, false);
} }
/** /**
...@@ -62,10 +62,11 @@ public class IndexType { ...@@ -62,10 +62,11 @@ public class IndexType {
* @param hash if a hash index should be used * @param hash if a hash index should be used
* @return the index type * @return the index type
*/ */
public static IndexType createNonUnique(boolean persistent, boolean hash) { public static IndexType createNonUnique(boolean persistent, boolean hash, boolean spatial) {
IndexType type = new IndexType(); IndexType type = new IndexType();
type.persistent = persistent; type.persistent = persistent;
type.hash = hash; type.hash = hash;
type.spatial = spatial;
return type; return type;
} }
...@@ -110,6 +111,15 @@ public class IndexType { ...@@ -110,6 +111,15 @@ public class IndexType {
return hash; return hash;
} }
/**
* Is this a spatial index?
*
* @return true if it is a spatial index
*/
public boolean isSpatial() {
return spatial;
}
/** /**
* Is this index persistent? * Is this index persistent?
* *
...@@ -156,6 +166,9 @@ public class IndexType { ...@@ -156,6 +166,9 @@ public class IndexType {
if (hash) { if (hash) {
buff.append("HASH "); buff.append("HASH ");
} }
if (spatial) {
buff.append("SPATIAL ");
}
buff.append("INDEX"); buff.append("INDEX");
} }
return buff.toString(); return buff.toString();
......
/*
* 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.index;
import org.h2.result.SearchRow;
import org.h2.table.TableFilter;
/**
* A spatial index. Spatial indexes are used to speed up searching spatial/geometric data.
*/
public interface SpatialIndex extends Index {
/**
* Find a row or a list of rows and create a cursor to iterate over the result.
*
* @param filter the table filter (which possibly knows
* about additional conditions)
* @param intersection the geometry which values should intersect with, or null for anything
* @return the cursor to iterate over the results
*/
Cursor findByGeometry(TableFilter filter, SearchRow intersection);
}
/*
* 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.index;
import java.util.List;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.IndexColumn;
import org.h2.table.RegularTable;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.index.quadtree.Quadtree;
/**
* This is an in-memory index based on a R-Tree.
*/
public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
private Quadtree root;
private final RegularTable tableData;
private long rowCount;
private boolean closed;
public SpatialTreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
if (indexType.isUnique()) {
throw DbException.getUnsupportedException("not unique");
}
if (columns.length > 1) {
throw DbException.getUnsupportedException("can only do one column");
}
if ((columns[0].sortType & SortOrder.DESCENDING) != 0) {
throw DbException.getUnsupportedException("cannot do descending");
}
if ((columns[0].sortType & SortOrder.NULLS_FIRST) != 0) {
throw DbException.getUnsupportedException("cannot do nulls first");
}
if ((columns[0].sortType & SortOrder.NULLS_LAST) != 0) {
throw DbException.getUnsupportedException("cannot do nulls last");
}
initBaseIndex(table, id, indexName, columns, indexType);
tableData = table;
if (!database.isStarting()) {
if (columns[0].column.getType() != Value.GEOMETRY) {
throw DbException.getUnsupportedException("spatial index on non-geometry column, "
+ columns[0].column.getCreateSQL());
}
}
root = new Quadtree();
}
@Override
public void close(Session session) {
root = null;
closed = true;
}
@Override
public void add(Session session, Row row) {
if (closed) {
throw DbException.throwInternalError();
}
root.insert(getEnvelope(row), row);
rowCount++;
}
private Envelope getEnvelope(SearchRow row) {
Value v = row.getValue(columnIds[0]);
Geometry g = ((ValueGeometry) v).getGeometry();
return g.getEnvelopeInternal();
}
@Override
public void remove(Session session, Row row) {
if (closed) {
throw DbException.throwInternalError();
}
if (!root.remove(getEnvelope(row), row)) {
throw DbException.throwInternalError("row not found");
}
rowCount--;
}
@Override
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
return find();
}
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
return find();
}
@SuppressWarnings("unchecked")
private Cursor find() {
// FIXME: ideally I need external iterators, but let's see if we can get
// it working first
// FIXME: in the context of a spatial index, a query that uses ">" or "<" has no real meaning, so for now just ignore
// it and return all rows
java.util.List<Row> list = root.queryAll();
return new ListCursor(list, true/*first*/);
}
@SuppressWarnings("unchecked")
@Override
public Cursor findByGeometry(TableFilter filter, SearchRow intersection) {
// FIXME: ideally I need external iterators, but let's see if we can get
// it working first
java.util.List<Row> list;
if (intersection != null) {
list = root.query(getEnvelope(intersection));
} else {
list = root.queryAll();
}
return new ListCursor(list, true/*first*/);
}
@Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) {
return getCostRangeIndex(masks, tableData.getRowCountApproximation(), sortOrder);
}
@Override
public void remove(Session session) {
truncate(session);
}
@Override
public void truncate(Session session) {
root = null;
rowCount = 0;
}
@Override
public void checkRename() {
// nothing to do
}
@Override
public boolean needRebuild() {
return true;
}
@Override
public boolean canGetFirstOrLast() {
return true;
}
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
if (closed) {
throw DbException.throwInternalError();
}
// FIXME: ideally I need external iterators, but let's see if we can get
// it working first
@SuppressWarnings("unchecked")
List<Row> list = root.queryAll();
return new ListCursor(list, first);
}
@Override
public long getRowCount(Session session) {
return rowCount;
}
@Override
public long getRowCountApproximation() {
return rowCount;
}
@Override
public long getDiskSpaceUsed() {
return 0;
}
private static final class ListCursor implements Cursor {
private final List<Row> rows;
private int index;
private Row current;
public ListCursor(List<Row> rows, boolean first) {
this.rows = rows;
this.index = first ? 0 : rows.size();
}
@Override
public Row get() {
return current;
}
@Override
public SearchRow getSearchRow() {
return current;
}
@Override
public boolean next() {
current = index >= rows.size() ? null : rows.get(index++);
return current != null;
}
@Override
public boolean previous() {
current = index < 0 ? null : rows.get(index--);
return current != null;
}
}
}
...@@ -31,6 +31,7 @@ import org.h2.index.PageBtreeIndex; ...@@ -31,6 +31,7 @@ import org.h2.index.PageBtreeIndex;
import org.h2.index.PageDataIndex; import org.h2.index.PageDataIndex;
import org.h2.index.PageDelegateIndex; import org.h2.index.PageDelegateIndex;
import org.h2.index.ScanIndex; import org.h2.index.ScanIndex;
import org.h2.index.SpatialTreeIndex;
import org.h2.index.TreeIndex; import org.h2.index.TreeIndex;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
...@@ -227,8 +228,10 @@ public class RegularTable extends TableBase { ...@@ -227,8 +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 { } else if (!indexType.isSpatial()) {
index = new PageBtreeIndex(this, indexId, indexName, cols, indexType, create, session); index = new PageBtreeIndex(this, indexId, indexName, cols, indexType, create, session);
} else {
throw new UnsupportedOperationException();
} }
} else { } else {
if (indexType.isHash() && cols.length <= 1) { if (indexType.isHash() && cols.length <= 1) {
...@@ -237,8 +240,10 @@ public class RegularTable extends TableBase { ...@@ -237,8 +240,10 @@ public class RegularTable extends TableBase {
} else { } else {
index = new NonUniqueHashIndex(this, indexId, indexName, cols, indexType); index = new NonUniqueHashIndex(this, indexId, indexName, cols, indexType);
} }
} else { } else if (!indexType.isSpatial()) {
index = new TreeIndex(this, indexId, indexName, cols, indexType); index = new TreeIndex(this, indexId, indexName, cols, indexType);
} else {
index = new SpatialTreeIndex(this, indexId, indexName, cols, indexType);
} }
} }
if (database.isMultiVersion()) { if (database.isMultiVersion()) {
......
...@@ -9,6 +9,7 @@ package org.h2.value; ...@@ -9,6 +9,7 @@ package org.h2.value;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.message.DbException; import org.h2.message.DbException;
import com.vividsolutions.jts.geom.Geometry;
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;
...@@ -23,9 +24,9 @@ public class ValueGeometry extends Value { ...@@ -23,9 +24,9 @@ public class ValueGeometry extends Value {
/** /**
* The value. * The value.
*/ */
private final com.vividsolutions.jts.geom.Geometry geometry; private final Geometry geometry;
private ValueGeometry(com.vividsolutions.jts.geom.Geometry geometry) { private ValueGeometry(Geometry geometry) {
this.geometry = geometry; this.geometry = geometry;
} }
...@@ -35,7 +36,7 @@ public class ValueGeometry extends Value { ...@@ -35,7 +36,7 @@ public class ValueGeometry extends Value {
* @param g the geometry * @param g the geometry
* @return the value * @return the value
*/ */
public static ValueGeometry get(com.vividsolutions.jts.geom.Geometry g) { public static ValueGeometry get(Geometry g) {
return (ValueGeometry) Value.cache(new ValueGeometry(g)); return (ValueGeometry) Value.cache(new ValueGeometry(g));
} }
...@@ -59,6 +60,22 @@ public class ValueGeometry extends Value { ...@@ -59,6 +60,22 @@ public class ValueGeometry extends Value {
return (ValueGeometry) Value.cache(new ValueGeometry(fromWKB(bytes))); return (ValueGeometry) Value.cache(new ValueGeometry(fromWKB(bytes)));
} }
public Geometry getGeometry() {
return geometry;
}
public boolean intersects(ValueGeometry r) {
return geometry.intersects(r.getGeometry());
}
public Value intersection(ValueGeometry r) {
return get(this.geometry.intersection(r.geometry));
}
public Value union(ValueGeometry r) {
return get(this.geometry.union(r.geometry));
}
@Override @Override
public int getType() { public int getType() {
return Value.GEOMETRY; return Value.GEOMETRY;
...@@ -71,7 +88,7 @@ public class ValueGeometry extends Value { ...@@ -71,7 +88,7 @@ public class ValueGeometry extends Value {
@Override @Override
protected int compareSecure(Value v, CompareMode mode) { protected int compareSecure(Value v, CompareMode mode) {
com.vividsolutions.jts.geom.Geometry g = ((ValueGeometry) v).geometry; Geometry g = ((ValueGeometry) v).geometry;
return this.geometry.compareTo(g); return this.geometry.compareTo(g);
} }
...@@ -139,7 +156,7 @@ public class ValueGeometry extends Value { ...@@ -139,7 +156,7 @@ public class ValueGeometry extends Value {
/** /**
* Convert from Well-Known-Text format. * Convert from Well-Known-Text format.
*/ */
private static com.vividsolutions.jts.geom.Geometry fromWKT(String s) { private static Geometry fromWKT(String s) {
WKTReader r = new WKTReader(); WKTReader r = new WKTReader();
try { try {
return r.read(s); return r.read(s);
...@@ -151,7 +168,7 @@ public class ValueGeometry extends Value { ...@@ -151,7 +168,7 @@ public class ValueGeometry extends Value {
/** /**
* Convert from Well-Known-Binary format. * Convert from Well-Known-Binary format.
*/ */
private static com.vividsolutions.jts.geom.Geometry fromWKB(byte[] bytes) { private static Geometry fromWKB(byte[] bytes) {
WKBReader r = new WKBReader(); WKBReader r = new WKBReader();
try { try {
return r.read(bytes); return r.read(bytes);
......
...@@ -32,6 +32,7 @@ public class TestSpatial extends TestBase { ...@@ -32,6 +32,7 @@ public class TestSpatial extends TestBase {
public void test() throws SQLException { public void test() throws SQLException {
deleteDb("spatial"); deleteDb("spatial");
testSpatialValues(); testSpatialValues();
testMemorySpatialIndex();
deleteDb("spatial"); deleteDb("spatial");
} }
...@@ -61,4 +62,38 @@ public class TestSpatial extends TestBase { ...@@ -61,4 +62,38 @@ public class TestSpatial extends TestBase {
deleteDb("spatial"); deleteDb("spatial");
} }
/** test in the in-memory spatial index */
private void testMemorySpatialIndex() throws SQLException {
deleteDb("spatialIndex");
Connection conn = getConnection("spatialIndex");
Statement stat = conn.createStatement();
stat.execute("create memory table test(id int primary key, poly geometry)");
stat.execute("create spatial index idx_test_poly on test(poly)");
stat.execute("insert into test values(1, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
ResultSet rs = stat.executeQuery("explain select * from test where poly = 'POLYGON ((1 1, 1 2, 2 2, 1 1))'");
rs.next();
assertContains(rs.getString(1), "/* PUBLIC.IDX_TEST_POLY: POLY =");
// these queries actually have no meaning in the context of a spatial index, but
// check them anyhow
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.executeQuery("select * from test where poly < 'POLYGON ((1 1, 1 2, 2 2, 1 1))'");
rs = stat.executeQuery("select * from test where intersects(poly, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
assertTrue(rs.next());
rs = stat.executeQuery("select * from test where intersects(poly, 'POINT (1 1)')");
assertTrue(rs.next());
rs = stat.executeQuery("select * from test where intersects(poly, 'POINT (0 0)')");
assertFalse(rs.next());
stat.execute("drop table test");
conn.close();
deleteDb("spatialIndex");
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论