LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_mapfile.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 768 378 49.2 %
Date: 2012-12-26 Functions: 53 36 67.9 %

       1                 : /**********************************************************************
       2                 :  * $Id: mitab_mapfile.cpp,v 1.46 2010-07-07 19:00:15 aboudreault Exp $
       3                 :  *
       4                 :  * Name:     mitab_mapfile.cpp
       5                 :  * Project:  MapInfo TAB Read/Write library
       6                 :  * Language: C++
       7                 :  * Purpose:  Implementation of the TABMAPFile class used to handle
       8                 :  *           reading/writing of the .MAP files at the MapInfo object level
       9                 :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
      10                 :  *
      11                 :  **********************************************************************
      12                 :  * Copyright (c) 1999-2002, 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_mapfile.cpp,v $
      34                 :  * Revision 1.46  2010-07-07 19:00:15  aboudreault
      35                 :  * Cleanup Win32 Compile Warnings (GDAL bug #2930)
      36                 :  *
      37                 :  * Revision 1.45  2010-01-08 22:02:51  aboudreault
      38                 :  * Fixed error issued when reading empty TAB with spatial index active (bug 2136)
      39                 :  *
      40                 :  * Revision 1.44  2009-03-03 20:44:23  dmorissette
      41                 :  * Use transparent brush in DumpSpatialIndexToMIF()
      42                 :  *
      43                 :  * Revision 1.43  2008/02/20 21:35:30  dmorissette
      44                 :  * Added support for V800 COLLECTION of large objects (bug 1496)
      45                 :  *
      46                 :  * Revision 1.42  2008/02/01 19:36:31  dmorissette
      47                 :  * Initial support for V800 REGION and MULTIPLINE (bug 1496)
      48                 :  *
      49                 :  * Revision 1.41  2007/11/08 18:57:56  dmorissette
      50                 :  * Upgrade of OGR and CPL libs to the version from GDAL/OGR 1.4.3
      51                 :  *
      52                 :  * Revision 1.40  2007/09/14 18:30:19  dmorissette
      53                 :  * Fixed the splitting of object blocks with the optimized spatial
      54                 :  * index mode that was producing files with misaligned bytes that
      55                 :  * confused MapInfo (bug 1732)
      56                 :  *
      57                 :  * Revision 1.39  2007/07/11 15:51:52  dmorissette
      58                 :  * Fixed duplicate 'int i' definition build errors in SplitObjBlock()
      59                 :  *
      60                 :  * Revision 1.38  2007/06/12 12:50:39  dmorissette
      61                 :  * Use Quick Spatial Index by default until bug 1732 is fixed (broken files
      62                 :  * produced by current coord block splitting technique).
      63                 :  *
      64                 :  * Revision 1.37  2007/06/05 13:23:57  dmorissette
      65                 :  * Fixed memory leak when writing .TAB with new (optimized) spatial index
      66                 :  * introduced in v1.6.0 (bug 1725)
      67                 :  *
      68                 :  * Revision 1.36  2007/03/21 21:15:56  dmorissette
      69                 :  * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial
      70                 :  * index but results in faster write time (bug 1669)
      71                 :  *
      72                 :  * Revision 1.35  2006/11/28 18:49:08  dmorissette
      73                 :  * Completed changes to split TABMAPObjectBlocks properly and produce an
      74                 :  * optimal spatial index (bug 1585)
      75                 :  *
      76                 :  * Revision 1.34  2006/11/20 20:05:58  dmorissette
      77                 :  * First pass at improving generation of spatial index in .map file (bug 1585)
      78                 :  * New methods for insertion and splittung in the spatial index are done.
      79                 :  * Also implemented a method to dump the spatial index to .mif/.mid
      80                 :  * Still need to implement splitting of TABMapObjectBlock to get optimal
      81                 :  * results.
      82                 :  *
      83                 :  * Revision 1.33  2006/09/05 23:05:08  dmorissette
      84                 :  * Added TABMAPFile::DumpSpatialIndex() (bug 1585)
      85                 :  *
      86                 :  * Revision 1.32  2005/10/06 19:15:31  dmorissette
      87                 :  * Collections: added support for reading/writing pen/brush/symbol ids and
      88                 :  * for writing collection objects to .TAB/.MAP (bug 1126)
      89                 :  *
      90                 :  * Revision 1.31  2004/09/22 13:07:58  fwarmerdam
      91                 :  * fixed return value in LoadNextMatchingObjectBlock() per rso bug 615
      92                 :  *
      93                 :  * Revision 1.30  2004/06/30 20:29:04  dmorissette
      94                 :  * Fixed refs to old address danmo@videotron.ca
      95                 :  *
      96                 :  * Revision 1.29  2003/08/12 23:17:21  dmorissette
      97                 :  * Added reading of v500+ coordsys affine params (Anthony D. - Encom)
      98                 :  *
      99                 :  * Revision 1.28  2002/08/27 17:18:43  warmerda
     100                 :  * improved CPL error testing
     101                 :  *
     102                 :  * Revision 1.27  2002/07/30 13:54:12  julien
     103                 :  * TABMAPFile::GetFeatureId() now return -1 when there's no geometry. (bug 169)
     104                 :  *
     105                 :  * Revision 1.26  2002/04/25 16:05:24  julien
     106                 :  * Disabled the overflow warning in SetCoordFilter() by adding bIgnoreOverflow
     107                 :  * variable in Coordsys2Int of the TABMAPFile class and TABMAPHeaderBlock class
     108                 :  *
     109                 :  * Revision 1.25  2002/03/26 19:27:43  daniel
     110                 :  * Got rid of tabs in source
     111                 :  *
     112                 :  * Revision 1.24  2002/03/26 01:48:40  daniel
     113                 :  * Added Multipoint object type (V650)
     114                 :  *
     115                 :  * Revision 1.23  2002/02/20 13:53:40  daniel
     116                 :  * Prevent an infinite loop of calls to LoadNextMatchingObjectBlock() in
     117                 :  * GetNextFeatureId() if no objects found in spatial index.
     118                 :  *
     119                 :  * Revision 1.22  2001/11/19 15:04:41  daniel
     120                 :  * Prevent writing of coordinates outside of the +/-1e9 integer bounds.
     121                 :  *
     122                 :  * Revision 1.21  2001/11/17 21:54:06  daniel
     123                 :  * Made several changes in order to support writing objects in 16 bits 
     124                 :  * coordinate format. New TABMAPObjHdr-derived classes are used to hold 
     125                 :  * object info in mem until block is full.
     126                 :  *
     127                 :  * Revision 1.20  2001/09/18 20:33:52  warmerda
     128                 :  * fixed case of spatial search on file with just one object block
     129                 :  *
     130                 :  * Revision 1.19  2001/09/14 03:23:55  warmerda
     131                 :  * Substantial upgrade to support spatial queries using spatial indexes
     132                 :  *
     133                 :  * Revision 1.18  2001/03/15 03:57:51  daniel
     134                 :  * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
     135                 :  *
     136                 :  * Revision 1.17  2000/11/23 21:11:07  daniel
     137                 :  * OOpps... VC++ didn't like the way TABPenDef, etc. were initialized
     138                 :  *
     139                 :  * Revision 1.16  2000/11/23 20:47:46  daniel
     140                 :  * Use MI defaults for Pen, Brush, Font, Symbol instead of all zeros
     141                 :  *
     142                 :  * Revision 1.15  2000/11/22 04:03:10  daniel
     143                 :  * Added warning when objects written outside of the +/-1e9 int. coord. range
     144                 :  *
     145                 :  * Revision 1.14  2000/11/15 04:13:49  daniel
     146                 :  * Fixed writing of TABMAPToolBlock to allocate a new block when full
     147                 :  *
     148                 :  * Revision 1.13  2000/05/19 06:44:55  daniel
     149                 :  * Modified generation of spatial index to split index nodes and produce a
     150                 :  * more balanced tree.
     151                 :  *
     152                 :  * Revision 1.12  2000/03/13 05:58:01  daniel
     153                 :  * Create 1024 bytes V500 .MAP header + limit m_nMaxCoordBufSize for V450 obj.
     154                 :  *
     155                 :  * Revision 1.11  2000/02/28 17:00:00  daniel
     156                 :  * Added V450 object types
     157                 :  *
     158                 :  * Revision 1.10  2000/01/15 22:30:44  daniel
     159                 :  * Switch to MIT/X-Consortium OpenSource license
     160                 :  *
     161                 :  * Revision 1.9  1999/12/19 17:37:52  daniel
     162                 :  * Fixed memory leaks
     163                 :  *
     164                 :  * Revision 1.8  1999/11/14 04:43:31  daniel
     165                 :  * Support dataset with no .MAP/.ID files
     166                 :  *
     167                 :  * Revision 1.7  1999/10/19 22:57:17  daniel
     168                 :  * Create m_poCurObjBlock only when needed to avoid empty blocks in files
     169                 :  * and problems with MBR in header block of files with only "NONE" geometries
     170                 :  *
     171                 :  * Revision 1.6  1999/10/06 13:17:46  daniel
     172                 :  * Update m_nMaxCoordBufSize in header block
     173                 :  *
     174                 :  * Revision 1.5  1999/10/01 03:52:22  daniel
     175                 :  * Avoid producing an unused block in the file when closing it.
     176                 :  *
     177                 :  * Revision 1.4  1999/09/26 14:59:36  daniel
     178                 :  * Implemented write support
     179                 :  *
     180                 :  * Revision 1.3  1999/09/20 18:42:42  daniel
     181                 :  * Use binary access to open file.
     182                 :  *
     183                 :  * Revision 1.2  1999/09/16 02:39:16  daniel
     184                 :  * Completed read support for most feature types
     185                 :  *
     186                 :  * Revision 1.1  1999/07/12 04:18:24  daniel
     187                 :  * Initial checkin
     188                 :  *
     189                 :  **********************************************************************/
     190                 : 
     191                 : #include "mitab.h"
     192                 : 
     193                 : /*=====================================================================
     194                 :  *                      class TABMAPFile
     195                 :  *====================================================================*/
     196                 : 
     197                 : 
     198                 : /**********************************************************************
     199                 :  *                   TABMAPFile::TABMAPFile()
     200                 :  *
     201                 :  * Constructor.
     202                 :  **********************************************************************/
     203               6 : TABMAPFile::TABMAPFile()
     204                 : {
     205               6 :     m_nMinTABVersion = 300;
     206               6 :     m_fp = NULL;
     207               6 :     m_pszFname = NULL;
     208               6 :     m_poHeader = NULL;
     209               6 :     m_poSpIndex = NULL;
     210               6 :     m_poSpIndexLeaf = NULL;
     211                 : /* See bug 1732: Optimized spatial index produces broken files because
     212                 :  * of the way CoordBlocks are split. For now we have to force using the
     213                 :  * Quick (old) spatial index mode by default until bug 1732 is fixed.
     214                 :  */
     215               6 :     m_bQuickSpatialIndexMode = TRUE;
     216                 : //  m_bQuickSpatialIndexMode = FALSE;
     217                 : 
     218               6 :     m_poCurObjBlock = NULL;
     219               6 :     m_nCurObjPtr = -1;
     220               6 :     m_nCurObjType = -1;
     221               6 :     m_nCurObjId = -1;
     222               6 :     m_poCurCoordBlock = NULL;
     223               6 :     m_poToolDefTable = NULL;
     224               6 : }
     225                 : 
     226                 : /**********************************************************************
     227                 :  *                   TABMAPFile::~TABMAPFile()
     228                 :  *
     229                 :  * Destructor.
     230                 :  **********************************************************************/
     231               6 : TABMAPFile::~TABMAPFile()
     232                 : {
     233               6 :     Close();
     234               6 : }
     235                 : 
     236                 : /**********************************************************************
     237                 :  *                   TABMAPFile::Open()
     238                 :  *
     239                 :  * Open a .MAP file, and initialize the structures to be ready to read
     240                 :  * objects from it.
     241                 :  *
     242                 :  * Since .MAP and .ID files are optional, you can set bNoErrorMsg=TRUE to
     243                 :  * disable the error message and receive an return value of 1 if file 
     244                 :  * cannot be opened.  
     245                 :  * In this case, only the methods MoveToObjId() and GetCurObjType() can 
     246                 :  * be used.  They will behave as if the .ID file contained only null
     247                 :  * references, so all object will look like they have NONE geometries.
     248                 :  *
     249                 :  * Returns 0 on success, -1 on error.
     250                 :  **********************************************************************/
     251               6 : int TABMAPFile::Open(const char *pszFname, const char *pszAccess,
     252                 :                      GBool bNoErrorMsg /* = FALSE */)
     253                 : {
     254               6 :     FILE        *fp=NULL;
     255               6 :     TABRawBinBlock *poBlock=NULL;
     256                 : 
     257               6 :     if (m_fp)
     258                 :     {
     259                 :         CPLError(CE_Failure, CPLE_FileIO,
     260               0 :                  "Open() failed: object already contains an open file");
     261               0 :         return -1;
     262                 :     }
     263                 : 
     264               6 :     m_nMinTABVersion = 300;
     265               6 :     m_fp = NULL;
     266               6 :     m_poHeader = NULL;
     267               6 :     m_poIdIndex = NULL;
     268               6 :     m_poSpIndex = NULL;
     269               6 :     m_poToolDefTable = NULL;
     270                 : 
     271                 :     /*-----------------------------------------------------------------
     272                 :      * Validate access mode and make sure we use binary access.
     273                 :      *----------------------------------------------------------------*/
     274               6 :     if (EQUALN(pszAccess, "r", 1))
     275                 :     {
     276               3 :         m_eAccessMode = TABRead;
     277               3 :         pszAccess = "rb";
     278                 :     }
     279               3 :     else if (EQUALN(pszAccess, "w", 1))
     280                 :     {
     281               3 :         m_eAccessMode = TABWrite;
     282               3 :         pszAccess = "wb+";
     283                 :     }
     284                 :     else
     285                 :     {
     286                 :         CPLError(CE_Failure, CPLE_FileIO,
     287               0 :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
     288               0 :         return -1;
     289                 :     }
     290                 : 
     291                 :     /*-----------------------------------------------------------------
     292                 :      * Open file
     293                 :      *----------------------------------------------------------------*/
     294               6 :     fp = VSIFOpen(pszFname, pszAccess);
     295                 : 
     296                 :     // TODO: In Read/Write mode we should also preload the chain of deleted
     297                 :     // blocks in the blockManager. Not needed for read-only or write-only.
     298               6 :     m_oBlockManager.Reset();
     299                 : 
     300               9 :     if (fp != NULL && m_eAccessMode == TABRead)
     301                 :     {
     302                 :         /*-----------------------------------------------------------------
     303                 :          * Read access: try to read header block
     304                 :          * First try with a 512 bytes block to check the .map version.
     305                 :          * If it's version 500 or more then read again a 1024 bytes block
     306                 :          *----------------------------------------------------------------*/
     307               3 :         poBlock = TABCreateMAPBlockFromFile(fp, 0, 512);
     308                 : 
     309               3 :         if (poBlock && poBlock->GetBlockClass() == TABMAP_HEADER_BLOCK &&
     310                 :             ((TABMAPHeaderBlock*)poBlock)->m_nMAPVersionNumber >= 500)
     311                 :         {
     312                 :             // Version 500 or higher.  Read 1024 bytes block instead of 512
     313               3 :             delete poBlock;
     314               3 :             poBlock = TABCreateMAPBlockFromFile(fp, 0, 1024);
     315                 :         }
     316                 : 
     317               3 :         if (poBlock==NULL || poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK)
     318                 :         {
     319               0 :             if (poBlock)
     320               0 :                 delete poBlock;
     321               0 :             poBlock = NULL;
     322               0 :             VSIFClose(fp);
     323                 :             CPLError(CE_Failure, CPLE_FileIO,
     324                 :                 "Open() failed: %s does not appear to be a valid .MAP file",
     325               0 :                      pszFname);
     326               0 :             return -1;
     327                 :         }
     328                 :     }
     329               6 :     else if (fp != NULL && m_eAccessMode == TABWrite)
     330                 :     {
     331                 :         /*-----------------------------------------------------------------
     332                 :          * Write access: create a new header block
     333                 :          * .MAP files of Version 500 and up appear to have a 1024 bytes
     334                 :          * header.  The last 512 bytes are usually all zeros.
     335                 :          *----------------------------------------------------------------*/
     336               3 :         poBlock = new TABMAPHeaderBlock(m_eAccessMode);
     337               3 :         poBlock->InitNewBlock(fp, 1024, m_oBlockManager.AllocNewBlock() );
     338                 : 
     339                 :         // Alloc a second 512 bytes of space since oBlockManager deals 
     340                 :         // with 512 bytes blocks.
     341               3 :         m_oBlockManager.AllocNewBlock(); 
     342                 :     }
     343               0 :     else if (bNoErrorMsg)
     344                 :     {
     345                 :         /*-----------------------------------------------------------------
     346                 :          * .MAP does not exist... produce no error message, but set
     347                 :          * the class members so that MoveToObjId() and GetCurObjType()
     348                 :          * can be used to return only NONE geometries.
     349                 :          *----------------------------------------------------------------*/
     350               0 :         m_fp = NULL;
     351               0 :         m_nCurObjType = TAB_GEOM_NONE;
     352                 : 
     353                 :         /* Create a false header block that will return default
     354                 :          * values for projection and coordsys conversion stuff...
     355                 :          */
     356               0 :         m_poHeader = new TABMAPHeaderBlock(m_eAccessMode);
     357               0 :         m_poHeader->InitNewBlock(NULL, 512, 0 );
     358                 : 
     359               0 :         return 1;
     360                 :     }
     361                 :     else
     362                 :     {
     363                 :         CPLError(CE_Failure, CPLE_FileIO,
     364               0 :                  "Open() failed for %s", pszFname);
     365               0 :         return -1;
     366                 :     }
     367                 : 
     368                 :     /*-----------------------------------------------------------------
     369                 :      * File appears to be valid... set the various class members
     370                 :      *----------------------------------------------------------------*/
     371               6 :     m_fp = fp;
     372               6 :     m_poHeader = (TABMAPHeaderBlock*)poBlock;
     373               6 :     m_pszFname = CPLStrdup(pszFname);
     374                 : 
     375                 :     /*-----------------------------------------------------------------
     376                 :      * Create a TABMAPObjectBlock, in READ mode only.
     377                 :      *
     378                 :      * In WRITE mode, the object block will be created only when needed.
     379                 :      * We do not create the object block in the open() call because
     380                 :      * files that contained only "NONE" geometries ended up with empty
     381                 :      * object and spatial index blocks.
     382                 :      *----------------------------------------------------------------*/
     383                 : 
     384               6 :     if (m_eAccessMode == TABRead)
     385                 :     {
     386               3 :         m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
     387               3 :         m_poCurObjBlock->InitNewBlock(m_fp, 512);
     388                 :     }
     389                 :     else
     390                 :     {
     391               3 :         m_poCurObjBlock = NULL;
     392                 :     }
     393                 : 
     394                 :     /*-----------------------------------------------------------------
     395                 :      * Open associated .ID (object id index) file
     396                 :      *----------------------------------------------------------------*/
     397               6 :     m_poIdIndex = new TABIDFile;
     398               6 :     if (m_poIdIndex->Open(pszFname, pszAccess) != 0)
     399                 :     {
     400                 :         // Failed... an error has already been reported
     401               0 :         Close();
     402               0 :         return -1;
     403                 :     }
     404                 : 
     405                 :     /*-----------------------------------------------------------------
     406                 :      * Default Coord filter is the MBR of the whole file
     407                 :      * This is currently unused but could eventually be used to handle
     408                 :      * spatial filters more efficiently.
     409                 :      *----------------------------------------------------------------*/
     410               6 :     if (m_eAccessMode == TABRead)
     411                 :     {
     412               3 :         ResetCoordFilter();
     413                 :     }
     414                 : 
     415                 :     /*-----------------------------------------------------------------
     416                 :      * We could scan a file through its quad tree index... but we don't!
     417                 :      *
     418                 :      * In read mode, we just ignore the spatial index.
     419                 :      *
     420                 :      * In write mode the index is created and maintained as new object
     421                 :      * blocks are added inside CommitObjBlock().
     422                 :      *----------------------------------------------------------------*/
     423               6 :     m_poSpIndex = NULL;
     424                 : 
     425                 :     /*-----------------------------------------------------------------
     426                 :      * Initialization of the Drawing Tools table will be done automatically
     427                 :      * as Read/Write calls are done later.
     428                 :      *----------------------------------------------------------------*/
     429               6 :     m_poToolDefTable = NULL;
     430                 : 
     431                 :     /*-----------------------------------------------------------------
     432                 :      * Make sure all previous calls succeded.
     433                 :      *----------------------------------------------------------------*/
     434               6 :     if (CPLGetLastErrorNo() != 0)
     435                 :     {
     436                 :         // Open Failed... an error has already been reported
     437               0 :         Close();
     438               0 :         return -1;
     439                 :     }
     440                 : 
     441               6 :     return 0;
     442                 : }
     443                 : 
     444                 : /**********************************************************************
     445                 :  *                   TABMAPFile::Close()
     446                 :  *
     447                 :  * Close current file, and release all memory used.
     448                 :  *
     449                 :  * Returns 0 on success, -1 on error.
     450                 :  **********************************************************************/
     451              12 : int TABMAPFile::Close()
     452                 : {
     453                 :     // Check if file is opened... it is possible to have a fake header
     454                 :     // without an actual file attached to it.
     455              12 :     if (m_fp == NULL && m_poHeader == NULL)
     456               6 :         return 0;
     457                 : 
     458                 :     /*----------------------------------------------------------------
     459                 :      * Write access: commit latest changes to the file.
     460                 :      *---------------------------------------------------------------*/
     461               6 :     if (m_eAccessMode == TABWrite)
     462                 :     {
     463                 :         // Start by committing current object and coord blocks
     464                 :         // Nothing happens if none has been created yet.
     465               3 :         CommitObjAndCoordBlocks(FALSE);
     466                 : 
     467                 :         // Write the drawing tools definitions now.
     468               3 :         CommitDrawingTools();
     469                 : 
     470                 :         // Commit spatial index blocks
     471               3 :         CommitSpatialIndex();
     472                 : 
     473                 :         // Update header fields and commit
     474               3 :         if (m_poHeader)
     475                 :         {
     476                 :             // OK, with V450 files, objects are not limited to 32k nodes
     477                 :             // any more, and this means that m_nMaxCoordBufSize can become
     478                 :             // huge, and actually more huge than can be held in memory.
     479                 :             // MapInfo counts m_nMaxCoordBufSize=0 for V450 objects, but 
     480                 :             // until this is cleanly implented, we will just prevent 
     481                 :             // m_nMaxCoordBufSizefrom going beyond 512k in V450 files.
     482               3 :             if (m_nMinTABVersion >= 450)
     483                 :             {
     484                 :                 m_poHeader->m_nMaxCoordBufSize = 
     485               0 :                                  MIN(m_poHeader->m_nMaxCoordBufSize, 512*1024);
     486                 :             }
     487                 : 
     488                 :             // Write Ref to beginning of the chain of garbage blocks
     489                 :             m_poHeader->m_nFirstGarbageBlock = 
     490               3 :                 m_oBlockManager.GetFirstGarbageBlock();
     491                 : 
     492               3 :             m_poHeader->CommitToFile();
     493                 :         }
     494                 :     }
     495                 :     
     496                 :     // Check for overflow of internal coordinates and produce a warning
     497                 :     // if that happened...
     498               6 :     if (m_poHeader && m_poHeader->m_bIntBoundsOverflow)
     499                 :     {
     500                 :         double dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY;
     501               0 :         Int2Coordsys(-1000000000, -1000000000, dBoundsMinX, dBoundsMinY);
     502               0 :         Int2Coordsys(1000000000, 1000000000, dBoundsMaxX, dBoundsMaxY);
     503                 : 
     504                 :         CPLError(CE_Warning, TAB_WarningBoundsOverflow,
     505                 :                  "Some objects were written outside of the file's "
     506                 :                  "predefined bounds.\n"
     507                 :                  "These objects may have invalid coordinates when the file "
     508                 :                  "is reopened.\n"
     509                 :                  "Predefined bounds: (%.15g,%.15g)-(%.15g,%.15g)\n",
     510               0 :                  dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY );
     511                 :     }
     512                 : 
     513                 :     // Delete all structures 
     514               6 :     if (m_poHeader)
     515               6 :         delete m_poHeader;
     516               6 :     m_poHeader = NULL;
     517                 : 
     518               6 :     if (m_poIdIndex)
     519                 :     {
     520               6 :         m_poIdIndex->Close();
     521               6 :         delete m_poIdIndex;
     522               6 :         m_poIdIndex = NULL;
     523                 :     }
     524                 : 
     525               6 :     if (m_poCurObjBlock)
     526                 :     {
     527               5 :         delete m_poCurObjBlock;
     528               5 :         m_poCurObjBlock = NULL;
     529               5 :         m_nCurObjPtr = -1;
     530               5 :         m_nCurObjType = -1;
     531               5 :         m_nCurObjId = -1;
     532                 :     }
     533                 : 
     534               6 :     if (m_poCurCoordBlock)
     535                 :     {
     536               4 :         delete m_poCurCoordBlock;
     537               4 :         m_poCurCoordBlock = NULL;
     538                 :     }
     539                 : 
     540               6 :     if (m_poSpIndex)
     541                 :     {
     542               2 :         delete m_poSpIndex;
     543               2 :         m_poSpIndex = NULL;
     544               2 :         m_poSpIndexLeaf = NULL;
     545                 :     }
     546                 : 
     547               6 :     if (m_poToolDefTable)
     548                 :     {
     549               4 :         delete m_poToolDefTable;
     550               4 :         m_poToolDefTable = NULL;
     551                 :     }
     552                 : 
     553                 :     // Close file
     554               6 :     if (m_fp)
     555               6 :         VSIFClose(m_fp);
     556               6 :     m_fp = NULL;
     557                 : 
     558               6 :     CPLFree(m_pszFname);
     559               6 :     m_pszFname = NULL;
     560                 : 
     561               6 :     return 0;
     562                 : }
     563                 : 
     564                 : 
     565                 : /**********************************************************************
     566                 :  *                   TABMAPFile::SetQuickSpatialIndexMode()
     567                 :  *
     568                 :  * Select "quick spatial index mode". 
     569                 :  *
     570                 :  * The default behavior of MITAB is to generate an optimized spatial index,
     571                 :  * but this results in slower write speed. 
     572                 :  *
     573                 :  * Applications that want faster write speed and do not care
     574                 :  * about the performance of spatial queries on the resulting file can
     575                 :  * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
     576                 :  * spatial index (actually emulating the type of spatial index produced
     577                 :  * by MITAB before version 1.6.0). In this mode writing files can be 
     578                 :  * about 5 times faster, but spatial queries can be up to 30 times slower.
     579                 :  *
     580                 :  * Returns 0 on success, -1 on error.
     581                 :  **********************************************************************/
     582               0 : int TABMAPFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
     583                 : {
     584               0 :     if (m_eAccessMode != TABWrite)
     585                 :     {
     586                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     587               0 :                  "SetQuickSpatialIndexMode() failed: file not opened for write access.");
     588               0 :         return -1;
     589                 :     }
     590                 : 
     591               0 :     if (m_poCurObjBlock != NULL || m_poSpIndex != NULL)
     592                 :     {
     593                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     594               0 :                  "SetQuickSpatialIndexMode() must be called before writing the first object.");
     595               0 :         return -1;
     596                 :     }
     597                 : 
     598               0 :     m_bQuickSpatialIndexMode = bQuickSpatialIndexMode;
     599                 : 
     600               0 :     return 0;
     601                 : }
     602                 : 
     603                 : /************************************************************************/
     604                 : /*                             PushBlock()                              */
     605                 : /*                                                                      */
     606                 : /*      Install a new block (object or spatial) as being current -      */
     607                 : /*      whatever that means.  This method is only intended to ever      */
     608                 : /*      be called from LoadNextMatchingObjectBlock().                   */
     609                 : /************************************************************************/
     610                 : 
     611               2 : TABRawBinBlock *TABMAPFile::PushBlock( int nFileOffset )
     612                 : 
     613                 : {
     614                 :     TABRawBinBlock *poBlock;
     615                 : 
     616               2 :     poBlock = GetIndexObjectBlock( nFileOffset );
     617               2 :     if( poBlock == NULL )
     618               0 :         return NULL;
     619                 : 
     620               2 :     if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK )
     621                 :     {
     622               1 :         TABMAPIndexBlock *poIndex = (TABMAPIndexBlock *) poBlock;
     623                 : 
     624               1 :         if( m_poSpIndexLeaf == NULL )
     625                 :         {
     626               1 :             m_poSpIndexLeaf = m_poSpIndex = poIndex;
     627                 :         }
     628                 :         else
     629                 :         {
     630                 :             CPLAssert( 
     631                 :                 m_poSpIndexLeaf->GetEntry(
     632                 :                     m_poSpIndexLeaf->GetCurChildIndex())->nBlockPtr 
     633               0 :                 == nFileOffset );
     634                 : 
     635                 :             m_poSpIndexLeaf->SetCurChildRef( poIndex, 
     636               0 :                                          m_poSpIndexLeaf->GetCurChildIndex() );
     637               0 :             poIndex->SetParentRef( m_poSpIndexLeaf );
     638               0 :             m_poSpIndexLeaf = poIndex;
     639                 :         }
     640                 :     }
     641                 :     else
     642                 :     {
     643               1 :         CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK );
     644                 :         
     645               1 :         if( m_poCurObjBlock != NULL )
     646               1 :             delete m_poCurObjBlock;
     647                 : 
     648               1 :         m_poCurObjBlock = (TABMAPObjectBlock *) poBlock;
     649                 : 
     650               1 :         m_nCurObjPtr = nFileOffset;
     651               1 :         m_nCurObjType = 0;
     652               1 :         m_nCurObjId   = -1;
     653                 :     }
     654                 : 
     655               2 :     return poBlock;
     656                 : }
     657                 : 
     658                 : /************************************************************************/
     659                 : /*                    LoadNextMatchingObjectBlock()                     */
     660                 : /*                                                                      */
     661                 : /*      Advance through the spatial indices till the next object        */
     662                 : /*      block is loaded that matching the spatial query extents.        */
     663                 : /************************************************************************/
     664                 : 
     665               2 : int TABMAPFile::LoadNextMatchingObjectBlock( int bFirstObject )
     666                 : 
     667                 : {
     668                 :     // If we are just starting, verify the stack is empty.
     669               2 :     if( bFirstObject )
     670                 :     {
     671               1 :         CPLAssert( m_poSpIndex == NULL && m_poSpIndexLeaf == NULL );
     672                 : 
     673                 :         /* m_nFirstIndexBlock set to 0 means that there is no feature */
     674               1 :         if ( m_poHeader->m_nFirstIndexBlock == 0 )
     675               0 :             return FALSE;
     676                 : 
     677               1 :         if( PushBlock( m_poHeader->m_nFirstIndexBlock ) == NULL )
     678               0 :             return FALSE;
     679                 : 
     680               1 :         if( m_poSpIndex == NULL )
     681                 :         {
     682               0 :             CPLAssert( m_poCurObjBlock != NULL );
     683               0 :             return TRUE;
     684                 :         }
     685                 :     }
     686                 : 
     687               5 :     while( m_poSpIndexLeaf != NULL )
     688                 :     {
     689               2 :         int     iEntry = m_poSpIndexLeaf->GetCurChildIndex();
     690                 : 
     691               2 :         if( iEntry >= m_poSpIndexLeaf->GetNumEntries()-1 )
     692                 :         {
     693               1 :             TABMAPIndexBlock *poParent = m_poSpIndexLeaf->GetParentRef();
     694               1 :             delete m_poSpIndexLeaf;
     695               1 :             m_poSpIndexLeaf = poParent;
     696                 :             
     697               1 :             if( poParent != NULL )
     698                 :             {
     699               0 :                 poParent->SetCurChildRef( NULL, poParent->GetCurChildIndex() );
     700                 :             }
     701                 :             else
     702                 :             {
     703               1 :                 m_poSpIndex = NULL;
     704                 :             }
     705               1 :             continue;
     706                 :         }
     707                 : 
     708               1 :         m_poSpIndexLeaf->SetCurChildRef( NULL, ++iEntry );
     709                 : 
     710               1 :         TABMAPIndexEntry *psEntry = m_poSpIndexLeaf->GetEntry( iEntry );
     711                 :         TABRawBinBlock *poBlock;
     712                 :         
     713               1 :         if( psEntry->XMax < m_XMinFilter
     714                 :             || psEntry->YMax < m_YMinFilter
     715                 :             || psEntry->XMin > m_XMaxFilter
     716                 :             || psEntry->YMin > m_YMaxFilter )
     717               0 :             continue;
     718                 : 
     719               1 :         poBlock = PushBlock( psEntry->nBlockPtr );
     720               1 :         if( poBlock == NULL )
     721               0 :             return FALSE;
     722               1 :         else if( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK )
     723               1 :             return TRUE;
     724                 :         else
     725                 :             /* continue processing new index block */;
     726                 :     }
     727                 : 
     728               1 :     return m_poSpIndexLeaf != NULL;
     729                 : }
     730                 : 
     731                 : /************************************************************************/
     732                 : /*                            ResetReading()                            */
     733                 : /*                                                                      */
     734                 : /*      Ensure that any resources related to a spatial traversal of     */
     735                 : /*      the file are recovered, and the state reinitialized to the      */
     736                 : /*      initial conditions.                                             */
     737                 : /************************************************************************/
     738                 : 
     739              10 : void TABMAPFile::ResetReading()
     740                 : 
     741                 : {
     742              10 :     if (m_poSpIndex && m_eAccessMode == TABRead )
     743                 :     {
     744               0 :         delete m_poSpIndex;
     745               0 :         m_poSpIndex = NULL;
     746               0 :         m_poSpIndexLeaf = NULL;
     747                 :     }
     748              10 : }
     749                 : 
     750                 : /************************************************************************/
     751                 : /*                          GetNextFeatureId()                          */
     752                 : /*                                                                      */
     753                 : /*      Fetch the next feature id based on a traversal of the           */
     754                 : /*      spatial index.                                                  */
     755                 : /************************************************************************/
     756                 : 
     757              11 : int TABMAPFile::GetNextFeatureId( int nPrevId )
     758                 : 
     759                 : {
     760                 : /* -------------------------------------------------------------------- */
     761                 : /*      m_fp is NULL when all geometry are NONE and/or there's          */
     762                 : /*          no .map file and/or there's no spatial indexes              */
     763                 : /* -------------------------------------------------------------------- */
     764              11 :     if( m_fp == NULL )
     765               0 :         return -1;
     766                 : 
     767              11 :     if( nPrevId == 0 )
     768               1 :         nPrevId = -1;
     769                 : 
     770                 : /* -------------------------------------------------------------------- */
     771                 : /*      This should always be true if we are being called properly.     */
     772                 : /* -------------------------------------------------------------------- */
     773              11 :     if( nPrevId != -1 && m_nCurObjId != nPrevId )
     774                 :     {
     775                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     776                 :                   "TABMAPFile::GetNextFeatureId(%d) called out of sequence.", 
     777               0 :                   nPrevId );
     778               0 :         return -1;
     779                 :     }
     780                 : 
     781              11 :     CPLAssert( nPrevId == -1 || m_poCurObjBlock != NULL );
     782                 : 
     783                 : /* -------------------------------------------------------------------- */
     784                 : /*      Ensure things are initialized properly if this is a request     */
     785                 : /*      for the first feature.                                          */
     786                 : /* -------------------------------------------------------------------- */
     787              11 :     if( nPrevId == -1 )
     788                 :     {
     789               1 :         m_nCurObjId = -1;
     790                 :     }
     791                 : 
     792                 : /* -------------------------------------------------------------------- */
     793                 : /*      Try to advance to the next object in the current object         */
     794                 : /*      block.                                                          */
     795                 : /* -------------------------------------------------------------------- */
     796              11 :     if( nPrevId == -1 
     797                 :         || m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 )
     798                 :     {
     799                 :         // If not, try to advance to the next object block, and get
     800                 :         // first object from it.  Note that some object blocks actually
     801                 :         // have no objects, so we may have to advance to additional 
     802                 :         // object blocks till we find a non-empty one.
     803               2 :         GBool bFirstCall = (nPrevId == -1);
     804               1 :         do 
     805                 :         {
     806               2 :             if( !LoadNextMatchingObjectBlock( bFirstCall ) )
     807               1 :                 return -1;
     808                 : 
     809               1 :             bFirstCall = FALSE;
     810                 :         } while( m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 );
     811                 :     }
     812                 : 
     813              10 :     m_nCurObjType = m_poCurObjBlock->GetCurObjectType();
     814              10 :     m_nCurObjId = m_poCurObjBlock->GetCurObjectId();
     815                 :     m_nCurObjPtr = m_poCurObjBlock->GetStartAddress() 
     816              10 :         + m_poCurObjBlock->GetCurObjectOffset();
     817                 : 
     818              10 :     CPLAssert( m_nCurObjId != -1 );
     819                 : 
     820              10 :     return m_nCurObjId;
     821                 : }
     822                 : 
     823                 : /**********************************************************************
     824                 :  *                   TABMAPFile::Int2Coordsys()
     825                 :  *
     826                 :  * Convert from long integer (internal) to coordinates system units
     827                 :  * as defined in the file's coordsys clause.
     828                 :  *
     829                 :  * Note that the false easting/northing and the conversion factor from
     830                 :  * datum to coordsys units are not included in the calculation.
     831                 :  *
     832                 :  * Returns 0 on success, -1 on error.
     833                 :  **********************************************************************/
     834            1417 : int TABMAPFile::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY)
     835                 : {
     836            1417 :     if (m_poHeader == NULL)
     837               0 :         return -1;
     838                 : 
     839            1417 :     return m_poHeader->Int2Coordsys(nX, nY, dX, dY);
     840                 : }
     841                 : 
     842                 : /**********************************************************************
     843                 :  *                   TABMAPFile::Coordsys2Int()
     844                 :  *
     845                 :  * Convert from coordinates system units as defined in the file's 
     846                 :  * coordsys clause to long integer (internal) coordinates.
     847                 :  *
     848                 :  * Note that the false easting/northing and the conversion factor from
     849                 :  * datum to coordsys units are not included in the calculation.
     850                 :  *
     851                 :  * Returns 0 on success, -1 on error.
     852                 :  **********************************************************************/
     853             311 : int TABMAPFile::Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY, 
     854                 :                              GBool bIgnoreOverflow/*=FALSE*/)
     855                 : {
     856             311 :     if (m_poHeader == NULL)
     857               0 :         return -1;
     858                 : 
     859             311 :     return m_poHeader->Coordsys2Int(dX, dY, nX, nY, bIgnoreOverflow);
     860                 : }
     861                 : 
     862                 : /**********************************************************************
     863                 :  *                   TABMAPFile::Int2CoordsysDist()
     864                 :  *
     865                 :  * Convert a pair of X,Y size (or distance) values from long integer
     866                 :  * (internal) to coordinates system units as defined in the file's coordsys
     867                 :  * clause.
     868                 :  *
     869                 :  * The difference with Int2Coordsys() is that this function only applies
     870                 :  * the scaling factor: it does not apply the displacement.
     871                 :  *
     872                 :  * Since the calculations on the X and Y values are independent, either
     873                 :  * one can be omitted (i.e. passed as 0)
     874                 :  *
     875                 :  * Returns 0 on success, -1 on error.
     876                 :  **********************************************************************/
     877               0 : int TABMAPFile::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY)
     878                 : {
     879               0 :     if (m_poHeader == NULL)
     880               0 :         return -1;
     881                 : 
     882               0 :     return m_poHeader->Int2CoordsysDist(nX, nY, dX, dY);
     883                 : }
     884                 : 
     885                 : /**********************************************************************
     886                 :  *                   TABMAPFile::Coordsys2IntDist()
     887                 :  *
     888                 :  * Convert a pair of X,Y size (or distance) values from coordinates 
     889                 :  * system units as defined in the file's coordsys clause to long 
     890                 :  * integer (internal) coordinate units.
     891                 :  *
     892                 :  * The difference with Int2Coordsys() is that this function only applies
     893                 :  * the scaling factor: it does not apply the displacement.
     894                 :  *
     895                 :  * Since the calculations on the X and Y values are independent, either
     896                 :  * one can be omitted (i.e. passed as 0)
     897                 :  *
     898                 :  * Returns 0 on success, -1 on error.
     899                 :  **********************************************************************/
     900               0 : int TABMAPFile::Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY)
     901                 : {
     902               0 :     if (m_poHeader == NULL)
     903               0 :         return -1;
     904                 : 
     905               0 :     return m_poHeader->Coordsys2IntDist(dX, dY, nX, nY);
     906                 : }
     907                 : 
     908                 : /**********************************************************************
     909                 :  *                   TABMAPFile::SetCoordsysBounds()
     910                 :  *
     911                 :  * Set projection coordinates bounds of the newly created dataset.
     912                 :  *
     913                 :  * This function must be called after creating a new dataset and before any
     914                 :  * feature can be written to it.
     915                 :  *
     916                 :  * Returns 0 on success, -1 on error.
     917                 :  **********************************************************************/
     918               3 : int TABMAPFile::SetCoordsysBounds(double dXMin, double dYMin, 
     919                 :                                   double dXMax, double dYMax)
     920                 : {
     921               3 :     int nStatus = 0;
     922                 : 
     923               3 :     if (m_poHeader == NULL)
     924               0 :         return -1;
     925                 : 
     926               3 :     nStatus = m_poHeader->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
     927                 : 
     928               3 :     if (nStatus == 0)
     929               3 :         ResetCoordFilter();
     930                 : 
     931               3 :     return nStatus;
     932                 : }
     933                 : 
     934                 : /**********************************************************************
     935                 :  *                   TABMAPFile::GetMaxObjId()
     936                 :  *
     937                 :  * Return the value of the biggest valid object id.
     938                 :  *
     939                 :  * Note that object ids are positive and start at 1.
     940                 :  *
     941                 :  * Returns a value >= 0 on success, -1 on error.
     942                 :  **********************************************************************/
     943               0 : GInt32 TABMAPFile::GetMaxObjId()
     944                 : {
     945               0 :     if (m_poIdIndex)
     946               0 :         return m_poIdIndex->GetMaxObjId();
     947                 : 
     948               0 :     return -1;
     949                 : }
     950                 : 
     951                 : /**********************************************************************
     952                 :  *                   TABMAPFile::MoveToObjId()
     953                 :  *
     954                 :  * Get ready to work with the object with the specified id.  The object
     955                 :  * data pointer (inside m_poCurObjBlock) will be moved to the first byte
     956                 :  * of data for this map object.  
     957                 :  *
     958                 :  * The object type and id (i.e. table row number) will be accessible 
     959                 :  * using GetCurObjType() and GetCurObjId().
     960                 :  * 
     961                 :  * Note that object ids are positive and start at 1.
     962                 :  *
     963                 :  * Returns 0 on success, -1 on error.
     964                 :  **********************************************************************/
     965              96 : int   TABMAPFile::MoveToObjId(int nObjId)
     966                 : {
     967                 :     int nFileOffset;
     968                 : 
     969                 :     /*-----------------------------------------------------------------
     970                 :      * In read access mode, since the .MAP/.ID are optional, if the 
     971                 :      * file is not opened then we can still act as if one existed and
     972                 :      * make any object id look like a TAB_GEOM_NONE
     973                 :      *----------------------------------------------------------------*/
     974              96 :     if (m_fp == NULL && m_eAccessMode == TABRead)
     975                 :     {
     976               0 :         CPLAssert(m_poIdIndex == NULL && m_poCurObjBlock == NULL);
     977               0 :         m_nCurObjPtr = 0;
     978               0 :         m_nCurObjId = nObjId;
     979               0 :         m_nCurObjType = TAB_GEOM_NONE;
     980                 : 
     981               0 :         return 0;
     982                 :     }
     983                 : 
     984              96 :     if (m_poIdIndex == NULL || m_poCurObjBlock == NULL)
     985                 :     {
     986                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     987               0 :                  "MoveToObjId(): file not opened!");
     988               0 :         m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
     989               0 :         return -1;
     990                 :     }
     991                 : 
     992                 :     /*-----------------------------------------------------------------
     993                 :      * Move map object pointer to the right location.  Fetch location
     994                 :      * from the index file, unless we are already pointing at it.
     995                 :      *----------------------------------------------------------------*/
     996              96 :     if( m_nCurObjId == nObjId )
     997              53 :         nFileOffset = m_nCurObjPtr;
     998                 :     else
     999              43 :         nFileOffset = m_poIdIndex->GetObjPtr(nObjId);
    1000                 : 
    1001              96 :     if (nFileOffset == 0)
    1002                 :     {
    1003                 :         /*---------------------------------------------------------
    1004                 :          * Object with no geometry... this is a valid case.
    1005                 :          *--------------------------------------------------------*/
    1006               2 :         m_nCurObjPtr = 0;
    1007               2 :         m_nCurObjId = nObjId;
    1008               2 :         m_nCurObjType = TAB_GEOM_NONE;
    1009                 :     }
    1010              94 :     else if ( m_poCurObjBlock->GotoByteInFile(nFileOffset, TRUE) == 0)
    1011                 :     {
    1012                 :         /*-------------------------------------------------------------
    1013                 :          * OK, it worked, read the object type and row id.
    1014                 :          *------------------------------------------------------------*/
    1015              94 :         m_nCurObjPtr = nFileOffset;
    1016              94 :         m_nCurObjType = m_poCurObjBlock->ReadByte();
    1017              94 :         m_nCurObjId   = m_poCurObjBlock->ReadInt32();
    1018                 : 
    1019                 :         // Do a consistency check...
    1020              94 :         if (m_nCurObjId != nObjId)
    1021                 :         {
    1022                 :             CPLError(CE_Failure, CPLE_FileIO,
    1023                 :                  "Object ID from the .ID file (%d) differs from the value "
    1024                 :                  "in the .MAP file (%d).  File may be corrupt.",
    1025               0 :                  nObjId, m_nCurObjId);
    1026               0 :             m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
    1027               0 :             return -1;
    1028                 :         }
    1029                 :     }
    1030                 :     else
    1031                 :     {
    1032                 :         /*---------------------------------------------------------
    1033                 :          * Failed positioning input file... CPLError has been called.
    1034                 :          *--------------------------------------------------------*/
    1035               0 :         m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
    1036               0 :         return -1;
    1037                 :     }
    1038                 : 
    1039              96 :     return 0;
    1040                 : }
    1041                 : 
    1042                 : /**********************************************************************
    1043                 :  *                   TABMAPFile::UpdateMapHeaderInfo()
    1044                 :  *
    1045                 :  * Update .map header information (counter of objects by type and minimum
    1046                 :  * required version) in light of a new object to be written to the file.
    1047                 :  *
    1048                 :  * Called only by PrepareNewObj() and by the TABCollection class.
    1049                 :  **********************************************************************/
    1050              12 : void  TABMAPFile::UpdateMapHeaderInfo(GByte nObjType)
    1051                 : {
    1052                 :     /*-----------------------------------------------------------------
    1053                 :      * Update count of objects by type in the header block
    1054                 :      *----------------------------------------------------------------*/
    1055              12 :     if (nObjType == TAB_GEOM_SYMBOL ||
    1056                 :         nObjType == TAB_GEOM_FONTSYMBOL ||
    1057                 :         nObjType == TAB_GEOM_CUSTOMSYMBOL ||
    1058                 :         nObjType == TAB_GEOM_MULTIPOINT ||
    1059                 :         nObjType == TAB_GEOM_V800_MULTIPOINT ||
    1060                 :         nObjType == TAB_GEOM_SYMBOL_C ||
    1061                 :         nObjType == TAB_GEOM_FONTSYMBOL_C ||
    1062                 :         nObjType == TAB_GEOM_CUSTOMSYMBOL_C ||
    1063                 :         nObjType == TAB_GEOM_MULTIPOINT_C ||
    1064                 :         nObjType == TAB_GEOM_V800_MULTIPOINT_C )
    1065                 :     {
    1066               0 :         m_poHeader->m_numPointObjects++;
    1067                 :     }
    1068              13 :     else if (nObjType == TAB_GEOM_LINE ||
    1069                 :              nObjType == TAB_GEOM_PLINE ||
    1070                 :              nObjType == TAB_GEOM_MULTIPLINE ||
    1071                 :              nObjType == TAB_GEOM_V450_MULTIPLINE ||
    1072                 :              nObjType == TAB_GEOM_V800_MULTIPLINE ||
    1073                 :              nObjType == TAB_GEOM_ARC ||
    1074                 :              nObjType == TAB_GEOM_LINE_C ||
    1075                 :              nObjType == TAB_GEOM_PLINE_C ||
    1076                 :              nObjType == TAB_GEOM_MULTIPLINE_C ||
    1077                 :              nObjType == TAB_GEOM_V450_MULTIPLINE_C ||
    1078                 :              nObjType == TAB_GEOM_V800_MULTIPLINE_C ||
    1079                 :              nObjType == TAB_GEOM_ARC_C)
    1080                 :     {
    1081               1 :         m_poHeader->m_numLineObjects++;
    1082                 :     }
    1083              22 :     else if (nObjType == TAB_GEOM_REGION ||
    1084                 :              nObjType == TAB_GEOM_V450_REGION ||
    1085                 :              nObjType == TAB_GEOM_V800_REGION ||
    1086                 :              nObjType == TAB_GEOM_RECT ||
    1087                 :              nObjType == TAB_GEOM_ROUNDRECT ||
    1088                 :              nObjType == TAB_GEOM_ELLIPSE ||
    1089                 :              nObjType == TAB_GEOM_REGION_C ||
    1090                 :              nObjType == TAB_GEOM_V450_REGION_C ||
    1091                 :              nObjType == TAB_GEOM_V800_REGION_C ||
    1092                 :              nObjType == TAB_GEOM_RECT_C ||
    1093                 :              nObjType == TAB_GEOM_ROUNDRECT_C ||
    1094                 :              nObjType == TAB_GEOM_ELLIPSE_C)
    1095                 :     {
    1096              11 :         m_poHeader->m_numRegionObjects++;
    1097                 :     }
    1098               0 :     else if (nObjType == TAB_GEOM_TEXT ||
    1099                 :              nObjType == TAB_GEOM_TEXT_C)
    1100                 :     {
    1101               0 :         m_poHeader->m_numTextObjects++;
    1102                 :     }
    1103                 : 
    1104                 :     /*-----------------------------------------------------------------
    1105                 :      * Check forminimum TAB file version number
    1106                 :      *----------------------------------------------------------------*/
    1107              12 :     int nVersion = TAB_GEOM_GET_VERSION(nObjType);
    1108                 : 
    1109              12 :     if (nVersion > m_nMinTABVersion )
    1110                 :     {
    1111               0 :         m_nMinTABVersion = nVersion;
    1112                 :     }
    1113                 : 
    1114              12 : }
    1115                 : 
    1116                 : /**********************************************************************
    1117                 :  *                   TABMAPFile::PrepareNewObj()
    1118                 :  *
    1119                 :  * Get ready to write a new object described by poObjHdr (using the
    1120                 :  * poObjHdr's m_nId (featureId), m_nType and IntMBR members which must 
    1121                 :  * have been set by the caller).
    1122                 :  *
    1123                 :  * Depending on whether "quick spatial index mode" is selected, we either:
    1124                 :  *
    1125                 :  * 1- Walk through the spatial index to find the best place to insert the
    1126                 :  * new object, update the spatial index references, and prepare the object
    1127                 :  * data block to be ready to write the object to it.
    1128                 :  * ... or ...
    1129                 :  * 2- prepare the current object data block to be ready to write the 
    1130                 :  * object to it. If the object block is full then it is inserted in the 
    1131                 :  * spatial index and committed to disk, and a new obj block is created.
    1132                 :  *
    1133                 :  * m_poCurObjBlock will be set to be ready to receive the new object, and
    1134                 :  * a new block will be created if necessary (in which case the current 
    1135                 :  * block contents will be committed to disk, etc.)  The actual ObjHdr
    1136                 :  * data won't be written to m_poCurObjBlock until CommitNewObj() is called. 
    1137                 :  *
    1138                 :  * If this object type uses coordinate blocks, then the coordinate block
    1139                 :  * will be prepared to receive coordinates.
    1140                 :  *
    1141                 :  * This function will also take care of updating the .ID index entry for
    1142                 :  * the new object.
    1143                 :  *
    1144                 :  * Note that object ids are positive and start at 1.
    1145                 :  *
    1146                 :  * Returns 0 on success, -1 on error.
    1147                 :  **********************************************************************/
    1148              14 : int   TABMAPFile::PrepareNewObj(TABMAPObjHdr *poObjHdr)
    1149                 : {
    1150                 : 
    1151              14 :     m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
    1152                 : 
    1153              14 :     if (m_eAccessMode != TABWrite || 
    1154                 :         m_poIdIndex == NULL || m_poHeader == NULL)
    1155                 :     {
    1156                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1157               0 :                  "PrepareNewObj() failed: file not opened for write access.");
    1158               0 :         return -1;
    1159                 :     }
    1160                 : 
    1161                 :     /*-----------------------------------------------------------------
    1162                 :      * For objects with no geometry, we just update the .ID file and return
    1163                 :      *----------------------------------------------------------------*/
    1164              14 :     if (poObjHdr->m_nType == TAB_GEOM_NONE)
    1165                 :     {
    1166               2 :         m_nCurObjType = poObjHdr->m_nType;
    1167               2 :         m_nCurObjId   = poObjHdr->m_nId;
    1168               2 :         m_nCurObjPtr  = 0;
    1169               2 :         m_poIdIndex->SetObjPtr(m_nCurObjId, 0);
    1170                 : 
    1171               2 :         return 0;
    1172                 :     }
    1173                 : 
    1174                 :     /*-----------------------------------------------------------------
    1175                 :      * Update count of objects by type in the header block and minimum
    1176                 :      * required version.
    1177                 :      *----------------------------------------------------------------*/
    1178              12 :     UpdateMapHeaderInfo(poObjHdr->m_nType);
    1179                 : 
    1180                 : 
    1181                 :     /*-----------------------------------------------------------------
    1182                 :      * Depending on the selected spatial index mode, we will either insert
    1183                 :      * new objects via the spatial index (slower write but results in optimal
    1184                 :      * spatial index) or directly in the current ObjBlock (faster write
    1185                 :      * but non-optimal spatial index)
    1186                 :      *----------------------------------------------------------------*/
    1187              12 :     if ( !m_bQuickSpatialIndexMode )
    1188                 :     {
    1189               0 :         if (PrepareNewObjViaSpatialIndex(poObjHdr) != 0)
    1190               0 :             return -1;  /* Error already reported */
    1191                 :     }
    1192                 :     else
    1193                 :     {
    1194              12 :         if (PrepareNewObjViaObjBlock(poObjHdr) != 0)
    1195               0 :             return -1;  /* Error already reported */
    1196                 :     }
    1197                 : 
    1198                 :     /*-----------------------------------------------------------------
    1199                 :      * Prepare ObjBlock for this new object.
    1200                 :      * Real data won't be written to the object block until CommitNewObj()
    1201                 :      * is called.
    1202                 :      *----------------------------------------------------------------*/
    1203              12 :     m_nCurObjPtr = m_poCurObjBlock->PrepareNewObject(poObjHdr);
    1204              12 :     if (m_nCurObjPtr < 0 )
    1205                 :     {
    1206                 :         CPLError(CE_Failure, CPLE_FileIO,
    1207                 :                  "Failed writing object header for feature id %d",
    1208               0 :                  poObjHdr->m_nId);
    1209               0 :         return -1;
    1210                 :     }
    1211                 : 
    1212              12 :     m_nCurObjType = poObjHdr->m_nType;
    1213              12 :     m_nCurObjId   = poObjHdr->m_nId;
    1214                 : 
    1215                 :     /*-----------------------------------------------------------------
    1216                 :      * Update .ID Index
    1217                 :      *----------------------------------------------------------------*/
    1218              12 :     m_poIdIndex->SetObjPtr(m_nCurObjId, m_nCurObjPtr);
    1219                 : 
    1220                 :     /*-----------------------------------------------------------------
    1221                 :      * Prepare Coords block... 
    1222                 :      * create a new TABMAPCoordBlock if it was not done yet.
    1223                 :      *----------------------------------------------------------------*/
    1224              12 :     PrepareCoordBlock(m_nCurObjType, m_poCurObjBlock, &m_poCurCoordBlock);
    1225                 : 
    1226              12 :     if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure)
    1227               0 :         return -1;
    1228                 : 
    1229              12 :     return 0;
    1230                 : }
    1231                 : 
    1232                 : /**********************************************************************
    1233                 :  *                   TABMAPFile::PrepareNewObjViaSpatialIndex()
    1234                 :  *
    1235                 :  * Used by TABMAPFile::PrepareNewObj() to walk through the spatial index
    1236                 :  * to find the best place to insert the new object, update the spatial 
    1237                 :  * index references, and prepare the object data block to be ready to 
    1238                 :  * write the object to it.
    1239                 :  *
    1240                 :  * This method is used when "quick spatial index mode" is NOT selected,
    1241                 :  * i.e. when we want to produce a file with an optimal spatial index
    1242                 :  *
    1243                 :  * Returns 0 on success, -1 on error.
    1244                 :  **********************************************************************/
    1245               0 : int   TABMAPFile::PrepareNewObjViaSpatialIndex(TABMAPObjHdr *poObjHdr)
    1246                 : {
    1247                 :     int nObjSize;
    1248               0 :     GInt32 nObjBlockForInsert = -1;
    1249                 : 
    1250                 :     /*-----------------------------------------------------------------
    1251                 :      * Create spatial index if we don't have one yet.
    1252                 :      * We do not create the index and object data blocks in the open() 
    1253                 :      * call because files that contained only "NONE" geometries ended up 
    1254                 :      * with empty object and spatial index blocks.
    1255                 :      *----------------------------------------------------------------*/
    1256               0 :     if (m_poSpIndex == NULL)
    1257                 :     {
    1258                 :         // Spatial Index not created yet...
    1259               0 :         m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
    1260                 : 
    1261                 :         m_poSpIndex->InitNewBlock(m_fp, 512, 
    1262               0 :                                   m_oBlockManager.AllocNewBlock());
    1263               0 :         m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
    1264                 : 
    1265               0 :         m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
    1266                 : 
    1267                 :         /* We'll also need to create an object data block (later) */
    1268               0 :         nObjBlockForInsert = -1;
    1269                 : 
    1270               0 :         CPLAssert(m_poCurObjBlock == NULL);
    1271                 :     }
    1272                 :     else
    1273                 :     /*-----------------------------------------------------------------
    1274                 :      * Search the spatial index to find the best place to insert this 
    1275                 :      * new object. 
    1276                 :      *----------------------------------------------------------------*/
    1277                 :     {
    1278                 :         nObjBlockForInsert=m_poSpIndex->ChooseLeafForInsert(poObjHdr->m_nMinX, 
    1279                 :                                                             poObjHdr->m_nMinY,
    1280                 :                                                             poObjHdr->m_nMaxX,
    1281               0 :                                                             poObjHdr->m_nMaxY);
    1282               0 :         if (nObjBlockForInsert == -1)
    1283                 :         {
    1284                 :             /* ChooseLeafForInsert() should not fail unless file is corrupt*/
    1285                 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    1286               0 :                      "ChooseLeafForInsert() Failed?!?!");
    1287               0 :             return -1;
    1288                 :         }
    1289                 :     }
    1290                 : 
    1291                 : 
    1292               0 :     if (nObjBlockForInsert == -1)
    1293                 :     {
    1294                 :         /*-------------------------------------------------------------
    1295                 :          * Create a new object data block from scratch
    1296                 :          *------------------------------------------------------------*/
    1297               0 :         m_poCurObjBlock = new TABMAPObjectBlock(TABReadWrite);
    1298                 : 
    1299               0 :         int nBlockOffset = m_oBlockManager.AllocNewBlock();
    1300                 : 
    1301               0 :         m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset);
    1302                 : 
    1303                 :         /*-------------------------------------------------------------
    1304                 :          * Insert new object block in index, based on MBR of poObjHdr
    1305                 :          *------------------------------------------------------------*/
    1306               0 :         if (m_poSpIndex->AddEntry(poObjHdr->m_nMinX, 
    1307                 :                                   poObjHdr->m_nMinY,
    1308                 :                                   poObjHdr->m_nMaxX,
    1309                 :                                   poObjHdr->m_nMaxY,
    1310                 :                                   m_poCurObjBlock->GetStartAddress()) != 0)
    1311               0 :             return -1;
    1312                 : 
    1313                 :         m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
    1314               0 :                                       (GByte)m_poSpIndex->GetCurMaxDepth()+1);
    1315                 :     }
    1316                 :     else
    1317                 :     {
    1318                 :         /*-------------------------------------------------------------
    1319                 :          * Load existing object and Coord blocks, unless we've already 
    1320                 :          * got the right object block in memory
    1321                 :          *------------------------------------------------------------*/
    1322               0 :         if (m_poCurObjBlock && 
    1323                 :             m_poCurObjBlock->GetStartAddress() != nObjBlockForInsert)
    1324                 :         {
    1325                 :             /* Got a block in memory but it's not the right one, flush it */
    1326               0 :             if (CommitObjAndCoordBlocks(TRUE) != 0 )
    1327               0 :                 return -1;
    1328                 :         }
    1329                 : 
    1330               0 :         if (m_poCurObjBlock == NULL)
    1331                 :         {
    1332               0 :             if (LoadObjAndCoordBlocks(nObjBlockForInsert) != 0)
    1333               0 :                 return -1;
    1334                 : 
    1335                 :             // The ObjBlock doesn't know its MBR. Get the value from the 
    1336                 :             // index and set it
    1337                 :             GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1338                 :             m_poSpIndex->GetCurLeafEntryMBR(m_poCurObjBlock->GetStartAddress(),
    1339               0 :                                             nMinX, nMinY, nMaxX, nMaxY);
    1340               0 :             m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1341                 :         }
    1342                 :     }
    1343                 : 
    1344                 :     /*-----------------------------------------------------------------
    1345                 :      * Fetch new object size, make sure there is enough room in obj. 
    1346                 :      * block for new object, update spatial index and split if necessary.
    1347                 :      *----------------------------------------------------------------*/
    1348               0 :     nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
    1349               0 :     if (m_poCurObjBlock->GetNumUnusedBytes() >= nObjSize )
    1350                 :     {
    1351                 :         /*-------------------------------------------------------------
    1352                 :          * New object fits in current block, just update the spatial index
    1353                 :          *------------------------------------------------------------*/
    1354                 :         GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1355               0 :         m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1356                 : 
    1357                 :         /* Need to calculate the enlarged MBR that includes new object */
    1358               0 :         nMinX = MIN(nMinX, poObjHdr->m_nMinX);
    1359               0 :         nMinY = MIN(nMinY, poObjHdr->m_nMinY);
    1360               0 :         nMaxX = MAX(nMaxX, poObjHdr->m_nMaxX);
    1361               0 :         nMaxY = MAX(nMaxY, poObjHdr->m_nMaxY);
    1362                 : 
    1363               0 :         if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(),
    1364                 :                                          nMinX, nMinY, nMaxX, nMaxY) != 0)
    1365               0 :             return -1;
    1366                 :     }
    1367                 :     else
    1368                 :     {
    1369                 :         /*-------------------------------------------------------------
    1370                 :          * OK, the new object won't fit in the current block, need to split
    1371                 :          * and update index.
    1372                 :          * Split() does its job so that the current obj block will remain 
    1373                 :          * the best candidate to receive the new object. It also flushes 
    1374                 :          * everything to disk and will update m_poCurCoordBlock to point to
    1375                 :          * the last coord block in the chain, ready to accept new data
    1376                 :          *------------------------------------------------------------*/
    1377                 :         TABMAPObjectBlock *poNewObjBlock;
    1378               0 :         poNewObjBlock= SplitObjBlock(poObjHdr, nObjSize);
    1379                 : 
    1380               0 :         if (poNewObjBlock == NULL)
    1381               0 :             return -1;  /* Split failed, error already reported. */
    1382                 : 
    1383                 :         /*-------------------------------------------------------------
    1384                 :          * Update index with info about m_poCurObjectBlock *first*
    1385                 :          * This is important since UpdateLeafEntry() needs the chain of
    1386                 :          * index nodes preloaded by ChooseLeafEntry() in order to do its job
    1387                 :          *------------------------------------------------------------*/
    1388                 :         GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1389               0 :         m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1390                 : 
    1391                 :         /* Need to calculate the enlarged MBR that includes new object */
    1392               0 :         nMinX = MIN(nMinX, poObjHdr->m_nMinX);
    1393               0 :         nMinY = MIN(nMinY, poObjHdr->m_nMinY);
    1394               0 :         nMaxX = MAX(nMaxX, poObjHdr->m_nMaxX);
    1395               0 :         nMaxY = MAX(nMaxY, poObjHdr->m_nMaxY);
    1396                 : 
    1397               0 :         if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(),
    1398                 :                                          nMinX, nMinY, nMaxX, nMaxY) != 0)
    1399               0 :             return -1;
    1400                 : 
    1401                 :         /*-------------------------------------------------------------
    1402                 :          * Add new obj block to index
    1403                 :          *------------------------------------------------------------*/
    1404               0 :         poNewObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1405                 : 
    1406               0 :         if (m_poSpIndex->AddEntry(nMinX, nMinY, nMaxX, nMaxY,
    1407                 :                                   poNewObjBlock->GetStartAddress()) != 0)
    1408               0 :             return -1;
    1409                 :         m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
    1410               0 :                                       (GByte)m_poSpIndex->GetCurMaxDepth()+1);
    1411                 : 
    1412                 :         /*-------------------------------------------------------------
    1413                 :          * Delete second object block, no need to commit to file first since
    1414                 :          * it's already been committed to disk by Split()
    1415                 :          *------------------------------------------------------------*/
    1416               0 :         delete poNewObjBlock;
    1417                 :     }
    1418                 : 
    1419               0 :     return 0;
    1420                 : }
    1421                 : 
    1422                 : /**********************************************************************
    1423                 :  *                   TABMAPFile::PrepareNewObjViaObjBlock()
    1424                 :  *
    1425                 :  * Used by TABMAPFile::PrepareNewObj() to prepare the current object 
    1426                 :  * data block to be ready to write the object to it. If the object block 
    1427                 :  * is full then it is inserted in the spatial index and committed to disk,
    1428                 :  * and a new obj block is created.
    1429                 :  *
    1430                 :  * This method is used when "quick spatial index mode" is selected,
    1431                 :  * i.e. faster write, but non-optimal spatial index.
    1432                 :  *
    1433                 :  * Returns 0 on success, -1 on error.
    1434                 :  **********************************************************************/
    1435              12 : int   TABMAPFile::PrepareNewObjViaObjBlock(TABMAPObjHdr *poObjHdr)
    1436                 : {
    1437                 :     int nObjSize;
    1438                 : 
    1439                 :     /*-------------------------------------------------------------
    1440                 :      * We will need an object block... check if it exists and
    1441                 :      * create it if it has not been created yet (first time for this file).
    1442                 :      * We do not create the object block in the open() call because
    1443                 :      * files that contained only "NONE" geometries ended up with empty
    1444                 :      * object and spatial index blocks.
    1445                 :      * Note: A coord block will be created only if needed later.
    1446                 :      *------------------------------------------------------------*/
    1447              12 :     if (m_poCurObjBlock == NULL)
    1448                 :     {
    1449               2 :         m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
    1450                 : 
    1451               2 :         int nBlockOffset = m_oBlockManager.AllocNewBlock();
    1452                 : 
    1453               2 :         m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset);
    1454                 : 
    1455                 :         // The reference to the first object block should 
    1456                 :         // actually go through the index blocks... this will be 
    1457                 :         // updated when file is closed.
    1458               2 :         m_poHeader->m_nFirstIndexBlock = nBlockOffset;
    1459                 :     }
    1460                 : 
    1461                 :     /*-----------------------------------------------------------------
    1462                 :      * Fetch new object size, make sure there is enough room in obj. 
    1463                 :      * block for new object, and save/create a new one if necessary.
    1464                 :      *----------------------------------------------------------------*/
    1465              12 :     nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
    1466              12 :     if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize )
    1467                 :     {
    1468                 :         /*-------------------------------------------------------------
    1469                 :          * OK, the new object won't fit in the current block. Add the
    1470                 :          * current block to the spatial index, commit it to disk and init
    1471                 :          * a new block
    1472                 :          *------------------------------------------------------------*/
    1473               0 :         CommitObjAndCoordBlocks(FALSE);
    1474                 : 
    1475               0 :         if (m_poCurObjBlock->InitNewBlock(m_fp,512,
    1476               0 :                                   m_oBlockManager.AllocNewBlock())!=0)
    1477               0 :             return -1; /* Error already reported */
    1478                 : 
    1479                 :         /*-------------------------------------------------------------
    1480                 :          * Coord block has been committed to disk but not deleted.
    1481                 :          * Delete it to require the creation of a new coord block chain
    1482                 :          * as needed.
    1483                 :          *-------------------------------------------------------------*/
    1484               0 :         if (m_poCurCoordBlock)
    1485                 :         {
    1486               0 :             delete m_poCurCoordBlock;
    1487               0 :             m_poCurCoordBlock = NULL;
    1488                 :         }
    1489                 : 
    1490                 :     }
    1491                 : 
    1492              12 :     return 0;
    1493                 : }
    1494                 : 
    1495                 : /**********************************************************************
    1496                 :  *                   TABMAPFile::CommitNewObj()
    1497                 :  *
    1498                 :  * Commit object header data to the ObjBlock. Should be called after 
    1499                 :  * PrepareNewObj, once all members of the ObjHdr have been set.
    1500                 :  *
    1501                 :  * Returns 0 on success, -1 on error.
    1502                 :  **********************************************************************/
    1503              14 : int   TABMAPFile::CommitNewObj(TABMAPObjHdr *poObjHdr)
    1504                 : {
    1505              14 :     return m_poCurObjBlock->CommitNewObject(poObjHdr);
    1506                 : }
    1507                 : 
    1508                 : 
    1509                 : /**********************************************************************
    1510                 :  *                   TABMAPFile::CommitObjAndCoordBlocks()
    1511                 :  *
    1512                 :  * Commit the TABMAPObjBlock and TABMAPCoordBlock to disk.
    1513                 :  *
    1514                 :  * The objects are deleted from memory if bDeleteObjects==TRUE.
    1515                 :  *
    1516                 :  * Returns 0 on success, -1 on error.
    1517                 :  **********************************************************************/
    1518               3 : int TABMAPFile::CommitObjAndCoordBlocks(GBool bDeleteObjects /*=FALSE*/)
    1519                 : {
    1520               3 :     int nStatus = 0;
    1521                 : 
    1522                 :     /*-----------------------------------------------------------------
    1523                 :      * First check that a objBlock has been created.  It is possible to have
    1524                 :      * no object block in files that contain only "NONE" geometries.
    1525                 :      *----------------------------------------------------------------*/
    1526               3 :     if (m_poCurObjBlock == NULL)
    1527               1 :         return 0; 
    1528                 : 
    1529               2 :     if (m_eAccessMode != TABWrite)
    1530                 :     {
    1531                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1532               0 :                  "CommitObjAndCoordBlocks() failed: file not opened for write access.");
    1533               0 :         return -1;
    1534                 :     }
    1535                 : 
    1536                 :     /*-----------------------------------------------------------------
    1537                 :      * We need to flush the coord block if there was one
    1538                 :      * since a list of coord blocks can belong to only one obj. block
    1539                 :      *----------------------------------------------------------------*/
    1540               2 :     if (m_poCurCoordBlock)
    1541                 :     {
    1542                 :         // Update the m_nMaxCoordBufSize member in the header block
    1543                 :         //
    1544               2 :         int nTotalCoordSize = m_poCurCoordBlock->GetNumBlocksInChain()*512;
    1545               2 :         if (nTotalCoordSize > m_poHeader->m_nMaxCoordBufSize)
    1546               2 :             m_poHeader->m_nMaxCoordBufSize = nTotalCoordSize;
    1547                 : 
    1548                 :         // Update the references to this coord block in the MAPObjBlock
    1549                 :         //
    1550                 :         m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->
    1551               2 :                                                          GetStartAddress());
    1552               2 :         nStatus = m_poCurCoordBlock->CommitToFile();
    1553                 : 
    1554               2 :         if (bDeleteObjects)
    1555                 :         {
    1556               0 :             delete m_poCurCoordBlock;
    1557               0 :             m_poCurCoordBlock = NULL;
    1558                 :         }
    1559                 :     }
    1560                 : 
    1561                 :     /*-----------------------------------------------------------------
    1562                 :      * Commit the obj block
    1563                 :      *----------------------------------------------------------------*/
    1564               2 :     if (nStatus == 0)
    1565                 :     {
    1566               2 :         nStatus = m_poCurObjBlock->CommitToFile();
    1567                 :     }
    1568                 : 
    1569                 : 
    1570                 :     /*-----------------------------------------------------------------
    1571                 :      * Update the spatial index ** only in "quick spatial index" mode ** 
    1572                 :      * In the (default) optimized spatial index mode, the spatial index 
    1573                 :      * is already maintained up to date as part of inserting the objects in
    1574                 :      * PrepareNewObj().
    1575                 :      *
    1576                 :      * Spatial index will be created here if it was not done yet.
    1577                 :      *----------------------------------------------------------------*/
    1578               2 :     if (nStatus == 0 && m_bQuickSpatialIndexMode)
    1579                 :     {
    1580                 :         GInt32 nXMin, nYMin, nXMax, nYMax;
    1581                 : 
    1582               2 :         if (m_poSpIndex == NULL)
    1583                 :         {
    1584                 :             // Spatial Index not created yet...
    1585               2 :             m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
    1586                 : 
    1587                 :             m_poSpIndex->InitNewBlock(m_fp, 512, 
    1588               2 :                                       m_oBlockManager.AllocNewBlock());
    1589               2 :             m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
    1590                 : 
    1591               2 :             m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
    1592                 :         }
    1593                 : 
    1594               2 :         m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
    1595                 :         nStatus = m_poSpIndex->AddEntry(nXMin, nYMin, nXMax, nYMax,
    1596               2 :                                         m_poCurObjBlock->GetStartAddress());
    1597                 : 
    1598                 :         m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
    1599               2 :                                       (GByte)m_poSpIndex->GetCurMaxDepth()+1);
    1600                 :     }
    1601                 : 
    1602                 :     /*-----------------------------------------------------------------
    1603                 :      * Delete obj block only if requested
    1604                 :      *----------------------------------------------------------------*/
    1605               2 :     if (bDeleteObjects)
    1606                 :     {
    1607               0 :         delete m_poCurObjBlock;
    1608               0 :         m_poCurObjBlock = NULL;
    1609                 :     }
    1610                 : 
    1611               2 :     return nStatus;
    1612                 : }
    1613                 : 
    1614                 : /**********************************************************************
    1615                 :  *                   TABMAPFile::LoadObjAndCoordBlocks()
    1616                 :  *
    1617                 :  * Load the TABMAPObjBlock at specified address and corresponding 
    1618                 :  * TABMAPCoordBlock, ready to write new objects to them.
    1619                 :  *
    1620                 :  * It is assumed that pre-existing m_poCurObjBlock and m_poCurCoordBlock 
    1621                 :  * have been flushed to disk already using CommitObjAndCoordBlocks()
    1622                 :  *
    1623                 :  * Returns 0 on success, -1 on error.
    1624                 :  **********************************************************************/
    1625               0 : int TABMAPFile::LoadObjAndCoordBlocks(GInt32 nBlockPtr)
    1626                 : {
    1627               0 :     TABRawBinBlock *poBlock = NULL;
    1628                 : 
    1629                 :     /*-----------------------------------------------------------------
    1630                 :      * In Write mode, if an object block is already in memory then flush it
    1631                 :      *----------------------------------------------------------------*/
    1632               0 :     if (m_eAccessMode == TABWrite && m_poCurObjBlock != NULL)
    1633                 :     {
    1634               0 :         int nStatus = CommitObjAndCoordBlocks(TRUE);
    1635               0 :         if (nStatus != 0)
    1636               0 :             return nStatus;
    1637                 :     }
    1638                 : 
    1639                 :     /*-----------------------------------------------------------------
    1640                 :      * Load Obj Block
    1641                 :      *----------------------------------------------------------------*/
    1642               0 :     if ((poBlock = TABCreateMAPBlockFromFile(m_fp, 
    1643                 :                                              nBlockPtr,
    1644                 :                                              512, TRUE, TABReadWrite)) &&
    1645               0 :         poBlock->GetBlockClass() == TABMAP_OBJECT_BLOCK)
    1646                 :     {
    1647               0 :         m_poCurObjBlock = (TABMAPObjectBlock*)poBlock;
    1648               0 :         poBlock = NULL;
    1649                 :     }
    1650                 :     else
    1651                 :     {
    1652                 :         CPLError(CE_Failure, CPLE_FileIO,
    1653                 :                  "LoadObjAndCoordBlocks() failed for object block at %d.", 
    1654               0 :                  nBlockPtr);
    1655               0 :         return -1;
    1656                 :     }
    1657                 : 
    1658                 :     /*-----------------------------------------------------------------
    1659                 :      * Load the last coord block in the chain
    1660                 :      *----------------------------------------------------------------*/
    1661               0 :     if (m_poCurObjBlock->GetLastCoordBlockAddress() == 0)
    1662                 :     {
    1663               0 :         m_poCurCoordBlock = NULL;
    1664                 :     }
    1665               0 :     else if ((poBlock = TABCreateMAPBlockFromFile(m_fp, 
    1666                 :                                    m_poCurObjBlock->GetLastCoordBlockAddress(),
    1667                 :                                                   512, TRUE, TABReadWrite)) &&
    1668               0 :              poBlock->GetBlockClass() == TABMAP_COORD_BLOCK)
    1669                 :     {
    1670               0 :         m_poCurCoordBlock = (TABMAPCoordBlock*)poBlock;
    1671               0 :         m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager);
    1672               0 :         poBlock = NULL;
    1673                 :     }
    1674                 :     else
    1675                 :     {
    1676                 :         CPLError(CE_Failure, CPLE_FileIO,
    1677                 :                  "LoadObjAndCoordBlocks() failed for coord block at %d.", 
    1678               0 :                  m_poCurObjBlock->GetLastCoordBlockAddress());
    1679               0 :         return -1;
    1680                 :     }
    1681                 : 
    1682               0 :     return 0;
    1683                 : }
    1684                 : 
    1685                 : /**********************************************************************
    1686                 :  *                   TABMAPFile::SplitObjBlock()
    1687                 :  *
    1688                 :  * Split m_poCurObjBlock using Guttman algorithm.
    1689                 :  *
    1690                 :  * SplitObjBlock() doe its job so that the current obj block will remain 
    1691                 :  * the best candidate to receive the new object to add. It also flushes
    1692                 :  * everything to disk and will update m_poCurCoordBlock to point to the 
    1693                 :  * last coord block in the chain, ready to accept new data
    1694                 :  *
    1695                 :  * Updates to the spatial index are left to the caller.
    1696                 :  *
    1697                 :  * Returns the TABMAPObjBlock of the second block for use by the caller
    1698                 :  * in updating the spatial index, or NULL in case of error.
    1699                 :  **********************************************************************/
    1700               0 : TABMAPObjectBlock *TABMAPFile::SplitObjBlock(TABMAPObjHdr *poObjHdrToAdd,
    1701                 :                                              int nSizeOfObjToAdd)
    1702                 : {
    1703               0 :     TABMAPObjHdr **papoSrcObjHdrs = NULL, *poObjHdr=NULL;
    1704               0 :     int i, numSrcObj = 0;
    1705                 : 
    1706                 :     /*-----------------------------------------------------------------
    1707                 :      * Read all object headers
    1708                 :      *----------------------------------------------------------------*/
    1709               0 :     m_poCurObjBlock->Rewind();
    1710               0 :     while ((poObjHdr = TABMAPObjHdr::ReadNextObj(m_poCurObjBlock, 
    1711                 :                                                  m_poHeader)) != NULL)
    1712                 :     {
    1713               0 :         if (papoSrcObjHdrs == NULL || numSrcObj%10 == 0)
    1714                 :         {
    1715                 :             // Realloc the array... by steps of 10
    1716                 :             papoSrcObjHdrs = (TABMAPObjHdr**)CPLRealloc(papoSrcObjHdrs, 
    1717                 :                                                         (numSrcObj+10)*
    1718               0 :                                                         sizeof(TABMAPObjHdr*));
    1719                 :         }
    1720               0 :         papoSrcObjHdrs[numSrcObj++] = poObjHdr;
    1721                 :     }
    1722               0 :     CPLAssert(numSrcObj > 0);
    1723                 : 
    1724                 :     /*-----------------------------------------------------------------
    1725                 :      * Reset current obj and coord block 
    1726                 :      *----------------------------------------------------------------*/
    1727               0 :     GInt32 nFirstSrcCoordBlock = m_poCurObjBlock->GetFirstCoordBlockAddress();
    1728                 : 
    1729                 :     m_poCurObjBlock->InitNewBlock(m_fp, 512, 
    1730               0 :                                   m_poCurObjBlock->GetStartAddress());
    1731                 : 
    1732               0 :     TABMAPCoordBlock *poSrcCoordBlock = m_poCurCoordBlock;
    1733               0 :     m_poCurCoordBlock = NULL;
    1734                 : 
    1735                 :     /*-----------------------------------------------------------------
    1736                 :      * Create new obj and coord block
    1737                 :      *----------------------------------------------------------------*/
    1738               0 :     TABMAPObjectBlock *poNewObjBlock = new TABMAPObjectBlock(m_eAccessMode);
    1739               0 :     poNewObjBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock());
    1740                 : 
    1741                 :     /* Coord block will be alloc'd automatically*/
    1742               0 :     TABMAPCoordBlock *poNewCoordBlock = NULL;  
    1743                 : 
    1744                 :     /*-----------------------------------------------------------------
    1745                 :      * Pick Seeds for each block
    1746                 :      *----------------------------------------------------------------*/
    1747                 :     TABMAPIndexEntry *pasSrcEntries = 
    1748               0 :         (TABMAPIndexEntry*)CPLMalloc(numSrcObj*sizeof(TABMAPIndexEntry));
    1749               0 :     for (i=0; i<numSrcObj; i++)
    1750                 :     {
    1751               0 :         pasSrcEntries[i].XMin = papoSrcObjHdrs[i]->m_nMinX;
    1752               0 :         pasSrcEntries[i].YMin = papoSrcObjHdrs[i]->m_nMinY;
    1753               0 :         pasSrcEntries[i].XMax = papoSrcObjHdrs[i]->m_nMaxX;
    1754               0 :         pasSrcEntries[i].YMax = papoSrcObjHdrs[i]->m_nMaxY;
    1755                 :     }
    1756                 : 
    1757                 :     int nSeed1, nSeed2;
    1758                 :     TABMAPIndexBlock::PickSeedsForSplit(pasSrcEntries, numSrcObj, -1,
    1759                 :                                         poObjHdrToAdd->m_nMinX,
    1760                 :                                         poObjHdrToAdd->m_nMinY,
    1761                 :                                         poObjHdrToAdd->m_nMaxX,
    1762                 :                                         poObjHdrToAdd->m_nMaxY,
    1763               0 :                                         nSeed1, nSeed2);
    1764               0 :     CPLFree(pasSrcEntries);
    1765               0 :     pasSrcEntries = NULL;
    1766                 : 
    1767                 :     /*-----------------------------------------------------------------
    1768                 :      * Assign the seeds to their respective block
    1769                 :      *----------------------------------------------------------------*/
    1770                 :     // Insert nSeed1 in this block
    1771               0 :     poObjHdr = papoSrcObjHdrs[nSeed1];
    1772               0 :     if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
    1773                 :                        m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
    1774               0 :         return NULL;
    1775                 : 
    1776                 :     // Move nSeed2 to 2nd block
    1777               0 :     poObjHdr = papoSrcObjHdrs[nSeed2];
    1778               0 :     if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
    1779                 :                        poNewObjBlock, &poNewCoordBlock) <= 0)
    1780               0 :         return NULL;
    1781                 : 
    1782                 :     /*-----------------------------------------------------------------
    1783                 :      * Go through the rest of the entries and assign them to one 
    1784                 :      * of the 2 blocks
    1785                 :      *
    1786                 :      * Criteria is minimal area difference.
    1787                 :      * Resolve ties by adding the entry to the block with smaller total
    1788                 :      * area, then to the one with fewer entries, then to either.
    1789                 :      *----------------------------------------------------------------*/
    1790               0 :     for(int iEntry=0; iEntry<numSrcObj; iEntry++)
    1791                 :     {
    1792               0 :         if (iEntry == nSeed1 || iEntry == nSeed2)
    1793               0 :             continue;
    1794                 : 
    1795               0 :         poObjHdr = papoSrcObjHdrs[iEntry];
    1796                 : 
    1797               0 :         int nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
    1798                 : 
    1799                 :         // If one of the two blocks is almost full then all remaining
    1800                 :         // entries should go to the other block
    1801               0 :         if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize+nSizeOfObjToAdd )
    1802                 :         {
    1803               0 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
    1804                 :                                poNewObjBlock, &poNewCoordBlock) <= 0)
    1805               0 :                 return NULL;
    1806               0 :             continue;
    1807                 :         }
    1808               0 :         else if (poNewObjBlock->GetNumUnusedBytes() < nObjSize+nSizeOfObjToAdd)
    1809                 :         {
    1810               0 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
    1811                 :                                m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
    1812               0 :                 return NULL;
    1813               0 :             continue;
    1814                 :         }
    1815                 : 
    1816                 : 
    1817                 :         // Decide which of the two blocks to put this entry in
    1818                 :         GInt32 nXMin, nYMin, nXMax, nYMax;
    1819               0 :         m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
    1820                 :         double dAreaDiff1 = 
    1821                 :             TABMAPIndexBlock::ComputeAreaDiff(nXMin, nYMin, 
    1822                 :                                               nXMax, nYMax,
    1823                 :                                               poObjHdr->m_nMinX, 
    1824                 :                                               poObjHdr->m_nMinY,
    1825                 :                                               poObjHdr->m_nMaxX,
    1826               0 :                                               poObjHdr->m_nMaxY);
    1827                 : 
    1828               0 :         poNewObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
    1829                 :         double dAreaDiff2 = 
    1830                 :             TABMAPIndexBlock::ComputeAreaDiff(nXMin, nYMin, nXMax, nYMax,
    1831                 :                                               poObjHdr->m_nMinX, 
    1832                 :                                               poObjHdr->m_nMinY,
    1833                 :                                               poObjHdr->m_nMaxX,
    1834               0 :                                               poObjHdr->m_nMaxY);
    1835                 : 
    1836               0 :         if (dAreaDiff1 < dAreaDiff2)
    1837                 :         {
    1838                 :             // This entry stays in this block
    1839               0 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
    1840                 :                                m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
    1841               0 :                 return NULL;
    1842                 :         }
    1843                 :         else
    1844                 :         {
    1845                 :             // This entry goes to new block
    1846               0 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
    1847                 :                                poNewObjBlock, &poNewCoordBlock) <= 0)
    1848               0 :                 return NULL;
    1849                 :         }
    1850                 :     }
    1851                 : 
    1852                 :     /* Cleanup papoSrcObjHdrs[] */
    1853               0 :     for(i=0; i<numSrcObj; i++)
    1854                 :     {
    1855               0 :         delete papoSrcObjHdrs[i];
    1856                 :     }
    1857               0 :     CPLFree(papoSrcObjHdrs);
    1858               0 :     papoSrcObjHdrs = NULL;
    1859                 : 
    1860                 :     /*-----------------------------------------------------------------
    1861                 :      * Delete second coord block if one was created
    1862                 :      * Refs to coord block were kept up to date by MoveObjToBlock()
    1863                 :      * We just need to commit to file and delete the object now.
    1864                 :      *----------------------------------------------------------------*/
    1865               0 :     if (poNewCoordBlock)
    1866                 :     {
    1867               0 :         if (poNewCoordBlock->CommitToFile() != 0)
    1868               0 :             return NULL;
    1869               0 :         delete poNewCoordBlock;
    1870                 :     }
    1871                 : 
    1872                 :     /*-----------------------------------------------------------------
    1873                 :      * Release unused coord. data blocks
    1874                 :      *----------------------------------------------------------------*/
    1875               0 :     if (poSrcCoordBlock)
    1876                 :     {
    1877               0 :         if (poSrcCoordBlock->GetStartAddress() != nFirstSrcCoordBlock)
    1878                 :         {
    1879               0 :             if (poSrcCoordBlock->GotoByteInFile(nFirstSrcCoordBlock, TRUE) != 0)
    1880               0 :                 return NULL;
    1881                 :         }
    1882                 : 
    1883               0 :         int nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock();
    1884               0 :         while(poSrcCoordBlock != NULL)
    1885                 :         {
    1886                 :             // Mark this block as deleted
    1887               0 :             if (poSrcCoordBlock->CommitAsDeleted(m_oBlockManager.
    1888                 :                                                  GetFirstGarbageBlock()) != 0)
    1889               0 :                 return NULL;
    1890               0 :             m_oBlockManager.PushGarbageBlock(poSrcCoordBlock->GetStartAddress());
    1891                 : 
    1892                 :             // Advance to next
    1893               0 :             if (nNextCoordBlock > 0)
    1894                 :             {
    1895               0 :                 if (poSrcCoordBlock->GotoByteInFile(nNextCoordBlock, TRUE) != 0)
    1896               0 :                     return NULL;
    1897               0 :                 nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock();
    1898                 :             }
    1899                 :             else
    1900                 :             {
    1901                 :                 // end of chain
    1902               0 :                 delete poSrcCoordBlock;
    1903               0 :                 poSrcCoordBlock = NULL;
    1904                 :             }
    1905                 :         }
    1906                 :     }
    1907                 :             
    1908                 : 
    1909               0 :     if (poNewObjBlock->CommitToFile() != 0)
    1910               0 :         return NULL;
    1911                 : 
    1912               0 :     return poNewObjBlock;
    1913                 : }
    1914                 : 
    1915                 : /**********************************************************************
    1916                 :  *                   TABMAPFile::MoveObjToBlock()
    1917                 :  *
    1918                 :  * Moves an object and its coord data to a new ObjBlock. Used when 
    1919                 :  * splitting Obj Blocks.
    1920                 :  *
    1921                 :  * May update the value of ppoCoordBlock if a new coord block had to
    1922                 :  * be created.
    1923                 :  *
    1924                 :  * Returns the address where new object is stored on success, -1 on error.
    1925                 :  **********************************************************************/
    1926               0 : int TABMAPFile::MoveObjToBlock(TABMAPObjHdr       *poObjHdr,
    1927                 :                                TABMAPCoordBlock   *poSrcCoordBlock,
    1928                 :                                TABMAPObjectBlock  *poDstObjBlock,
    1929                 :                                TABMAPCoordBlock   **ppoDstCoordBlock)
    1930                 : {
    1931                 :     /*-----------------------------------------------------------------
    1932                 :      * Copy Coord data if applicable
    1933                 :      * We use a temporary TABFeature object to handle the reading/writing
    1934                 :      * of coord block data.
    1935                 :      *----------------------------------------------------------------*/
    1936               0 :     if (m_poHeader->MapObjectUsesCoordBlock(poObjHdr->m_nType))
    1937                 :     {
    1938               0 :         TABMAPObjHdrWithCoord *poObjHdrCoord =(TABMAPObjHdrWithCoord*)poObjHdr;
    1939               0 :         OGRFeatureDefn * poDummyDefn = new OGRFeatureDefn;
    1940                 :         // Ref count defaults to 0... set it to 1
    1941               0 :         poDummyDefn->Reference();
    1942                 : 
    1943                 :         TABFeature *poFeature = 
    1944               0 :             TABFeature::CreateFromMapInfoType(poObjHdr->m_nType, poDummyDefn);
    1945                 : 
    1946                 : 
    1947               0 :         if (PrepareCoordBlock(poObjHdrCoord->m_nType, 
    1948                 :                               poDstObjBlock, ppoDstCoordBlock) != 0)
    1949               0 :             return -1;
    1950                 : 
    1951               0 :         GInt32 nSrcCoordPtr = poObjHdrCoord->m_nCoordBlockPtr;
    1952                 : 
    1953                 :         /* Copy Coord data
    1954                 :          * poObjHdrCoord->m_nCoordBlockPtr will be set by WriteGeometry...
    1955                 :          * We pass second arg to GotoByteInFile() to force reading from file
    1956                 :          * if nSrcCoordPtr is not in current block
    1957                 :          */
    1958               0 :         if (poSrcCoordBlock->GotoByteInFile(nSrcCoordPtr, TRUE) != 0 ||
    1959                 :             poFeature->ReadGeometryFromMAPFile(this, poObjHdr,
    1960                 :                                                TRUE /* bCoordDataOnly */,
    1961               0 :                                                &poSrcCoordBlock) != 0 ||
    1962                 :             poFeature->WriteGeometryToMAPFile(this, poObjHdr,
    1963                 :                                               TRUE /* bCoordDataOnly */,
    1964               0 :                                               ppoDstCoordBlock) != 0)
    1965                 :         {
    1966               0 :             delete poFeature;
    1967               0 :             delete poDummyDefn;
    1968               0 :             return -1;
    1969                 :         }
    1970                 : 
    1971                 : 
    1972                 :         // Update the references to dest coord block in the MAPObjBlock
    1973                 :         // in case new block has been alloc'd since PrepareCoordBlock()
    1974                 :         //
    1975               0 :         poDstObjBlock->AddCoordBlockRef((*ppoDstCoordBlock)->GetStartAddress());
    1976                 :         /* Cleanup */
    1977               0 :         delete poFeature;
    1978               0 :         poDummyDefn->Release();
    1979                 :     }
    1980                 : 
    1981                 :     /*-----------------------------------------------------------------
    1982                 :      * Prepare and Write ObjHdr to this ObjBlock
    1983                 :      *----------------------------------------------------------------*/
    1984               0 :     int nObjPtr = poDstObjBlock->PrepareNewObject(poObjHdr);
    1985               0 :     if (nObjPtr < 0 ||
    1986                 :         poDstObjBlock->CommitNewObject(poObjHdr) != 0)
    1987                 :     {
    1988                 :         CPLError(CE_Failure, CPLE_FileIO,
    1989                 :                  "Failed writing object header for feature id %d",
    1990               0 :                  poObjHdr->m_nId);
    1991               0 :         return -1;
    1992                 :     }
    1993                 : 
    1994                 :     /*-----------------------------------------------------------------
    1995                 :      * Update .ID Index
    1996                 :      *----------------------------------------------------------------*/
    1997               0 :     m_poIdIndex->SetObjPtr(poObjHdr->m_nId, nObjPtr);
    1998                 : 
    1999               0 :     return nObjPtr;
    2000                 : }
    2001                 : 
    2002                 : /**********************************************************************
    2003                 :  *                   TABMAPFile::PrepareCoordBlock()
    2004                 :  *
    2005                 :  * Prepare the coord block to receive an object of specified type if one
    2006                 :  * is needed, and update corresponding members in ObjBlock.
    2007                 :  *
    2008                 :  * May update the value of ppoCoordBlock and Returns 0 on success, -1 on error.
    2009                 :  **********************************************************************/
    2010              12 : int TABMAPFile::PrepareCoordBlock(int nObjType,
    2011                 :                                   TABMAPObjectBlock *poObjBlock,
    2012                 :                                   TABMAPCoordBlock  **ppoCoordBlock)
    2013                 : {
    2014                 : 
    2015                 :     /*-----------------------------------------------------------------
    2016                 :      * Prepare Coords block... 
    2017                 :      * create a new TABMAPCoordBlock if it was not done yet.
    2018                 :      * Note that in write mode, TABCollections require read/write access
    2019                 :      * to the coord block.
    2020                 :      *----------------------------------------------------------------*/
    2021              12 :     if (m_poHeader->MapObjectUsesCoordBlock(nObjType))
    2022                 :     {
    2023              11 :         if (*ppoCoordBlock == NULL)
    2024                 :         {
    2025                 :             *ppoCoordBlock = new TABMAPCoordBlock(m_eAccessMode==TABWrite?
    2026                 :                                                   TABReadWrite: 
    2027               2 :                                                   m_eAccessMode);
    2028                 :             (*ppoCoordBlock)->InitNewBlock(m_fp, 512, 
    2029               2 :                                            m_oBlockManager.AllocNewBlock());
    2030               2 :             (*ppoCoordBlock)->SetMAPBlockManagerRef(&m_oBlockManager);
    2031                 : 
    2032                 :             // Set the references to this coord block in the MAPObjBlock
    2033               2 :             poObjBlock->AddCoordBlockRef((*ppoCoordBlock)->GetStartAddress());
    2034                 : 
    2035                 :         }
    2036                 : 
    2037              11 :         if ((*ppoCoordBlock)->GetNumUnusedBytes() < 4)
    2038                 :         {
    2039               0 :             int nNewBlockOffset = m_oBlockManager.AllocNewBlock();
    2040               0 :             (*ppoCoordBlock)->SetNextCoordBlock(nNewBlockOffset);
    2041               0 :             (*ppoCoordBlock)->CommitToFile();
    2042               0 :             (*ppoCoordBlock)->InitNewBlock(m_fp, 512, nNewBlockOffset);
    2043                 :         }
    2044                 : 
    2045                 :         // Make sure read/write pointer is at the end of the block
    2046              11 :         (*ppoCoordBlock)->SeekEnd();
    2047                 : 
    2048              11 :         if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure)
    2049               0 :             return -1;
    2050                 :     }
    2051                 : 
    2052              12 :     return 0;
    2053                 : }
    2054                 : 
    2055                 : /**********************************************************************
    2056                 :  *                   TABMAPFile::GetCurObjType()
    2057                 :  *
    2058                 :  * Return the MapInfo object type of the object that the m_poCurObjBlock
    2059                 :  * is pointing to.  This value is set after a call to MoveToObjId().
    2060                 :  *
    2061                 :  * Returns a value >= 0 on success, -1 on error.
    2062                 :  **********************************************************************/
    2063             149 : int TABMAPFile::GetCurObjType()
    2064                 : {
    2065             149 :     return m_nCurObjType;
    2066                 : }
    2067                 : 
    2068                 : /**********************************************************************
    2069                 :  *                   TABMAPFile::GetCurObjId()
    2070                 :  *
    2071                 :  * Return the MapInfo object id of the object that the m_poCurObjBlock
    2072                 :  * is pointing to.  This value is set after a call to MoveToObjId().
    2073                 :  *
    2074                 :  * Returns a value >= 0 on success, -1 on error.
    2075                 :  **********************************************************************/
    2076              53 : int TABMAPFile::GetCurObjId()
    2077                 : {
    2078              53 :     return m_nCurObjId;
    2079                 : }
    2080                 : 
    2081                 : /**********************************************************************
    2082                 :  *                   TABMAPFile::GetCurObjBlock()
    2083                 :  *
    2084                 :  * Return the m_poCurObjBlock.  If MoveToObjId() has previously been 
    2085                 :  * called then m_poCurObjBlock points to the beginning of the current 
    2086                 :  * object data.
    2087                 :  *
    2088                 :  * Returns a reference to an object owned by this TABMAPFile object, or
    2089                 :  * NULL on error.
    2090                 :  **********************************************************************/
    2091              53 : TABMAPObjectBlock *TABMAPFile::GetCurObjBlock()
    2092                 : {
    2093              53 :     return m_poCurObjBlock;
    2094                 : }
    2095                 : 
    2096                 : /**********************************************************************
    2097                 :  *                   TABMAPFile::GetCurCoordBlock()
    2098                 :  *
    2099                 :  * Return the m_poCurCoordBlock.  This function should be used after 
    2100                 :  * PrepareNewObj() to get the reference to the coord block that has
    2101                 :  * just been initialized.
    2102                 :  *
    2103                 :  * Returns a reference to an object owned by this TABMAPFile object, or
    2104                 :  * NULL on error.
    2105                 :  **********************************************************************/
    2106              11 : TABMAPCoordBlock *TABMAPFile::GetCurCoordBlock()
    2107                 : {
    2108              11 :     return m_poCurCoordBlock;
    2109                 : }
    2110                 : 
    2111                 : /**********************************************************************
    2112                 :  *                   TABMAPFile::GetCoordBlock()
    2113                 :  *
    2114                 :  * Return a TABMAPCoordBlock object ready to read coordinates from it.
    2115                 :  * The block that contains nFileOffset will automatically be
    2116                 :  * loaded, and if nFileOffset is the beginning of a new block then the
    2117                 :  * pointer will be moved to the beginning of the data.
    2118                 :  *
    2119                 :  * The contents of the returned object is only valid until the next call
    2120                 :  * to GetCoordBlock().
    2121                 :  *
    2122                 :  * Returns a reference to an object owned by this TABMAPFile object, or
    2123                 :  * NULL on error.
    2124                 :  **********************************************************************/
    2125              51 : TABMAPCoordBlock *TABMAPFile::GetCoordBlock(int nFileOffset)
    2126                 : {
    2127              51 :     if (m_eAccessMode != TABRead)
    2128               0 :         return NULL;
    2129                 : 
    2130              51 :     if (m_poCurCoordBlock == NULL)
    2131                 :     {
    2132               2 :         m_poCurCoordBlock = new TABMAPCoordBlock(m_eAccessMode);
    2133               2 :         m_poCurCoordBlock->InitNewBlock(m_fp, 512);
    2134                 :     }
    2135                 : 
    2136                 :     /*-----------------------------------------------------------------
    2137                 :      * Use GotoByteInFile() to go to the requested location.  This will
    2138                 :      * force loading the block if necessary and reading its header.
    2139                 :      * If nFileOffset is at the beginning of the requested block, then
    2140                 :      * we make sure to move the read pointer past the 8 bytes header
    2141                 :      * to be ready to read coordinates data
    2142                 :      *----------------------------------------------------------------*/
    2143              51 :     if ( m_poCurCoordBlock->GotoByteInFile(nFileOffset, TRUE) != 0)
    2144                 :     {
    2145                 :         // Failed... an error has already been reported.
    2146               0 :         return NULL;
    2147                 :     }
    2148                 : 
    2149              51 :     if (nFileOffset % 512 == 0)
    2150               0 :         m_poCurCoordBlock->GotoByteInBlock(8);      // Skip Header
    2151                 : 
    2152              51 :     return m_poCurCoordBlock;
    2153                 : }
    2154                 : 
    2155                 : /**********************************************************************
    2156                 :  *                   TABMAPFile::GetHeaderBlock()
    2157                 :  *
    2158                 :  * Return a reference to the MAP file's header block.
    2159                 :  *
    2160                 :  * The returned pointer is a reference to an object owned by this TABMAPFile
    2161                 :  * object and should not be deleted by the caller.
    2162                 :  *
    2163                 :  * Return NULL if file has not been opened yet.
    2164                 :  **********************************************************************/
    2165               8 : TABMAPHeaderBlock *TABMAPFile::GetHeaderBlock()
    2166                 : {
    2167               8 :     return m_poHeader;
    2168                 : }
    2169                 : 
    2170                 : /**********************************************************************
    2171                 :  *                   TABMAPFile::GetIDFileRef()
    2172                 :  *
    2173                 :  * Return a reference to the .ID file attached to this .MAP file
    2174                 :  *
    2175                 :  * The returned pointer is a reference to an object owned by this TABMAPFile
    2176                 :  * object and should not be deleted by the caller.
    2177                 :  *
    2178                 :  * Return NULL if file has not been opened yet.
    2179                 :  **********************************************************************/
    2180               0 : TABIDFile *TABMAPFile::GetIDFileRef()
    2181                 : {
    2182               0 :     return m_poIdIndex;
    2183                 : }
    2184                 : 
    2185                 : /**********************************************************************
    2186                 :  *                   TABMAPFile::GetIndexBlock()
    2187                 :  *
    2188                 :  * Return a reference to the requested index or object block..
    2189                 :  *
    2190                 :  * Ownership of the returned block is turned over to the caller, who should
    2191                 :  * delete it when no longer needed.  The type of the block can be determined
    2192                 :  * with the GetBlockType() method. 
    2193                 :  *
    2194                 :  * @param nFileOffset the offset in the map file of the spatial index
    2195                 :  * block or object block to load.
    2196                 :  *
    2197                 :  * @return The requested TABMAPIndexBlock, TABMAPObjectBlock or NULL if the 
    2198                 :  * read fails for some reason.
    2199                 :  **********************************************************************/
    2200               2 : TABRawBinBlock *TABMAPFile::GetIndexObjectBlock( int nFileOffset )
    2201                 : {
    2202                 :     /*----------------------------------------------------------------
    2203                 :      * Read from the file
    2204                 :      *---------------------------------------------------------------*/
    2205                 :     GByte abyData[512];
    2206                 : 
    2207               2 :     if (VSIFSeek(m_fp, nFileOffset, SEEK_SET) != 0 
    2208                 :         || VSIFRead(abyData, sizeof(GByte), 512, m_fp) != 512 )
    2209                 :     {
    2210                 :         CPLError(CE_Failure, CPLE_FileIO,
    2211                 :                  "GetIndexBlock() failed reading %d bytes at offset %d.",
    2212               0 :                  512, nFileOffset);
    2213               0 :         return NULL;
    2214                 :     }
    2215                 : 
    2216                 : /* -------------------------------------------------------------------- */
    2217                 : /*      Create and initialize depending on the block type.              */
    2218                 : /* -------------------------------------------------------------------- */
    2219               2 :     int nBlockType = abyData[0];
    2220                 :     TABRawBinBlock *poBlock;
    2221                 : 
    2222               2 :     if( nBlockType == TABMAP_INDEX_BLOCK )
    2223               1 :         poBlock = new TABMAPIndexBlock();
    2224                 :     else
    2225               1 :         poBlock = new TABMAPObjectBlock();
    2226                 :     
    2227               2 :     if( poBlock->InitBlockFromData(abyData, 512, 512,
    2228               2 :                                    TRUE, m_fp, nFileOffset) == -1 )
    2229                 :     {
    2230               0 :         delete poBlock;
    2231               0 :         poBlock = NULL;
    2232                 :     }
    2233                 : 
    2234               2 :     return poBlock;
    2235                 : }
    2236                 : 
    2237                 : /**********************************************************************
    2238                 :  *                   TABMAPFile::InitDrawingTools()
    2239                 :  *
    2240                 :  * Init the drawing tools for this file.
    2241                 :  *
    2242                 :  * In Read mode, this will load the drawing tools from the file.
    2243                 :  *
    2244                 :  * In Write mode, this function will init an empty the tool def table.
    2245                 :  *
    2246                 :  * Reutrns 0 on success, -1 on error.
    2247                 :  **********************************************************************/
    2248               4 : int TABMAPFile::InitDrawingTools()
    2249                 : {
    2250               4 :     int nStatus = 0;
    2251                 : 
    2252               4 :     if (m_poHeader == NULL)
    2253               0 :         return -1;    // File not opened yet!
    2254                 : 
    2255                 :     /*-------------------------------------------------------------
    2256                 :      * We want to perform this initialisation only ONCE
    2257                 :      *------------------------------------------------------------*/
    2258               4 :     if (m_poToolDefTable != NULL)
    2259               0 :         return 0;
    2260                 : 
    2261                 :     /*-------------------------------------------------------------
    2262                 :      * Create a new ToolDefTable... no more initialization is required 
    2263                 :      * unless we want to read tool blocks from file.
    2264                 :      *------------------------------------------------------------*/
    2265               4 :     m_poToolDefTable = new TABToolDefTable;
    2266                 : 
    2267               6 :     if (m_eAccessMode == TABRead && m_poHeader->m_nFirstToolBlock != 0)
    2268                 :     {
    2269                 :         TABMAPToolBlock *poBlock;
    2270                 : 
    2271               2 :         poBlock = new TABMAPToolBlock(m_eAccessMode);
    2272               2 :         poBlock->InitNewBlock(m_fp, 512);
    2273                 :     
    2274                 :         /*-------------------------------------------------------------
    2275                 :          * Use GotoByteInFile() to go to the first block's location.  This will
    2276                 :          * force loading the block if necessary and reading its header.
    2277                 :          * Also make sure to move the read pointer past the 8 bytes header
    2278                 :          * to be ready to read drawing tools data
    2279                 :          *------------------------------------------------------------*/
    2280               2 :         if ( poBlock->GotoByteInFile(m_poHeader->m_nFirstToolBlock)!= 0)
    2281                 :         {
    2282                 :             // Failed... an error has already been reported.
    2283               0 :             delete poBlock;
    2284               0 :             return -1;
    2285                 :         }
    2286                 : 
    2287               2 :         poBlock->GotoByteInBlock(8);
    2288                 : 
    2289               2 :         nStatus = m_poToolDefTable->ReadAllToolDefs(poBlock);
    2290               2 :         delete poBlock;
    2291                 :     }
    2292                 : 
    2293               4 :     return nStatus;
    2294                 : }
    2295                 : 
    2296                 : 
    2297                 : /**********************************************************************
    2298                 :  *                   TABMAPFile::CommitDrawingTools()
    2299                 :  *
    2300                 :  * Write the drawing tools for this file.
    2301                 :  *
    2302                 :  * This function applies only to write access mode.
    2303                 :  * 
    2304                 :  * Returns 0 on success, -1 on error.
    2305                 :  **********************************************************************/
    2306               3 : int TABMAPFile::CommitDrawingTools()
    2307                 : {
    2308               3 :     int nStatus = 0;
    2309                 : 
    2310               3 :     if (m_eAccessMode != TABWrite || m_poHeader == NULL)
    2311                 :     {
    2312                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2313               0 :             "CommitDrawingTools() failed: file not opened for write access.");
    2314               0 :         return -1;
    2315                 :     }
    2316                 : 
    2317               3 :     if (m_poToolDefTable == NULL ||
    2318                 :         (m_poToolDefTable->GetNumPen() +
    2319                 :          m_poToolDefTable->GetNumBrushes() +
    2320                 :          m_poToolDefTable->GetNumFonts() +
    2321                 :          m_poToolDefTable->GetNumSymbols()) == 0)
    2322                 :     {
    2323               1 :         return 0;       // Nothing to do!
    2324                 :     }
    2325                 : 
    2326                 :     /*-------------------------------------------------------------
    2327                 :      * Create a new TABMAPToolBlock and update header fields
    2328                 :      *------------------------------------------------------------*/
    2329                 :     TABMAPToolBlock *poBlock;
    2330                 :     
    2331               2 :     poBlock = new TABMAPToolBlock(m_eAccessMode);
    2332               2 :     poBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock());
    2333               2 :     poBlock->SetMAPBlockManagerRef(&m_oBlockManager);
    2334                 : 
    2335               2 :     m_poHeader->m_nFirstToolBlock = poBlock->GetStartAddress();
    2336                 : 
    2337               2 :     m_poHeader->m_numPenDefs = (GByte)m_poToolDefTable->GetNumPen();
    2338               2 :     m_poHeader->m_numBrushDefs = (GByte)m_poToolDefTable->GetNumBrushes();
    2339               2 :     m_poHeader->m_numFontDefs = (GByte)m_poToolDefTable->GetNumFonts();
    2340               2 :     m_poHeader->m_numSymbolDefs = (GByte)m_poToolDefTable->GetNumSymbols();
    2341                 : 
    2342                 :     /*-------------------------------------------------------------
    2343                 :      * Do the actual work and delete poBlock
    2344                 :      * (Note that poBlock will have already been committed to the file
    2345                 :      * by WriteAllToolDefs() )
    2346                 :      *------------------------------------------------------------*/
    2347               2 :     nStatus = m_poToolDefTable->WriteAllToolDefs(poBlock);
    2348                 :     
    2349               2 :     m_poHeader->m_numMapToolBlocks = (GInt16)poBlock->GetNumBlocksInChain();
    2350                 : 
    2351               4 :     delete poBlock;
    2352                 : 
    2353               2 :     return nStatus;
    2354                 : }
    2355                 : 
    2356                 : 
    2357                 : /**********************************************************************
    2358                 :  *                   TABMAPFile::ReadPenDef()
    2359                 :  *
    2360                 :  * Fill the TABPenDef structure with the definition of the specified pen
    2361                 :  * index... (1-based pen index)
    2362                 :  *
    2363                 :  * If nPenIndex==0 or is invalid, then the structure is cleared.
    2364                 :  *
    2365                 :  * Returns 0 on success, -1 on error (i.e. Pen not found).
    2366                 :  **********************************************************************/
    2367              52 : int   TABMAPFile::ReadPenDef(int nPenIndex, TABPenDef *psDef)
    2368                 : {
    2369                 :     TABPenDef *psTmp;
    2370                 : 
    2371              52 :     if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
    2372               0 :         return -1;
    2373                 : 
    2374              52 :     if (psDef && m_poToolDefTable &&
    2375                 :         (psTmp = m_poToolDefTable->GetPenDefRef(nPenIndex)) != NULL)
    2376                 :     {
    2377              52 :         *psDef = *psTmp;
    2378                 :     }
    2379               0 :     else if (psDef)
    2380                 :     {
    2381                 :         /* Init to MapInfo default */
    2382                 :         static const TABPenDef csDefaultPen = MITAB_PEN_DEFAULT;
    2383               0 :         *psDef = csDefaultPen;
    2384               0 :         return -1;
    2385                 :     }
    2386              52 :     return 0;
    2387                 : }
    2388                 : 
    2389                 : /**********************************************************************
    2390                 :  *                   TABMAPFile::WritePenDef()
    2391                 :  *
    2392                 :  * Write a Pen Tool to the map file and return the pen index that has
    2393                 :  * been attributed to this Pen tool definition, or -1 if something went
    2394                 :  * wrong
    2395                 :  *
    2396                 :  * Note that the returned index is a 1-based index.  A value of 0 
    2397                 :  * indicates "none" in MapInfo.
    2398                 : 
    2399                 :  * Returns a value >= 0 on success, -1 on error
    2400                 :  **********************************************************************/
    2401              12 : int   TABMAPFile::WritePenDef(TABPenDef *psDef)
    2402                 : {
    2403              12 :     if (psDef == NULL || 
    2404                 :         (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
    2405                 :         m_poToolDefTable==NULL )
    2406                 :     {
    2407               0 :         return -1;
    2408                 :     }
    2409                 : 
    2410              12 :     return m_poToolDefTable->AddPenDefRef(psDef);
    2411                 : }
    2412                 : 
    2413                 : 
    2414                 : /**********************************************************************
    2415                 :  *                   TABMAPFile::ReadBrushDef()
    2416                 :  *
    2417                 :  * Fill the TABBrushDef structure with the definition of the specified Brush
    2418                 :  * index... (1-based Brush index)
    2419                 :  *
    2420                 :  * If nBrushIndex==0 or is invalid, then the structure is cleared.
    2421                 :  *
    2422                 :  * Returns 0 on success, -1 on error (i.e. Brush not found).
    2423                 :  **********************************************************************/
    2424              51 : int   TABMAPFile::ReadBrushDef(int nBrushIndex, TABBrushDef *psDef)
    2425                 : {
    2426                 :     TABBrushDef *psTmp;
    2427                 : 
    2428              51 :     if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
    2429               0 :         return -1;
    2430                 : 
    2431              51 :     if (psDef && m_poToolDefTable &&
    2432                 :         (psTmp = m_poToolDefTable->GetBrushDefRef(nBrushIndex)) != NULL)
    2433                 :     {
    2434              51 :         *psDef = *psTmp;
    2435                 :     }
    2436               0 :     else if (psDef)
    2437                 :     {
    2438                 :         /* Init to MapInfo default */
    2439                 :         static const TABBrushDef csDefaultBrush = MITAB_BRUSH_DEFAULT;
    2440               0 :         *psDef = csDefaultBrush;
    2441               0 :         return -1;
    2442                 :     }
    2443              51 :     return 0;
    2444                 : }
    2445                 : 
    2446                 : /**********************************************************************
    2447                 :  *                   TABMAPFile::WriteBrushDef()
    2448                 :  *
    2449                 :  * Write a Brush Tool to the map file and return the Brush index that has
    2450                 :  * been attributed to this Brush tool definition, or -1 if something went
    2451                 :  * wrong
    2452                 :  *
    2453                 :  * Note that the returned index is a 1-based index.  A value of 0 
    2454                 :  * indicates "none" in MapInfo.
    2455                 : 
    2456                 :  * Returns a value >= 0 on success, -1 on error
    2457                 :  **********************************************************************/
    2458              11 : int   TABMAPFile::WriteBrushDef(TABBrushDef *psDef)
    2459                 : {
    2460              11 :     if (psDef == NULL || 
    2461                 :         (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
    2462                 :         m_poToolDefTable==NULL )
    2463                 :     {
    2464               0 :         return -1;
    2465                 :     }
    2466                 : 
    2467              11 :     return m_poToolDefTable->AddBrushDefRef(psDef);
    2468                 : }
    2469                 : 
    2470                 : 
    2471                 : /**********************************************************************
    2472                 :  *                   TABMAPFile::ReadFontDef()
    2473                 :  *
    2474                 :  * Fill the TABFontDef structure with the definition of the specified Font
    2475                 :  * index... (1-based Font index)
    2476                 :  *
    2477                 :  * If nFontIndex==0 or is invalid, then the structure is cleared.
    2478                 :  *
    2479                 :  * Returns 0 on success, -1 on error (i.e. Font not found).
    2480                 :  **********************************************************************/
    2481               0 : int   TABMAPFile::ReadFontDef(int nFontIndex, TABFontDef *psDef)
    2482                 : {
    2483                 :     TABFontDef *psTmp;
    2484                 : 
    2485               0 :     if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
    2486               0 :         return -1;
    2487                 : 
    2488               0 :     if (psDef && m_poToolDefTable &&
    2489                 :         (psTmp = m_poToolDefTable->GetFontDefRef(nFontIndex)) != NULL)
    2490                 :     {
    2491               0 :         *psDef = *psTmp;
    2492                 :     }
    2493               0 :     else if (psDef)
    2494                 :     {
    2495                 :         /* Init to MapInfo default */
    2496                 :         static const TABFontDef csDefaultFont = MITAB_FONT_DEFAULT;
    2497               0 :         *psDef = csDefaultFont;
    2498               0 :         return -1;
    2499                 :     }
    2500               0 :     return 0;
    2501                 : }
    2502                 : 
    2503                 : /**********************************************************************
    2504                 :  *                   TABMAPFile::WriteFontDef()
    2505                 :  *
    2506                 :  * Write a Font Tool to the map file and return the Font index that has
    2507                 :  * been attributed to this Font tool definition, or -1 if something went
    2508                 :  * wrong
    2509                 :  *
    2510                 :  * Note that the returned index is a 1-based index.  A value of 0 
    2511                 :  * indicates "none" in MapInfo.
    2512                 : 
    2513                 :  * Returns a value >= 0 on success, -1 on error
    2514                 :  **********************************************************************/
    2515               0 : int   TABMAPFile::WriteFontDef(TABFontDef *psDef)
    2516                 : {
    2517               0 :     if (psDef == NULL || 
    2518                 :         (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
    2519                 :         m_poToolDefTable==NULL )
    2520                 :     {
    2521               0 :         return -1;
    2522                 :     }
    2523                 : 
    2524               0 :     return m_poToolDefTable->AddFontDefRef(psDef);
    2525                 : }
    2526                 : 
    2527                 : /**********************************************************************
    2528                 :  *                   TABMAPFile::ReadSymbolDef()
    2529                 :  *
    2530                 :  * Fill the TABSymbolDef structure with the definition of the specified Symbol
    2531                 :  * index... (1-based Symbol index)
    2532                 :  *
    2533                 :  * If nSymbolIndex==0 or is invalid, then the structure is cleared.
    2534                 :  *
    2535                 :  * Returns 0 on success, -1 on error (i.e. Symbol not found).
    2536                 :  **********************************************************************/
    2537               0 : int   TABMAPFile::ReadSymbolDef(int nSymbolIndex, TABSymbolDef *psDef)
    2538                 : {
    2539                 :     TABSymbolDef *psTmp;
    2540                 : 
    2541               0 :     if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
    2542               0 :         return -1;
    2543                 : 
    2544               0 :     if (psDef && m_poToolDefTable &&
    2545                 :         (psTmp = m_poToolDefTable->GetSymbolDefRef(nSymbolIndex)) != NULL)
    2546                 :     {
    2547               0 :         *psDef = *psTmp;
    2548                 :     }
    2549               0 :     else if (psDef)
    2550                 :     {
    2551                 :         /* Init to MapInfo default */
    2552                 :         static const TABSymbolDef csDefaultSymbol = MITAB_SYMBOL_DEFAULT;
    2553               0 :         *psDef = csDefaultSymbol;
    2554               0 :         return -1;
    2555                 :     }
    2556               0 :     return 0;
    2557                 : }
    2558                 : 
    2559                 : /**********************************************************************
    2560                 :  *                   TABMAPFile::WriteSymbolDef()
    2561                 :  *
    2562                 :  * Write a Symbol Tool to the map file and return the Symbol index that has
    2563                 :  * been attributed to this Symbol tool definition, or -1 if something went
    2564                 :  * wrong
    2565                 :  *
    2566                 :  * Note that the returned index is a 1-based index.  A value of 0 
    2567                 :  * indicates "none" in MapInfo.
    2568                 : 
    2569                 :  * Returns a value >= 0 on success, -1 on error
    2570                 :  **********************************************************************/
    2571               0 : int   TABMAPFile::WriteSymbolDef(TABSymbolDef *psDef)
    2572                 : {
    2573               0 :     if (psDef == NULL || 
    2574                 :         (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
    2575                 :         m_poToolDefTable==NULL )
    2576                 :     {
    2577               0 :         return -1;
    2578                 :     }
    2579                 : 
    2580               0 :     return m_poToolDefTable->AddSymbolDefRef(psDef);
    2581                 : }
    2582                 : 
    2583                 : #define ORDER_MIN_MAX(type,min,max)                                    \
    2584                 :     {   if( (max) < (min) )                                            \
    2585                 :           { type temp = (max); (max) = (min); (min) = temp; } }
    2586                 : 
    2587                 : /**********************************************************************
    2588                 :  *                   TABMAPFile::SetCoordFilter()
    2589                 :  *
    2590                 :  * Set the MBR of the area of interest... only objects that at least 
    2591                 :  * overlap with that area will be returned.
    2592                 :  *
    2593                 :  * @param sMin minimum x/y the file's projection coord.
    2594                 :  * @param sMax maximum x/y the file's projection coord.
    2595                 :  **********************************************************************/
    2596               1 : void TABMAPFile::SetCoordFilter(TABVertex sMin, TABVertex sMax)
    2597                 : {
    2598               1 :     m_sMinFilter = sMin;
    2599               1 :     m_sMaxFilter = sMax;
    2600                 : 
    2601               1 :     Coordsys2Int(sMin.x, sMin.y, m_XMinFilter, m_YMinFilter, TRUE);
    2602               1 :     Coordsys2Int(sMax.x, sMax.y, m_XMaxFilter, m_YMaxFilter, TRUE);
    2603                 : 
    2604               1 :     ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter);
    2605               1 :     ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter);
    2606               1 :     ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x);
    2607               1 :     ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y);
    2608               1 : }
    2609                 : 
    2610                 : /**********************************************************************
    2611                 :  *                   TABMAPFile::ResetCoordFilter()
    2612                 :  *
    2613                 :  * Reset the MBR of the area of interest to be the extents as defined
    2614                 :  * in the header. 
    2615                 :  **********************************************************************/
    2616                 : 
    2617              16 : void TABMAPFile::ResetCoordFilter()
    2618                 : 
    2619                 : {
    2620              16 :     m_XMinFilter = m_poHeader->m_nXMin;
    2621              16 :     m_YMinFilter = m_poHeader->m_nYMin;
    2622              16 :     m_XMaxFilter = m_poHeader->m_nXMax;
    2623              16 :     m_YMaxFilter = m_poHeader->m_nYMax;
    2624                 :     Int2Coordsys(m_XMinFilter, m_YMinFilter,
    2625              16 :                  m_sMinFilter.x, m_sMinFilter.y);
    2626                 :     Int2Coordsys(m_XMaxFilter, m_YMaxFilter, 
    2627              16 :                  m_sMaxFilter.x, m_sMaxFilter.y);
    2628                 : 
    2629              16 :     ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter);
    2630              16 :     ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter);
    2631              16 :     ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x);
    2632              16 :     ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y);
    2633              16 : }
    2634                 : 
    2635                 : /**********************************************************************
    2636                 :  *                   TABMAPFile::GetCoordFilter()
    2637                 :  *
    2638                 :  * Get the MBR of the area of interest, as previously set by
    2639                 :  * SetCoordFilter().
    2640                 :  *
    2641                 :  * @param sMin vertex into which the minimum x/y values put in coordsys space.
    2642                 :  * @param sMax vertex into which the maximum x/y values put in coordsys space.
    2643                 :  **********************************************************************/
    2644               1 : void TABMAPFile::GetCoordFilter(TABVertex &sMin, TABVertex &sMax)
    2645                 : {
    2646               1 :     sMin = m_sMinFilter;
    2647               1 :     sMax = m_sMaxFilter;
    2648               1 : }
    2649                 : 
    2650                 : /**********************************************************************
    2651                 :  *                   TABMAPFile::CommitSpatialIndex()
    2652                 :  *
    2653                 :  * Write the spatial index blocks tree for this file.
    2654                 :  *
    2655                 :  * This function applies only to write access mode.
    2656                 :  * 
    2657                 :  * Returns 0 on success, -1 on error.
    2658                 :  **********************************************************************/
    2659               3 : int TABMAPFile::CommitSpatialIndex()
    2660                 : {
    2661               3 :     if (m_eAccessMode != TABWrite || m_poHeader == NULL)
    2662                 :     {
    2663                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2664               0 :             "CommitSpatialIndex() failed: file not opened for write access.");
    2665               0 :         return -1;
    2666                 :     }
    2667                 : 
    2668               3 :     if (m_poSpIndex == NULL)
    2669                 :     {
    2670               1 :         return 0;       // Nothing to do!
    2671                 :     }
    2672                 : 
    2673                 :     /*-------------------------------------------------------------
    2674                 :      * Update header fields and commit index block
    2675                 :      * (it's children will be recursively committed as well)
    2676                 :      *------------------------------------------------------------*/
    2677                 :     // Add 1 to Spatial Index Depth to account to the MapObjectBlocks
    2678                 :     m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
    2679               2 :                                   (GByte)m_poSpIndex->GetCurMaxDepth()+1);
    2680                 : 
    2681                 :     m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
    2682               2 :                         m_poHeader->m_nXMax, m_poHeader->m_nYMax);
    2683                 : 
    2684               2 :     return m_poSpIndex->CommitToFile();
    2685                 : }
    2686                 : 
    2687                 : 
    2688                 : /**********************************************************************
    2689                 :  *                   TABMAPFile::GetMinTABFileVersion()
    2690                 :  *
    2691                 :  * Returns the minimum TAB file version number that can contain all the
    2692                 :  * objects stored in this file.
    2693                 :  **********************************************************************/
    2694               3 : int   TABMAPFile::GetMinTABFileVersion()
    2695                 : {
    2696               3 :     int nToolVersion = 0;
    2697                 : 
    2698               3 :     if (m_poToolDefTable)
    2699               2 :         nToolVersion = m_poToolDefTable->GetMinVersionNumber();
    2700                 : 
    2701               3 :     return MAX(nToolVersion, m_nMinTABVersion);
    2702                 : }
    2703                 : 
    2704                 : 
    2705                 : /**********************************************************************
    2706                 :  *                   TABMAPFile::Dump()
    2707                 :  *
    2708                 :  * Dump block contents... available only in DEBUG mode.
    2709                 :  **********************************************************************/
    2710                 : #ifdef DEBUG
    2711                 : 
    2712               0 : void TABMAPFile::Dump(FILE *fpOut /*=NULL*/)
    2713                 : {
    2714               0 :     if (fpOut == NULL)
    2715               0 :         fpOut = stdout;
    2716                 : 
    2717               0 :     fprintf(fpOut, "----- TABMAPFile::Dump() -----\n");
    2718                 : 
    2719               0 :     if (m_fp == NULL)
    2720                 :     {
    2721               0 :         fprintf(fpOut, "File is not opened.\n");
    2722                 :     }
    2723                 :     else
    2724                 :     {
    2725               0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    2726                 :         fprintf(fpOut, "Coordsys filter  = (%g,%g)-(%g,%g)\n", 
    2727               0 :                 m_sMinFilter.x, m_sMinFilter.y, m_sMaxFilter.x,m_sMaxFilter.y);
    2728                 :         fprintf(fpOut, "Int coord filter = (%d,%d)-(%d,%d)\n", 
    2729               0 :                 m_XMinFilter, m_YMinFilter, m_XMaxFilter,m_YMaxFilter);
    2730                 : 
    2731               0 :         fprintf(fpOut, "\nFile Header follows ...\n\n");
    2732               0 :         m_poHeader->Dump(fpOut);
    2733               0 :         fprintf(fpOut, "... end of file header.\n\n");
    2734                 : 
    2735               0 :         fprintf(fpOut, "Associated .ID file ...\n\n");
    2736               0 :         m_poIdIndex->Dump(fpOut);
    2737               0 :         fprintf(fpOut, "... end of ID file dump.\n\n");
    2738                 :     }
    2739                 : 
    2740               0 :     fflush(fpOut);
    2741               0 : }
    2742                 : 
    2743                 : #endif // DEBUG
    2744                 : 
    2745                 : 
    2746                 : /**********************************************************************
    2747                 :  *                   TABMAPFile::DumpSpatialIndexToMIF()
    2748                 :  *
    2749                 :  * Dump the spatial index tree... available only in DEBUG mode.
    2750                 :  **********************************************************************/
    2751                 : #ifdef DEBUG
    2752                 : 
    2753               0 : void TABMAPFile::DumpSpatialIndexToMIF(TABMAPIndexBlock *poNode, 
    2754                 :                                        FILE *fpMIF, FILE *fpMID, 
    2755                 :                                        int nParentId /*=-1*/, 
    2756                 :                                        int nIndexInNode /*=-1*/,
    2757                 :                                        int nCurDepth /*=0*/,
    2758                 :                                        int nMaxDepth /*=-1*/)
    2759                 : {
    2760               0 :     if (poNode == NULL)
    2761                 :     {
    2762               0 :         if (m_poHeader && m_poHeader->m_nFirstIndexBlock != 0)
    2763                 :         {
    2764                 :             TABRawBinBlock *poBlock;
    2765                 : 
    2766               0 :             poBlock = GetIndexObjectBlock(m_poHeader->m_nFirstIndexBlock);
    2767               0 :             if (poBlock && poBlock->GetBlockType() == TABMAP_INDEX_BLOCK)
    2768               0 :                 poNode = (TABMAPIndexBlock *)poBlock;
    2769                 :         }
    2770                 : 
    2771               0 :         if (poNode == NULL)
    2772               0 :             return;
    2773                 :     }
    2774                 : 
    2775                 : 
    2776                 :     /*-------------------------------------------------------------
    2777                 :      * Report info on current tree node
    2778                 :      *------------------------------------------------------------*/
    2779               0 :     int numEntries = poNode->GetNumEntries();
    2780                 :     GInt32 nXMin, nYMin, nXMax, nYMax;
    2781                 :     double dXMin, dYMin, dXMax, dYMax;
    2782                 : 
    2783               0 :     poNode->RecomputeMBR();
    2784               0 :     poNode->GetMBR(nXMin, nYMin, nXMax, nYMax);
    2785                 : 
    2786               0 :     Int2Coordsys(nXMin, nYMin, dXMin, dYMin);
    2787               0 :     Int2Coordsys(nXMax, nYMax, dXMax, dYMax);
    2788                 : 
    2789               0 :     VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax);
    2790               0 :     VSIFPrintf(fpMIF, "  Brush(1, 0)\n");  /* No fill */
    2791                 :                
    2792                 :     VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n", 
    2793                 :                poNode->GetStartAddress(),
    2794                 :                nParentId,
    2795                 :                nIndexInNode,
    2796                 :                nCurDepth,
    2797                 :                MITAB_AREA(nXMin, nYMin, nXMax, nYMax),
    2798               0 :                nXMin, nYMin, nXMax, nYMax);
    2799                 : 
    2800               0 :     if (nMaxDepth != 0)
    2801                 :     {
    2802                 :         /*-------------------------------------------------------------
    2803                 :          * Loop through all entries, dumping each of them
    2804                 :          *------------------------------------------------------------*/
    2805               0 :         for(int i=0; i<numEntries; i++)
    2806                 :         {
    2807               0 :             TABMAPIndexEntry *psEntry = poNode->GetEntry(i);
    2808                 : 
    2809                 :             TABRawBinBlock *poBlock;
    2810               0 :             poBlock = GetIndexObjectBlock( psEntry->nBlockPtr );
    2811               0 :             if( poBlock == NULL )
    2812               0 :                 continue;
    2813                 : 
    2814               0 :             if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK )
    2815                 :             {
    2816                 :                 /* Index block, dump recursively */
    2817                 :                 DumpSpatialIndexToMIF((TABMAPIndexBlock *)poBlock, 
    2818                 :                                       fpMIF, fpMID, 
    2819                 :                                       poNode->GetStartAddress(), 
    2820               0 :                                       i, nCurDepth+1, nMaxDepth-1);
    2821                 :             }
    2822                 :             else
    2823                 :             {
    2824                 :                 /* Object block, dump directly */
    2825               0 :                 CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK );
    2826                 :         
    2827               0 :                 Int2Coordsys(psEntry->XMin, psEntry->YMin, dXMin, dYMin);
    2828               0 :                 Int2Coordsys(psEntry->XMax, psEntry->YMax, dXMax, dYMax);
    2829                 : 
    2830               0 :                 VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax);
    2831               0 :                 VSIFPrintf(fpMIF, "  Brush(1, 0)\n");  /* No fill */
    2832                 : 
    2833                 :                 VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n", 
    2834                 :                            psEntry->nBlockPtr,
    2835                 :                            poNode->GetStartAddress(),
    2836                 :                            i,
    2837                 :                            nCurDepth+1,
    2838                 :                            MITAB_AREA(psEntry->XMin, psEntry->YMin,
    2839                 :                                       psEntry->XMax, psEntry->YMax),
    2840                 :                            psEntry->XMin, psEntry->YMin,
    2841               0 :                            psEntry->XMax, psEntry->YMax);
    2842                 :             }
    2843                 : 
    2844               0 :             delete poBlock;
    2845                 :         }
    2846                 : 
    2847                 :     }
    2848                 : 
    2849                 : }
    2850                 : 
    2851                 : #endif // DEBUG

Generated by: LCOV version 1.7