⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtree.java

📁 shape file read and write
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2003-2006, GeoTools Project Managment Committee (PMC)
 *    (C) 2002, Centre for Computational Geography
 *
 *    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.rtree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geotools.filter.visitor.ExtractBoundsFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.index.Data;
import org.geotools.index.DataDefinition;
import org.geotools.index.Lock;
import org.geotools.index.LockTimeoutException;
import org.geotools.index.TreeException;
import org.geotools.index.UnsupportedFilterException;
import org.opengis.filter.Filter;

import com.vividsolutions.jts.geom.Envelope;

/**
 * Relational index.
 * 
 * @author Tommaso Nolli
 * @source $URL:
 *         http://svn.geotools.org/geotools/trunk/gt/modules/plugin/shapefile/src/main/java/org/geotools/index/rtree/RTree.java $
 */
public class RTree {
    private Logger logger = org.geotools.util.logging.Logging
            .getLogger("org.geotools.index.rtree");
    private PageStore store;

    public RTree(PageStore store) throws TreeException {
        this.store = store;
    }

    /**
     * Gets this index bounding box
     * 
     * @return An Envelope
     * 
     * @throws TreeException
     *                 DOCUMENT ME!
     */
    public Envelope getBounds() throws TreeException {
        this.checkOpen();

        Node root = this.store.getRoot();

        return (root == null) ? null : root.getBounds();
    }

    /**
     * Returns the maxiumal boudns for the provided filter.
     * <p>
     * This method will try and produce a filter for the provided bounds, see
     * ExtractBoundsFilterVisitor.BOUNDS_VISITOR for details of generation.
     * 
     * @param filter
     * @throws TreeException
     * @throws UnsupportedFilterException
     *                 For Filter.EXCLUDES
     */
    public Envelope getBounds(Filter filter) throws TreeException,
            UnsupportedFilterException {
        this.checkOpen();

        Envelope env;
        env = (Envelope) filter.accept(
                ExtractBoundsFilterVisitor.BOUNDS_VISITOR,
                new ReferencedEnvelope());

        if (env == null || env.isNull()) {
            throw new UnsupportedFilterException(
                    "Filter does not contains any Geometry");
        }

        Node root = this.store.getRoot();

        return env.contains(root.getBounds()) ? root.getBounds() : this
                .getBoundsInternal(env, root);
    }

    /*
     * public Envelope getBounds(Envelope env) {
     *  }
     */

    /**
     * DOCUMENT ME!
     * 
     * @param query
     * @param node
     * 
     * @return DOCUMENT ME!
     * 
     * @throws TreeException
     */
    private Envelope getBoundsInternal(final Envelope query, final Node node)
            throws TreeException {
        Envelope result = null;
        Entry entry = null;

        for (int i = 0; i < node.getEntriesCount(); i++) {
            entry = node.getEntry(i);

            if (entry.getBounds().intersects(query)) {
                if (node.isLeaf()) {
                    if (result == null) {
                        result = new Envelope(entry.getBounds());
                    } else {
                        result.expandToInclude(entry.getBounds());
                    }
                } else {
                    this.getBoundsInternal(query, this.store.getNode(entry,
                            node));
                }
            }
        }

        return result;
    }

    public DataDefinition getDataDefinition() {
        return this.store.getDataDefinition();
    }

    /**
     * Performs a search on this <code>RTree</code>
     * 
     * @param query
     *                the query <code>Envelope</code>
     * 
     * @return a <code>Collection</code> of <code>Data</code>
     * 
     * @throws TreeException
     *                 DOCUMENT ME!
     * @throws LockTimeoutException
     *                 DOCUMENT ME!
     */
    public List search(Envelope query) throws TreeException,
            LockTimeoutException {
        // Aquire a read lock
        Lock lock = this.store.getReadLock();
        List ret = null;

        try {
            ret = this.search(query, lock);
        } finally {
            // Release the lock
            this.store.releaseLock(lock);
        }

        return ret;
    }

