/********************************************************************** * TABMAPIndexBlock::SplitRootNode() * * (private method) * * Split a Root Node. * First, a level of nodes must be added to the tree, then the contents * of what used to be the root node is moved 1 level down and then that * node is split like a regular node. * * Returns 0 on success, -1 on error **********************************************************************/ int TABMAPIndexBlock::SplitRootNode(int nNewEntryX, int nNewEntryY) { CPLAssert(m_poBlockManagerRef); CPLAssert(m_poParentRef == NULL); /*----------------------------------------------------------------- * Since a root note cannot be split, we add a level of nodes * under it and we'll do the split at that level. *----------------------------------------------------------------*/ TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess); if (poNewNode->InitNewBlock(m_fp, 512, m_poBlockManagerRef->AllocNewBlock()) != 0) { return -1; } poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef); // Move all entries to the new child int nSrcEntries = m_numEntries; m_numEntries = 0; for(int iEntry=0; iEntry<nSrcEntries; iEntry++) { poNewNode->InsertEntry(m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin, m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax, m_asEntries[iEntry].nBlockPtr); } /*----------------------------------------------------------------- * Transfer current child object to new node. *----------------------------------------------------------------*/ if (m_poCurChild) { poNewNode->SetCurChildRef(m_poCurChild, m_nCurChildIndex); m_poCurChild->SetParentRef(poNewNode); m_poCurChild = NULL; m_nCurChildIndex = -1; } /*----------------------------------------------------------------- * Place info about new child in current node. *----------------------------------------------------------------*/ poNewNode->RecomputeMBR(); int nMinX, nMinY, nMaxX, nMaxY; poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY); InsertEntry(nMinX, nMinY, nMaxX, nMaxY, poNewNode->GetNodeBlockPtr()); /*----------------------------------------------------------------- * Keep a reference to the new child *----------------------------------------------------------------*/ poNewNode->SetParentRef(this); m_poCurChild = poNewNode; m_nCurChildIndex = m_numEntries -1; /*----------------------------------------------------------------- * And finally force the child to split itself *----------------------------------------------------------------*/ return m_poCurChild->SplitNode(nNewEntryX, nNewEntryY); }
/********************************************************************** * TABMAPIndexBlock::SplitNode() * * Split current Node, update the references in the parent node, etc. * Note that Root Nodes cannot be split using this method... SplitRootNode() * should be used instead. * * nNewEntryX, nNewEntryY are the coord. of the center of the new entry that * will be added after the split. The split is done so that the current * node will be the one in which the new object should be stored. * * Returns 0 on success, -1 on error. **********************************************************************/ int TABMAPIndexBlock::SplitNode(int nNewEntryX, int nNewEntryY) { int nSrcEntries = m_numEntries; int nWidth, nHeight, nCenterX1, nCenterY1, nCenterX2, nCenterY2; CPLAssert(m_poBlockManagerRef); /*----------------------------------------------------------------- * Create a 2nd node, and assign both nodes a MBR that is half * of the biggest dimension (width or height) of the current node's MBR * * We also want to keep this node's current child in here. * Since splitting happens only during an addentry() operation and * then both the current child and nNewEntryX/Y should fit in the same * area. *----------------------------------------------------------------*/ TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess); if (poNewNode->InitNewBlock(m_fp, 512, m_poBlockManagerRef->AllocNewBlock()) != 0) { return -1; } poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef); nWidth = ABS(m_nMaxX - m_nMinX); nHeight = ABS(m_nMaxY - m_nMinY); if (nWidth > nHeight) { // Split node horizontally nCenterY1 = nCenterY2 = m_nMinY + nHeight/2; if (nNewEntryX < (m_nMinX + m_nMaxX)/2) { nCenterX1 = m_nMinX + nWidth/4; nCenterX2 = m_nMaxX - nWidth/4; } else { nCenterX2 = m_nMinX + nWidth/4; nCenterX1 = m_nMaxX - nWidth/4; } } else { // Split node vertically nCenterX1 = nCenterX2 = m_nMinX + nWidth/2; if (nNewEntryY < (m_nMinY + m_nMaxY)/2) { nCenterY1 = m_nMinY + nHeight/4; nCenterY2 = m_nMaxY - nHeight/4; } else { nCenterY2 = m_nMinY + nHeight/4; nCenterY1 = m_nMaxY - nHeight/4; } } /*----------------------------------------------------------------- * Go through all entries and assign them to one of the 2 nodes. * * Criteria is that entries are assigned to the node in which their * center falls. * * Hummm... this does not prevent the possibility that one of the * 2 nodes might end up empty at the end. *----------------------------------------------------------------*/ m_numEntries = 0; for(int iEntry=0; iEntry<nSrcEntries; iEntry++) { int nEntryCenterX = (m_asEntries[iEntry].XMax + m_asEntries[iEntry].XMin) / 2; int nEntryCenterY = (m_asEntries[iEntry].YMax + m_asEntries[iEntry].YMin) / 2; if (iEntry == m_nCurChildIndex || (nWidth > nHeight && ABS(nEntryCenterX-nCenterX1) < ABS(nEntryCenterX-nCenterX2)) || (nWidth <= nHeight && ABS(nEntryCenterY-nCenterY1) < ABS(nEntryCenterY-nCenterY2) ) ) { // This entry stays in current node. InsertEntry(m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin, m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax, m_asEntries[iEntry].nBlockPtr); // We have to keep track of new m_nCurChildIndex value if (iEntry == m_nCurChildIndex) { m_nCurChildIndex = m_numEntries-1; } } else { // This entry goes in the new node. poNewNode->InsertEntry(m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin, m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax, m_asEntries[iEntry].nBlockPtr); } } /*----------------------------------------------------------------- * If no entry was moved to second node, then move ALL entries except * the current child to the second node... this way current node will * have room for a new entry when this function exits. *----------------------------------------------------------------*/ if (poNewNode->GetNumEntries() == 0) { nSrcEntries = m_numEntries; m_numEntries = 0; for(int iEntry=0; iEntry<nSrcEntries; iEntry++) { if (iEntry == m_nCurChildIndex) { // Keep current child in current node InsertEntry(m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin, m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax, m_asEntries[iEntry].nBlockPtr); m_nCurChildIndex = m_numEntries-1; } else { // All other entries go to second node poNewNode->InsertEntry(m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin, m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax, m_asEntries[iEntry].nBlockPtr); } } } /*----------------------------------------------------------------- * Recompute MBR and update current node info in parent *----------------------------------------------------------------*/ RecomputeMBR(); poNewNode->RecomputeMBR(); /*----------------------------------------------------------------- * Add second node info to parent and then flush it to disk. * This may trigger splitting of parent *----------------------------------------------------------------*/ CPLAssert(m_poParentRef); int nMinX, nMinY, nMaxX, nMaxY; poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY); m_poParentRef->AddEntry(nMinX, nMinY, nMaxX, nMaxY, poNewNode->GetNodeBlockPtr(), TRUE); poNewNode->CommitToFile(); delete poNewNode; return 0; }