LUCENE-4742: Rename spatial Node back to Cell

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1462090 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2013-03-28 13:44:09 +00:00
parent 6448dce4ea
commit 56a18440e2
12 changed files with 146 additions and 146 deletions

View File

@ -21,7 +21,7 @@ import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.TermsEnum; import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
@ -84,13 +84,13 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
* that there are indexed terms; if not it quickly returns null. Then it calls * that there are indexed terms; if not it quickly returns null. Then it calls
* {@link #start()} so a subclass can set up a return value, like an * {@link #start()} so a subclass can set up a return value, like an
* {@link org.apache.lucene.util.OpenBitSet}. Then it starts the traversal * {@link org.apache.lucene.util.OpenBitSet}. Then it starts the traversal
* process, calling {@link #findSubCellsToVisit(org.apache.lucene.spatial.prefix.tree.Node)} * process, calling {@link #findSubCellsToVisit(org.apache.lucene.spatial.prefix.tree.Cell)}
* which by default finds the top cells that intersect {@code queryShape}. If * which by default finds the top cells that intersect {@code queryShape}. If
* there isn't an indexed cell for a corresponding cell returned for this * there isn't an indexed cell for a corresponding cell returned for this
* method then it's short-circuited until it finds one, at which point * method then it's short-circuited until it finds one, at which point
* {@link #visit(org.apache.lucene.spatial.prefix.tree.Node)} is called. At * {@link #visit(org.apache.lucene.spatial.prefix.tree.Cell)} is called. At
* some depths, of the tree, the algorithm switches to a scanning mode that * some depths, of the tree, the algorithm switches to a scanning mode that
* finds calls {@link #visitScanned(org.apache.lucene.spatial.prefix.tree.Node)} * finds calls {@link #visitScanned(org.apache.lucene.spatial.prefix.tree.Cell)}
* for each leaf cell found. * for each leaf cell found.
* *
* @lucene.internal * @lucene.internal
@ -113,7 +113,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
private VNode curVNode;//current pointer, derived from query shape private VNode curVNode;//current pointer, derived from query shape
private BytesRef curVNodeTerm = new BytesRef();//curVNode.cell's term. private BytesRef curVNodeTerm = new BytesRef();//curVNode.cell's term.
private Node scanCell; private Cell scanCell;
private BytesRef thisTerm;//the result of termsEnum.term() private BytesRef thisTerm;//the result of termsEnum.term()
@ -132,7 +132,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
return null; // all done return null; // all done
curVNode = new VNode(null); curVNode = new VNode(null);
curVNode.reset(grid.getWorldNode()); curVNode.reset(grid.getWorldCell());
start(); start();
@ -198,11 +198,11 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
return finish(); return finish();
} }
/** Called initially, and whenever {@link #visit(org.apache.lucene.spatial.prefix.tree.Node)} /** Called initially, and whenever {@link #visit(org.apache.lucene.spatial.prefix.tree.Cell)}
* returns true. */ * returns true. */
private void addIntersectingChildren() throws IOException { private void addIntersectingChildren() throws IOException {
assert thisTerm != null; assert thisTerm != null;
Node cell = curVNode.cell; Cell cell = curVNode.cell;
if (cell.getLevel() >= detailLevel) if (cell.getLevel() >= detailLevel)
throw new IllegalStateException("Spatial logic error"); throw new IllegalStateException("Spatial logic error");
@ -211,7 +211,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
//If the next indexed term just adds a leaf marker ('+') to cell, //If the next indexed term just adds a leaf marker ('+') to cell,
// then add all of those docs // then add all of those docs
assert StringHelper.startsWith(thisTerm, curVNodeTerm); assert StringHelper.startsWith(thisTerm, curVNodeTerm);
scanCell = grid.getNode(thisTerm.bytes, thisTerm.offset, thisTerm.length, scanCell); scanCell = grid.getCell(thisTerm.bytes, thisTerm.offset, thisTerm.length, scanCell);
if (scanCell.getLevel() == cell.getLevel() && scanCell.isLeaf()) { if (scanCell.getLevel() == cell.getLevel() && scanCell.isLeaf()) {
visitLeaf(scanCell); visitLeaf(scanCell);
//advance //advance
@ -230,7 +230,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
if (!scan) { if (!scan) {
//Divide & conquer (ultimately termsEnum.seek()) //Divide & conquer (ultimately termsEnum.seek())
Iterator<Node> subCellsIter = findSubCellsToVisit(cell); Iterator<Cell> subCellsIter = findSubCellsToVisit(cell);
if (!subCellsIter.hasNext())//not expected if (!subCellsIter.hasNext())//not expected
return; return;
curVNode.children = new VNodeCellIterator(subCellsIter, new VNode(curVNode)); curVNode.children = new VNodeCellIterator(subCellsIter, new VNode(curVNode));
@ -248,7 +248,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
* guaranteed to have an intersection and thus this must return some number * guaranteed to have an intersection and thus this must return some number
* of nodes. * of nodes.
*/ */
protected Iterator<Node> findSubCellsToVisit(Node cell) { protected Iterator<Cell> findSubCellsToVisit(Cell cell) {
return cell.getSubCells(queryShape).iterator(); return cell.getSubCells(queryShape).iterator();
} }
@ -256,13 +256,13 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
* Scans ({@code termsEnum.next()}) terms until a term is found that does * Scans ({@code termsEnum.next()}) terms until a term is found that does
* not start with curVNode's cell. If it finds a leaf cell or a cell at * not start with curVNode's cell. If it finds a leaf cell or a cell at
* level {@code scanDetailLevel} then it calls {@link * level {@code scanDetailLevel} then it calls {@link
* #visitScanned(org.apache.lucene.spatial.prefix.tree.Node)}. * #visitScanned(org.apache.lucene.spatial.prefix.tree.Cell)}.
*/ */
protected void scan(int scanDetailLevel) throws IOException { protected void scan(int scanDetailLevel) throws IOException {
for (; for (;
thisTerm != null && StringHelper.startsWith(thisTerm, curVNodeTerm); thisTerm != null && StringHelper.startsWith(thisTerm, curVNodeTerm);
thisTerm = termsEnum.next()) { thisTerm = termsEnum.next()) {
scanCell = grid.getNode(thisTerm.bytes, thisTerm.offset, thisTerm.length, scanCell); scanCell = grid.getCell(thisTerm.bytes, thisTerm.offset, thisTerm.length, scanCell);
int termLevel = scanCell.getLevel(); int termLevel = scanCell.getLevel();
if (termLevel > scanDetailLevel) if (termLevel > scanDetailLevel)
@ -276,10 +276,10 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
/** Used for {@link VNode#children}. */ /** Used for {@link VNode#children}. */
private class VNodeCellIterator implements Iterator<VNode> { private class VNodeCellIterator implements Iterator<VNode> {
final Iterator<Node> cellIter; final Iterator<Cell> cellIter;
private final VNode vNode; private final VNode vNode;
VNodeCellIterator(Iterator<Node> cellIter, VNode vNode) { VNodeCellIterator(Iterator<Cell> cellIter, VNode vNode) {
this.cellIter = cellIter; this.cellIter = cellIter;
this.vNode = vNode; this.vNode = vNode;
} }
@ -309,26 +309,26 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
/** /**
* Visit an indexed cell returned from * Visit an indexed cell returned from
* {@link #findSubCellsToVisit(org.apache.lucene.spatial.prefix.tree.Node)}. * {@link #findSubCellsToVisit(org.apache.lucene.spatial.prefix.tree.Cell)}.
* *
* @param cell An intersecting cell. * @param cell An intersecting cell.
* @return true to descend to more levels. It is an error to return true * @return true to descend to more levels. It is an error to return true
* if cell.level == detailLevel * if cell.level == detailLevel
*/ */
protected abstract boolean visit(Node cell) throws IOException; protected abstract boolean visit(Cell cell) throws IOException;
/** /**
* Called after visit() returns true and an indexed leaf cell is found. An * Called after visit() returns true and an indexed leaf cell is found. An
* indexed leaf cell means associated documents generally won't be found at * indexed leaf cell means associated documents generally won't be found at
* further detail levels. * further detail levels.
*/ */
protected abstract void visitLeaf(Node cell) throws IOException; protected abstract void visitLeaf(Cell cell) throws IOException;
/** /**
* The cell is either indexed as a leaf or is the last level of detail. It * The cell is either indexed as a leaf or is the last level of detail. It
* might not even intersect the query shape, so be sure to check for that. * might not even intersect the query shape, so be sure to check for that.
*/ */
protected abstract void visitScanned(Node cell) throws IOException; protected abstract void visitScanned(Cell cell) throws IOException;
protected void preSiblings(VNode vNode) throws IOException { protected void preSiblings(VNode vNode) throws IOException {
@ -339,7 +339,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
}//class VisitorTemplate }//class VisitorTemplate
/** /**
* A Visitor Node/Cell found via the query shape for {@link VisitorTemplate}. * A Visitor Cell/Cell found via the query shape for {@link VisitorTemplate}.
* Sometimes these are reset(cell). It's like a LinkedList node but forms a * Sometimes these are reset(cell). It's like a LinkedList node but forms a
* tree. * tree.
* *
@ -353,7 +353,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
final VNode parent;//only null at the root final VNode parent;//only null at the root
Iterator<VNode> children;//null, then sometimes set, then null Iterator<VNode> children;//null, then sometimes set, then null
Node cell;//not null (except initially before reset()) Cell cell;//not null (except initially before reset())
/** /**
* call reset(cell) after to set the cell. * call reset(cell) after to set the cell.
@ -362,7 +362,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
this.parent = parent; this.parent = parent;
} }
void reset(Node cell) { void reset(Cell cell) {
assert cell != null; assert cell != null;
this.cell = cell; this.cell = cell;
assert children == null; assert children == null;

View File

@ -21,7 +21,7 @@ import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.SpatialRelation; import com.spatial4j.core.shape.SpatialRelation;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -66,7 +66,7 @@ public class IntersectsPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter
} }
@Override @Override
protected boolean visit(Node cell) throws IOException { protected boolean visit(Cell cell) throws IOException {
if (cell.getShapeRel() == SpatialRelation.WITHIN || cell.getLevel() == detailLevel) { if (cell.getShapeRel() == SpatialRelation.WITHIN || cell.getLevel() == detailLevel) {
collectDocs(results); collectDocs(results);
return false; return false;
@ -75,12 +75,12 @@ public class IntersectsPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter
} }
@Override @Override
protected void visitLeaf(Node cell) throws IOException { protected void visitLeaf(Cell cell) throws IOException {
collectDocs(results); collectDocs(results);
} }
@Override @Override
protected void visitScanned(Node cell) throws IOException { protected void visitScanned(Cell cell) throws IOException {
Shape cShape; Shape cShape;
//if this cell represents a point, use the cell center vs the box //if this cell represents a point, use the cell center vs the box
// TODO this behavior is debatable; might want to be configurable // TODO this behavior is debatable; might want to be configurable

View File

@ -1,3 +1,5 @@
package org.apache.lucene.spatial.prefix;
/* /*
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
@ -15,10 +17,8 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.lucene.spatial.prefix;
import com.spatial4j.core.shape.Point; import com.spatial4j.core.shape.Point;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.util.ShapeFieldCacheProvider; import org.apache.lucene.spatial.util.ShapeFieldCacheProvider;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
@ -40,11 +40,11 @@ public class PointPrefixTreeFieldCacheProvider extends ShapeFieldCacheProvider<P
this.grid = grid; this.grid = grid;
} }
private Node scanCell = null;//re-used in readShape to save GC private Cell scanCell = null;//re-used in readShape to save GC
@Override @Override
protected Point readShape(BytesRef term) { protected Point readShape(BytesRef term) {
scanCell = grid.getNode(term.bytes, term.offset, term.length, scanCell); scanCell = grid.getCell(term.bytes, term.offset, term.length, scanCell);
if (scanCell.getLevel() == grid.getMaxLevels() && !scanCell.isLeaf()) if (scanCell.getLevel() == grid.getMaxLevels() && !scanCell.isLeaf())
return scanCell.getCenter(); return scanCell.getCenter();
return null; return null;

View File

@ -26,7 +26,7 @@ import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.FieldInfo; import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.spatial.SpatialStrategy; import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.util.ShapeFieldCacheDistanceValueSource; import org.apache.lucene.spatial.util.ShapeFieldCacheDistanceValueSource;
@ -125,7 +125,7 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
public Field[] createIndexableFields(Shape shape, double distErr) { public Field[] createIndexableFields(Shape shape, double distErr) {
int detailLevel = grid.getLevelForDistance(distErr); int detailLevel = grid.getLevelForDistance(distErr);
List<Node> cells = grid.getNodes(shape, detailLevel, true, simplifyIndexedCells);//intermediates cells List<Cell> cells = grid.getCells(shape, detailLevel, true, simplifyIndexedCells);//intermediates cells
//TODO is CellTokenStream supposed to be re-used somehow? see Uwe's comments: //TODO is CellTokenStream supposed to be re-used somehow? see Uwe's comments:
// http://code.google.com/p/lucene-spatial-playground/issues/detail?id=4 // http://code.google.com/p/lucene-spatial-playground/issues/detail?id=4
@ -151,9 +151,9 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
private Iterator<Node> iter = null; private Iterator<Cell> iter = null;
public CellTokenStream(Iterator<Node> tokens) { public CellTokenStream(Iterator<Cell> tokens) {
this.iter = tokens; this.iter = tokens;
} }
@ -164,12 +164,12 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
clearAttributes(); clearAttributes();
if (nextTokenStringNeedingLeaf != null) { if (nextTokenStringNeedingLeaf != null) {
termAtt.append(nextTokenStringNeedingLeaf); termAtt.append(nextTokenStringNeedingLeaf);
termAtt.append((char) Node.LEAF_BYTE); termAtt.append((char) Cell.LEAF_BYTE);
nextTokenStringNeedingLeaf = null; nextTokenStringNeedingLeaf = null;
return true; return true;
} }
if (iter.hasNext()) { if (iter.hasNext()) {
Node cell = iter.next(); Cell cell = iter.next();
CharSequence token = cell.getTokenString(); CharSequence token = cell.getTokenString();
termAtt.append(token); termAtt.append(token);
if (cell.isLeaf()) if (cell.isLeaf())

View File

@ -20,7 +20,7 @@ package org.apache.lucene.spatial.prefix;
import com.spatial4j.core.shape.Shape; import com.spatial4j.core.shape.Shape;
import org.apache.lucene.queries.TermsFilter; import org.apache.lucene.queries.TermsFilter;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation; import org.apache.lucene.spatial.query.SpatialOperation;
@ -31,7 +31,7 @@ import java.util.List;
/** /**
* A basic implementation of {@link PrefixTreeStrategy} using a large {@link * A basic implementation of {@link PrefixTreeStrategy} using a large {@link
* TermsFilter} of all the nodes from {@link SpatialPrefixTree#getNodes(com.spatial4j.core.shape.Shape, * TermsFilter} of all the cells from {@link SpatialPrefixTree#getCells(com.spatial4j.core.shape.Shape,
* int, boolean, boolean)}. It only supports the search of indexed Point shapes. * int, boolean, boolean)}. It only supports the search of indexed Point shapes.
* <p/> * <p/>
* The precision of query shapes (distErrPct) is an important factor in using * The precision of query shapes (distErrPct) is an important factor in using
@ -55,12 +55,12 @@ public class TermQueryPrefixTreeStrategy extends PrefixTreeStrategy {
Shape shape = args.getShape(); Shape shape = args.getShape();
int detailLevel = grid.getLevelForDistance(args.resolveDistErr(ctx, distErrPct)); int detailLevel = grid.getLevelForDistance(args.resolveDistErr(ctx, distErrPct));
List<Node> cells = grid.getNodes(shape, detailLevel, List<Cell> cells = grid.getCells(shape, detailLevel,
false,//no parents false,//no parents
true);//simplify true);//simplify
BytesRef[] terms = new BytesRef[cells.size()]; BytesRef[] terms = new BytesRef[cells.size()];
int i = 0; int i = 0;
for (Node cell : cells) { for (Cell cell : cells) {
terms[i++] = new BytesRef(cell.getTokenString()); terms[i++] = new BytesRef(cell.getTokenString());
} }
return new TermsFilter(getFieldName(), terms); return new TermsFilter(getFieldName(), terms);

View File

@ -26,7 +26,7 @@ import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.SpatialRelation; import com.spatial4j.core.shape.SpatialRelation;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -135,13 +135,13 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
} }
@Override @Override
protected Iterator<Node> findSubCellsToVisit(Node cell) { protected Iterator<Cell> findSubCellsToVisit(Cell cell) {
//use buffered query shape instead of orig. Works with null too. //use buffered query shape instead of orig. Works with null too.
return cell.getSubCells(bufferedQueryShape).iterator(); return cell.getSubCells(bufferedQueryShape).iterator();
} }
@Override @Override
protected boolean visit(Node cell) throws IOException { protected boolean visit(Cell cell) throws IOException {
//cell.relate is based on the bufferedQueryShape; we need to examine what //cell.relate is based on the bufferedQueryShape; we need to examine what
// the relation is against the queryShape // the relation is against the queryShape
visitRelation = cell.getShape().relate(queryShape); visitRelation = cell.getShape().relate(queryShape);
@ -159,7 +159,7 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
} }
@Override @Override
protected void visitLeaf(Node cell) throws IOException { protected void visitLeaf(Cell cell) throws IOException {
SpatialRelation relation = visitRelation; SpatialRelation relation = visitRelation;
assert visitRelation == cell.getShape().relate(queryShape); assert visitRelation == cell.getShape().relate(queryShape);
if (relation.intersects()) { if (relation.intersects()) {
@ -170,7 +170,7 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
} }
@Override @Override
protected void visitScanned(Node cell) throws IOException { protected void visitScanned(Cell cell) throws IOException {
if (queryShape.relate(cell.getShape()).intersects()) { if (queryShape.relate(cell.getShape()).intersects()) {
collectDocs(inside); collectDocs(inside);
} else { } else {

View File

@ -32,7 +32,7 @@ import java.util.List;
* *
* @lucene.experimental * @lucene.experimental
*/ */
public abstract class Node implements Comparable<Node> { public abstract class Cell implements Comparable<Cell> {
public static final byte LEAF_BYTE = '+';//NOTE: must sort before letters & numbers public static final byte LEAF_BYTE = '+';//NOTE: must sort before letters & numbers
/* /*
@ -58,7 +58,7 @@ public abstract class Node implements Comparable<Node> {
*/ */
protected boolean leaf; protected boolean leaf;
protected Node(String token) { protected Cell(String token) {
this.token = token; this.token = token;
if (token.length() > 0 && token.charAt(token.length() - 1) == (char) LEAF_BYTE) { if (token.length() > 0 && token.charAt(token.length() - 1) == (char) LEAF_BYTE) {
this.token = token.substring(0, token.length() - 1); this.token = token.substring(0, token.length() - 1);
@ -69,7 +69,7 @@ public abstract class Node implements Comparable<Node> {
getShape();//ensure any lazy instantiation completes to make this threadsafe getShape();//ensure any lazy instantiation completes to make this threadsafe
} }
protected Node(byte[] bytes, int off, int len) { protected Cell(byte[] bytes, int off, int len) {
this.bytes = bytes; this.bytes = bytes;
this.b_off = off; this.b_off = off;
this.b_len = len; this.b_len = len;
@ -149,8 +149,8 @@ public abstract class Node implements Comparable<Node> {
* Like {@link #getSubCells()} but with the results filtered by a shape. If * Like {@link #getSubCells()} but with the results filtered by a shape. If
* that shape is a {@link com.spatial4j.core.shape.Point} then it must call * that shape is a {@link com.spatial4j.core.shape.Point} then it must call
* {@link #getSubCell(com.spatial4j.core.shape.Point)}. The returned cells * {@link #getSubCell(com.spatial4j.core.shape.Point)}. The returned cells
* should have {@link Node#getShapeRel()} set to their relation with {@code * should have {@link Cell#getShapeRel()} set to their relation with {@code
* shapeFilter}. In addition, {@link org.apache.lucene.spatial.prefix.tree.Node#isLeaf()} * shapeFilter}. In addition, {@link Cell#isLeaf()}
* must be true when that relation is WITHIN. * must be true when that relation is WITHIN.
* <p/> * <p/>
* Precondition: Never called when getLevel() == maxLevel. * Precondition: Never called when getLevel() == maxLevel.
@ -158,22 +158,22 @@ public abstract class Node implements Comparable<Node> {
* @param shapeFilter an optional filter for the returned cells. * @param shapeFilter an optional filter for the returned cells.
* @return A set of cells (no dups), sorted. Not Modifiable. * @return A set of cells (no dups), sorted. Not Modifiable.
*/ */
public Collection<Node> getSubCells(Shape shapeFilter) { public Collection<Cell> getSubCells(Shape shapeFilter) {
//Note: Higher-performing subclasses might override to consider the shape filter to generate fewer cells. //Note: Higher-performing subclasses might override to consider the shape filter to generate fewer cells.
if (shapeFilter instanceof Point) { if (shapeFilter instanceof Point) {
Node subCell = getSubCell((Point) shapeFilter); Cell subCell = getSubCell((Point) shapeFilter);
subCell.shapeRel = SpatialRelation.CONTAINS; subCell.shapeRel = SpatialRelation.CONTAINS;
return Collections.singletonList(subCell); return Collections.singletonList(subCell);
} }
Collection<Node> cells = getSubCells(); Collection<Cell> cells = getSubCells();
if (shapeFilter == null) { if (shapeFilter == null) {
return cells; return cells;
} }
//TODO change API to return a filtering iterator //TODO change API to return a filtering iterator
List<Node> copy = new ArrayList<Node>(cells.size()); List<Cell> copy = new ArrayList<Cell>(cells.size());
for (Node cell : cells) { for (Cell cell : cells) {
SpatialRelation rel = cell.getShape().relate(shapeFilter); SpatialRelation rel = cell.getShape().relate(shapeFilter);
if (rel == SpatialRelation.DISJOINT) if (rel == SpatialRelation.DISJOINT)
continue; continue;
@ -192,7 +192,7 @@ public abstract class Node implements Comparable<Node> {
* <p/> * <p/>
* Precondition: this.getShape().relate(p) != DISJOINT. * Precondition: this.getShape().relate(p) != DISJOINT.
*/ */
public abstract Node getSubCell(Point p); public abstract Cell getSubCell(Point p);
//TODO Cell getSubCell(byte b) //TODO Cell getSubCell(byte b)
@ -202,7 +202,7 @@ public abstract class Node implements Comparable<Node> {
* *
* @return A set of cells (no dups), sorted, modifiable, not empty, not null. * @return A set of cells (no dups), sorted, modifiable, not empty, not null.
*/ */
protected abstract Collection<Node> getSubCells(); protected abstract Collection<Cell> getSubCells();
/** /**
* {@link #getSubCells()}.size() -- usually a constant. Should be >=2 * {@link #getSubCells()}.size() -- usually a constant. Should be >=2
@ -216,13 +216,13 @@ public abstract class Node implements Comparable<Node> {
} }
@Override @Override
public int compareTo(Node o) { public int compareTo(Cell o) {
return getTokenString().compareTo(o.getTokenString()); return getTokenString().compareTo(o.getTokenString());
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
return !(obj == null || !(obj instanceof Node)) && getTokenString().equals(((Node) obj).getTokenString()); return !(obj == null || !(obj instanceof Cell)) && getTokenString().equals(((Cell) obj).getTokenString());
} }
@Override @Override

View File

@ -79,21 +79,21 @@ public class GeohashPrefixTree extends SpatialPrefixTree {
} }
@Override @Override
public Node getNode(Point p, int level) { public Cell getCell(Point p, int level) {
return new GhCell(GeohashUtils.encodeLatLon(p.getY(), p.getX(), level));//args are lat,lon (y,x) return new GhCell(GeohashUtils.encodeLatLon(p.getY(), p.getX(), level));//args are lat,lon (y,x)
} }
@Override @Override
public Node getNode(String token) { public Cell getCell(String token) {
return new GhCell(token); return new GhCell(token);
} }
@Override @Override
public Node getNode(byte[] bytes, int offset, int len) { public Cell getCell(byte[] bytes, int offset, int len) {
return new GhCell(bytes, offset, len); return new GhCell(bytes, offset, len);
} }
class GhCell extends Node { class GhCell extends Cell {
GhCell(String token) { GhCell(String token) {
super(token); super(token);
} }
@ -109,9 +109,9 @@ public class GeohashPrefixTree extends SpatialPrefixTree {
} }
@Override @Override
public Collection<Node> getSubCells() { public Collection<Cell> getSubCells() {
String[] hashes = GeohashUtils.getSubGeohashes(getGeohash());//sorted String[] hashes = GeohashUtils.getSubGeohashes(getGeohash());//sorted
List<Node> cells = new ArrayList<Node>(hashes.length); List<Cell> cells = new ArrayList<Cell>(hashes.length);
for (String hash : hashes) { for (String hash : hashes) {
cells.add(new GhCell(hash)); cells.add(new GhCell(hash));
} }
@ -124,8 +124,8 @@ public class GeohashPrefixTree extends SpatialPrefixTree {
} }
@Override @Override
public Node getSubCell(Point p) { public Cell getSubCell(Point p) {
return GeohashPrefixTree.this.getNode(p,getLevel()+1);//not performant! return GeohashPrefixTree.this.getCell(p, getLevel() + 1);//not performant!
} }
private Shape shape;//cache private Shape shape;//cache

View File

@ -33,7 +33,7 @@ import java.util.Locale;
/** /**
* A {@link SpatialPrefixTree} which uses a * A {@link SpatialPrefixTree} which uses a
* <a href="http://en.wikipedia.org/wiki/Quadtree">quad tree</a> in which an * <a href="http://en.wikipedia.org/wiki/Quadtree">quad tree</a> in which an
* indexed term will be generated for each node, 'A', 'B', 'C', 'D'. * indexed term will be generated for each cell, 'A', 'B', 'C', 'D'.
* *
* @lucene.experimental * @lucene.experimental
*/ */
@ -140,19 +140,19 @@ public class QuadPrefixTree extends SpatialPrefixTree {
} }
@Override @Override
public Node getNode(Point p, int level) { public Cell getCell(Point p, int level) {
List<Node> cells = new ArrayList<Node>(1); List<Cell> cells = new ArrayList<Cell>(1);
build(xmid, ymid, 0, cells, new StringBuilder(), ctx.makePoint(p.getX(),p.getY()), level); build(xmid, ymid, 0, cells, new StringBuilder(), ctx.makePoint(p.getX(),p.getY()), level);
return cells.get(0);//note cells could be longer if p on edge return cells.get(0);//note cells could be longer if p on edge
} }
@Override @Override
public Node getNode(String token) { public Cell getCell(String token) {
return new QuadCell(token); return new QuadCell(token);
} }
@Override @Override
public Node getNode(byte[] bytes, int offset, int len) { public Cell getCell(byte[] bytes, int offset, int len) {
return new QuadCell(bytes, offset, len); return new QuadCell(bytes, offset, len);
} }
@ -160,7 +160,7 @@ public class QuadPrefixTree extends SpatialPrefixTree {
double x, double x,
double y, double y,
int level, int level,
List<Node> matches, List<Cell> matches,
StringBuilder str, StringBuilder str,
Shape shape, Shape shape,
int maxLevel) { int maxLevel) {
@ -186,7 +186,7 @@ public class QuadPrefixTree extends SpatialPrefixTree {
double cx, double cx,
double cy, double cy,
int level, int level,
List<Node> matches, List<Cell> matches,
StringBuilder str, StringBuilder str,
Shape shape, Shape shape,
int maxLevel) { int maxLevel) {
@ -217,7 +217,7 @@ public class QuadPrefixTree extends SpatialPrefixTree {
str.setLength(strlen); str.setLength(strlen);
} }
class QuadCell extends Node { class QuadCell extends Cell {
public QuadCell(String token) { public QuadCell(String token) {
super(token); super(token);
@ -239,8 +239,8 @@ public class QuadPrefixTree extends SpatialPrefixTree {
} }
@Override @Override
public Collection<Node> getSubCells() { public Collection<Cell> getSubCells() {
List<Node> cells = new ArrayList<Node>(4); List<Cell> cells = new ArrayList<Cell>(4);
cells.add(new QuadCell(getTokenString()+"A")); cells.add(new QuadCell(getTokenString()+"A"));
cells.add(new QuadCell(getTokenString()+"B")); cells.add(new QuadCell(getTokenString()+"B"));
cells.add(new QuadCell(getTokenString()+"C")); cells.add(new QuadCell(getTokenString()+"C"));
@ -254,8 +254,8 @@ public class QuadPrefixTree extends SpatialPrefixTree {
} }
@Override @Override
public Node getSubCell(Point p) { public Cell getSubCell(Point p) {
return QuadPrefixTree.this.getNode(p,getLevel()+1);//not performant! return QuadPrefixTree.this.getCell(p, getLevel() + 1);//not performant!
} }
private Shape shape;//cache private Shape shape;//cache

View File

@ -79,8 +79,8 @@ public abstract class SpatialPrefixTree {
public abstract int getLevelForDistance(double dist); public abstract int getLevelForDistance(double dist);
/** /**
* Given a node having the specified level, returns the distance from opposite * Given a cell having the specified level, returns the distance from opposite
* corners. Since this might very depending on where the node is, this method * corners. Since this might very depending on where the cell is, this method
* may over-estimate. * may over-estimate.
* *
* @param level [1 to maxLevels] * @param level [1 to maxLevels]
@ -90,8 +90,8 @@ public abstract class SpatialPrefixTree {
if (level < 1 || level > getMaxLevels()) if (level < 1 || level > getMaxLevels())
throw new IllegalArgumentException("Level must be in 1 to maxLevels range"); throw new IllegalArgumentException("Level must be in 1 to maxLevels range");
//TODO cache for each level //TODO cache for each level
Node node = getNode(ctx.getWorldBounds().getCenter(), level); Cell cell = getCell(ctx.getWorldBounds().getCenter(), level);
Rectangle bbox = node.getShape().getBoundingBox(); Rectangle bbox = cell.getShape().getBoundingBox();
double width = bbox.getWidth(); double width = bbox.getWidth();
double height = bbox.getHeight(); double height = bbox.getHeight();
//Use standard cartesian hypotenuse. For geospatial, this answer is larger //Use standard cartesian hypotenuse. For geospatial, this answer is larger
@ -99,32 +99,32 @@ public abstract class SpatialPrefixTree {
return Math.sqrt(width * width + height * height); return Math.sqrt(width * width + height * height);
} }
private transient Node worldNode;//cached private transient Cell worldCell;//cached
/** /**
* Returns the level 0 cell which encompasses all spatial data. Equivalent to {@link #getNode(String)} with "". * Returns the level 0 cell which encompasses all spatial data. Equivalent to {@link #getCell(String)} with "".
* This cell is threadsafe, just like a spatial prefix grid is, although cells aren't * This cell is threadsafe, just like a spatial prefix grid is, although cells aren't
* generally threadsafe. * generally threadsafe.
* TODO rename to getTopCell or is this fine? * TODO rename to getTopCell or is this fine?
*/ */
public Node getWorldNode() { public Cell getWorldCell() {
if (worldNode == null) { if (worldCell == null) {
worldNode = getNode(""); worldCell = getCell("");
} }
return worldNode; return worldCell;
} }
/** /**
* The cell for the specified token. The empty string should be equal to {@link #getWorldNode()}. * The cell for the specified token. The empty string should be equal to {@link #getWorldCell()}.
* Precondition: Never called when token length > maxLevel. * Precondition: Never called when token length > maxLevel.
*/ */
public abstract Node getNode(String token); public abstract Cell getCell(String token);
public abstract Node getNode(byte[] bytes, int offset, int len); public abstract Cell getCell(byte[] bytes, int offset, int len);
public final Node getNode(byte[] bytes, int offset, int len, Node target) { public final Cell getCell(byte[] bytes, int offset, int len, Cell target) {
if (target == null) { if (target == null) {
return getNode(bytes, offset, len); return getCell(bytes, offset, len);
} }
target.reset(bytes, offset, len); target.reset(bytes, offset, len);
@ -134,8 +134,8 @@ public abstract class SpatialPrefixTree {
/** /**
* Returns the cell containing point {@code p} at the specified {@code level}. * Returns the cell containing point {@code p} at the specified {@code level}.
*/ */
protected Node getNode(Point p, int level) { protected Cell getCell(Point p, int level) {
return getNodes(p, level, false).get(0); return getCells(p, level, false).get(0);
} }
/** /**
@ -144,7 +144,7 @@ public abstract class SpatialPrefixTree {
* leaf and none of its children are added. * leaf and none of its children are added.
* <p/> * <p/>
* This implementation checks if shape is a Point and if so returns {@link * This implementation checks if shape is a Point and if so returns {@link
* #getNodes(com.spatial4j.core.shape.Point, int, boolean)}. * #getCells(com.spatial4j.core.shape.Point, int, boolean)}.
* *
* @param shape the shape; non-null * @param shape the shape; non-null
* @param detailLevel the maximum detail level to get cells for * @param detailLevel the maximum detail level to get cells for
@ -155,45 +155,45 @@ public abstract class SpatialPrefixTree {
* ~20-25% fewer cells. * ~20-25% fewer cells.
* @return a set of cells (no dups), sorted, immutable, non-null * @return a set of cells (no dups), sorted, immutable, non-null
*/ */
public List<Node> getNodes(Shape shape, int detailLevel, boolean inclParents, public List<Cell> getCells(Shape shape, int detailLevel, boolean inclParents,
boolean simplify) { boolean simplify) {
//TODO consider an on-demand iterator -- it won't build up all cells in memory. //TODO consider an on-demand iterator -- it won't build up all cells in memory.
if (detailLevel > maxLevels) { if (detailLevel > maxLevels) {
throw new IllegalArgumentException("detailLevel > maxLevels"); throw new IllegalArgumentException("detailLevel > maxLevels");
} }
if (shape instanceof Point) { if (shape instanceof Point) {
return getNodes((Point) shape, detailLevel, inclParents); return getCells((Point) shape, detailLevel, inclParents);
} }
List<Node> cells = new ArrayList<Node>(inclParents ? 4096 : 2048); List<Cell> cells = new ArrayList<Cell>(inclParents ? 4096 : 2048);
recursiveGetNodes(getWorldNode(), shape, detailLevel, inclParents, simplify, cells); recursiveGetCells(getWorldCell(), shape, detailLevel, inclParents, simplify, cells);
return cells; return cells;
} }
/** /**
* Returns true if node was added as a leaf. If it wasn't it recursively * Returns true if cell was added as a leaf. If it wasn't it recursively
* descends. * descends.
*/ */
private boolean recursiveGetNodes(Node node, Shape shape, int detailLevel, private boolean recursiveGetCells(Cell cell, Shape shape, int detailLevel,
boolean inclParents, boolean simplify, boolean inclParents, boolean simplify,
List<Node> result) { List<Cell> result) {
if (node.getLevel() == detailLevel) { if (cell.getLevel() == detailLevel) {
node.setLeaf();//FYI might already be a leaf cell.setLeaf();//FYI might already be a leaf
} }
if (node.isLeaf()) { if (cell.isLeaf()) {
result.add(node); result.add(cell);
return true; return true;
} }
if (inclParents && node.getLevel() != 0) if (inclParents && cell.getLevel() != 0)
result.add(node); result.add(cell);
Collection<Node> subCells = node.getSubCells(shape); Collection<Cell> subCells = cell.getSubCells(shape);
int leaves = 0; int leaves = 0;
for (Node subCell : subCells) { for (Cell subCell : subCells) {
if (recursiveGetNodes(subCell, shape, detailLevel, inclParents, simplify, result)) if (recursiveGetCells(subCell, shape, detailLevel, inclParents, simplify, result))
leaves++; leaves++;
} }
//can we simplify? //can we simplify?
if (simplify && leaves == node.getSubCellsSize() && node.getLevel() != 0) { if (simplify && leaves == cell.getSubCellsSize() && cell.getLevel() != 0) {
//Optimization: substitute the parent as a leaf instead of adding all //Optimization: substitute the parent as a leaf instead of adding all
// children as leaves // children as leaves
@ -201,10 +201,10 @@ public abstract class SpatialPrefixTree {
do { do {
result.remove(result.size() - 1);//remove last result.remove(result.size() - 1);//remove last
} while (--leaves > 0); } while (--leaves > 0);
//add node as the leaf //add cell as the leaf
node.setLeaf(); cell.setLeaf();
if (!inclParents) // otherwise it was already added up above if (!inclParents) // otherwise it was already added up above
result.add(node); result.add(cell);
return true; return true;
} }
return false; return false;
@ -212,23 +212,23 @@ public abstract class SpatialPrefixTree {
/** /**
* A Point-optimized implementation of * A Point-optimized implementation of
* {@link #getNodes(com.spatial4j.core.shape.Shape, int, boolean, boolean)}. That * {@link #getCells(com.spatial4j.core.shape.Shape, int, boolean, boolean)}. That
* method in facts calls this for points. * method in facts calls this for points.
* <p/> * <p/>
* This implementation depends on {@link #getNode(String)} being fast, as its * This implementation depends on {@link #getCell(String)} being fast, as its
* called repeatedly when incPlarents is true. * called repeatedly when incPlarents is true.
*/ */
public List<Node> getNodes(Point p, int detailLevel, boolean inclParents) { public List<Cell> getCells(Point p, int detailLevel, boolean inclParents) {
Node cell = getNode(p, detailLevel); Cell cell = getCell(p, detailLevel);
if (!inclParents) { if (!inclParents) {
return Collections.singletonList(cell); return Collections.singletonList(cell);
} }
String endToken = cell.getTokenString(); String endToken = cell.getTokenString();
assert endToken.length() == detailLevel; assert endToken.length() == detailLevel;
List<Node> cells = new ArrayList<Node>(detailLevel); List<Cell> cells = new ArrayList<Cell>(detailLevel);
for (int i = 1; i < detailLevel; i++) { for (int i = 1; i < detailLevel; i++) {
cells.add(getNode(endToken.substring(0, i))); cells.add(getCell(endToken.substring(0, i)));
} }
cells.add(cell); cells.add(cell);
return cells; return cells;
@ -237,12 +237,12 @@ public abstract class SpatialPrefixTree {
/** /**
* Will add the trailing leaf byte for leaves. This isn't particularly efficient. * Will add the trailing leaf byte for leaves. This isn't particularly efficient.
*/ */
public static List<String> nodesToTokenStrings(Collection<Node> nodes) { public static List<String> cellsToTokenStrings(Collection<Cell> cells) {
List<String> tokens = new ArrayList<String>((nodes.size())); List<String> tokens = new ArrayList<String>((cells.size()));
for (Node node : nodes) { for (Cell cell : cells) {
final String token = node.getTokenString(); final String token = cell.getTokenString();
if (node.isLeaf()) { if (cell.isLeaf()) {
tokens.add(token + (char) Node.LEAF_BYTE); tokens.add(token + (char) Cell.LEAF_BYTE);
} else { } else {
tokens.add(token); tokens.add(token);
} }

View File

@ -26,7 +26,7 @@ import com.spatial4j.core.shape.SpatialRelation;
import com.spatial4j.core.shape.impl.RectangleImpl; import com.spatial4j.core.shape.impl.RectangleImpl;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.StrategyTestCase; import org.apache.lucene.spatial.StrategyTestCase;
import org.apache.lucene.spatial.prefix.tree.Node; import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree; import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialArgs;
@ -164,12 +164,12 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
double distErrPct = ((PrefixTreeStrategy) strategy).getDistErrPct(); double distErrPct = ((PrefixTreeStrategy) strategy).getDistErrPct();
double distErr = SpatialArgs.calcDistanceFromErrPct(snapMe, distErrPct, ctx); double distErr = SpatialArgs.calcDistanceFromErrPct(snapMe, distErrPct, ctx);
int detailLevel = grid.getLevelForDistance(distErr); int detailLevel = grid.getLevelForDistance(distErr);
List<Node> cells = grid.getNodes(snapMe, detailLevel, false, true); List<Cell> cells = grid.getCells(snapMe, detailLevel, false, true);
//calc bounding box of cells. //calc bounding box of cells.
double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY; double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY; double minY = Double.POSITIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY;
for (Node cell : cells) { for (Cell cell : cells) {
assert cell.getLevel() <= detailLevel; assert cell.getLevel() <= detailLevel;
Rectangle cellR = cell.getShape().getBoundingBox(); Rectangle cellR = cell.getShape().getBoundingBox();

View File

@ -1,3 +1,5 @@
package org.apache.lucene.spatial.prefix.tree;
/* /*
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
@ -15,8 +17,6 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.lucene.spatial.prefix.tree;
import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.shape.Point; import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle; import com.spatial4j.core.shape.Rectangle;
@ -49,20 +49,20 @@ public class SpatialPrefixTreeTest extends SpatialTestCase {
} }
@Test @Test
public void testNodeTraverse() { public void testCellTraverse() {
trie = new GeohashPrefixTree(ctx,4); trie = new GeohashPrefixTree(ctx,4);
Node prevN = null; Cell prevC = null;
Node n = trie.getWorldNode(); Cell c = trie.getWorldCell();
assertEquals(0,n.getLevel()); assertEquals(0, c.getLevel());
assertEquals(ctx.getWorldBounds(),n.getShape()); assertEquals(ctx.getWorldBounds(), c.getShape());
while(n.getLevel() < trie.getMaxLevels()) { while(c.getLevel() < trie.getMaxLevels()) {
prevN = n; prevC = c;
n = n.getSubCells().iterator().next();//TODO random which one? c = c.getSubCells().iterator().next();//TODO random which one?
assertEquals(prevN.getLevel()+1,n.getLevel()); assertEquals(prevC.getLevel()+1,c.getLevel());
Rectangle prevNShape = (Rectangle) prevN.getShape(); Rectangle prevNShape = (Rectangle) prevC.getShape();
Shape s = n.getShape(); Shape s = c.getShape();
Rectangle sbox = s.getBoundingBox(); Rectangle sbox = s.getBoundingBox();
assertTrue(prevNShape.getWidth() > sbox.getWidth()); assertTrue(prevNShape.getWidth() > sbox.getWidth());
assertTrue(prevNShape.getHeight() > sbox.getHeight()); assertTrue(prevNShape.getHeight() > sbox.getHeight());