    /**
     * Performs a search on this <code>RTree</code>
     * 
     * @param filter
     *                a <code>Filter</code>
     * 
     * @return a <code>Collection</code> of <code>Data</code>
     * 
     * @throws TreeException
     * @throws UnsupportedFilterException
     *                 DOCUMENT ME!
     * @throws LockTimeoutException
     *                 DOCUMENT ME!
     */
    public List search(Filter filter) throws TreeException,
            UnsupportedFilterException, LockTimeoutException {
        // Aquire a read lock
        Lock lock = this.store.getReadLock();
        List ret = null;

        try {
            Envelope env = (Envelope) filter.accept(
                    ExtractBoundsFilterVisitor.BOUNDS_VISITOR,
                    new ReferencedEnvelope());

            if (env == null || env.isNull()) {
                throw new UnsupportedFilterException("Not a valid filter");
            }
            ret = this.search(env, lock);
        } finally {
            // Release the lock
            this.store.releaseLock(lock);
        }

        return ret;
    }

    /**
     * Performs a search on the index
     * 
     * @param query
     *                The query <code>Envelope</code>
     * @param lock
     *                A <code>Lock</code> on the index
     * 
     * @return A <code>Collection</code> of <code>Data</code>
     * 
     * @throws TreeException
     * @throws LockTimeoutException
     */
    private List search(Envelope query, Lock lock) throws TreeException,
            LockTimeoutException {
        long start = System.currentTimeMillis();
        this.checkOpen();

        ArrayList matches = new ArrayList();

        Node root = this.store.getRoot();
        this.searchNode(query, root, matches);

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, matches.size()
                    + " Data objects retrieved in "
                    + (System.currentTimeMillis() - start) + "ms.");
        }

        return matches;
    }

    /**
     * DOCUMENT ME!
     * 
     * @param query
     * @param node
     * @param matches
     * 
     * @throws TreeException
     */
    private void searchNode(final Envelope query, final Node node,
            final ArrayList matches) throws TreeException {
        Entry entry = null;

        for (int i = 0; i < node.getEntriesCount(); i++) {
            entry = node.getEntry(i);

            if (entry.getBounds().intersects(query)) {
                if (node.isLeaf()) {
                    matches.add(entry.getData());
                } else {
                    searchNode(query, this.store.getNode(entry, node), matches);
                }
            }
        }
    }

    /**
     * DOCUMENT ME!
     * 
     * @param bounds
     * @param data
     * 
     * @throws TreeException
     * @throws LockTimeoutException
     */
    public void insert(Envelope bounds, Data data) throws TreeException,
            LockTimeoutException {
        if (!data.isValid()) {
            throw new TreeException("Invalid data supplied!");
        }

        // Aquire a write lock
        Lock lock = this.store.getWriteLock();

        try {
            this.insert(lock, new Entry(bounds, data));
        } finally {
            // Release the lock
            this.store.releaseLock(lock);
        }
    }

    /**
     * DOCUMENT ME!
     * 
     * @param lock
     * @param entry
     * 
     * @throws TreeException
     */
    private void insert(Lock lock, Entry entry) throws TreeException {
        this.checkOpen();

        // Get the right leaf
        Node leaf = this.chooseLeaf(this.store.getRoot(), entry);

        leaf.addEntry(entry);

        if (leaf.getEntriesCount() <= this.store.getMaxNodeEntries()) {
            // If leaf has room add to it
            leaf.save();
            this.adjustTree(leaf, null);
        } else {
            // Overflow...
            Node[] split = this.splitNode(leaf);
            this.adjustTree(split[0], split[1]);
        }
    }

    /**
     * DOCUMENT ME!
     * 
     * @param node
     * @param newEntry
     * 
     * 
     * @throws TreeException
     *                 DOCUMENT ME!
     */
    private Node chooseLeaf(Node node, Entry newEntry) throws TreeException {
        if (node.isLeaf()) {
            return node;
        }

        Collection entries = node.getEntries();

        // Find the best Entry
        Entry best = null;
        Envelope env = null;
        double lastArea = Double.POSITIVE_INFINITY;
        double currentArea = 0d;
        double w = 0;
        double h = 0;
        double nw = 0;
        double nh = 0;

        Entry element = null;

        for (Iterator iter = entries.iterator(); iter.hasNext();) {
            element = (Entry) iter.next();

            currentArea = this.getAreaIncrease(element.getBounds(), newEntry
                    .getBounds());

            if (currentArea < lastArea) {
                lastArea = currentArea;
                best = element;
            } else if ((currentArea == lastArea)
                    && (this.getEntryArea(best) > this.getEntryArea(element))) {
                best = element;
            }
        }

        // Now best is the best Entry
        return this.chooseLeaf(this.store.getNode(best, node), newEntry);
    }

    /**
     * DOCUMENT ME!
     * 
     * @param node
     * 
     * 
     * @throws TreeException
     */
    private Node[] splitNode(Node node) throws TreeException {
        Collection entriesTmp = node.getEntries();

        Entry[] e = (Entry[]) entriesTmp.toArray(new Entry[entriesTmp.size()]);
        Entry[] firsts = null;

        if (this.store.getSplitAlgorithm() == PageStore.SPLIT_QUADRATIC) {
            firsts = this.quadraticPickSeeds(e);
        } else {
            firsts = this.linearPickSeeds(e);
        }

        ArrayList entries = new ArrayList(e.length - 2);

        for (int i = 0; i < e.length; i++) {
            if (!e[i].equals(firsts[0]) && !e[i].equals(firsts[1])) {
                entries.add(e[i]);
            }
        }

        // Clear the node in order to reuse it
        node.clear();

        Node newNode = this.store.getEmptyNode(node.isLeaf());

        Node[] ret = new Node[] { node, newNode };
        ret[0].addEntry(firsts[0]);
        ret[1].addEntry(firsts[1]);

        Entry toAssign = null;
        double d1 = 0d;
        double d2 = 0d;
        int pointer = -1;

        while (true) {
            if (entries.size() == 0) {
                break;
            } else {
                /*
                 * If the remaining elements are not enough for reaching the
                 * minNodeElements of a group or are just right to reach it, add
                 * all entries to the group
                 */
                if ((ret[0].getEntriesCount() + entries.size()) <= this.store
                        .getMinNodeEntries()) {
                    for (int i = 0; i < entries.size(); i++) {
                        ret[0].addEntry((Entry) entries.get(i));
                    }

                    break;
                }

                if ((ret[1].getEntriesCount() + entries.size()) <= this.store
                        .getMinNodeEntries()) {
                    for (int i = 0; i < entries.size(); i++) {
                        ret[1].addEntry((Entry) entries.get(i));
                    }

                    break;
                }
            }

            toAssign = null;

            if (this.store.getSplitAlgorithm() == PageStore.SPLIT_QUADRATIC) {
                toAssign = this.quadraticPickNext(ret, entries);
            } else {
                toAssign = this.linearPickNext(ret, entries);
            }

            d1 = this.getAreaIncrease(ret[0].getBounds(), toAssign.getBounds());
            d2 = this.getAreaIncrease(ret[1].getBounds(), toAssign.getBounds());

            if (d1 < d2) {
                pointer = 0;
            } else if (d1 > d2) {
                pointer = 1;
            } else {
                // If areas increase are the same, smallest wins
                d1 = this.getEnvelopeArea(ret[0].getBounds());
                d2 = this.getEnvelopeArea(ret[1].getBounds());

                if (d1 < d2) {
                    pointer = 0;
                } else if (d1 > d2) {
                    pointer = 1;
                } else {
                    /*
                     * If areas are the same the one with less entries wins

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -