Package org.osm2world.core.map_data.creation.index

Source Code of org.osm2world.core.map_data.creation.index.MapQuadtree$QuadLeaf

package org.osm2world.core.map_data.creation.index;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.MapElement;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.map_data.data.MapWaySegment;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXZ;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;

/**
* a Quadtree managing {@link MapElement}s of a data set
* according on their coordinates in the XZ plane.
*/
public class MapQuadtree implements MapDataIndex {
 
  static final int LEAF_SPLIT_SIZE = 11;
 
  final QuadInnerNode root;
 
  static abstract class QuadNode {
   
    public final double minX, maxX, minZ, maxZ;
   
    private final SimplePolygonXZ boundary;
   
    QuadNode(double minX2, double maxX2, double minZ2, double maxZ2) {
     
      this.minX = minX2;
      this.maxX = maxX2;
      this.minZ = minZ2;
      this.maxZ = maxZ2;
     
      List<VectorXZ> vertices = new ArrayList<VectorXZ>(5);
      vertices.add(new VectorXZ(minX2, minZ2));
      vertices.add(new VectorXZ(maxX2, minZ2));
      vertices.add(new VectorXZ(maxX2, maxZ2));
      vertices.add(new VectorXZ(minX2, maxZ2));
      vertices.add(vertices.get(0));
      boundary = new SimplePolygonXZ(vertices);
     
    }
   
    /** returns true if this node's bounds contain at least a part of the element */
    boolean contains(MapElement element) {
     
      if (element instanceof MapNode) {
       
        return contains(((MapNode)element).getPos());
       
      } else if (element instanceof MapWaySegment) {
        MapWaySegment line = (MapWaySegment)element;
               
        VectorXZ lineStart = line.getStartNode().getPos();
        VectorXZ lineEnd = line.getEndNode().getPos();
       
        if (contains(lineStart) || contains(lineEnd)) {
          return true;
        } else if (boundary.intersects(lineStart, lineEnd)) {
          //SUGGEST (performance): use that the box is axis-aligned?
          return true;
        }
        return false;
       
      } else { // element instanceof MapArea
        MapArea area = ((MapArea)element);
               
        for (MapNode node : area.getBoundaryNodes()) {
          if (contains(node.getPos())) {
            return true;
          }
        }
       
        if (boundary.intersects(area.getPolygon().getOuter())
            || area.getPolygon().contains(boundary)) {
          //SUGGEST (performance): use that the box is axis-aligned?
          return true;
        }
       
        return false;
      }
    }
   
    boolean contains(VectorXZ pos) {
      return pos.x >= minX && pos.x <= maxX
        && pos.z >= minZ && pos.z <= maxZ;
    }
   
    abstract void add(MapElement element);

    abstract void addAll(Collection<MapElement> elements);

    /** adds all leaves in the subtree starting at this node to a list */
    abstract void collectLeaves(List<QuadLeaf> leaves);
   
  }
 
  static class QuadInnerNode extends QuadNode {
   
    /** array with four elements */
    final QuadNode childNodes[];

    QuadInnerNode(double minX, double maxX, double minZ, double maxZ) {
      super(minX, maxX, minZ, maxZ);
     
      childNodes = new QuadNode[4];
     
      double halfX = (minX+maxX)/2;
      double halfZ = (minZ+maxZ)/2;
     
      childNodes[0] = new QuadLeaf(this, minX, halfX, minZ, halfZ);
      childNodes[1] = new QuadLeaf(this, halfX, maxX, minZ, halfZ);
      childNodes[2] = new QuadLeaf(this, minX, halfX, halfZ, maxZ);
      childNodes[3] = new QuadLeaf(this, halfX, maxX, halfZ, maxZ);
     
    }
   
    @Override
    void add(MapElement element) {
      for (int i=0; i<4; i++) {
        if (childNodes[i].contains(element)) {
          childNodes[i].add(element);
          //continue loop, the element can cross leaf borders
        }
      }
    }
   
    @Override
    void addAll(Collection<MapElement> elements) {
      for (MapElement element : elements) {
        add(element);
      }
    }

    void trySplitLeaf(QuadLeaf leaf) {
     
      QuadInnerNode newChild =
        new QuadInnerNode(leaf.minX, leaf.maxX, leaf.minZ, leaf.maxZ);
     
      /* check whether splitting will reduce the maximum node size */
     
      boolean nodeSizeReduced = true;
           
      for (int i=0; i<4; i++) {
        boolean newLeafContainsAllElements = true;
        for (MapElement element : leaf) {
          if (!newChild.childNodes[i].contains(element)) {
            newLeafContainsAllElements = false;
            break;
          }
        }
        if (newLeafContainsAllElements) {
          nodeSizeReduced = false;
          break;
        }
      }
     
      if (nodeSizeReduced) {
       
        /* replace the leaf with the new child node */
       
        for (int i=0; i<4; i++) {
          if (childNodes[i] == leaf) {
            childNodes[i] = newChild;
            childNodes[i].addAll(leaf.elements);
            return;
          }
        }
       
        throw new AssertionError("leaf is not a child of this node");
       
      }
         
    }

    void collectLeaves(List<QuadLeaf> leaves) {
      for (int i=0; i<4; i++) {
        childNodes[i].collectLeaves(leaves);
      }
    }
   
  }
 
  static public class QuadLeaf extends QuadNode implements Iterable<MapElement> {

    final QuadInnerNode parent;
    final ArrayList<MapElement> elements;
   
    QuadLeaf(QuadInnerNode parent, double minX, double maxX, double minZ, double maxZ) {
      super(minX, maxX, minZ, maxZ);
     
      this.parent = parent;
     
      elements = new ArrayList<MapElement>(LEAF_SPLIT_SIZE);
     
    }
   
    @Override
    void add(MapElement element) {
     
      elements.add(element);

      if (elements.size() >= LEAF_SPLIT_SIZE) {
        parent.trySplitLeaf(this);
      }
     
    }
   
    @Override
    void addAll(Collection<MapElement> element) {
      /* addAll cannot be implemented by iterating over add:
       * if the leaf would be "split"(replaced with an inner node)
       * during the iteration, the remaining elements would still
       * be added to the now-useless leaf object */
     
      elements.addAll(element);

      if (elements.size() >= LEAF_SPLIT_SIZE) {
        parent.trySplitLeaf(this);
      }
     
    }
   
    @Override
    public Iterator<MapElement> iterator() {
      return elements.iterator();
    }
   
    @Override
    void collectLeaves(List<QuadLeaf> leaves) {
      leaves.add(this);
    }
   
  }
 
  public MapQuadtree(AxisAlignedBoundingBoxXZ dataBoundary) {
   
    root = new QuadInnerNode(
        dataBoundary.minX, dataBoundary.maxX,
        dataBoundary.minZ, dataBoundary.maxZ);
   
  }
 
  @Override
  public void insert(MapElement e) {
    root.add(e);
  }
 
  @Override
  public Collection<? extends Iterable<MapElement>> insertAndProbe(
      final MapElement e) {
   
    insert(e);
   
    return Collections2.<QuadLeaf>filter(getLeaves(), new Predicate<QuadLeaf>() {
      @Override public boolean apply(QuadLeaf leaf) {
        return leaf.contains(e);
      }
    });
   
  }
 
  @Override
  public Collection<QuadLeaf> getLeaves() {
   
    List<QuadLeaf> leaves = new ArrayList<QuadLeaf>();
   
    root.collectLeaves(leaves);
   
    return leaves;
   
  }
 
}
TOP

Related Classes of org.osm2world.core.map_data.creation.index.MapQuadtree$QuadLeaf

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.