package com.opendoorlogistics.core.distances.external; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import com.opendoorlogistics.core.distances.external.InsertionOnlySpatialTree.SpatialTreeNode; import com.opendoorlogistics.core.utils.MultiHashMap; public class InsertionOnlySpatialTree { public static class DefaultSpatialTreeNode implements SpatialTreeNode{ private ArrayList<Map.Entry<SpatialTreeCoord,Object>> entries = new ArrayList<>(); private ArrayList<SpatialTreeNode> children = new ArrayList<>(); private SpatialTreeCoord min; private SpatialTreeCoord max; @Override public List<SpatialTreeNode> getChildren() { return children; } @Override public List<Entry<SpatialTreeCoord, Object>> getEntries() { return entries; } public SpatialTreeCoord getMin() { return min; } public void setMin(SpatialTreeCoord min) { this.min = min; } public SpatialTreeCoord getMax() { return max; } public void setMax(SpatialTreeCoord max) { this.max = max; } } /** * Interface must subclass hashcode and equals * @author Phil * */ public static interface SpatialTreeCoord{ } public static interface SpatialTreeManager{ Collection<SpatialTreeNode> createChildren(SpatialTreeNode parent); SpatialTreeNode createRoot(); int getChildIndex(SpatialTreeNode parent,SpatialTreeCoord coord); int getMaxObjectsPerNode(); boolean isContained(SpatialTreeQuery query, SpatialTreeCoord coord); boolean isOverlappingNode(SpatialTreeQuery query, SpatialTreeNode node); boolean isSplittable(SpatialTreeNode node); } public static interface SpatialTreeNode{ List<SpatialTreeNode> getChildren(); List<Map.Entry<SpatialTreeCoord,Object>> getEntries(); } public static interface SpatialTreeQuery{ } private final SpatialTreeManager mgr; private SpatialTreeNode root; public InsertionOnlySpatialTree(SpatialTreeManager mgr) { this.mgr = mgr; this.root = mgr.createRoot(); } public void insert(SpatialTreeCoord coord,Object object){ insert(root, new AbstractMap.SimpleEntry<SpatialTreeCoord,Object>(coord, object)); } protected void insert(SpatialTreeNode node,Map.Entry<SpatialTreeCoord,Object> newEntry){ // split if (a) not split already and (b) will have more than max entries and (c) is splittable if(node.getChildren().size() == 0 && node.getEntries().size() >= mgr.getMaxObjectsPerNode() && mgr.isSplittable(node)){ // build a hashset to check the number of distinct coords HashSet<SpatialTreeCoord> allCoords = new HashSet<>(); allCoords.add(newEntry.getKey()); for(Map.Entry<SpatialTreeCoord,Object> entry:node.getEntries()){ allCoords.add(entry.getKey()); } // split if we don't have completely identical coords if(allCoords.size()>1){ // split node.getChildren().clear(); node.getChildren().addAll(mgr.createChildren(node)); for(Map.Entry<SpatialTreeCoord,Object> entry:node.getEntries()){ int indx = mgr.getChildIndex(node, entry.getKey()); insert(node.getChildren().get(indx), entry); } // call me again to place in correct child insert(node, newEntry); return; } } // are we already split? if(node.getChildren().size()>0){ int indx = mgr.getChildIndex(node, newEntry.getKey()); insert(node.getChildren().get(indx), newEntry); }else{ // not split node.getEntries().add(newEntry); } } public Collection<Map.Entry<SpatialTreeCoord,Object>> query(SpatialTreeQuery query){ LinkedList<Map.Entry<SpatialTreeCoord,Object>> results = new LinkedList<>(); query(query,root,results); return results; } protected void query(SpatialTreeQuery query, SpatialTreeNode node,List<Map.Entry<SpatialTreeCoord,Object>> results){ if(!mgr.isOverlappingNode(query, node)){ return; } if(node.getChildren().size()>0){ for(SpatialTreeNode child:node.getChildren()){ query(query,child,results); } } else{ // check against coords for(Map.Entry<SpatialTreeCoord,Object> entry: node.getEntries()){ if(mgr.isContained(query, entry.getKey())){ results.add(entry); } } } } // TODO build knn query.... }