Exemple #1
0
  /** Adds a new entry at a specified level in the tree */
  private void add(Rectangle r, int id, int level) {
    // I1 [Find position for new record] Invoke ChooseLeaf to select a
    // leaf node L in which to place r
    Node n = chooseNode(r, level);
    Node newLeaf = null;

    // I2 [Add record to leaf node] If L has room for another entry,
    // install E. Otherwise invoke SplitNode to obtain L and LL containing
    // E and all the old entries of L
    if (n.entryCount < maxNodeEntries) {
      n.addEntryNoCopy(r, id);
    } else {
      newLeaf = splitNode(n, r, id);
    }

    // I3 [Propagate changes upwards] Invoke AdjustTree on L, also passing
    // LL
    // if a split was performed
    Node newNode = adjustTree(n, newLeaf);

    // I4 [Grow tree taller] If node split propagation caused the root to
    // split, create a new root whose children are the two resulting nodes.
    if (newNode != null) {
      int oldRootNodeId = rootNodeId;
      Node oldRoot = getNode(oldRootNodeId);

      rootNodeId = getNextNodeId();
      treeHeight++;
      Node root = new Node(rootNodeId, treeHeight, maxNodeEntries);
      root.addEntry(newNode.mbr, newNode.nodeId);
      root.addEntry(oldRoot.mbr, oldRoot.nodeId);
      nodeMap.put(rootNodeId, root);
    }

    if (INTERNAL_CONSISTENCY_CHECKING) {
      checkConsistency(rootNodeId, treeHeight, null);
    }
  }
Exemple #2
0
  /**
   * Pick the next entry to be assigned to a group during a node split.
   *
   * <p>[Determine cost of putting each entry in each group] For each entry not yet in a group,
   * calculate the area increase required in the covering rectangles of each group
   */
  private int pickNext(Node n, Node newNode) {
    float maxDifference = Float.NEGATIVE_INFINITY;
    int next = 0;
    int nextGroup = 0;

    maxDifference = Float.NEGATIVE_INFINITY;

    if (log.isDebugEnabled()) {
      log.debug("pickNext()");
    }

    for (int i = 0; i < maxNodeEntries; i++) {
      if (entryStatus[i] == ENTRY_STATUS_UNASSIGNED) {

        if (n.entries[i] == null) {
          log.error("Error: Node " + n.nodeId + ", entry " + i + " is null");
        }

        float nIncrease = n.mbr.enlargement(n.entries[i]);
        float newNodeIncrease = newNode.mbr.enlargement(n.entries[i]);
        float difference = Math.abs(nIncrease - newNodeIncrease);

        if (difference > maxDifference) {
          next = i;

          if (nIncrease < newNodeIncrease) {
            nextGroup = 0;
          } else if (newNodeIncrease < nIncrease) {
            nextGroup = 1;
          } else if (n.mbr.area() < newNode.mbr.area()) {
            nextGroup = 0;
          } else if (newNode.mbr.area() < n.mbr.area()) {
            nextGroup = 1;
          } else if (newNode.entryCount < maxNodeEntries / 2) {
            nextGroup = 0;
          } else {
            nextGroup = 1;
          }
          maxDifference = difference;
        }
        if (log.isDebugEnabled()) {
          log.debug(
              "Entry "
                  + i
                  + " group0 increase = "
                  + nIncrease
                  + ", group1 increase = "
                  + newNodeIncrease
                  + ", diff = "
                  + difference
                  + ", MaxDiff = "
                  + maxDifference
                  + " (entry "
                  + next
                  + ")");
        }
      }
    }

    entryStatus[next] = ENTRY_STATUS_ASSIGNED;

    if (nextGroup == 0) {
      n.mbr.add(n.entries[next]);
      n.entryCount++;
    } else {
      // move to new node.
      newNode.addEntryNoCopy(n.entries[next], n.ids[next]);
      n.entries[next] = null;
    }

    return next;
  }
