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

Source Code of org.osm2world.core.map_data.creation.index.Map2dTree$Node

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

import static java.util.Collections.*;

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.VectorXZ;

/**
* a 2D tree (two-dimensional k-d tree) managing {@link MapElement}s of a
* data set according on their coordinates in the XZ plane.
*
* Data is contained within the leafs.
* The inner nodes split the XZ plane along parallels to the Z and X axes,
* alternatingly.
*/
public class Map2dTree implements MapDataIndex {
 
  protected static final int LEAF_SPLIT_SIZE = 11;
 
  protected final Node root;
 
  protected static interface Node {
   
    void add(MapElement element, boolean suppressSplits);
   
    List<Leaf> probe(MapElement element);

    /** adds all leaves in the subtree starting at this node to a list */
    void collectLeaves(List<Leaf> leaves);
   
  }
 
  protected static class InnerNode implements Node {
   
    public final boolean splitAlongX;
    public final double splitValue;
   
    public Node lowerChild;
    public Node upperChild;
   
    protected InnerNode(boolean splitAlongX, double splitValue) {
     
      this.splitAlongX = splitAlongX;
      this.splitValue = splitValue;
     
      this.lowerChild = new Leaf(this);
      this.upperChild = new Leaf(this);
     
    }

    @Override
    public void add(MapElement element, boolean suppressSplits) {
     
      boolean addToLowerChild = false;
      boolean addToUpperChild = false;
     
      for (MapNode node : getMapNodes(element)) {
       
        VectorXZ pos = node.getPos();
       
        if (splitAlongX) {
       
          addToLowerChild |= pos.x <= splitValue;
          addToUpperChild |= pos.x >= splitValue;
         
        } else {

          addToLowerChild |= pos.z <= splitValue;
          addToUpperChild |= pos.z >= splitValue;
         
        }
       
      }
     
      if (addToLowerChild) {
        lowerChild.add(element, suppressSplits);
      }
      if (addToUpperChild) {
        upperChild.add(element, suppressSplits);
      }
     
    }

    private void trySplitLeaf(Leaf leaf) {

      boolean splitChildAlongX = !splitAlongX;
      double splitChildValue = 0;
     
      /* determine split value as average of all values */
     
      int numNodes = 0;
     
      for (MapElement element : leaf) {
        for (MapNode node : getMapNodes(element)) {
         
          if (splitChildAlongX) {
            splitChildValue += node.getPos().x;
          } else {
            splitChildValue += node.getPos().z;
          }
         
          numNodes += 1;
         
        }
      }
     
      splitChildValue /= numNodes;
     
      /* create the new inner node */
     
      InnerNode newChild = new InnerNode(splitChildAlongX, splitChildValue);
     
      /* check whether splitting will reduce the maximum node size */
     
      for (MapElement element : leaf.elements) {
        newChild.add(element, true);
      }
     
      if (((Leaf)(newChild.lowerChild)).elements.size() < leaf.elements.size() - 5
          && ((Leaf)(newChild.upperChild)).elements.size() < leaf.elements.size() - 5) {
       
        /* replace the leaf with the new child node */
       
        if (lowerChild == leaf) {
          lowerChild = newChild;
        } else if (upperChild == leaf) {
          upperChild = newChild;
        } else {
          throw new AssertionError("leaf is not a child of this node");
        }
       
      }
         
    }
   
    @Override
    public List<Leaf> probe(MapElement element) {
     
      boolean addToLowerChild = false;
      boolean addToUpperChild = false;
     
      for (MapNode node : getMapNodes(element)) {
       
        VectorXZ pos = node.getPos();
       
        if (splitAlongX) {
         
          addToLowerChild |= pos.x <= splitValue;
          addToUpperChild |= pos.x >= splitValue;
         
        } else {
         
          addToLowerChild |= pos.z <= splitValue;
          addToUpperChild |= pos.z >= splitValue;
         
        }
       
      }
     
      if (addToLowerChild && addToUpperChild) {
        List<Leaf> leaves = new ArrayList<Leaf>();
        leaves.addAll(lowerChild.probe(element));
        leaves.addAll(upperChild.probe(element));
        return leaves;
      } else if (addToLowerChild) {
        return lowerChild.probe(element);
      } else if (addToUpperChild) {
        return upperChild.probe(element);
      } else {
        throw new AssertionError ("The element is not in this Node");
      }
     
    }
   
    public void collectLeaves(List<Leaf> leaves) {
      lowerChild.collectLeaves(leaves);
      upperChild.collectLeaves(leaves);
    }
   
  }
 
  protected static class Leaf implements Node, Iterable<MapElement> {

    protected final InnerNode parent;
    protected final ArrayList<MapElement> elements;
   
    protected int numberWaysAndAreas = 0;
   
    protected Leaf(InnerNode parent) {
      this.parent = parent;
      elements = new ArrayList<MapElement>(LEAF_SPLIT_SIZE);
    }
   
    @Override
    public void add(MapElement element, boolean suppressSplits) {
     
      elements.add(element);

      if (!(element instanceof MapNode)) {
        numberWaysAndAreas += 1;
      }
     
      if (!suppressSplits && numberWaysAndAreas >= LEAF_SPLIT_SIZE) {
        parent.trySplitLeaf(this);
      }
     
    }
   
    @Override
    public Iterator<MapElement> iterator() {
      return elements.iterator();
    }
   
    @Override
    public List<Leaf> probe(MapElement element) {
      return singletonList(this);
    }
   
    @Override
    public void collectLeaves(List<Leaf> leaves) {
      leaves.add(this);
    }
   
  }
 
  public Map2dTree(AxisAlignedBoundingBoxXZ dataBoundary) {
   
    root = new InnerNode(true, (dataBoundary.minX + dataBoundary.maxX) / 2);
       
  }
 
  @Override
  public void insert(MapElement element) {
    root.add(element, false);
  }
 
  @Override
  public Collection<Leaf> insertAndProbe(MapElement e) {
    insert(e);
    return root.probe(e);
  }
 
  protected static Iterable<MapNode> getMapNodes(MapElement element) {
   
    if (element instanceof MapNode) {
     
      return singleton((MapNode)element);
     
    } else if (element instanceof MapWaySegment) {
     
      return ((MapWaySegment)element).getStartEndNodes();
     
    } else { // element instanceof MapArea
     
      return ((MapArea)element).getBoundaryNodes();
     
    }
   
  }
 
  @Override
  public Iterable<Leaf> getLeaves() {
   
    List<Leaf> leaves = new ArrayList<Leaf>();
   
    root.collectLeaves(leaves);
   
    return leaves;
   
  }
 
}
TOP

Related Classes of org.osm2world.core.map_data.creation.index.Map2dTree$Node

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.