Skip to content

Commit

Permalink
Merge remote branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
brycenz committed Sep 28, 2011
2 parents 987b441 + aa0a421 commit cea66e3
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 88 deletions.
86 changes: 39 additions & 47 deletions src/main/java/org/neo4j/collections/rtree/RTreeIndex.java
Expand Up @@ -26,7 +26,6 @@
import org.neo4j.collections.rtree.filter.SearchFilter;
import org.neo4j.collections.rtree.filter.SearchResults;
import org.neo4j.collections.rtree.search.Search;
import org.neo4j.collections.rtree.search.SearchAll;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
Expand Down Expand Up @@ -244,8 +243,8 @@ public Iterable<Node> getAllIndexedNodes() {
}

private class SearchEvaluator implements ReturnableEvaluator, StopEvaluator {

private SearchFilter filter;
private TraversalPosition lastPosition;
private boolean isReturnableNode;
private boolean isStopNode;

Expand All @@ -254,21 +253,18 @@ public SearchEvaluator(SearchFilter filter) {
}

void checkPosition(TraversalPosition position) {
if (!position.equals(lastPosition)) {
Relationship rel = position.lastRelationshipTraversed();
Node node = position.currentNode();
if (rel == null) {
isStopNode = false;
isReturnableNode = false;
} else if (rel.getType().equals(RTreeRelationshipTypes.RTREE_CHILD)) {
isStopNode = filter.needsToVisit(getIndexNodeEnvelope(node));
isReturnableNode = false;
} else {
isReturnableNode = filter.geometryMatches(node);
isStopNode = !isReturnableNode;
}
Relationship rel = position.lastRelationshipTraversed();
Node node = position.currentNode();
if (rel == null) {
isStopNode = false;
isReturnableNode = false;
} else if (rel.getType().equals(RTreeRelationshipTypes.RTREE_CHILD)) {
isReturnableNode = false;
isStopNode = !filter.needsToVisit(getIndexNodeEnvelope(node));
} else if (rel.getType().equals(RTreeRelationshipTypes.RTREE_REFERENCE)) {
isReturnableNode = filter.geometryMatches(node);
isStopNode = true;
}
lastPosition = position;
}

@Override
Expand Down Expand Up @@ -316,6 +312,14 @@ public Node getIndexRoot() {

// Private methods

private Envelope getChildNodeEnvelope(Node child, RelationshipType relType) {
if (relType == RTreeRelationshipTypes.RTREE_REFERENCE) {
return getLeafNodeEnvelope(child);
} else {
return getIndexNodeEnvelope(child);
}
}

/**
* The leaf nodes belong to the domain model, and as such need to use the
* layers domain-specific GeometryEncoder for decoding the envelope.
Expand All @@ -332,15 +336,18 @@ private Envelope getLeafNodeEnvelope(Node geomNode) {
protected Envelope getIndexNodeEnvelope(Node indexNode) {
if (indexNode == null) indexNode = getIndexRoot();
if (!indexNode.hasProperty(PROP_BBOX)) {
System.err.println("node[" + indexNode + "] has no bounding box property '" + PROP_BBOX + "'");
// this is ok after an index node split
return null;
}
return bboxToEnvelope((double[]) indexNode.getProperty(PROP_BBOX));

double[] bbox = (double[]) indexNode.getProperty(PROP_BBOX);
// Envelope parameters: xmin, xmax, ymin, ymax
return new Envelope(bbox[0], bbox[2], bbox[1], bbox[3]);
}

private void visitInTx(SpatialIndexVisitor visitor, Long indexNodeId) {
Node indexNode = database.getNodeById(indexNodeId);
if(!visitor.needsToVisit(getIndexNodeEnvelope(indexNode))) return;
if (!visitor.needsToVisit(getIndexNodeEnvelope(indexNode))) return;

if (indexNode.hasRelationship(RTreeRelationshipTypes.RTREE_CHILD, Direction.OUTGOING)) {
// Node is not a leaf
Expand Down Expand Up @@ -493,7 +500,7 @@ private Node chooseIndexNodeWithSmallestArea(List<Node> indexNodes) {
double smallestArea = -1;

for (Node indexNode : indexNodes) {
double area = getArea(indexNode);
double area = getArea(getIndexNodeEnvelope(indexNode));
if (result == null || area < smallestArea) {
result = indexNode;
smallestArea = area;
Expand Down Expand Up @@ -559,9 +566,9 @@ private Node quadraticSplit(Node indexNode, RelationshipType relationshipType) {
Node seed2 = null;
double worst = Double.NEGATIVE_INFINITY;
for (Node e : entries) {
Envelope eEnvelope = getLeafNodeEnvelope(e);
Envelope eEnvelope = getChildNodeEnvelope(e, relationshipType);
for (Node e1 : entries) {
Envelope e1Envelope = getLeafNodeEnvelope(e1);
Envelope e1Envelope = getChildNodeEnvelope(e1, relationshipType);
double deadSpace = getArea(createEnvelope(eEnvelope, e1Envelope)) - getArea(eEnvelope) - getArea(e1Envelope);
if (deadSpace > worst) {
worst = deadSpace;
Expand All @@ -573,11 +580,11 @@ private Node quadraticSplit(Node indexNode, RelationshipType relationshipType) {

List<Node> group1 = new ArrayList<Node>();
group1.add(seed1);
Envelope group1envelope = getLeafNodeEnvelope(seed1);
Envelope group1envelope = getChildNodeEnvelope(seed1, relationshipType);

List<Node> group2 = new ArrayList<Node>();
group2.add(seed2);
Envelope group2envelope = getLeafNodeEnvelope(seed2);
Envelope group2envelope = getChildNodeEnvelope(seed2, relationshipType);

entries.remove(seed1);
entries.remove(seed2);
Expand All @@ -588,7 +595,7 @@ private Node quadraticSplit(Node indexNode, RelationshipType relationshipType) {
Node bestEntry = null;
double expansionMin = Double.POSITIVE_INFINITY;
for (Node e : entries) {
Envelope nodeEnvelope = getLeafNodeEnvelope(e);
Envelope nodeEnvelope = getChildNodeEnvelope(e, relationshipType);
double expansion1 = getArea(createEnvelope(nodeEnvelope, group1envelope)) - getArea(group1envelope);
double expansion2 = getArea(createEnvelope(nodeEnvelope, group2envelope)) - getArea(group2envelope);

Expand Down Expand Up @@ -618,7 +625,7 @@ private Node quadraticSplit(Node indexNode, RelationshipType relationshipType) {

// insert the best candidate entry in the best group
bestGroup.add(bestEntry);
bestGroupEnvelope.expandToInclude(getLeafNodeEnvelope(bestEntry));
bestGroupEnvelope.expandToInclude(getChildNodeEnvelope(bestEntry, relationshipType));

entries.remove(bestEntry);
}
Expand Down Expand Up @@ -648,22 +655,11 @@ private void createNewRoot(Node oldRoot, Node newIndexNode) {
layerNode.createRelationshipTo(newRoot, RTreeRelationshipTypes.RTREE_ROOT);
}

private double[] envelopeToBBox(Envelope bounds) {
return new double[]{ bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY() };
}

protected Envelope bboxToEnvelope(double[] bbox) {
// Envelope parameters: xmin, xmax, ymin, ymax
return new Envelope(bbox[0], bbox[2], bbox[1], bbox[3]);
}

private boolean addChild(Node parent, RelationshipType type, Node newChild) {
double[] childBBox = null;
if (type == RTreeRelationshipTypes.RTREE_REFERENCE) {
childBBox = envelopeToBBox(envelopeDecoder.decodeEnvelope(newChild));
} else {
childBBox = (double[]) newChild.getProperty(PROP_BBOX);
}
Envelope childEnvelope = getChildNodeEnvelope(newChild, type);
double[] childBBox = new double[] {
childEnvelope.getMinX(), childEnvelope.getMinY(),
childEnvelope.getMaxX(), childEnvelope.getMaxY() };
parent.createRelationshipTo(newChild, type);
return expandParentBoundingBoxAfterNewChild(parent, childBBox);
}
Expand Down Expand Up @@ -696,9 +692,9 @@ private boolean adjustParentBoundingBox(Node indexNode, RelationshipType relatio
Node childNode = iterator.next().getEndNode();

if (bbox == null) {
bbox = new Envelope(getLeafNodeEnvelope(childNode));
bbox = new Envelope(getChildNodeEnvelope(childNode, relationshipType));
} else {
bbox.expandToInclude(getLeafNodeEnvelope(childNode));
bbox.expandToInclude(getChildNodeEnvelope(childNode, relationshipType));
}
}

Expand Down Expand Up @@ -770,10 +766,6 @@ private Node getIndexNodeParent(Node indexNode) {
else return relationship.getStartNode();
}

private double getArea(Node node) {
return getArea(getLeafNodeEnvelope(node));
}

private double getArea(Envelope e) {
return e.getWidth() * e.getHeight();
}
Expand Down
Expand Up @@ -24,6 +24,7 @@
import org.neo4j.graphdb.Node;

public abstract class AbstractSearchEnvelopeIntersection implements SearchFilter {

protected EnvelopeDecoder decoder;
protected Envelope referenceEnvelope;

Expand All @@ -43,6 +44,7 @@ public final boolean geometryMatches(Node geomNode) {
if (geomEnvelope.intersects(referenceEnvelope)) {
return onEnvelopeIntersection(geomNode, geomEnvelope);
}

return false;
}

Expand Down
Expand Up @@ -23,6 +23,9 @@
import org.neo4j.graphdb.Node;

public interface SearchFilter {

boolean needsToVisit(Envelope envelope);
boolean geometryMatches(Node geomNode);

boolean geometryMatches(Node geomNode);

}
12 changes: 2 additions & 10 deletions src/test/java/org/neo4j/collections/rtree/TestSearch.java
Expand Up @@ -67,22 +67,14 @@ public void myFirstTest() {
Envelope expectedBbox = new Envelope(0, 25, 0, 32);
assertEnvelopeEquals(bbox, expectedBbox);

// Search search = new SearchInvalidEnvelopes(index.getEnvelopeDecoder());
// index.executeSearch(search);
// assertEquals(1, search.getResults().size());

Search search = new SearchEqualEnvelopes(index.getEnvelopeDecoder(),
new Envelope(0, 2, 0, 3));
Search search = new SearchEqualEnvelopes(index.getEnvelopeDecoder(), new Envelope(0, 2, 0, 3));
index.executeSearch(search);
assertEquals(1, search.getResults().size());

search = new SearchCoveredByEnvelope(index.getEnvelopeDecoder(),
new Envelope(9, 15, -1, 3));
search = new SearchCoveredByEnvelope(index.getEnvelopeDecoder(), new Envelope(9, 15, -1, 3));
index.executeSearch(search);
assertEquals(3, search.getResults().size());

// TODO test index.removeAll(deleteGeomNodes, monitor)

index.clear(new NullListener());
assertEquals(0, index.count());

Expand Down
53 changes: 23 additions & 30 deletions src/test/java/org/neo4j/collections/rtree/TestSearchFilter.java
Expand Up @@ -21,61 +21,54 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.neo4j.collections.rtree.filter.SearchFilter;
import org.neo4j.collections.rtree.filter.SearchCoveredByEnvelope;
import org.neo4j.collections.rtree.filter.SearchEqualEnvelopes;
import org.neo4j.collections.rtree.filter.SearchFilter;
import org.neo4j.collections.rtree.filter.SearchResults;
import org.neo4j.graphdb.Node;


public class TestSearchFilter extends SpatialTestCase {

@Test
public void searchIndexWithFilter() {
RTreeIndex index = new RTreeIndex(graphDb(), graphDb().getReferenceNode(),
RTreeIndex index = new RTreeIndex(graphDb(), graphDb().getReferenceNode(),
new EnvelopeDecoderFromDoubleArray("bbox"));

assertTrue(index.isEmpty());
assertEquals(0, index.count());

// equal bbox test
index.add(createGeomNode(0, 0, 2, 3));

index.add(createGeomNode(10, 0));
index.add(createGeomNode(12, 0));
index.add(createGeomNode(14, 2));
index.add(createGeomNode(25, 32));

Node geomNode = createGeomNode(11, 1);
index.add(geomNode);

assertFalse(index.isEmpty());
assertEquals(6, index.count());

assertTrue(index.isNodeIndexed(geomNode.getId()));
index.remove(geomNode.getId(), false);
assertFalse(index.isNodeIndexed(geomNode.getId()));

assertEquals(5, index.count());

Envelope bbox = index.getBoundingBox();
Envelope expectedBbox = new Envelope(0, 25, 0, 32);
assertEnvelopeEquals(bbox, expectedBbox);

SearchFilter filter = new SearchEqualEnvelopes(index.getEnvelopeDecoder(),
new Envelope(0, 2, 0, 3));
assertEquals(1, index.searchIndex(filter).count());
SearchFilter filter = new SearchEqualEnvelopes(index.getEnvelopeDecoder(), new Envelope(0, 2, 0, 3));
SearchResults results = index.searchIndex(filter);

filter = new SearchCoveredByEnvelope(index.getEnvelopeDecoder(),
new Envelope(9, 15, -1, 3));
assertEquals(3, index.searchIndex(filter).count());
int count = 0;
for (Node node : results) {
System.out.println("found node: " + node.getId());
count++;
}

// TODO test index.removeAll(deleteGeomNodes, monitor)

index.clear(new NullListener());
assertEquals(0, index.count());
assertEquals(1, count);

filter = new SearchCoveredByEnvelope(index.getEnvelopeDecoder(), new Envelope(9, 15, -1, 3));
results = index.searchIndex(filter);

count = 0;
for (Node node : results) {
System.out.println("found node: " + node.getId());
count++;
}

debugIndexTree(index, graphDb().getReferenceNode());
assertEquals(3, count);
}

}

0 comments on commit cea66e3

Please sign in to comment.