Exemple #3
0
  /**
   * Pick the seeds used to split a node. Select two entries to be the first elements of the groups
   */
  private void pickSeeds(Node n, Rectangle newRect, int newId, Node newNode) {
    // Find extreme rectangles along all dimension. Along each dimension,
    // find the entry whose rectangle has the highest low side, and the one
    // with the lowest high side. Record the separation.
    float maxNormalizedSeparation = 0;
    int highestLowIndex = 0;
    int lowestHighIndex = 0;

    // for the purposes of picking seeds, take the MBR of the node to include
    // the new rectangle aswell.
    n.mbr.add(newRect);

    if (log.isDebugEnabled()) {
      log.debug("pickSeeds(): NodeId = " + n.nodeId + ", newRect = " + newRect);
    }

    for (int d = 0; d < Rectangle.DIMENSIONS; d++) {
      float tempHighestLow = newRect.min[d];
      int tempHighestLowIndex = -1; // -1 indicates the new rectangle is the seed

      float tempLowestHigh = newRect.max[d];
      int tempLowestHighIndex = -1;

      for (int i = 0; i < n.entryCount; i++) {
        float tempLow = n.entries[i].min[d];
        if (tempLow >= tempHighestLow) {
          tempHighestLow = tempLow;
          tempHighestLowIndex = i;
        } else { // ensure that the same index cannot be both lowestHigh and highestLow
          float tempHigh = n.entries[i].max[d];
          if (tempHigh <= tempLowestHigh) {
            tempLowestHigh = tempHigh;
            tempLowestHighIndex = i;
          }
        }

        // PS2 [Adjust for shape of the rectangle cluster] Normalize the separations
        // by dividing by the widths of the entire set along the corresponding
        // dimension
        float normalizedSeparation =
            (tempHighestLow - tempLowestHigh) / (n.mbr.max[d] - n.mbr.min[d]);

        if (normalizedSeparation > 1 || normalizedSeparation < -1) {
          log.error("Invalid normalized separation");
        }

        if (log.isDebugEnabled()) {
          log.debug(
              "Entry "
                  + i
                  + ", dimension "
                  + d
                  + ": HighestLow = "
                  + tempHighestLow
                  + " (index "
                  + tempHighestLowIndex
                  + ")"
                  + ", LowestHigh = "
                  + tempLowestHigh
                  + " (index "
                  + tempLowestHighIndex
                  + ", NormalizedSeparation = "
                  + normalizedSeparation);
        }

        // PS3 [Select the most extreme pair] Choose the pair with the greatest
        // normalized separation along any dimension.
        if (normalizedSeparation > maxNormalizedSeparation) {
          maxNormalizedSeparation = normalizedSeparation;
          highestLowIndex = tempHighestLowIndex;
          lowestHighIndex = tempLowestHighIndex;
        }
      }
    }

    // highestLowIndex is the seed for the new node.
    if (highestLowIndex == -1) {
      newNode.addEntry(newRect, newId);
    } else {
      newNode.addEntryNoCopy(n.entries[highestLowIndex], n.ids[highestLowIndex]);
      n.entries[highestLowIndex] = null;

      // move the new rectangle into the space vacated by the seed for the new node
      n.entries[highestLowIndex] = newRect;
      n.ids[highestLowIndex] = newId;
    }

    // lowestHighIndex is the seed for the original node.
    if (lowestHighIndex == -1) {
      lowestHighIndex = highestLowIndex;
    }

    entryStatus[lowestHighIndex] = ENTRY_STATUS_ASSIGNED;
    n.entryCount = 1;
    n.mbr.set(n.entries[lowestHighIndex].min, n.entries[lowestHighIndex].max);
  }
Exemple #4
0
  /**
   * Split a node. Algorithm is taken pretty much verbatim from Guttman's original paper.
   *
   * @return new node object.
   */
  private Node splitNode(Node n, Rectangle newRect, int newId) {
    // [Pick first entry for each group] Apply algorithm pickSeeds to
    // choose two entries to be the first elements of the groups. Assign
    // each to a group.

    // debug code
    float initialArea = 0;
    if (log.isDebugEnabled()) {
      Rectangle union = n.mbr.union(newRect);
      initialArea = union.area();
    }

    System.arraycopy(initialEntryStatus, 0, entryStatus, 0, maxNodeEntries);

    Node newNode = null;
    newNode = new Node(getNextNodeId(), n.level, maxNodeEntries);
    nodeMap.put(newNode.nodeId, newNode);

    pickSeeds(n, newRect, newId, newNode); // this also sets the entryCount to 1

    // [Check if done] If all entries have been assigned, stop. If one
    // group has so few entries that all the rest must be assigned to it in
    // order for it to have the minimum number m, assign them and stop.
    while (n.entryCount + newNode.entryCount < maxNodeEntries + 1) {
      if (maxNodeEntries + 1 - newNode.entryCount == minNodeEntries) {
        // assign all remaining entries to original node
        for (int i = 0; i < maxNodeEntries; i++) {
          if (entryStatus[i] == ENTRY_STATUS_UNASSIGNED) {
            entryStatus[i] = ENTRY_STATUS_ASSIGNED;
            n.mbr.add(n.entries[i]);
            n.entryCount++;
          }
        }
        break;
      }
      if (maxNodeEntries + 1 - n.entryCount == minNodeEntries) {
        // assign all remaining entries to new node
        for (int i = 0; i < maxNodeEntries; i++) {
          if (entryStatus[i] == ENTRY_STATUS_UNASSIGNED) {
            entryStatus[i] = ENTRY_STATUS_ASSIGNED;
            newNode.addEntryNoCopy(n.entries[i], n.ids[i]);
            n.entries[i] = null;
          }
        }
        break;
      }

      // [Select entry to assign] Invoke algorithm pickNext to choose the
      // next entry to assign. Add it to the group whose covering rectangle
      // will have to be enlarged least to accommodate it. Resolve ties
      // by adding the entry to the group with smaller area, then to the
      // the one with fewer entries, then to either. Repeat from S2
      pickNext(n, newNode);
    }

    n.reorganize(this);

    // check that the MBR stored for each node is correct.
    if (INTERNAL_CONSISTENCY_CHECKING) {
      if (!n.mbr.equals(calculateMBR(n))) {
        log.error("Error: splitNode old node MBR wrong");
      }

      if (!newNode.mbr.equals(calculateMBR(newNode))) {
        log.error("Error: splitNode new node MBR wrong");
      }
    }

    // debug code
    if (log.isDebugEnabled()) {
      float newArea = n.mbr.area() + newNode.mbr.area();
      float percentageIncrease = (100 * (newArea - initialArea)) / initialArea;
      log.debug("Node " + n.nodeId + " split. New area increased by " + percentageIncrease + "%");
    }

    return newNode;
  }