/* * Copyright (c) 2016 Vivid Solutions. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package org.locationtech.jts.index.intervalrtree; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.index.ItemVisitor; import org.locationtech.jts.io.WKTWriter; /** * A static index on a set of 1-dimensional intervals, * using an R-Tree packed based on the order of the interval midpoints. * It supports range searching, * where the range is an interval of the real line (which may be a single point). * A common use is to index 1-dimensional intervals which * are the projection of 2-D objects onto an axis of the coordinate system. * <p> * This index structure is <i>static</i> * - items cannot be added or removed once the first query has been made. * The advantage of this characteristic is that the index performance * can be optimized based on a fixed set of items. * * @author Martin Davis */ public class SortedPackedIntervalRTree { private List leaves = new ArrayList(); private IntervalRTreeNode root = null; public SortedPackedIntervalRTree() { } /** * Adds an item to the index which is associated with the given interval * * @param min the lower bound of the item interval * @param max the upper bound of the item interval * @param item the item to insert * * @throws IllegalStateException if the index has already been queried */ public void insert(double min, double max, Object item) { if (root != null) throw new IllegalStateException("Index cannot be added to once it has been queried"); leaves.add(new IntervalRTreeLeafNode(min, max, item)); } private void init() { if (root != null) return; buildRoot(); } private synchronized void buildRoot() { if (root != null) return; root = buildTree(); } private IntervalRTreeNode buildTree() { // sort the leaf nodes Collections.sort(leaves, new IntervalRTreeNode.NodeComparator()); // now group nodes into blocks of two and build tree up recursively List src = leaves; List temp = null; List dest = new ArrayList(); while (true) { buildLevel(src, dest); if (dest.size() == 1) return (IntervalRTreeNode) dest.get(0); temp = src; src = dest; dest = temp; } } private int level = 0; private void buildLevel(List src, List dest) { level++; dest.clear(); for (int i = 0; i < src.size(); i += 2) { IntervalRTreeNode n1 = (IntervalRTreeNode) src.get(i); IntervalRTreeNode n2 = (i + 1 < src.size()) ? (IntervalRTreeNode) src.get(i) : null; if (n2 == null) { dest.add(n1); } else { IntervalRTreeNode node = new IntervalRTreeBranchNode( (IntervalRTreeNode) src.get(i), (IntervalRTreeNode) src.get(i + 1)); // printNode(node); // System.out.println(node); dest.add(node); } } } private void printNode(IntervalRTreeNode node) { System.out.println(WKTWriter.toLineString(new Coordinate(node.min, level), new Coordinate(node.max, level))); } /** * Search for intervals in the index which intersect the given closed interval * and apply the visitor to them. * * @param min the lower bound of the query interval * @param max the upper bound of the query interval * @param visitor the visitor to pass any matched items to */ public void query(double min, double max, ItemVisitor visitor) { init(); root.query(min, max, visitor); } }