提交 845fc68f authored 作者: Thomas Mueller's avatar Thomas Mueller

Spatial index (work in progress) source code formatting

上级 4a353c0f
...@@ -16,10 +16,6 @@ import java.util.HashSet; ...@@ -16,10 +16,6 @@ import java.util.HashSet;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.jdbc.JdbcConnection;
import org.h2.mvstore.MVStore;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.tools.SimpleRowSource; import org.h2.tools.SimpleRowSource;
...@@ -101,7 +97,8 @@ public class TestSpatial extends TestBase { ...@@ -101,7 +97,8 @@ public class TestSpatial extends TestBase {
} }
/** /**
* Generate a random linestring under the given bounding box * Generate a random linestring under the given bounding box.
*
* @param minX Bounding box min x * @param minX Bounding box min x
* @param maxX Bounding box max x * @param maxX Bounding box max x
* @param minY Bounding box min y * @param minY Bounding box min y
...@@ -109,18 +106,23 @@ public class TestSpatial extends TestBase { ...@@ -109,18 +106,23 @@ public class TestSpatial extends TestBase {
* @param maxLength LineString maximum length * @param maxLength LineString maximum length
* @return A segment within this bounding box * @return A segment within this bounding box
*/ */
public static Geometry getRandomGeometry(Random geometryRand,double minX,double maxX,double minY, double maxY, double maxLength) { public static Geometry getRandomGeometry(Random geometryRand,
double minX, double maxX,
double minY, double maxY, double maxLength) {
GeometryFactory factory = new GeometryFactory(); GeometryFactory factory = new GeometryFactory();
// Create the start point // Create the start point
Coordinate start = new Coordinate(geometryRand.nextDouble()*(maxX-minX)+minX, Coordinate start = new Coordinate(
geometryRand.nextDouble()*(maxY-minY)+minY); geometryRand.nextDouble() * (maxX - minX) + minX,
geometryRand.nextDouble() * (maxY - minY) + minY);
// Compute an angle // Compute an angle
double angle = geometryRand.nextDouble() * Math.PI * 2; double angle = geometryRand.nextDouble() * Math.PI * 2;
// Compute length // Compute length
double length = geometryRand.nextDouble() * maxLength; double length = geometryRand.nextDouble() * maxLength;
// Compute end point // Compute end point
Coordinate end = new Coordinate(start.x + Math.cos(angle) * length, start.y + Math.sin(angle) * length); Coordinate end = new Coordinate(
return factory.createLineString(new Coordinate[]{start,end}); start.x + Math.cos(angle) * length,
start.y + Math.sin(angle) * length);
return factory.createLineString(new Coordinate[] { start, end });
} }
private void testRandom() throws SQLException { private void testRandom() throws SQLException {
...@@ -131,52 +133,64 @@ public class TestSpatial extends TestBase { ...@@ -131,52 +133,64 @@ public class TestSpatial extends TestBase {
conn.close(); conn.close();
} }
private void testRandom(Connection conn, long seed,long size) throws SQLException { private void testRandom(Connection conn, long seed, long size) throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("drop table if exists test"); stat.execute("drop table if exists test");
Random geometryRand = new Random(seed); Random geometryRand = new Random(seed);
// Generate a set of geometry // Generate a set of geometry
// It is marked as random, but it generate always the same geometry set, given the same seed // It is marked as random, but it generate always the same geometry set, given the same seed
stat.execute("create memory table test(id long primary key auto_increment, poly geometry)"); stat.execute("create memory table test(" +
"id long primary key auto_increment, poly geometry)");
// Create segment generation bounding box // Create segment generation bounding box
Envelope bbox = ValueGeometry.get("POLYGON ((301804.1049793153 2251719.1222191923," + Envelope bbox = ValueGeometry.get("POLYGON ((" +
" 301804.1049793153 2254747.2888244865, 304646.87362918374 2254747.2888244865," + "301804.1049793153 2251719.1222191923, " +
" 304646.87362918374 2251719.1222191923, 301804.1049793153 2251719.1222191923))") "301804.1049793153 2254747.2888244865, " +
"304646.87362918374 2254747.2888244865, " +
"304646.87362918374 2251719.1222191923, " +
"301804.1049793153 2251719.1222191923))")
.getGeometry().getEnvelopeInternal(); .getGeometry().getEnvelopeInternal();
// Create overlap test bounding box // Create overlap test bounding box
String testBBoxString = "POLYGON ((302215.44416332216 2252748, 302215.44416332216 2253851.781225762," + String testBBoxString = "POLYGON ((" +
" 303582.85796541866 2253851.781225762, 303582.85796541866 2252748.526908161," + "302215.44416332216 2252748, " +
" 302215.44416332216 2252748))"; "302215.44416332216 2253851.781225762, " +
"303582.85796541866 2253851.781225762, " +
"303582.85796541866 2252748.526908161, " +
"302215.44416332216 2252748))";
Envelope testBBox = ValueGeometry.get(testBBoxString).getGeometry().getEnvelopeInternal(); Envelope testBBox = ValueGeometry.get(testBBoxString).getGeometry().getEnvelopeInternal();
PreparedStatement ps = conn.prepareStatement("insert into test(poly) values (?)"); PreparedStatement ps = conn.prepareStatement(
"insert into test(poly) values (?)");
long overlapCount = 0; long overlapCount = 0;
Set<Integer> overlaps = new HashSet<Integer>(680); Set<Integer> overlaps = new HashSet<Integer>(680);
for(int i=1;i<=size;i++) { for (int i = 1; i <= size; i++) {
Geometry geometry = getRandomGeometry(geometryRand,bbox.getMinX(),bbox.getMaxX(),bbox.getMinY(),bbox.getMaxY(),200); Geometry geometry = getRandomGeometry(
ps.setObject(1,geometry); geometryRand,
ps.execute(); bbox.getMinX(), bbox.getMaxX(),
ResultSet keys = ps.getGeneratedKeys(); bbox.getMinY(), bbox.getMaxY(), 200);
keys.next(); ps.setObject(1, geometry);
if(geometry.getEnvelopeInternal().intersects(testBBox)) { ps.execute();
overlapCount++; ResultSet keys = ps.getGeneratedKeys();
overlaps.add(keys.getInt(1)); keys.next();
} if (geometry.getEnvelopeInternal().intersects(testBBox)) {
overlapCount++;
overlaps.add(keys.getInt(1));
}
} }
ps.close(); ps.close();
// Create index // Create index
stat.execute("create spatial index idx_test_poly on test(poly)"); stat.execute("create spatial index idx_test_poly on test(poly)");
// Must find the same overlap count with index // Must find the same overlap count with index
ps = conn.prepareStatement("select id from test where poly && ?::Geometry"); ps = conn.prepareStatement(
ps.setString(1,testBBoxString); "select id from test where poly && ?::Geometry");
ResultSet rs = ps.executeQuery(); ps.setString(1, testBBoxString);
long found = 0; ResultSet rs = ps.executeQuery();
while(rs.next()) { long found = 0;
overlaps.remove(rs.getInt(1)); while (rs.next()) {
found++; overlaps.remove(rs.getInt(1));
} found++;
// Index count must be the same as sequential count }
assertEquals(overlapCount,found); // Index count must be the same as sequential count
assertEquals(overlapCount, found);
// Missing id still in overlaps map // Missing id still in overlaps map
assertTrue(overlaps.isEmpty()); assertTrue(overlaps.isEmpty());
stat.execute("drop table if exists test"); stat.execute("drop table if exists test");
...@@ -191,9 +205,11 @@ public class TestSpatial extends TestBase { ...@@ -191,9 +205,11 @@ public class TestSpatial extends TestBase {
stat.execute("insert into test values(2, 'POLYGON ((3 1, 3 2, 4 2, 3 1))')"); stat.execute("insert into test values(2, 'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')"); stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')");
ResultSet rs = stat.executeQuery("select * from test where poly && 'POINT (1.5 1.5)'::Geometry"); ResultSet rs = stat.executeQuery(
"select * from test " +
"where poly && 'POINT (1.5 1.5)'::Geometry");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(1,rs.getInt("id")); assertEquals(1, rs.getInt("id"));
assertFalse(rs.next()); assertFalse(rs.next());
stat.execute("drop table test"); stat.execute("drop table test");
} finally { } finally {
...@@ -211,14 +227,19 @@ public class TestSpatial extends TestBase { ...@@ -211,14 +227,19 @@ public class TestSpatial extends TestBase {
stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')"); stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')");
stat.execute("create spatial index on test(poly)"); stat.execute("create spatial index on test(poly)");
ResultSet rs = stat.executeQuery("select * from test where poly && 'POINT (1.5 1.5)'::Geometry"); ResultSet rs = stat.executeQuery(
"select * from test " +
"where poly && 'POINT (1.5 1.5)'::Geometry");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(1, rs.getInt("id")); assertEquals(1, rs.getInt("id"));
assertFalse(rs.next()); assertFalse(rs.next());
rs.close(); rs.close();
// Test with multiple operator // Test with multiple operator
rs = stat.executeQuery("select * from test where poly && 'POINT (1.5 1.5)'::Geometry AND poly && 'POINT (1.7 1.75)'::Geometry"); rs = stat.executeQuery(
"select * from test " +
"where poly && 'POINT (1.5 1.5)'::Geometry " +
"AND poly && 'POINT (1.7 1.75)'::Geometry");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(1, rs.getInt("id")); assertEquals(1, rs.getInt("id"));
assertFalse(rs.next()); assertFalse(rs.next());
...@@ -231,9 +252,11 @@ public class TestSpatial extends TestBase { ...@@ -231,9 +252,11 @@ public class TestSpatial extends TestBase {
conn = getConnection("spatial_pers"); conn = getConnection("spatial_pers");
try { try {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select * from test where poly && 'POINT (1.5 1.5)'::Geometry"); ResultSet rs = stat.executeQuery(
"select * from test " +
"where poly && 'POINT (1.5 1.5)'::Geometry");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(1,rs.getInt("id")); assertEquals(1, rs.getInt("id"));
assertFalse(rs.next()); assertFalse(rs.next());
stat.execute("drop table test"); stat.execute("drop table test");
} finally { } finally {
...@@ -251,11 +274,13 @@ public class TestSpatial extends TestBase { ...@@ -251,11 +274,13 @@ public class TestSpatial extends TestBase {
stat.execute("insert into test values(2, 'POLYGON ((3 1, 3 2, 4 2, 3 1))')"); stat.execute("insert into test values(2, 'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')"); stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')");
ResultSet rs = stat.executeQuery("select * from test where NOT poly && 'POINT (1.5 1.5)'::Geometry"); ResultSet rs = stat.executeQuery(
"select * from test " +
"where NOT poly && 'POINT (1.5 1.5)'::Geometry");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(2,rs.getInt("id")); assertEquals(2, rs.getInt("id"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(3,rs.getInt("id")); assertEquals(3, rs.getInt("id"));
assertFalse(rs.next()); assertFalse(rs.next());
stat.execute("drop table test"); stat.execute("drop table test");
} finally { } finally {
...@@ -297,25 +322,29 @@ public class TestSpatial extends TestBase { ...@@ -297,25 +322,29 @@ public class TestSpatial extends TestBase {
deleteDb("spatial"); deleteDb("spatial");
} }
private void testRoadAndArea(Statement stat) throws SQLException { private void testRoadAndArea(Statement stat) throws SQLException {
ResultSet rs = stat.executeQuery("select idarea, COUNT(idroad) roadscount from area,roads where area.the_geom && roads.the_geom GROUP BY idarea ORDER BY idarea"); ResultSet rs = stat.executeQuery(
"select idarea, COUNT(idroad) roadscount " +
"from area, roads " +
"where area.the_geom && roads.the_geom " +
"GROUP BY idarea ORDER BY idarea");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(1,rs.getInt("idarea")); assertEquals(1, rs.getInt("idarea"));
assertEquals(3,rs.getInt("roadscount")); assertEquals(3, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(2,rs.getInt("idarea")); assertEquals(2, rs.getInt("idarea"));
assertEquals(4,rs.getInt("roadscount")); assertEquals(4, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(3,rs.getInt("idarea")); assertEquals(3, rs.getInt("idarea"));
assertEquals(1,rs.getInt("roadscount")); assertEquals(1, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(4,rs.getInt("idarea")); assertEquals(4, rs.getInt("idarea"));
assertEquals(2,rs.getInt("roadscount")); assertEquals(2, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(5,rs.getInt("idarea")); assertEquals(5, rs.getInt("idarea"));
assertEquals(3,rs.getInt("roadscount")); assertEquals(3, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(6,rs.getInt("idarea")); assertEquals(6, rs.getInt("idarea"));
assertEquals(1,rs.getInt("roadscount")); assertEquals(1, rs.getInt("roadscount"));
assertFalse(rs.next()); assertFalse(rs.next());
rs.close(); rs.close();
} }
...@@ -331,25 +360,29 @@ public class TestSpatial extends TestBase { ...@@ -331,25 +360,29 @@ public class TestSpatial extends TestBase {
// Remove a row but do not commit // Remove a row but do not commit
stat.execute("delete from roads where idroad=7"); stat.execute("delete from roads where idroad=7");
// Check if index is updated // Check if index is updated
ResultSet rs = stat.executeQuery("select idarea, COUNT(idroad) roadscount from area,roads where area.the_geom && roads.the_geom GROUP BY idarea ORDER BY idarea"); ResultSet rs = stat.executeQuery(
"select idarea, COUNT(idroad) roadscount " +
"from area, roads " +
"where area.the_geom && roads.the_geom " +
"GROUP BY idarea ORDER BY idarea");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(1,rs.getInt("idarea")); assertEquals(1, rs.getInt("idarea"));
assertEquals(3,rs.getInt("roadscount")); assertEquals(3, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(2,rs.getInt("idarea")); assertEquals(2, rs.getInt("idarea"));
assertEquals(4,rs.getInt("roadscount")); assertEquals(4, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(3,rs.getInt("idarea")); assertEquals(3, rs.getInt("idarea"));
assertEquals(1,rs.getInt("roadscount")); assertEquals(1, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(4,rs.getInt("idarea")); assertEquals(4, rs.getInt("idarea"));
assertEquals(1,rs.getInt("roadscount")); assertEquals(1, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(5,rs.getInt("idarea")); assertEquals(5, rs.getInt("idarea"));
assertEquals(2,rs.getInt("roadscount")); assertEquals(2, rs.getInt("roadscount"));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(6,rs.getInt("idarea")); assertEquals(6, rs.getInt("idarea"));
assertEquals(1,rs.getInt("roadscount")); assertEquals(1, rs.getInt("roadscount"));
assertFalse(rs.next()); assertFalse(rs.next());
rs.close(); rs.close();
conn.rollback(sp); conn.rollback(sp);
...@@ -380,7 +413,9 @@ public class TestSpatial extends TestBase { ...@@ -380,7 +413,9 @@ public class TestSpatial extends TestBase {
"and polygon && 'POLYGON ((10 10, 10 20, 20 20, 10 10))'::Geometry"); "and polygon && 'POLYGON ((10 10, 10 20, 20 20, 10 10))'::Geometry");
assertFalse(rs.next()); assertFalse(rs.next());
rs = stat.executeQuery("explain select * from test where polygon && 'POLYGON ((1 1, 1 2, 2 2, 1 1))'::Geometry"); rs = stat.executeQuery(
"explain select * from test " +
"where polygon && 'POLYGON ((1 1, 1 2, 2 2, 1 1))'::Geometry");
rs.next(); rs.next();
assertContains(rs.getString(1), "/* PUBLIC.IDX_TEST_POLYGON: POLYGON &&"); assertContains(rs.getString(1), "/* PUBLIC.IDX_TEST_POLYGON: POLYGON &&");
...@@ -395,13 +430,19 @@ public class TestSpatial extends TestBase { ...@@ -395,13 +430,19 @@ public class TestSpatial extends TestBase {
stat.executeQuery("select * from test where polygon > 'POLYGON ((1 1, 1 2, 2 2, 1 1))'::Geometry"); stat.executeQuery("select * from test where polygon > 'POLYGON ((1 1, 1 2, 2 2, 1 1))'::Geometry");
stat.executeQuery("select * from test where polygon < 'POLYGON ((1 1, 1 2, 2 2, 1 1))'::Geometry"); stat.executeQuery("select * from test where polygon < 'POLYGON ((1 1, 1 2, 2 2, 1 1))'::Geometry");
rs = stat.executeQuery("select * from test where intersects(polygon, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')"); rs = stat.executeQuery(
"select * from test " +
"where intersects(polygon, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
assertTrue(rs.next()); assertTrue(rs.next());
rs = stat.executeQuery("select * from test where intersects(polygon, 'POINT (1 1)')"); rs = stat.executeQuery(
"select * from test " +
"where intersects(polygon, 'POINT (1 1)')");
assertTrue(rs.next()); assertTrue(rs.next());
rs = stat.executeQuery("select * from test where intersects(polygon, 'POINT (0 0)')"); rs = stat.executeQuery(
"select * from test " +
"where intersects(polygon, 'POINT (0 0)')");
assertFalse(rs.next()); assertFalse(rs.next());
stat.execute("drop table test"); stat.execute("drop table test");
...@@ -438,8 +479,11 @@ public class TestSpatial extends TestBase { ...@@ -438,8 +479,11 @@ public class TestSpatial extends TestBase {
Connection conn = getConnection("spatialIndex"); Connection conn = getConnection("spatialIndex");
try { try {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("CREATE ALIAS T_RANDOM_GEOM_TABLE FOR \"" + TestSpatial.class.getName() + ".getRandomGeometryTable\""); stat.execute("CREATE ALIAS T_RANDOM_GEOM_TABLE FOR \"" +
stat.execute("create table test as select * from T_RANDOM_GEOM_TABLE(42,20,-100,100,-100,100,4)"); TestSpatial.class.getName() + ".getRandomGeometryTable\"");
stat.execute(
"create table test as " +
"select * from T_RANDOM_GEOM_TABLE(42,20,-100,100,-100,100,4)");
stat.execute("DROP ALIAS T_RANDOM_GEOM_TABLE"); stat.execute("DROP ALIAS T_RANDOM_GEOM_TABLE");
ResultSet rs = stat.executeQuery("select count(*) cpt from test"); ResultSet rs = stat.executeQuery("select count(*) cpt from test");
assertTrue(rs.next()); assertTrue(rs.next());
...@@ -450,35 +494,55 @@ public class TestSpatial extends TestBase { ...@@ -450,35 +494,55 @@ public class TestSpatial extends TestBase {
deleteDb("spatialIndex"); deleteDb("spatialIndex");
} }
/**
public static ResultSet getRandomGeometryTable(final long seed,final long rowCount, final double minX,final double maxX,final double minY, final double maxY, final double maxLength) { * Generate a result set with random geometry data.
*
* @param seed the random seed
* @param rowCount the number of rows
* @param minX the smallest x
* @param maxX the largest x
* @param minY the smallest y
* @param maxY the largest y
* @param maxLength the maximum length
* @return a result set
*/
public static ResultSet getRandomGeometryTable(
final long seed, final long rowCount,
final double minX, final double maxX,
final double minY, final double maxY, final double maxLength) {
SimpleResultSet rs = new SimpleResultSet(new SimpleRowSource() { SimpleResultSet rs = new SimpleResultSet(new SimpleRowSource() {
private final Random rnd = new Random(seed);
private int cpt = 0; private final Random random = new Random(seed);
private int currentRow;
@Override @Override
public Object[] readRow() throws SQLException { public Object[] readRow() throws SQLException {
if(cpt++<rowCount) { if (currentRow++ < rowCount) {
return new Object[]{getRandomGeometry(rnd,minX,maxX,minY,maxY,maxLength)}; //To change body of implemented methods use File | Settings | File Templates. return new Object[] {
} else { getRandomGeometry(random,
return null; minX, maxX, minY, maxY, maxLength) };
} }
return null;
} }
@Override @Override
public void close() { public void close() {
// nothing to do
} }
@Override @Override
public void reset() throws SQLException { public void reset() throws SQLException {
rnd.setSeed(seed); random.setSeed(seed);
} }
}); });
rs.addColumn("the_geom", Types.OTHER,Integer.MAX_VALUE,0); rs.addColumn("the_geom", Types.OTHER, Integer.MAX_VALUE, 0);
return rs; return rs;
} }
/** /**
* * Convert the text to a geometry object.
*
* @param text Geometry in Well Known Text * @param text Geometry in Well Known Text
* @param srid Projection ID * @param srid Projection ID
* @return Geometry object * @return Geometry object
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论