/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.index.quadtree;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;
import org.geotools.data.shapefile.shp.IndexFile;
import org.geotools.index.Data;
import org.geotools.index.DataDefinition;
import com.vividsolutions.jts.geom.Envelope;
/**
* Iterator that search the quad tree depth first. 32000 indices are cached at a
* time and each time a node is visited the indices are removed from the node so
* that the memory footprint is kept small. Note that if other iterators operate
* on the same tree then they can interfere with each other.
*
* @author Jesse
*
* @source $URL$
*/
public class LazySearchIterator implements Iterator<Data> {
static final DataDefinition DATA_DEFINITION = new DataDefinition("US-ASCII");
private static final int MAX_INDICES = 32768;
static {
DATA_DEFINITION.addField(Integer.class);
DATA_DEFINITION.addField(Long.class);
}
Data next = null;
Node current;
int idIndex = 0;
private boolean closed;
private Envelope bounds;
Iterator data;
private IndexFile indexfile;
ArrayList<Node> parents = new ArrayList<Node>();
public LazySearchIterator(Node root, IndexFile indexfile, Envelope bounds) {
super();
this.current = root;
this.bounds = bounds;
this.closed = false;
this.next = null;
this.indexfile = indexfile;
}
public boolean hasNext() {
if (closed)
throw new IllegalStateException("Iterator has been closed!");
if (next != null)
return true;
if (data != null && data.hasNext()) {
next = (Data) data.next();
} else {
data = null;
fillCache();
if (data != null && data.hasNext())
next = (Data) data.next();
}
return next != null;
}
private void fillCache() {
List indices = new ArrayList(MAX_INDICES);
ArrayList dataList = null;
try {
while (indices.size() < MAX_INDICES && current != null) {
if (idIndex < current.getNumShapeIds() && !current.isVisited()
&& current.getBounds().intersects(bounds)) {
indices.add(new Integer(current.getShapeId(idIndex)));
idIndex++;
} else {
// free the shapes id array of the current node and prepare to move to the next
current.setShapesId(new int[0]);
idIndex = 0;
boolean foundUnvisited = false;
for (int i = 0; i < current.getNumSubNodes(); i++) {
Node node = current.getSubNode(i);
if (!node.isVisited()
&& node.getBounds().intersects(bounds)) {
foundUnvisited = true;
parents.add(current);
current = node;
break;
}
}
if (!foundUnvisited) {
// mark as visited and free the subnodes
current.setVisited(true);
current.clearSubNodes();
// move up to parent
if(parents.isEmpty())
current = null;
else
current = parents.remove(parents.size() - 1);
}
}
}
// sort so offset lookup is faster
Collections.sort(indices);
dataList = new ArrayList(indices.size());
for (Iterator iter = indices.iterator(); iter.hasNext();) {
Integer recno = (Integer) iter.next();
Data data = new Data(DATA_DEFINITION);
data.addValue(new Integer(recno.intValue() + 1));
data.addValue(new Long(indexfile.getOffsetInBytes(recno
.intValue())));
dataList.add(data);
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (StoreException e) {
throw new RuntimeException(e);
}
data = dataList.iterator();
}
public Data next() {
if (!hasNext())
throw new NoSuchElementException("No more elements available");
Data temp = next;
next = null;
return temp;
}
public void remove() {
throw new UnsupportedOperationException();
}
public void close() throws StoreException {
this.closed = true;
}
}