LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_mapindexblock.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 506 143 28.3 %
Date: 2012-04-28 Functions: 31 17 54.8 %

       1                 : /**********************************************************************
       2                 :  * $Id: mitab_mapindexblock.cpp,v 1.14 2010-07-07 19:00:15 aboudreault Exp $
       3                 :  *
       4                 :  * Name:     mitab_mapindexblock.cpp
       5                 :  * Project:  MapInfo TAB Read/Write library
       6                 :  * Language: C++
       7                 :  * Purpose:  Implementation of the TABMAPIndexBlock class used to handle
       8                 :  *           reading/writing of the .MAP files' index blocks
       9                 :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
      10                 :  *
      11                 :  **********************************************************************
      12                 :  * Copyright (c) 1999, 2000, Daniel Morissette
      13                 :  *
      14                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      15                 :  * copy of this software and associated documentation files (the "Software"),
      16                 :  * to deal in the Software without restriction, including without limitation
      17                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      18                 :  * and/or sell copies of the Software, and to permit persons to whom the
      19                 :  * Software is furnished to do so, subject to the following conditions:
      20                 :  * 
      21                 :  * The above copyright notice and this permission notice shall be included
      22                 :  * in all copies or substantial portions of the Software.
      23                 :  * 
      24                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      25                 :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      26                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      27                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      28                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      29                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
      30                 :  * DEALINGS IN THE SOFTWARE.
      31                 :  **********************************************************************
      32                 :  *
      33                 :  * $Log: mitab_mapindexblock.cpp,v $
      34                 :  * Revision 1.14  2010-07-07 19:00:15  aboudreault
      35                 :  * Cleanup Win32 Compile Warnings (GDAL bug #2930)
      36                 :  *
      37                 :  * Revision 1.13  2007-04-02 18:58:03  dmorissette
      38                 :  * Fixed uninitialized variable warning in PickSeedsForSplit()
      39                 :  *
      40                 :  * Revision 1.12  2006/12/14 20:03:02  dmorissette
      41                 :  * Improve write performance by keeping track of changes to index blocks
      42                 :  * and committing to disk only if modified (related to bug 1585)
      43                 :  *
      44                 :  * Revision 1.11  2006/11/28 18:49:08  dmorissette
      45                 :  * Completed changes to split TABMAPObjectBlocks properly and produce an
      46                 :  * optimal spatial index (bug 1585)
      47                 :  *
      48                 :  * Revision 1.10  2006/11/20 20:05:58  dmorissette
      49                 :  * First pass at improving generation of spatial index in .map file (bug 1585)
      50                 :  * New methods for insertion and splittung in the spatial index are done.
      51                 :  * Also implemented a method to dump the spatial index to .mif/.mid
      52                 :  * Still need to implement splitting of TABMapObjectBlock to get optimal
      53                 :  * results.
      54                 :  *
      55                 :  * Revision 1.9  2004/06/30 20:29:04  dmorissette
      56                 :  * Fixed refs to old address danmo@videotron.ca
      57                 :  *
      58                 :  * Revision 1.8  2001/09/14 03:23:55  warmerda
      59                 :  * Substantial upgrade to support spatial queries using spatial indexes
      60                 :  *
      61                 :  * Revision 1.7  2000/05/23 17:02:54  daniel
      62                 :  * Removed unused variables
      63                 :  *
      64                 :  * Revision 1.6  2000/05/19 06:45:10  daniel
      65                 :  * Modified generation of spatial index to split index nodes and produce a
      66                 :  * more balanced tree.
      67                 :  *
      68                 :  * Revision 1.5  2000/01/15 22:30:44  daniel
      69                 :  * Switch to MIT/X-Consortium OpenSource license
      70                 :  *
      71                 :  * Revision 1.4  1999/10/01 03:46:31  daniel
      72                 :  * Added ReadAllEntries() and more complete Dump() for debugging files
      73                 :  *
      74                 :  * Revision 1.3  1999/09/29 04:23:51  daniel
      75                 :  * Fixed typo in GetMBR()
      76                 :  *
      77                 :  * Revision 1.2  1999/09/26 14:59:37  daniel
      78                 :  * Implemented write support
      79                 :  *
      80                 :  * Revision 1.1  1999/07/12 04:18:25  daniel
      81                 :  * Initial checkin
      82                 :  *
      83                 :  **********************************************************************/
      84                 : 
      85                 : #include "mitab.h"
      86                 : 
      87                 : /*=====================================================================
      88                 :  *                      class TABMAPIndexBlock
      89                 :  *====================================================================*/
      90                 : 
      91                 : 
      92                 : /**********************************************************************
      93                 :  *                   TABMAPIndexBlock::TABMAPIndexBlock()
      94                 :  *
      95                 :  * Constructor.
      96                 :  **********************************************************************/
      97               6 : TABMAPIndexBlock::TABMAPIndexBlock(TABAccess eAccessMode /*= TABRead*/):
      98               6 :     TABRawBinBlock(eAccessMode, TRUE)
      99                 : {
     100               6 :     m_numEntries = 0;
     101                 : 
     102               6 :     m_nMinX = 1000000000;
     103               6 :     m_nMinY = 1000000000;
     104               6 :     m_nMaxX = -1000000000;
     105               6 :     m_nMaxY = -1000000000;
     106                 : 
     107               6 :     m_poCurChild = NULL;
     108               6 :     m_nCurChildIndex = -1;
     109               6 :     m_poParentRef = NULL;
     110               6 :     m_poBlockManagerRef = NULL;
     111               6 : }
     112                 : 
     113                 : /**********************************************************************
     114                 :  *                   TABMAPIndexBlock::~TABMAPIndexBlock()
     115                 :  *
     116                 :  * Destructor.
     117                 :  **********************************************************************/
     118               6 : TABMAPIndexBlock::~TABMAPIndexBlock()
     119                 : {
     120               6 :     if (m_poCurChild)
     121                 :     {
     122               0 :         if (m_eAccess == TABWrite || m_eAccess == TABReadWrite)
     123               0 :             m_poCurChild->CommitToFile();
     124               0 :         delete m_poCurChild;
     125                 :     }
     126               6 : }
     127                 : 
     128                 : /**********************************************************************
     129                 :  *                   TABMAPIndexBlock::InitBlockFromData()
     130                 :  *
     131                 :  * Perform some initialization on the block after its binary data has
     132                 :  * been set or changed (or loaded from a file).
     133                 :  *
     134                 :  * Returns 0 if succesful or -1 if an error happened, in which case 
     135                 :  * CPLError() will have been called.
     136                 :  **********************************************************************/
     137               2 : int     TABMAPIndexBlock::InitBlockFromData(GByte *pabyBuf, 
     138                 :                                             int nBlockSize, int nSizeUsed, 
     139                 :                                             GBool bMakeCopy /* = TRUE */,
     140                 :                                             FILE *fpSrc /* = NULL */, 
     141                 :                                             int nOffset /* = 0 */)
     142                 : {
     143                 :     int nStatus;
     144                 : 
     145                 :     /*-----------------------------------------------------------------
     146                 :      * First of all, we must call the base class' InitBlockFromData()
     147                 :      *----------------------------------------------------------------*/
     148                 :     nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, 
     149                 :                                                 nBlockSize, nSizeUsed,
     150                 :                                                 bMakeCopy,
     151               2 :                                                 fpSrc, nOffset);
     152               2 :     if (nStatus != 0)   
     153               0 :         return nStatus;
     154                 : 
     155                 :     /*-----------------------------------------------------------------
     156                 :      * Validate block type
     157                 :      *----------------------------------------------------------------*/
     158               2 :     if (m_nBlockType != TABMAP_INDEX_BLOCK)
     159                 :     {
     160                 :         CPLError(CE_Failure, CPLE_FileIO,
     161                 :                  "InitBlockFromData(): Invalid Block Type: got %d expected %d",
     162               0 :                  m_nBlockType, TABMAP_INDEX_BLOCK);
     163               0 :         CPLFree(m_pabyBuf);
     164               0 :         m_pabyBuf = NULL;
     165               0 :         return -1;
     166                 :     }
     167                 : 
     168                 :     /*-----------------------------------------------------------------
     169                 :      * Init member variables
     170                 :      *----------------------------------------------------------------*/
     171               2 :     GotoByteInBlock(0x002);
     172               2 :     m_numEntries = ReadInt16();
     173                 : 
     174               2 :     if (m_numEntries > 0)
     175               2 :         ReadAllEntries();
     176                 : 
     177               2 :     return 0;
     178                 : }
     179                 : 
     180                 : /**********************************************************************
     181                 :  *                   TABMAPIndexBlock::CommitToFile()
     182                 :  *
     183                 :  * Commit the current state of the binary block to the file to which 
     184                 :  * it has been previously attached.
     185                 :  *
     186                 :  * This method makes sure all values are properly set in the map object
     187                 :  * block header and then calls TABRawBinBlock::CommitToFile() to do
     188                 :  * the actual writing to disk.
     189                 :  *
     190                 :  * Returns 0 if succesful or -1 if an error happened, in which case 
     191                 :  * CPLError() will have been called.
     192                 :  **********************************************************************/
     193               4 : int     TABMAPIndexBlock::CommitToFile()
     194                 : {
     195               4 :     int nStatus = 0;
     196                 : 
     197               4 :     if ( m_pabyBuf == NULL )
     198                 :     {
     199                 :         CPLError(CE_Failure, CPLE_AssertionFailed, 
     200               0 :                  "CommitToFile(): Block has not been initialized yet!");
     201               0 :         return -1;
     202                 :     }
     203                 : 
     204                 :     /*-----------------------------------------------------------------
     205                 :      * Commit child first
     206                 :      *----------------------------------------------------------------*/
     207               4 :     if (m_poCurChild)
     208                 :     {
     209               0 :         if (m_poCurChild->CommitToFile() != 0)
     210               0 :             return -1;
     211                 :     }
     212                 : 
     213                 :     /*-----------------------------------------------------------------
     214                 :      * Nothing to do here if block has not been modified
     215                 :      *----------------------------------------------------------------*/
     216               4 :     if (!m_bModified)
     217               0 :         return 0;
     218                 : 
     219                 :     /*-----------------------------------------------------------------
     220                 :      * Make sure 4 bytes block header is up to date.
     221                 :      *----------------------------------------------------------------*/
     222               4 :     GotoByteInBlock(0x000);
     223                 : 
     224               4 :     WriteInt16(TABMAP_INDEX_BLOCK);    // Block type code
     225               4 :     WriteInt16((GInt16)m_numEntries);
     226                 : 
     227               4 :     nStatus = CPLGetLastErrorNo();
     228                 : 
     229                 :     /*-----------------------------------------------------------------
     230                 :      * Loop through all entries, writing each of them, and calling
     231                 :      * CommitToFile() (recursively) on any child index entries we may
     232                 :      * encounter.
     233                 :      *----------------------------------------------------------------*/
     234               8 :     for(int i=0; nStatus == 0 && i<m_numEntries; i++)
     235                 :     {
     236               4 :         if (nStatus == 0)
     237               4 :             nStatus = WriteNextEntry(&(m_asEntries[i]));
     238                 :     }
     239                 : 
     240                 : 
     241                 :     /*-----------------------------------------------------------------
     242                 :      * OK, call the base class to write the block to disk.
     243                 :      *----------------------------------------------------------------*/
     244               4 :     if (nStatus == 0)
     245               4 :         nStatus = TABRawBinBlock::CommitToFile();
     246                 : 
     247               4 :     return nStatus;
     248                 : }
     249                 : 
     250                 : 
     251                 : /**********************************************************************
     252                 :  *                   TABMAPIndexBlock::InitNewBlock()
     253                 :  *
     254                 :  * Initialize a newly created block so that it knows to which file it
     255                 :  * is attached, its block size, etc . and then perform any specific 
     256                 :  * initialization for this block type, including writing a default 
     257                 :  * block header, etc. and leave the block ready to receive data.
     258                 :  *
     259                 :  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
     260                 :  * that puts the block in a stable state without loading any initial
     261                 :  * data in it.
     262                 :  *
     263                 :  * Returns 0 if succesful or -1 if an error happened, in which case 
     264                 :  * CPLError() will have been called.
     265                 :  **********************************************************************/
     266               4 : int     TABMAPIndexBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, 
     267                 :                                         int nFileOffset /* = 0*/)
     268                 : {
     269                 :     /*-----------------------------------------------------------------
     270                 :      * Start with the default initialisation
     271                 :      *----------------------------------------------------------------*/
     272               4 :     if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
     273               0 :         return -1;
     274                 : 
     275                 :     /*-----------------------------------------------------------------
     276                 :      * And then set default values for the block header.
     277                 :      *----------------------------------------------------------------*/
     278               4 :     m_numEntries = 0;
     279                 : 
     280               4 :     m_nMinX = 1000000000;
     281               4 :     m_nMinY = 1000000000;
     282               4 :     m_nMaxX = -1000000000;
     283               4 :     m_nMaxY = -1000000000;
     284                 : 
     285               4 :     if (m_eAccess != TABRead)
     286                 :     {
     287               4 :         GotoByteInBlock(0x000);
     288                 : 
     289               4 :         WriteInt16(TABMAP_INDEX_BLOCK);     // Block type code
     290               4 :         WriteInt16(0);                      // num. index entries
     291                 :     }
     292                 : 
     293               4 :     if (CPLGetLastErrorNo() != 0)
     294               0 :         return -1;
     295                 : 
     296               4 :     return 0;
     297                 : }
     298                 : 
     299                 : 
     300                 : 
     301                 : /**********************************************************************
     302                 :  *                   TABMAPIndexBlock::ReadNextEntry()
     303                 :  *
     304                 :  * Read the next index entry from the block and fill the sEntry
     305                 :  * structure. 
     306                 :  *
     307                 :  * Returns 0 if succesful or -1 if we reached the end of the block.
     308                 :  **********************************************************************/
     309               2 : int     TABMAPIndexBlock::ReadNextEntry(TABMAPIndexEntry *psEntry)
     310                 : {
     311               2 :     if (m_nCurPos < 4)
     312               0 :         GotoByteInBlock( 0x004 );
     313                 : 
     314               2 :     if (m_nCurPos > 4+(20*m_numEntries) )
     315                 :     {
     316                 :         // End of BLock
     317               0 :         return -1;
     318                 :     }
     319                 : 
     320               2 :     psEntry->XMin = ReadInt32();
     321               2 :     psEntry->YMin = ReadInt32();
     322               2 :     psEntry->XMax = ReadInt32();
     323               2 :     psEntry->YMax = ReadInt32();
     324               2 :     psEntry->nBlockPtr = ReadInt32();
     325                 : 
     326               2 :     if (CPLGetLastErrorNo() != 0)
     327               0 :         return -1;
     328                 : 
     329               2 :     return 0;
     330                 : }
     331                 : 
     332                 : /**********************************************************************
     333                 :  *                   TABMAPIndexBlock::ReadAllEntries()
     334                 :  *
     335                 :  * Init the block by reading all entries from the data block.
     336                 :  *
     337                 :  * Returns 0 if succesful or -1 on error.
     338                 :  **********************************************************************/
     339               2 : int     TABMAPIndexBlock::ReadAllEntries()
     340                 : {
     341               2 :     CPLAssert(m_numEntries <= TAB_MAX_ENTRIES_INDEX_BLOCK);
     342               2 :     if (m_numEntries == 0)
     343               0 :         return 0;
     344                 :     
     345               2 :     if (GotoByteInBlock( 0x004 ) != 0)
     346               0 :         return -1;
     347                 : 
     348               4 :     for(int i=0; i<m_numEntries; i++)
     349                 :     {
     350               2 :         if ( ReadNextEntry(&(m_asEntries[i])) != 0)
     351               0 :             return -1;
     352                 :     }
     353                 : 
     354               2 :     return 0;
     355                 : }
     356                 : 
     357                 : /**********************************************************************
     358                 :  *                   TABMAPIndexBlock::WriteNextEntry()
     359                 :  *
     360                 :  * Write the sEntry index entry at current position in the block.
     361                 :  *
     362                 :  * Returns 0 if succesful or -1 if we reached the end of the block.
     363                 :  **********************************************************************/
     364               4 : int     TABMAPIndexBlock::WriteNextEntry(TABMAPIndexEntry *psEntry)
     365                 : {
     366               4 :     if (m_nCurPos < 4)
     367               0 :         GotoByteInBlock( 0x004 );
     368                 : 
     369               4 :     WriteInt32(psEntry->XMin);
     370               4 :     WriteInt32(psEntry->YMin);
     371               4 :     WriteInt32(psEntry->XMax);
     372               4 :     WriteInt32(psEntry->YMax);
     373               4 :     WriteInt32(psEntry->nBlockPtr);
     374                 : 
     375               4 :     if (CPLGetLastErrorNo() != 0)
     376               0 :         return -1;
     377                 : 
     378               4 :     return 0;
     379                 : }
     380                 : 
     381                 : /**********************************************************************
     382                 :  *                   TABMAPIndexBlock::GetNumFreeEntries()
     383                 :  *
     384                 :  * Return the number of available entries in this block.
     385                 :  *
     386                 :  * __TODO__ This function could eventually be improved to search
     387                 :  *          children leaves as well.
     388                 :  **********************************************************************/
     389               8 : int     TABMAPIndexBlock::GetNumFreeEntries()
     390                 : {
     391                 :     /* nMaxEntries = (m_nBlockSize-4)/20;*/
     392                 : 
     393               8 :     return (TAB_MAX_ENTRIES_INDEX_BLOCK - m_numEntries);
     394                 : }
     395                 : 
     396                 : /**********************************************************************
     397                 :  *                   TABMAPIndexBlock::GetEntry()
     398                 :  *
     399                 :  * Fetch a reference to the requested entry.
     400                 :  *
     401                 :  * @param iIndex index of entry, must be from 0 to GetNumEntries()-1. 
     402                 :  *
     403                 :  * @return a reference to the internal copy of the entry, or NULL if out
     404                 :  * of range. 
     405                 :  **********************************************************************/
     406               2 : TABMAPIndexEntry *TABMAPIndexBlock::GetEntry( int iIndex )
     407                 : {
     408               2 :     if( iIndex < 0 || iIndex >= m_numEntries )
     409               0 :         return NULL;
     410                 : 
     411               2 :     return m_asEntries + iIndex;
     412                 : }
     413                 : 
     414                 : /**********************************************************************
     415                 :  *                   TABMAPIndexBlock::GetCurMaxDepth()
     416                 :  *
     417                 :  * Return maximum depth in the currently loaded part of the index tree
     418                 :  **********************************************************************/
     419              16 : int     TABMAPIndexBlock::GetCurMaxDepth()
     420                 : {
     421              16 :     if (m_poCurChild)
     422               0 :         return m_poCurChild->GetCurMaxDepth() + 1;
     423                 : 
     424              16 :     return 1;  /* No current child... this node counts for one. */
     425                 : }
     426                 : 
     427                 : /**********************************************************************
     428                 :  *                   TABMAPIndexBlock::GetMBR()
     429                 :  *
     430                 :  * Return the MBR for the current block.
     431                 :  **********************************************************************/
     432               4 : void TABMAPIndexBlock::GetMBR(GInt32 &nXMin, GInt32 &nYMin, 
     433                 :                                      GInt32 &nXMax, GInt32 &nYMax)
     434                 : {
     435               4 :     nXMin = m_nMinX;
     436               4 :     nYMin = m_nMinY;
     437               4 :     nXMax = m_nMaxX;
     438               4 :     nYMax = m_nMaxY; 
     439               4 : }
     440                 : 
     441                 : /**********************************************************************
     442                 :  *                   TABMAPIndexBlock::InsertEntry()
     443                 :  *
     444                 :  * Add a new entry to this index block.  It is assumed that there is at
     445                 :  * least one free slot available, so if the block has to be split then it
     446                 :  * should have been done prior to calling this function.
     447                 :  *
     448                 :  * Returns 0 on success, -1 on error.
     449                 :  **********************************************************************/
     450               4 : int     TABMAPIndexBlock::InsertEntry(GInt32 nXMin, GInt32 nYMin,
     451                 :                                       GInt32 nXMax, GInt32 nYMax,
     452                 :                                       GInt32 nBlockPtr)
     453                 : {
     454               4 :     if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
     455                 :     {
     456                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     457               0 :                "Failed adding index entry: File not opened for write access.");
     458               0 :         return -1;
     459                 :     }
     460                 : 
     461               4 :     if (GetNumFreeEntries() < 1)
     462                 :     {
     463                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     464               0 :                  "Current Block Index is full, cannot add new entry.");
     465               0 :         return -1;
     466                 :     }
     467                 : 
     468                 :     /*-----------------------------------------------------------------
     469                 :      * Update count of entries and store new entry.
     470                 :      *----------------------------------------------------------------*/
     471               4 :     m_numEntries++;
     472               4 :     CPLAssert(m_numEntries <= TAB_MAX_ENTRIES_INDEX_BLOCK);
     473                 : 
     474               4 :     m_asEntries[m_numEntries-1].XMin = nXMin;
     475               4 :     m_asEntries[m_numEntries-1].YMin = nYMin;
     476               4 :     m_asEntries[m_numEntries-1].XMax = nXMax;
     477               4 :     m_asEntries[m_numEntries-1].YMax = nYMax;
     478               4 :     m_asEntries[m_numEntries-1].nBlockPtr = nBlockPtr;
     479                 : 
     480               4 :     m_bModified = TRUE;
     481                 : 
     482               4 :     return 0;
     483                 : }
     484                 : 
     485                 : /**********************************************************************
     486                 :  *                   TABMAPIndexBlock::ChooseSubEntryForInsert()
     487                 :  *
     488                 :  * Select the entry in this index block in which the new entry should 
     489                 :  * be inserted. The criteria used is to select the node whose MBR needs
     490                 :  * the least enlargement to include the new entry. We resolve ties by
     491                 :  * chosing the entry with the rectangle of smallest area.
     492                 :  * (This is the ChooseSubtree part of Guttman's "ChooseLeaf" algorithm.)
     493                 :  *
     494                 :  * Returns the index of the best candidate or -1 of node is empty.
     495                 :  **********************************************************************/
     496               0 : int     TABMAPIndexBlock::ChooseSubEntryForInsert(GInt32 nXMin, GInt32 nYMin,
     497                 :                                                   GInt32 nXMax, GInt32 nYMax)
     498                 : {
     499               0 :     GInt32 i, nBestCandidate=-1;
     500                 : 
     501               0 :     double dOptimalAreaDiff=0;
     502                 : 
     503               0 :     double dNewEntryArea = MITAB_AREA(nXMin, nYMin, nXMax, nYMax);
     504                 : 
     505               0 :     for(i=0; i<m_numEntries; i++)
     506                 :     {
     507               0 :         double dAreaDiff = 0;
     508               0 :         double dAreaBefore = MITAB_AREA(m_asEntries[i].XMin,
     509                 :                                         m_asEntries[i].YMin,
     510                 :                                         m_asEntries[i].XMax,
     511                 :                                         m_asEntries[i].YMax);
     512                 : 
     513                 :         /* Does this entry fully contain the new entry's MBR ?
     514                 :          */
     515                 :         GBool bIsContained = (nXMin >= m_asEntries[i].XMin &&
     516                 :                               nYMin >= m_asEntries[i].YMin &&
     517                 :                               nXMax <= m_asEntries[i].XMax &&
     518               0 :                               nYMax <= m_asEntries[i].YMax );
     519                 : 
     520               0 :         if (bIsContained)
     521                 :         {
     522                 :             /* If new entry is fully contained in this entry then
     523                 :              * the area difference will be the difference between the area
     524                 :              * of the entry to insert and the area of m_asEntries[i]
     525                 :              *
     526                 :              * The diff value is negative in this case.
     527                 :              */
     528               0 :             dAreaDiff = dNewEntryArea - dAreaBefore;
     529                 :         }
     530                 :         else
     531                 :         {
     532                 :             /* Need to calculate the expanded MBR to calculate the area
     533                 :              * difference.
     534                 :              */
     535                 :             GInt32 nXMin2, nYMin2, nXMax2, nYMax2;
     536               0 :             nXMin2 = MIN(m_asEntries[i].XMin, nXMin);
     537               0 :             nYMin2 = MIN(m_asEntries[i].YMin, nYMin);
     538               0 :             nXMax2 = MAX(m_asEntries[i].XMax, nXMax);
     539               0 :             nYMax2 = MAX(m_asEntries[i].YMax, nYMax);
     540                 : 
     541               0 :             dAreaDiff = MITAB_AREA(nXMin2,nYMin2,nXMax2,nYMax2) - dAreaBefore;
     542                 :         }
     543                 : 
     544                 :         /* Is this a better candidate?
     545                 :          * Note, possible Optimization: In case of tie, we could to pick the 
     546                 :          * candidate with the smallest area
     547                 :          */
     548                 : 
     549               0 :         if (/* No best candidate yet */
     550                 :             (nBestCandidate == -1)
     551                 :             /* or current candidate is contained and best candidate is not contained */
     552                 :             || (dAreaDiff < 0 && dOptimalAreaDiff >= 0)
     553                 :             /* or if both are either contained or not contained then use the one
     554                 :              * with the smallest area diff, which means maximum coverage in the case
     555                 :              * of contained rects, or minimum area increase when not contained 
     556                 :              */
     557                 :             || (((dOptimalAreaDiff < 0 && dAreaDiff < 0) ||
     558                 :                  (dOptimalAreaDiff > 0 && dAreaDiff > 0)) && 
     559                 :                 ABS(dAreaDiff) < ABS(dOptimalAreaDiff)) )
     560                 :         {
     561               0 :             nBestCandidate = i;
     562               0 :             dOptimalAreaDiff = dAreaDiff;
     563                 :         }
     564                 : 
     565                 :     }
     566                 : 
     567               0 :     return nBestCandidate;
     568                 : }
     569                 : 
     570                 : /**********************************************************************
     571                 :  *                   TABMAPIndexBlock::ChooseLeafForInsert()
     572                 :  *
     573                 :  * Recursively search the tree until we find the best leaf to
     574                 :  * contain the specified object MBR.
     575                 :  *
     576                 :  * Returns the nBlockPtr of the selected leaf node entry (should be a
     577                 :  * ref to a TABMAPObjectBlock) or -1 on error.
     578                 :  *
     579                 :  * After this call, m_poCurChild will be pointing at the selected child 
     580                 :  * node, for use by later calls to UpdateLeafEntry()
     581                 :  **********************************************************************/
     582               0 : GInt32  TABMAPIndexBlock::ChooseLeafForInsert(GInt32 nXMin, GInt32 nYMin,
     583                 :                                               GInt32 nXMax, GInt32 nYMax)
     584                 : {
     585               0 :     GBool bFound = FALSE;
     586                 : 
     587               0 :     if (m_numEntries < 0)
     588               0 :         return -1;
     589                 : 
     590                 :     /*-----------------------------------------------------------------
     591                 :      * Look for the best candidate to contain the new entry
     592                 :      *----------------------------------------------------------------*/
     593                 : 
     594                 :     // Make sure blocks currently in memory are written to disk.
     595                 :     // TODO: Could we avoid deleting m_poCurChild if it's already 
     596                 :     //       the best candidate for insert?
     597               0 :     if (m_poCurChild)
     598                 :     {
     599               0 :         m_poCurChild->CommitToFile();
     600               0 :         delete m_poCurChild;
     601               0 :         m_poCurChild = NULL;
     602               0 :         m_nCurChildIndex = -1;
     603                 :     }
     604                 : 
     605               0 :     int nBestCandidate = ChooseSubEntryForInsert(nXMin,nYMin,nXMax,nYMax);
     606                 :        
     607               0 :     CPLAssert(nBestCandidate != -1);
     608               0 :     if (nBestCandidate == -1)
     609               0 :         return -1;  /* This should never happen! */
     610                 : 
     611                 :     // Try to load corresponding child... if it fails then we are
     612                 :     // likely in a leaf node, so we'll add the new entry in the current
     613                 :     // node.
     614               0 :     TABRawBinBlock *poBlock = NULL;
     615                 : 
     616                 :     // Prevent error message if referred block not committed yet.
     617               0 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     618                 : 
     619               0 :     if ((poBlock = TABCreateMAPBlockFromFile(m_fp, 
     620                 :                                     m_asEntries[nBestCandidate].nBlockPtr,
     621                 :                                     512, TRUE, TABReadWrite)) &&
     622               0 :         poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK)
     623                 :     {
     624               0 :         m_poCurChild = (TABMAPIndexBlock*)poBlock;
     625               0 :         poBlock = NULL;
     626               0 :         m_nCurChildIndex = nBestCandidate;
     627               0 :         m_poCurChild->SetParentRef(this);
     628               0 :         m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef);
     629               0 :         bFound = TRUE;
     630                 :     }
     631                 :                 
     632               0 :     if (poBlock)
     633               0 :         delete poBlock;
     634                 :             
     635               0 :     CPLPopErrorHandler();
     636               0 :     CPLErrorReset();
     637                 : 
     638                 : 
     639               0 :     if (bFound)
     640                 :     {
     641                 :         /*-------------------------------------------------------------
     642                 :          * Found a child leaf... pass the call to it.
     643                 :          *------------------------------------------------------------*/
     644               0 :         return m_poCurChild->ChooseLeafForInsert(nXMin, nYMin, nXMax, nYMax);
     645                 :     }
     646                 : 
     647                 :     /*-------------------------------------------------------------
     648                 :      * Found no child index node... we must be at the leaf level 
     649                 :      * (leaf points at map object data blocks) so we return a ref 
     650                 :      * to the TABMAPObjBlock for insertion
     651                 :      *------------------------------------------------------------*/
     652               0 :     return m_asEntries[nBestCandidate].nBlockPtr;
     653                 : }
     654                 : 
     655                 : 
     656                 : /**********************************************************************
     657                 :  *                   TABMAPIndexBlock::GetCurLeafEntryMBR()
     658                 :  *
     659                 :  * Get the MBR for specified nBlockPtr in the leaf at the end of the
     660                 :  * chain of m_poCurChild refs.
     661                 :  *
     662                 :  * This method requires that the chain of m_poCurChild refs already point
     663                 :  * to a leaf that contains the specified nBlockPtr, it is usually called
     664                 :  * right after ChooseLeafForInsert().
     665                 :  *
     666                 :  * Returns 0 on success, -1 on error.
     667                 :  **********************************************************************/
     668               0 : int     TABMAPIndexBlock::GetCurLeafEntryMBR(GInt32 nBlockPtr,
     669                 :                                              GInt32 &nXMin, GInt32 &nYMin,
     670                 :                                              GInt32 &nXMax, GInt32 &nYMax)
     671                 : {
     672               0 :     if (m_poCurChild)
     673                 :     {
     674                 :         /* Pass the call down to current child */
     675                 :         return m_poCurChild->GetCurLeafEntryMBR(nBlockPtr,
     676               0 :                                                 nXMin, nYMin, nXMax, nYMax);
     677                 :     }
     678                 : 
     679                 :     /* We're at the leaf level, look for the entry */
     680               0 :     for(int i=0; i<m_numEntries; i++)
     681                 :     {
     682               0 :         if (m_asEntries[i].nBlockPtr == nBlockPtr)
     683                 :         {
     684                 :             /* Found it. Return its MBR */
     685               0 :             nXMin = m_asEntries[i].XMin;
     686               0 :             nYMin = m_asEntries[i].YMin;
     687               0 :             nXMax = m_asEntries[i].XMax;
     688               0 :             nYMax = m_asEntries[i].YMax;
     689                 : 
     690               0 :             return 0;
     691                 :         }
     692                 :     }
     693                 : 
     694                 :     /* Not found! This should not happen if method is used properly. */
     695                 :     CPLError(CE_Failure, CPLE_AssertionFailed,
     696               0 :              "Entry to update not found in GetCurLeafEntryMBR()!");
     697               0 :     return -1; 
     698                 : 
     699                 : }
     700                 : 
     701                 : 
     702                 : /**********************************************************************
     703                 :  *                   TABMAPIndexBlock::UpdateLeafEntry()
     704                 :  *
     705                 :  * Update the MBR for specified nBlockPtr in the leaf at the end of the
     706                 :  * chain of m_poCurChild refs and update MBR of parents if required.
     707                 :  *
     708                 :  * This method requires that the chain of m_poCurChild refs already point
     709                 :  * to a leaf that contains the specified nBlockPtr, it is usually called
     710                 :  * right after ChooseLeafForInsert().
     711                 :  *
     712                 :  * Returns 0 on success, -1 on error.
     713                 :  **********************************************************************/
     714               0 : int     TABMAPIndexBlock::UpdateLeafEntry(GInt32 nBlockPtr,
     715                 :                                           GInt32 nXMin, GInt32 nYMin,
     716                 :                                           GInt32 nXMax, GInt32 nYMax )
     717                 : {
     718               0 :     if (m_poCurChild)
     719                 :     {
     720                 :         /* Pass the call down to current child */
     721                 :         return m_poCurChild->UpdateLeafEntry(nBlockPtr,
     722               0 :                                              nXMin, nYMin, nXMax, nYMax);
     723                 :     }
     724                 : 
     725                 :     /* We're at the leaf level, look for the entry to update */
     726               0 :     for(int i=0; i<m_numEntries; i++)
     727                 :     {
     728               0 :         if (m_asEntries[i].nBlockPtr == nBlockPtr)
     729                 :         {
     730                 :             /* Found it. */
     731               0 :             TABMAPIndexEntry *psEntry = &m_asEntries[i];
     732                 : 
     733               0 :             if (psEntry->XMin != nXMin ||
     734                 :                 psEntry->YMin != nYMin ||
     735                 :                 psEntry->XMax != nXMax ||
     736                 :                 psEntry->YMax != nYMax )
     737                 :             {
     738                 :                 /* MBR changed. Update MBR of entry */
     739               0 :                 psEntry->XMin = nXMin;
     740               0 :                 psEntry->YMin = nYMin;
     741               0 :                 psEntry->XMax = nXMax;
     742               0 :                 psEntry->YMax = nYMax;
     743                 : 
     744               0 :                 m_bModified = TRUE;
     745                 : 
     746                 :                 /* Update MBR of this node and all parents */
     747               0 :                 RecomputeMBR();
     748                 :             }
     749                 : 
     750               0 :             return 0;
     751                 :         }
     752                 :     }
     753                 : 
     754                 :     /* Not found! This should not happen if method is used properly. */
     755                 :     CPLError(CE_Failure, CPLE_AssertionFailed,
     756               0 :              "Entry to update not found in UpdateLeafEntry()!");
     757               0 :     return -1; 
     758                 : }
     759                 : 
     760                 : 
     761                 : /**********************************************************************
     762                 :  *                   TABMAPIndexBlock::AddEntry()
     763                 :  *
     764                 :  * Recursively search the tree until we encounter the best leaf to
     765                 :  * contain the specified object MBR and add the new entry to it.
     766                 :  *
     767                 :  * In the even that the selected leaf node would be full, then it will be
     768                 :  * split and this split can propagate up to its parent, etc.
     769                 :  *
     770                 :  * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and
     771                 :  * we do not try to update the child node.  This is used when the parent 
     772                 :  * of a node that is being splitted has to be updated.
     773                 :  *
     774                 :  * Returns 0 on success, -1 on error.
     775                 :  **********************************************************************/
     776               4 : int     TABMAPIndexBlock::AddEntry(GInt32 nXMin, GInt32 nYMin,
     777                 :                                    GInt32 nXMax, GInt32 nYMax,
     778                 :                                    GInt32 nBlockPtr,
     779                 :                                    GBool bAddInThisNodeOnly /*=FALSE*/)
     780                 : {
     781               4 :     GBool bFound = FALSE;
     782                 : 
     783               4 :     if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
     784                 :     {
     785                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     786               0 :                "Failed adding index entry: File not opened for write access.");
     787               0 :         return -1;
     788                 :     }
     789                 : 
     790                 :     /*-----------------------------------------------------------------
     791                 :      * Look for the best candidate to contain the new entry
     792                 :      *----------------------------------------------------------------*/
     793                 : 
     794                 :     /*-----------------------------------------------------------------
     795                 :      * If bAddInThisNodeOnly=TRUE then we add the entry only locally
     796                 :      * and do not need to look for the proper leaf to insert it.
     797                 :      *----------------------------------------------------------------*/
     798               4 :     if (bAddInThisNodeOnly)
     799               0 :         bFound = TRUE;
     800                 : 
     801               4 :     if (!bFound && m_numEntries > 0)
     802                 :     {
     803                 :         // Make sure blocks currently in memory are written to disk.
     804               0 :         if (m_poCurChild)
     805                 :         {
     806               0 :             m_poCurChild->CommitToFile();
     807               0 :             delete m_poCurChild;
     808               0 :             m_poCurChild = NULL;
     809               0 :             m_nCurChildIndex = -1;
     810                 :         }
     811                 : 
     812               0 :         int nBestCandidate = ChooseSubEntryForInsert(nXMin,nYMin,nXMax,nYMax);
     813                 :        
     814               0 :         CPLAssert(nBestCandidate != -1);
     815                 : 
     816               0 :         if (nBestCandidate != -1)
     817                 :         {
     818                 :             // Try to load corresponding child... if it fails then we are
     819                 :             // likely in a leaf node, so we'll add the new entry in the current
     820                 :             // node.
     821               0 :             TABRawBinBlock *poBlock = NULL;
     822                 : 
     823                 :             // Prevent error message if referred block not committed yet.
     824               0 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     825                 : 
     826               0 :             if ((poBlock = TABCreateMAPBlockFromFile(m_fp, 
     827                 :                                        m_asEntries[nBestCandidate].nBlockPtr,
     828                 :                                        512, TRUE, TABReadWrite)) &&
     829               0 :                 poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK)
     830                 :             {
     831               0 :                 m_poCurChild = (TABMAPIndexBlock*)poBlock;
     832               0 :                 poBlock = NULL;
     833               0 :                 m_nCurChildIndex = nBestCandidate;
     834               0 :                 m_poCurChild->SetParentRef(this);
     835               0 :                 m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef);
     836               0 :                 bFound = TRUE;
     837                 :             }
     838                 :                 
     839               0 :             if (poBlock)
     840               0 :                 delete poBlock;
     841                 :             
     842               0 :             CPLPopErrorHandler();
     843               0 :             CPLErrorReset();
     844                 :         }
     845                 :     }
     846                 : 
     847               4 :     if (bFound && !bAddInThisNodeOnly)
     848                 :     {
     849                 :         /*-------------------------------------------------------------
     850                 :          * Found a child leaf... pass the call to it.
     851                 :          *------------------------------------------------------------*/
     852               0 :         if (m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0)
     853               0 :             return -1;
     854                 :     }
     855                 :     else
     856                 :     {
     857                 :         /*-------------------------------------------------------------
     858                 :          * Found no child to store new object... we're likely at the leaf
     859                 :          * level so we'll store new object in current node
     860                 :          *------------------------------------------------------------*/
     861                 : 
     862                 :         /*-------------------------------------------------------------
     863                 :          * First thing to do is make sure that there is room for a new
     864                 :          * entry in this node, and to split it if necessary.
     865                 :          *------------------------------------------------------------*/
     866               4 :         if (GetNumFreeEntries() < 1)
     867                 :         {
     868               0 :             if (m_poParentRef == NULL)
     869                 :             {
     870                 :                 /*-----------------------------------------------------
     871                 :                  * Splitting the root node adds one level to the tree, so
     872                 :                  * after splitting we just redirect the call to the new
     873                 :                  * child that's just been created.
     874                 :                  *----------------------------------------------------*/
     875               0 :                 if (SplitRootNode(nXMin, nYMin, nXMax, nYMax) != 0)
     876               0 :                     return -1;  // Error happened and has already been reported
     877                 : 
     878               0 :                 CPLAssert(m_poCurChild);
     879                 :                 return m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax,
     880               0 :                                               nBlockPtr, TRUE);
     881                 :             }
     882                 :             else
     883                 :             {
     884                 :                 /*-----------------------------------------------------
     885                 :                  * Splitting a regular node
     886                 :                  *----------------------------------------------------*/
     887               0 :                 if (SplitNode(nXMin, nYMin, nXMax, nYMax) != 0)
     888               0 :                     return -1; 
     889                 :             }
     890                 :         }
     891                 : 
     892               4 :         if (InsertEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0)
     893               0 :             return -1;
     894                 :     }
     895                 : 
     896                 :     /*-----------------------------------------------------------------
     897                 :      * Update current node MBR and the reference to it in our parent.
     898                 :      *----------------------------------------------------------------*/
     899               4 :     RecomputeMBR();
     900                 : 
     901               4 :     return 0;
     902                 : }
     903                 : 
     904                 : /**********************************************************************
     905                 :  *                   TABMAPIndexBlock::ComputeAreaDiff()
     906                 :  *
     907                 :  * (static method, also used by the TABMAPObjBlock class)
     908                 :  *
     909                 :  * Compute the area difference between two MBRs. Used in the SplitNode
     910                 :  * algorithm to decide to which of the two nodes an entry should be added.
     911                 :  *
     912                 :  * The returned AreaDiff value is positive if NodeMBR has to be enlarged
     913                 :  * and negative if new Entry is fully contained in the NodeMBR.
     914                 :  **********************************************************************/
     915               0 : double  TABMAPIndexBlock::ComputeAreaDiff(GInt32 nNodeXMin, GInt32 nNodeYMin,
     916                 :                                           GInt32 nNodeXMax, GInt32 nNodeYMax,
     917                 :                                           GInt32 nEntryXMin, GInt32 nEntryYMin,
     918                 :                                           GInt32 nEntryXMax, GInt32 nEntryYMax)
     919                 : {
     920                 : 
     921               0 :     double dAreaDiff=0;
     922                 : 
     923               0 :     double dNodeAreaBefore = MITAB_AREA(nNodeXMin,
     924                 :                                         nNodeYMin,
     925                 :                                         nNodeXMax,
     926                 :                                         nNodeYMax);
     927                 : 
     928                 :     /* Does the node fully contain the new entry's MBR ?
     929                 :      */
     930                 :     GBool bIsContained = (nEntryXMin >= nNodeXMin &&
     931                 :                           nEntryYMin >= nNodeYMin &&
     932                 :                           nEntryXMax <= nNodeXMax &&
     933               0 :                           nEntryYMax <= nNodeYMax );
     934                 : 
     935               0 :     if (bIsContained)
     936                 :     {
     937                 :         /* If new entry is fully contained in this entry then
     938                 :          * the area difference will be the difference between the area
     939                 :          * of the entry to insert and the area of the node
     940                 :          */
     941                 :         dAreaDiff = MITAB_AREA(nEntryXMin, nEntryYMin, 
     942               0 :                                nEntryXMax, nEntryYMax) - dNodeAreaBefore;
     943                 :     }
     944                 :     else
     945                 :     {
     946                 :         /* Need to calculate the expanded MBR to calculate the area
     947                 :          * difference.
     948                 :          */
     949               0 :         nNodeXMin = MIN(nNodeXMin, nEntryXMin);
     950               0 :         nNodeYMin = MIN(nNodeYMin, nEntryYMin);
     951               0 :         nNodeXMax = MAX(nNodeXMax, nEntryXMax);
     952               0 :         nNodeYMax = MAX(nNodeYMax, nEntryYMax);
     953                 : 
     954                 :         dAreaDiff = MITAB_AREA(nNodeXMin,nNodeYMin,
     955               0 :                                nNodeXMax,nNodeYMax) - dNodeAreaBefore;
     956                 :     }
     957                 : 
     958               0 :     return dAreaDiff;
     959                 : }
     960                 : 
     961                 : 
     962                 : 
     963                 : /**********************************************************************
     964                 :  *                   TABMAPIndexBlock::PickSeedsForSplit()
     965                 :  *
     966                 :  * (static method, also used by the TABMAPObjBlock class)
     967                 :  *
     968                 :  * Pick two seeds to use to start splitting this node.
     969                 :  *
     970                 :  * Guttman's LinearPickSeed:
     971                 :  * - Along each dimension find the entry whose rectangle has the 
     972                 :  *   highest low side, and the one with the lowest high side
     973                 :  * - Calculate the separation for each pair
     974                 :  * - Normalize the separation by dividing by the extents of the 
     975                 :  *   corresponding dimension
     976                 :  * - Choose the pair with the greatest normalized separation along
     977                 :  *   any dimension
     978                 :  **********************************************************************/
     979               0 : int  TABMAPIndexBlock::PickSeedsForSplit(TABMAPIndexEntry *pasEntries,
     980                 :                                          int numEntries,
     981                 :                                          int nSrcCurChildIndex,
     982                 :                                          GInt32 nNewEntryXMin, 
     983                 :                                          GInt32 nNewEntryYMin,
     984                 :                                          GInt32 nNewEntryXMax, 
     985                 :                                          GInt32 nNewEntryYMax,
     986                 :                                          int &nSeed1, int &nSeed2)
     987                 : {
     988               0 :     GInt32 nSrcMinX=0, nSrcMinY=0, nSrcMaxX=0, nSrcMaxY=0;
     989               0 :     int nLowestMaxX=-1, nHighestMinX=-1, nLowestMaxY=-1, nHighestMinY=-1;
     990               0 :     GInt32 nLowestMaxXId=-1, nHighestMinXId=-1, nLowestMaxYId=-1, nHighestMinYId=-1;
     991                 : 
     992               0 :     nSeed1=-1;
     993               0 :     nSeed2=-1;
     994                 : 
     995                 :     // Along each dimension find the entry whose rectangle has the 
     996                 :     // highest low side, and the one with the lowest high side
     997               0 :     for(int iEntry=0; iEntry<numEntries; iEntry++)
     998                 :     {
     999               0 :         if (nLowestMaxXId == -1 ||
    1000               0 :             pasEntries[iEntry].XMax < nLowestMaxX)
    1001                 :         {
    1002               0 :             nLowestMaxX = pasEntries[iEntry].XMax;
    1003               0 :             nLowestMaxXId = iEntry;
    1004                 :         }
    1005                 : 
    1006               0 :         if (nHighestMinXId == -1 ||
    1007               0 :             pasEntries[iEntry].XMin > nHighestMinX)
    1008                 :         {
    1009               0 :             nHighestMinX = pasEntries[iEntry].XMin;
    1010               0 :             nHighestMinXId = iEntry;
    1011                 :         }
    1012                 : 
    1013               0 :         if (nLowestMaxYId == -1 ||
    1014               0 :             pasEntries[iEntry].YMax < nLowestMaxY)
    1015                 :         {
    1016               0 :             nLowestMaxY = pasEntries[iEntry].YMax;
    1017               0 :             nLowestMaxYId = iEntry;
    1018                 :         }
    1019                 : 
    1020               0 :         if (nHighestMinYId == -1 ||
    1021               0 :             pasEntries[iEntry].YMin > nHighestMinY)
    1022                 :         {
    1023               0 :             nHighestMinY = pasEntries[iEntry].YMin;
    1024               0 :             nHighestMinYId = iEntry;
    1025                 :         }
    1026                 : 
    1027                 :         // Also keep track of MBR of all entries
    1028               0 :         if (iEntry == 0)
    1029                 :         {
    1030               0 :             nSrcMinX = pasEntries[iEntry].XMin;
    1031               0 :             nSrcMinY = pasEntries[iEntry].YMin;
    1032               0 :             nSrcMaxX = pasEntries[iEntry].XMax;
    1033               0 :             nSrcMaxY = pasEntries[iEntry].YMax;
    1034                 :         }
    1035                 :         else
    1036                 :         {
    1037               0 :             nSrcMinX = MIN(nSrcMinX, pasEntries[iEntry].XMin);
    1038               0 :             nSrcMinY = MIN(nSrcMinY ,pasEntries[iEntry].YMin);
    1039               0 :             nSrcMaxX = MAX(nSrcMaxX ,pasEntries[iEntry].XMax);
    1040               0 :             nSrcMaxY = MAX(nSrcMaxY ,pasEntries[iEntry].YMax);
    1041                 :         }
    1042                 :     }
    1043                 : 
    1044                 :     int nSrcWidth, nSrcHeight;
    1045               0 :     nSrcWidth = ABS(nSrcMaxX - nSrcMinX);
    1046               0 :     nSrcHeight = ABS(nSrcMaxY - nSrcMinY);
    1047                 : 
    1048                 :     // Calculate the separation for each pair (note that it may be negative
    1049                 :     // in case of overlap)
    1050                 :     // Normalize the separation by dividing by the extents of the 
    1051                 :     // corresponding dimension
    1052                 :     double dX, dY;
    1053                 : 
    1054               0 :     dX = (double)(nHighestMinX - nLowestMaxX) / nSrcWidth;
    1055               0 :     dY = (double)(nHighestMinY - nLowestMaxY) / nSrcHeight;
    1056                 : 
    1057                 :     // Choose the pair with the greatest normalized separation along
    1058                 :     // any dimension
    1059               0 :     if (dX > dY)
    1060                 :     {
    1061               0 :         nSeed1 = nHighestMinXId;
    1062               0 :         nSeed2 = nLowestMaxXId;
    1063                 :     }
    1064                 :     else
    1065                 :     {
    1066               0 :         nSeed1 = nHighestMinYId;
    1067               0 :         nSeed2 = nLowestMaxYId;
    1068                 :     }
    1069                 : 
    1070                 :     // If nSeed1==nSeed2 then just pick any two (giving pref to current child)
    1071               0 :     if (nSeed1 == nSeed2)
    1072                 :     {
    1073               0 :         if (nSeed1 != nSrcCurChildIndex && nSrcCurChildIndex != -1)
    1074               0 :             nSeed1 = nSrcCurChildIndex;
    1075               0 :         else if (nSeed1 != 0)
    1076               0 :             nSeed1 = 0;
    1077                 :         else
    1078               0 :             nSeed1 = 1;
    1079                 :     }
    1080                 : 
    1081                 :     // Decide which of the two seeds best matches the new entry. That seed and
    1082                 :     // the new entry will stay in current node (new entry will be added by the
    1083                 :     // caller later). The other seed will go in the 2nd node
    1084                 :     double dAreaDiff1, dAreaDiff2;
    1085               0 :     dAreaDiff1 = ComputeAreaDiff(pasEntries[nSeed1].XMin, 
    1086               0 :                                  pasEntries[nSeed1].YMin,
    1087               0 :                                  pasEntries[nSeed1].XMax,
    1088               0 :                                  pasEntries[nSeed1].YMax,
    1089                 :                                  nNewEntryXMin, nNewEntryYMin,
    1090               0 :                                  nNewEntryXMax, nNewEntryYMax);
    1091                 : 
    1092               0 :     dAreaDiff2 = ComputeAreaDiff(pasEntries[nSeed2].XMin, 
    1093               0 :                                  pasEntries[nSeed2].YMin,
    1094               0 :                                  pasEntries[nSeed2].XMax,
    1095               0 :                                  pasEntries[nSeed2].YMax,
    1096                 :                                  nNewEntryXMin, nNewEntryYMin,
    1097               0 :                                  nNewEntryXMax, nNewEntryYMax);
    1098                 : 
    1099                 :     /* Note that we want to keep this node's current child in here.
    1100                 :      * Since splitting happens only during an addentry() operation and 
    1101                 :      * then both the current child and the New Entry should fit in the same
    1102                 :      * area.
    1103                 :      */
    1104               0 :     if (nSeed1 != nSrcCurChildIndex && 
    1105                 :         (dAreaDiff1 > dAreaDiff2 || nSeed2 == nSrcCurChildIndex))
    1106                 :     {
    1107                 :         // Seed2 stays in this node, Seed1 moves to new node
    1108                 :         // ... swap Seed1 and Seed2 indices
    1109               0 :         int nTmp = nSeed1;
    1110               0 :         nSeed1 = nSeed2;
    1111               0 :         nSeed2 = nTmp;
    1112                 :     }
    1113                 : 
    1114               0 :     return 0;
    1115                 : }
    1116                 : 
    1117                 : 
    1118                 : /**********************************************************************
    1119                 :  *                   TABMAPIndexBlock::SplitNode()
    1120                 :  *
    1121                 :  * Split current Node, update the references in the parent node, etc.
    1122                 :  * Note that Root Nodes cannot be split using this method... SplitRootNode()
    1123                 :  * should be used instead.
    1124                 :  *
    1125                 :  * nNewEntry* are the coord. of the new entry that 
    1126                 :  * will be added after the split.  The split is done so that the current
    1127                 :  * node will be the one in which the new object should be stored.
    1128                 :  *
    1129                 :  * Returns 0 on success, -1 on error.
    1130                 :  **********************************************************************/
    1131               0 : int     TABMAPIndexBlock::SplitNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin,
    1132                 :                                     GInt32 nNewEntryXMax, GInt32 nNewEntryYMax)
    1133                 : {
    1134               0 :     CPLAssert(m_poBlockManagerRef);
    1135                 : 
    1136                 :     /*-----------------------------------------------------------------
    1137                 :      * Create a 2nd node
    1138                 :      *----------------------------------------------------------------*/
    1139               0 :     TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess);
    1140               0 :     if (poNewNode->InitNewBlock(m_fp, 512, 
    1141               0 :                                 m_poBlockManagerRef->AllocNewBlock()) != 0)
    1142                 :     {
    1143               0 :         return -1;
    1144                 :     }
    1145               0 :     poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef);
    1146                 : 
    1147                 :     /*-----------------------------------------------------------------
    1148                 :      * Make a temporary copy of the entries in current node
    1149                 :      *----------------------------------------------------------------*/
    1150               0 :     int nSrcEntries = m_numEntries;
    1151               0 :     TABMAPIndexEntry *pasSrcEntries = (TABMAPIndexEntry*)CPLMalloc(m_numEntries*sizeof(TABMAPIndexEntry));
    1152               0 :     memcpy(pasSrcEntries, &m_asEntries, m_numEntries*sizeof(TABMAPIndexEntry));
    1153                 : 
    1154               0 :     int nSrcCurChildIndex = m_nCurChildIndex;
    1155                 : 
    1156                 :     /*-----------------------------------------------------------------
    1157                 :      * Pick Seeds for each node
    1158                 :      *----------------------------------------------------------------*/
    1159                 :     int nSeed1, nSeed2;
    1160                 :     PickSeedsForSplit(pasSrcEntries, nSrcEntries, nSrcCurChildIndex,
    1161                 :                       nNewEntryXMin, nNewEntryYMin, 
    1162                 :                       nNewEntryXMax, nNewEntryYMax,
    1163               0 :                       nSeed1, nSeed2);
    1164                 : 
    1165                 :     /*-----------------------------------------------------------------
    1166                 :      * Reset number of entries in this node and start moving new entries
    1167                 :      *----------------------------------------------------------------*/
    1168               0 :     m_numEntries = 0;
    1169                 : 
    1170                 :     // Insert nSeed1 in this node
    1171               0 :     InsertEntry(pasSrcEntries[nSeed1].XMin, 
    1172               0 :                 pasSrcEntries[nSeed1].YMin,
    1173               0 :                 pasSrcEntries[nSeed1].XMax, 
    1174               0 :                 pasSrcEntries[nSeed1].YMax,
    1175               0 :                 pasSrcEntries[nSeed1].nBlockPtr);
    1176                 : 
    1177                 :     // Move nSeed2 to 2nd node
    1178               0 :     poNewNode->InsertEntry(pasSrcEntries[nSeed2].XMin, 
    1179               0 :                            pasSrcEntries[nSeed2].YMin,
    1180               0 :                            pasSrcEntries[nSeed2].XMax, 
    1181               0 :                            pasSrcEntries[nSeed2].YMax,
    1182               0 :                            pasSrcEntries[nSeed2].nBlockPtr);
    1183                 : 
    1184                 :     // Update cur child index if necessary
    1185               0 :     if (nSeed1 == nSrcCurChildIndex)
    1186               0 :         m_nCurChildIndex = m_numEntries-1;
    1187                 : 
    1188                 :     /*-----------------------------------------------------------------
    1189                 :      * Go through the rest of the entries and assign them to one 
    1190                 :      * of the 2 nodes.
    1191                 :      *
    1192                 :      * Criteria is minimal area difference.
    1193                 :      * Resolve ties by adding the entry to the node with smaller total
    1194                 :      * area, then to the one with fewer entries, then to either.
    1195                 :      *----------------------------------------------------------------*/
    1196               0 :     for(int iEntry=0; iEntry<nSrcEntries; iEntry++)
    1197                 :     {
    1198               0 :         if (iEntry == nSeed1 || iEntry == nSeed2)
    1199               0 :             continue;
    1200                 : 
    1201                 :         // If one of the two nodes is almost full then all remaining
    1202                 :         // entries should go to the other node
    1203                 :         // The entry corresponding to the current child also automatically
    1204                 :         // stays in this node.
    1205               0 :         if (iEntry == nSrcCurChildIndex)
    1206                 :         {
    1207               0 :             InsertEntry(pasSrcEntries[iEntry].XMin, 
    1208               0 :                         pasSrcEntries[iEntry].YMin,
    1209               0 :                         pasSrcEntries[iEntry].XMax, 
    1210               0 :                         pasSrcEntries[iEntry].YMax,
    1211               0 :                         pasSrcEntries[iEntry].nBlockPtr);
    1212                 : 
    1213                 :             // Update current child index
    1214               0 :             m_nCurChildIndex = m_numEntries-1;
    1215                 : 
    1216               0 :             continue;
    1217                 : 
    1218                 :         }
    1219               0 :         else if (m_numEntries >= TAB_MAX_ENTRIES_INDEX_BLOCK-1)
    1220                 :         {
    1221               0 :             poNewNode->InsertEntry(pasSrcEntries[iEntry].XMin, 
    1222               0 :                                    pasSrcEntries[iEntry].YMin,
    1223               0 :                                    pasSrcEntries[iEntry].XMax, 
    1224               0 :                                    pasSrcEntries[iEntry].YMax,
    1225               0 :                                    pasSrcEntries[iEntry].nBlockPtr);
    1226               0 :             continue;
    1227                 :         }
    1228               0 :         else if (poNewNode->GetNumEntries() >= TAB_MAX_ENTRIES_INDEX_BLOCK-1)
    1229                 :         {
    1230               0 :             InsertEntry(pasSrcEntries[iEntry].XMin, 
    1231               0 :                         pasSrcEntries[iEntry].YMin,
    1232               0 :                         pasSrcEntries[iEntry].XMax, 
    1233               0 :                         pasSrcEntries[iEntry].YMax,
    1234               0 :                         pasSrcEntries[iEntry].nBlockPtr);
    1235               0 :             continue;
    1236                 :         }
    1237                 : 
    1238                 : 
    1239                 :         // Decide which of the two nodes to put this entry in
    1240                 :         double dAreaDiff1, dAreaDiff2;
    1241               0 :         RecomputeMBR();
    1242                 :         dAreaDiff1 = ComputeAreaDiff(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
    1243               0 :                                      pasSrcEntries[iEntry].XMin, 
    1244               0 :                                      pasSrcEntries[iEntry].YMin,
    1245               0 :                                      pasSrcEntries[iEntry].XMax,
    1246               0 :                                      pasSrcEntries[iEntry].YMax);
    1247                 : 
    1248                 :         GInt32 nXMin2, nYMin2, nXMax2, nYMax2;
    1249               0 :         poNewNode->RecomputeMBR();
    1250               0 :         poNewNode->GetMBR(nXMin2, nYMin2, nXMax2, nYMax2);
    1251                 :         dAreaDiff2 = ComputeAreaDiff(nXMin2, nYMin2, nXMax2, nYMax2,
    1252               0 :                                      pasSrcEntries[iEntry].XMin, 
    1253               0 :                                      pasSrcEntries[iEntry].YMin,
    1254               0 :                                      pasSrcEntries[iEntry].XMax,
    1255               0 :                                      pasSrcEntries[iEntry].YMax);
    1256               0 :         if (dAreaDiff1 < dAreaDiff2)
    1257                 :         {
    1258                 :             // This entry stays in this node
    1259               0 :             InsertEntry(pasSrcEntries[iEntry].XMin, 
    1260               0 :                         pasSrcEntries[iEntry].YMin,
    1261               0 :                         pasSrcEntries[iEntry].XMax, 
    1262               0 :                         pasSrcEntries[iEntry].YMax,
    1263               0 :                         pasSrcEntries[iEntry].nBlockPtr);
    1264                 :         }
    1265                 :         else
    1266                 :         {
    1267                 :             // This entry goes to new node
    1268               0 :             poNewNode->InsertEntry(pasSrcEntries[iEntry].XMin, 
    1269               0 :                                    pasSrcEntries[iEntry].YMin,
    1270               0 :                                    pasSrcEntries[iEntry].XMax, 
    1271               0 :                                    pasSrcEntries[iEntry].YMax,
    1272               0 :                                    pasSrcEntries[iEntry].nBlockPtr);
    1273                 :         }
    1274                 :     }
    1275                 : 
    1276                 :     /*-----------------------------------------------------------------
    1277                 :      * Recompute MBR and update current node info in parent
    1278                 :      *----------------------------------------------------------------*/
    1279               0 :     RecomputeMBR();
    1280               0 :     poNewNode->RecomputeMBR();
    1281                 : 
    1282                 :     /*-----------------------------------------------------------------
    1283                 :      * Add second node info to parent and then flush it to disk.
    1284                 :      * This may trigger splitting of parent
    1285                 :      *----------------------------------------------------------------*/
    1286               0 :     CPLAssert(m_poParentRef);
    1287                 :     int nMinX, nMinY, nMaxX, nMaxY;
    1288               0 :     poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1289                 :     m_poParentRef->AddEntry(nMinX, nMinY, nMaxX, nMaxY,
    1290               0 :                             poNewNode->GetNodeBlockPtr(), TRUE);
    1291               0 :     poNewNode->CommitToFile();
    1292               0 :     delete poNewNode;
    1293                 : 
    1294               0 :     CPLFree(pasSrcEntries);
    1295                 : 
    1296               0 :     return 0;
    1297                 : }
    1298                 : 
    1299                 : /**********************************************************************
    1300                 :  *                   TABMAPIndexBlock::SplitRootNode()
    1301                 :  *
    1302                 :  * (private method)
    1303                 :  *
    1304                 :  * Split a Root Node.
    1305                 :  * First, a level of nodes must be added to the tree, then the contents
    1306                 :  * of what used to be the root node is moved 1 level down and then that
    1307                 :  * node is split like a regular node.
    1308                 :  *
    1309                 :  * Returns 0 on success, -1 on error
    1310                 :  **********************************************************************/
    1311               0 : int TABMAPIndexBlock::SplitRootNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin,
    1312                 :                                     GInt32 nNewEntryXMax, GInt32 nNewEntryYMax)
    1313                 : {
    1314               0 :     CPLAssert(m_poBlockManagerRef);
    1315               0 :     CPLAssert(m_poParentRef == NULL);
    1316                 : 
    1317                 :     /*-----------------------------------------------------------------
    1318                 :      * Since a root note cannot be split, we add a level of nodes
    1319                 :      * under it and we'll do the split at that level.
    1320                 :      *----------------------------------------------------------------*/
    1321               0 :     TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess);
    1322                 : 
    1323               0 :     if (poNewNode->InitNewBlock(m_fp, 512, 
    1324               0 :                                 m_poBlockManagerRef->AllocNewBlock()) != 0)
    1325                 :     {
    1326               0 :         return -1;
    1327                 :     }
    1328               0 :     poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef);
    1329                 : 
    1330                 :     // Move all entries to the new child
    1331               0 :     int nSrcEntries = m_numEntries;
    1332               0 :     m_numEntries = 0;
    1333               0 :     for(int iEntry=0; iEntry<nSrcEntries; iEntry++)
    1334                 :     {
    1335                 :         poNewNode->InsertEntry(m_asEntries[iEntry].XMin, 
    1336                 :                                m_asEntries[iEntry].YMin,
    1337                 :                                m_asEntries[iEntry].XMax, 
    1338                 :                                m_asEntries[iEntry].YMax,
    1339               0 :                                m_asEntries[iEntry].nBlockPtr);
    1340                 :     }
    1341                 :     
    1342                 :     /*-----------------------------------------------------------------
    1343                 :      * Transfer current child object to new node.
    1344                 :      *----------------------------------------------------------------*/
    1345               0 :     if (m_poCurChild)
    1346                 :     {
    1347               0 :         poNewNode->SetCurChildRef(m_poCurChild, m_nCurChildIndex);
    1348               0 :         m_poCurChild->SetParentRef(poNewNode);
    1349               0 :         m_poCurChild = NULL;
    1350               0 :         m_nCurChildIndex = -1;
    1351                 :     }
    1352                 : 
    1353                 :     /*-----------------------------------------------------------------
    1354                 :      * Place info about new child in current node.
    1355                 :      *----------------------------------------------------------------*/
    1356               0 :     poNewNode->RecomputeMBR();
    1357                 :     int nMinX, nMinY, nMaxX, nMaxY;
    1358               0 :     poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1359               0 :     InsertEntry(nMinX, nMinY, nMaxX, nMaxY, poNewNode->GetNodeBlockPtr());
    1360                 : 
    1361                 :     /*-----------------------------------------------------------------
    1362                 :      * Keep a reference to the new child
    1363                 :      *----------------------------------------------------------------*/
    1364               0 :     poNewNode->SetParentRef(this);
    1365               0 :     m_poCurChild = poNewNode;
    1366               0 :     m_nCurChildIndex = m_numEntries -1;
    1367                 : 
    1368                 :     /*-----------------------------------------------------------------
    1369                 :      * And finally force the child to split itself
    1370                 :      *----------------------------------------------------------------*/
    1371                 :     return m_poCurChild->SplitNode(nNewEntryXMin, nNewEntryYMin,
    1372               0 :                                    nNewEntryXMax, nNewEntryYMax);
    1373                 : }
    1374                 : 
    1375                 : 
    1376                 : /**********************************************************************
    1377                 :  *                   TABMAPIndexBlock::RecomputeMBR()
    1378                 :  *
    1379                 :  * Recompute current block MBR, and update info in parent.
    1380                 :  **********************************************************************/
    1381               4 : void TABMAPIndexBlock::RecomputeMBR()
    1382                 : {
    1383                 :     GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1384                 : 
    1385               4 :     nMinX = 1000000000;
    1386               4 :     nMinY = 1000000000;
    1387               4 :     nMaxX = -1000000000;
    1388               4 :     nMaxY = -1000000000;
    1389                 : 
    1390               8 :     for(int i=0; i<m_numEntries; i++)
    1391                 :     {
    1392               4 :         if (m_asEntries[i].XMin < nMinX)
    1393               4 :             nMinX = m_asEntries[i].XMin;
    1394               4 :         if (m_asEntries[i].XMax > nMaxX)
    1395               4 :             nMaxX = m_asEntries[i].XMax;
    1396                 : 
    1397               4 :         if (m_asEntries[i].YMin < nMinY)
    1398               4 :             nMinY = m_asEntries[i].YMin;
    1399               4 :         if (m_asEntries[i].YMax > nMaxY)
    1400               4 :             nMaxY = m_asEntries[i].YMax;
    1401                 :     }
    1402                 : 
    1403               4 :     if (m_nMinX != nMinX ||
    1404                 :         m_nMinY != nMinY ||
    1405                 :         m_nMaxX != nMaxX ||
    1406                 :         m_nMaxY != nMaxY )
    1407                 :     {
    1408               4 :         m_nMinX = nMinX;
    1409               4 :         m_nMinY = nMinY;
    1410               4 :         m_nMaxX = nMaxX;
    1411               4 :         m_nMaxY = nMaxY;
    1412                 : 
    1413               4 :         m_bModified = TRUE;
    1414                 : 
    1415               4 :         if (m_poParentRef)
    1416                 :             m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, 
    1417                 :                                              m_nMaxX, m_nMaxY,
    1418               0 :                                              GetNodeBlockPtr());
    1419                 :     }
    1420                 : 
    1421               4 : }
    1422                 : 
    1423                 : /**********************************************************************
    1424                 :  *                   TABMAPIndexBlock::UpateCurChildMBR()
    1425                 :  *
    1426                 :  * Update current child MBR info, and propagate info in parent.
    1427                 :  *
    1428                 :  * nBlockPtr is passed only to validate the consistency of the tree.
    1429                 :  **********************************************************************/
    1430               0 : void TABMAPIndexBlock::UpdateCurChildMBR(GInt32 nXMin, GInt32 nYMin,
    1431                 :                                          GInt32 nXMax, GInt32 nYMax,
    1432                 :                                          GInt32 nBlockPtr)
    1433                 : {
    1434               0 :     CPLAssert(m_poCurChild);
    1435               0 :     CPLAssert(m_asEntries[m_nCurChildIndex].nBlockPtr == nBlockPtr);
    1436                 : 
    1437               0 :     if (m_asEntries[m_nCurChildIndex].XMin == nXMin &&
    1438                 :         m_asEntries[m_nCurChildIndex].YMin == nYMin &&
    1439                 :         m_asEntries[m_nCurChildIndex].XMax == nXMax &&
    1440                 :         m_asEntries[m_nCurChildIndex].YMax == nYMax)
    1441                 :     {
    1442               0 :         return;  /* Nothing changed... nothing to do */
    1443                 :     }
    1444                 : 
    1445               0 :     m_bModified = TRUE;
    1446                 : 
    1447               0 :     m_asEntries[m_nCurChildIndex].XMin = nXMin;
    1448               0 :     m_asEntries[m_nCurChildIndex].YMin = nYMin;
    1449               0 :     m_asEntries[m_nCurChildIndex].XMax = nXMax;
    1450               0 :     m_asEntries[m_nCurChildIndex].YMax = nYMax;
    1451                 : 
    1452               0 :     m_nMinX = 1000000000;
    1453               0 :     m_nMinY = 1000000000;
    1454               0 :     m_nMaxX = -1000000000;
    1455               0 :     m_nMaxY = -1000000000;
    1456                 : 
    1457               0 :     for(int i=0; i<m_numEntries; i++)
    1458                 :     {
    1459               0 :         if (m_asEntries[i].XMin < m_nMinX)
    1460               0 :             m_nMinX = m_asEntries[i].XMin;
    1461               0 :         if (m_asEntries[i].XMax > m_nMaxX)
    1462               0 :             m_nMaxX = m_asEntries[i].XMax;
    1463                 :     
    1464               0 :         if (m_asEntries[i].YMin < m_nMinY)
    1465               0 :             m_nMinY = m_asEntries[i].YMin;
    1466               0 :         if (m_asEntries[i].YMax > m_nMaxY)
    1467               0 :             m_nMaxY = m_asEntries[i].YMax;
    1468                 :     }
    1469                 : 
    1470               0 :     if (m_poParentRef)
    1471                 :         m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
    1472               0 :                                          GetNodeBlockPtr());
    1473                 : 
    1474                 : }
    1475                 : 
    1476                 : 
    1477                 : /**********************************************************************
    1478                 :  *                   TABMAPIndexBlock::SetMAPBlockManagerRef()
    1479                 :  *
    1480                 :  * Pass a reference to the block manager object for the file this 
    1481                 :  * block belongs to.  The block manager will be used by this object
    1482                 :  * when it needs to automatically allocate a new block.
    1483                 :  **********************************************************************/
    1484               4 : void TABMAPIndexBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
    1485                 : {
    1486               4 :     m_poBlockManagerRef = poBlockMgr;
    1487               4 : };
    1488                 : 
    1489                 : /**********************************************************************
    1490                 :  *                   TABMAPIndexBlock::SetParentRef()
    1491                 :  *
    1492                 :  * Used to pass a reference to this node's parent.
    1493                 :  **********************************************************************/
    1494               0 : void    TABMAPIndexBlock::SetParentRef(TABMAPIndexBlock *poParent)
    1495                 : {
    1496               0 :     m_poParentRef = poParent;
    1497               0 : }
    1498                 : 
    1499                 : /**********************************************************************
    1500                 :  *                   TABMAPIndexBlock::SetCurChildRef()
    1501                 :  *
    1502                 :  * Used to transfer a child object from one node to another
    1503                 :  **********************************************************************/
    1504               2 : void    TABMAPIndexBlock::SetCurChildRef(TABMAPIndexBlock *poChild,
    1505                 :                                          int nChildIndex)
    1506                 : {
    1507               2 :     m_poCurChild = poChild;
    1508               2 :     m_nCurChildIndex = nChildIndex;
    1509               2 : }
    1510                 : 
    1511                 : /**********************************************************************
    1512                 :  *                   TABMAPIndexBlock::Dump()
    1513                 :  *
    1514                 :  * Dump block contents... available only in DEBUG mode.
    1515                 :  **********************************************************************/
    1516                 : #ifdef DEBUG
    1517                 : 
    1518               0 : void TABMAPIndexBlock::Dump(FILE *fpOut /*=NULL*/)
    1519                 : {
    1520               0 :     if (fpOut == NULL)
    1521               0 :         fpOut = stdout;
    1522                 : 
    1523               0 :     fprintf(fpOut, "----- TABMAPIndexBlock::Dump() -----\n");
    1524               0 :     if (m_pabyBuf == NULL)
    1525                 :     {
    1526               0 :         fprintf(fpOut, "Block has not been initialized yet.");
    1527                 :     }
    1528                 :     else
    1529                 :     {
    1530                 :         fprintf(fpOut,"Index Block (type %d) at offset %d.\n", 
    1531               0 :                                                 m_nBlockType, m_nFileOffset);
    1532               0 :         fprintf(fpOut,"  m_numEntries          = %d\n", m_numEntries);
    1533                 : 
    1534                 :         /*-------------------------------------------------------------
    1535                 :          * Loop through all entries, dumping each of them
    1536                 :          *------------------------------------------------------------*/
    1537               0 :         if (m_numEntries > 0)
    1538               0 :             ReadAllEntries();
    1539                 : 
    1540               0 :         for(int i=0; i<m_numEntries; i++)
    1541                 :         {
    1542                 :             fprintf(fpOut, "    %6d -> (%d, %d) - (%d, %d)\n",
    1543                 :                     m_asEntries[i].nBlockPtr,
    1544                 :                     m_asEntries[i].XMin, m_asEntries[i].YMin,
    1545               0 :                     m_asEntries[i].XMax, m_asEntries[i].YMax );
    1546                 :         }
    1547                 : 
    1548                 :     }
    1549                 : 
    1550               0 :     fflush(fpOut);
    1551               0 : }
    1552                 : #endif // DEBUG
    1553                 : 
    1554                 : 

Generated by: LCOV version 1.7