www.pudn.com > mitab.rar > mitab_mapfile.cpp, change:2005-10-06,size:71879b
/**********************************************************************
* $Id: mitab_mapfile.cpp,v 1.32 2005/10/06 19:15:31 dmorissette Exp $
*
* Name: mitab_mapfile.cpp
* Project: MapInfo TAB Read/Write library
* Language: C++
* Purpose: Implementation of the TABMAPFile class used to handle
* reading/writing of the .MAP files at the MapInfo object level
* Author: Daniel Morissette, dmorissette@dmsolutions.ca
*
**********************************************************************
* Copyright (c) 1999-2002, Daniel Morissette
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**********************************************************************
*
* $Log: mitab_mapfile.cpp,v $
* Revision 1.32 2005/10/06 19:15:31 dmorissette
* Collections: added support for reading/writing pen/brush/symbol ids and
* for writing collection objects to .TAB/.MAP (bug 1126)
*
* Revision 1.31 2004/09/22 13:07:58 fwarmerdam
* fixed return value in LoadNextMatchingObjectBlock() per rso bug 615
*
* Revision 1.30 2004/06/30 20:29:04 dmorissette
* Fixed refs to old address danmo@videotron.ca
*
* Revision 1.29 2003/08/12 23:17:21 dmorissette
* Added reading of v500+ coordsys affine params (Anthony D. - Encom)
*
* Revision 1.28 2002/08/27 17:18:43 warmerda
* improved CPL error testing
*
* Revision 1.27 2002/07/30 13:54:12 julien
* TABMAPFile::GetFeatureId() now return -1 when there's no geometry. (bug 169)
*
* Revision 1.26 2002/04/25 16:05:24 julien
* Disabled the overflow warning in SetCoordFilter() by adding bIgnoreOverflow
* variable in Coordsys2Int of the TABMAPFile class and TABMAPHeaderBlock class
*
* Revision 1.25 2002/03/26 19:27:43 daniel
* Got rid of tabs in source
*
* Revision 1.24 2002/03/26 01:48:40 daniel
* Added Multipoint object type (V650)
*
* Revision 1.23 2002/02/20 13:53:40 daniel
* Prevent an infinite loop of calls to LoadNextMatchingObjectBlock() in
* GetNextFeatureId() if no objects found in spatial index.
*
* Revision 1.22 2001/11/19 15:04:41 daniel
* Prevent writing of coordinates outside of the +/-1e9 integer bounds.
*
* Revision 1.21 2001/11/17 21:54:06 daniel
* Made several changes in order to support writing objects in 16 bits
* coordinate format. New TABMAPObjHdr-derived classes are used to hold
* object info in mem until block is full.
*
* Revision 1.20 2001/09/18 20:33:52 warmerda
* fixed case of spatial search on file with just one object block
*
* Revision 1.19 2001/09/14 03:23:55 warmerda
* Substantial upgrade to support spatial queries using spatial indexes
*
* Revision 1.18 2001/03/15 03:57:51 daniel
* Added implementation for new OGRLayer::GetExtent(), returning data MBR.
*
* Revision 1.17 2000/11/23 21:11:07 daniel
* OOpps... VC++ didn't like the way TABPenDef, etc. were initialized
*
* Revision 1.16 2000/11/23 20:47:46 daniel
* Use MI defaults for Pen, Brush, Font, Symbol instead of all zeros
*
* Revision 1.15 2000/11/22 04:03:10 daniel
* Added warning when objects written outside of the +/-1e9 int. coord. range
*
* Revision 1.14 2000/11/15 04:13:49 daniel
* Fixed writing of TABMAPToolBlock to allocate a new block when full
*
* Revision 1.13 2000/05/19 06:44:55 daniel
* Modified generation of spatial index to split index nodes and produce a
* more balanced tree.
*
* Revision 1.12 2000/03/13 05:58:01 daniel
* Create 1024 bytes V500 .MAP header + limit m_nMaxCoordBufSize for V450 obj.
*
* Revision 1.11 2000/02/28 17:00:00 daniel
* Added V450 object types
*
* Revision 1.10 2000/01/15 22:30:44 daniel
* Switch to MIT/X-Consortium OpenSource license
*
* Revision 1.9 1999/12/19 17:37:52 daniel
* Fixed memory leaks
*
* Revision 1.8 1999/11/14 04:43:31 daniel
* Support dataset with no .MAP/.ID files
*
* Revision 1.7 1999/10/19 22:57:17 daniel
* Create m_poCurObjBlock only when needed to avoid empty blocks in files
* and problems with MBR in header block of files with only "NONE" geometries
*
* Revision 1.6 1999/10/06 13:17:46 daniel
* Update m_nMaxCoordBufSize in header block
*
* Revision 1.5 1999/10/01 03:52:22 daniel
* Avoid producing an unused block in the file when closing it.
*
* Revision 1.4 1999/09/26 14:59:36 daniel
* Implemented write support
*
* Revision 1.3 1999/09/20 18:42:42 daniel
* Use binary access to open file.
*
* Revision 1.2 1999/09/16 02:39:16 daniel
* Completed read support for most feature types
*
* Revision 1.1 1999/07/12 04:18:24 daniel
* Initial checkin
*
**********************************************************************/
#include "mitab.h"
/*=====================================================================
* class TABMAPFile
*====================================================================*/
/**********************************************************************
* TABMAPFile::TABMAPFile()
*
* Constructor.
**********************************************************************/
TABMAPFile::TABMAPFile()
{
m_nMinTABVersion = 300;
m_fp = NULL;
m_pszFname = NULL;
m_poHeader = NULL;
m_poSpIndex = NULL;
m_poSpIndexLeaf = NULL;
m_poCurObjBlock = NULL;
m_nCurObjPtr = -1;
m_nCurObjType = -1;
m_nCurObjId = -1;
m_poCurCoordBlock = NULL;
m_poToolDefTable = NULL;
}
/**********************************************************************
* TABMAPFile::~TABMAPFile()
*
* Destructor.
**********************************************************************/
TABMAPFile::~TABMAPFile()
{
Close();
}
/**********************************************************************
* TABMAPFile::Open()
*
* Open a .MAP file, and initialize the structures to be ready to read
* objects from it.
*
* Since .MAP and .ID files are optional, you can set bNoErrorMsg=TRUE to
* disable the error message and receive an return value of 1 if file
* cannot be opened.
* In this case, only the methods MoveToObjId() and GetCurObjType() can
* be used. They will behave as if the .ID file contained only null
* references, so all object will look like they have NONE geometries.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::Open(const char *pszFname, const char *pszAccess,
GBool bNoErrorMsg /* = FALSE */)
{
FILE *fp=NULL;
TABRawBinBlock *poBlock=NULL;
if (m_fp)
{
CPLError(CE_Failure, CPLE_FileIO,
"Open() failed: object already contains an open file");
return -1;
}
m_nMinTABVersion = 300;
m_fp = NULL;
m_poHeader = NULL;
m_poIdIndex = NULL;
m_poSpIndex = NULL;
m_poToolDefTable = NULL;
/*-----------------------------------------------------------------
* Validate access mode and make sure we use binary access.
*----------------------------------------------------------------*/
if (EQUALN(pszAccess, "r", 1))
{
m_eAccessMode = TABRead;
pszAccess = "rb";
}
else if (EQUALN(pszAccess, "w", 1))
{
m_eAccessMode = TABWrite;
pszAccess = "wb+";
}
else
{
CPLError(CE_Failure, CPLE_FileIO,
"Open() failed: access mode \"%s\" not supported", pszAccess);
return -1;
}
/*-----------------------------------------------------------------
* Open file
*----------------------------------------------------------------*/
fp = VSIFOpen(pszFname, pszAccess);
m_oBlockManager.Reset();
if (fp != NULL && m_eAccessMode == TABRead)
{
/*-----------------------------------------------------------------
* Read access: try to read header block
* First try with a 512 bytes block to check the .map version.
* If it's version 500 or more then read again a 1024 bytes block
*----------------------------------------------------------------*/
poBlock = TABCreateMAPBlockFromFile(fp, 0, 512);
if (poBlock && poBlock->GetBlockClass() == TABMAP_HEADER_BLOCK &&
((TABMAPHeaderBlock*)poBlock)->m_nMAPVersionNumber >= 500)
{
// Version 500 or higher. Read 1024 bytes block instead of 512
delete poBlock;
poBlock = TABCreateMAPBlockFromFile(fp, 0, 1024);
}
if (poBlock==NULL || poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK)
{
if (poBlock)
delete poBlock;
poBlock = NULL;
VSIFClose(fp);
CPLError(CE_Failure, CPLE_FileIO,
"Open() failed: %s does not appear to be a valid .MAP file",
pszFname);
return -1;
}
}
else if (fp != NULL && m_eAccessMode == TABWrite)
{
/*-----------------------------------------------------------------
* Write access: create a new header block
* .MAP files of Version 500 and up appear to have a 1024 bytes
* header. The last 512 bytes are usually all zeros.
*----------------------------------------------------------------*/
poBlock = new TABMAPHeaderBlock(m_eAccessMode);
poBlock->InitNewBlock(fp, 1024, m_oBlockManager.AllocNewBlock() );
// Alloc a second 512 bytes of space since oBlockManager deals
// with 512 bytes blocks.
m_oBlockManager.AllocNewBlock();
}
else if (bNoErrorMsg)
{
/*-----------------------------------------------------------------
* .MAP does not exist... produce no error message, but set
* the class members so that MoveToObjId() and GetCurObjType()
* can be used to return only NONE geometries.
*----------------------------------------------------------------*/
m_fp = NULL;
m_nCurObjType = TAB_GEOM_NONE;
/* Create a false header block that will return default
* values for projection and coordsys conversion stuff...
*/
m_poHeader = new TABMAPHeaderBlock(m_eAccessMode);
m_poHeader->InitNewBlock(NULL, 512, 0 );
return 1;
}
else
{
CPLError(CE_Failure, CPLE_FileIO,
"Open() failed for %s", pszFname);
return -1;
}
/*-----------------------------------------------------------------
* File appears to be valid... set the various class members
*----------------------------------------------------------------*/
m_fp = fp;
m_poHeader = (TABMAPHeaderBlock*)poBlock;
m_pszFname = CPLStrdup(pszFname);
/*-----------------------------------------------------------------
* Create a TABMAPObjectBlock, in READ mode only.
*
* In WRITE mode, the object block will be created only when needed.
* We do not create the object block in the open() call because
* files that contained only "NONE" geometries ended up with empty
* object and spatial index blocks.
*----------------------------------------------------------------*/
if (m_eAccessMode == TABRead)
{
m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
m_poCurObjBlock->InitNewBlock(m_fp, 512);
}
else
{
m_poCurObjBlock = NULL;
}
/*-----------------------------------------------------------------
* Open associated .ID (object id index) file
*----------------------------------------------------------------*/
m_poIdIndex = new TABIDFile;
if (m_poIdIndex->Open(pszFname, pszAccess) != 0)
{
// Failed... an error has already been reported
Close();
return -1;
}
/*-----------------------------------------------------------------
* Default Coord filter is the MBR of the whole file
* This is currently unused but could eventually be used to handle
* spatial filters more efficiently.
*----------------------------------------------------------------*/
if (m_eAccessMode == TABRead)
{
ResetCoordFilter();
}
/*-----------------------------------------------------------------
* We could scan a file through its quad tree index... but we don't!
*
* In read mode, we just ignore the spatial index.
*
* In write mode the index is created and maintained as new object
* blocks are added inside CommitObjBlock().
*----------------------------------------------------------------*/
m_poSpIndex = NULL;
/*-----------------------------------------------------------------
* Initialization of the Drawing Tools table will be done automatically
* as Read/Write calls are done later.
*----------------------------------------------------------------*/
m_poToolDefTable = NULL;
/*-----------------------------------------------------------------
* Make sure all previous calls succeded.
*----------------------------------------------------------------*/
if (CPLGetLastErrorNo() != 0)
{
// Open Failed... an error has already been reported
Close();
return -1;
}
return 0;
}
/**********************************************************************
* TABMAPFile::Close()
*
* Close current file, and release all memory used.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::Close()
{
// Check if file is opened... it is possible to have a fake header
// without an actual file attached to it.
if (m_fp == NULL && m_poHeader == NULL)
return 0;
/*----------------------------------------------------------------
* Write access: commit latest changes to the file.
*---------------------------------------------------------------*/
if (m_eAccessMode == TABWrite)
{
// Start by committing current object and coord blocks
// Nothing happens if none has been created yet.
CommitObjBlock(FALSE);
// Write the drawing tools definitions now.
CommitDrawingTools();
// Commit spatial index blocks
CommitSpatialIndex();
// __TODO__ We probably need to update some header fields first.
if (m_poHeader)
{
// OK, with V450 files, objects are not limited to 32k nodes
// any more, and this means that m_nMaxCoordBufSize can become
// huge, and actually more huge than can be held in memory.
// MapInfo counts m_nMaxCoordBufSize=0 for V450 objects, but
// until this is cleanly implented, we will just prevent
// m_nMaxCoordBufSizefrom going beyond 512k in V450 files.
if (m_nMinTABVersion >= 450)
{
m_poHeader->m_nMaxCoordBufSize =
MIN(m_poHeader->m_nMaxCoordBufSize, 512*1024);
}
m_poHeader->CommitToFile();
}
}
// Check for overflow of internal coordinates and produce a warning
// if that happened...
if (m_poHeader && m_poHeader->m_bIntBoundsOverflow)
{
double dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY;
Int2Coordsys(-1000000000, -1000000000, dBoundsMinX, dBoundsMinY);
Int2Coordsys(1000000000, 1000000000, dBoundsMaxX, dBoundsMaxY);
CPLError(CE_Warning, TAB_WarningBoundsOverflow,
"Some objects were written outside of the file's "
"predefined bounds.\n"
"These objects may have invalid coordinates when the file "
"is reopened.\n"
"Predefined bounds: (%.15g,%.15g)-(%.15g,%.15g)\n",
dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY );
}
// Delete all structures
if (m_poHeader)
delete m_poHeader;
m_poHeader = NULL;
if (m_poIdIndex)
{
m_poIdIndex->Close();
delete m_poIdIndex;
m_poIdIndex = NULL;
}
if (m_poCurObjBlock)
{
delete m_poCurObjBlock;
m_poCurObjBlock = NULL;
m_nCurObjPtr = -1;
m_nCurObjType = -1;
m_nCurObjId = -1;
}
if (m_poCurCoordBlock)
{
delete m_poCurCoordBlock;
m_poCurCoordBlock = NULL;
}
if (m_poSpIndex)
{
delete m_poSpIndex;
m_poSpIndex = NULL;
m_poSpIndexLeaf = NULL;
}
if (m_poToolDefTable)
{
delete m_poToolDefTable;
m_poToolDefTable = NULL;
}
// Close file
if (m_fp)
VSIFClose(m_fp);
m_fp = NULL;
CPLFree(m_pszFname);
m_pszFname = NULL;
return 0;
}
/************************************************************************/
/* PushBlock() */
/* */
/* Install a new block (object or spatial) as being current - */
/* whatever that means. This method is only intended to ever */
/* be called from LoadNextMatchingObjectBlock(). */
/************************************************************************/
TABRawBinBlock *TABMAPFile::PushBlock( int nFileOffset )
{
TABRawBinBlock *poBlock;
poBlock = GetIndexObjectBlock( nFileOffset );
if( poBlock == NULL )
return NULL;
if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK )
{
TABMAPIndexBlock *poIndex = (TABMAPIndexBlock *) poBlock;
if( m_poSpIndexLeaf == NULL )
{
m_poSpIndexLeaf = m_poSpIndex = poIndex;
}
else
{
CPLAssert(
m_poSpIndexLeaf->GetEntry(
m_poSpIndexLeaf->GetCurChildIndex())->nBlockPtr
== nFileOffset );
m_poSpIndexLeaf->SetCurChildRef( poIndex,
m_poSpIndexLeaf->GetCurChildIndex() );
poIndex->SetParentRef( m_poSpIndexLeaf );
m_poSpIndexLeaf = poIndex;
}
}
else
{
CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK );
if( m_poCurObjBlock != NULL )
delete m_poCurObjBlock;
m_poCurObjBlock = (TABMAPObjectBlock *) poBlock;
m_nCurObjPtr = nFileOffset;
m_nCurObjType = 0;
m_nCurObjId = -1;
}
return poBlock;
}
/************************************************************************/
/* LoadNextMatchingObjectBlock() */
/* */
/* Advance through the spatial indices till the next object */
/* block is loaded that matching the spatial query extents. */
/************************************************************************/
int TABMAPFile::LoadNextMatchingObjectBlock( int bFirstObject )
{
// If we are just starting, verify the stack is empty.
if( bFirstObject )
{
CPLAssert( m_poSpIndex == NULL && m_poSpIndexLeaf == NULL );
if( PushBlock( m_poHeader->m_nFirstIndexBlock ) == NULL )
return FALSE;
if( m_poSpIndex == NULL )
{
CPLAssert( m_poCurObjBlock != NULL );
return TRUE;
}
}
while( m_poSpIndexLeaf != NULL )
{
int iEntry = m_poSpIndexLeaf->GetCurChildIndex();
if( iEntry >= m_poSpIndexLeaf->GetNumEntries()-1 )
{
TABMAPIndexBlock *poParent = m_poSpIndexLeaf->GetParentRef();
delete m_poSpIndexLeaf;
m_poSpIndexLeaf = poParent;
if( poParent != NULL )
{
poParent->SetCurChildRef( NULL, poParent->GetCurChildIndex() );
}
else
{
m_poSpIndex = NULL;
}
continue;
}
m_poSpIndexLeaf->SetCurChildRef( NULL, ++iEntry );
TABMAPIndexEntry *psEntry = m_poSpIndexLeaf->GetEntry( iEntry );
TABRawBinBlock *poBlock;
if( psEntry->XMax < m_XMinFilter
|| psEntry->YMax < m_YMinFilter
|| psEntry->XMin > m_XMaxFilter
|| psEntry->YMin > m_YMaxFilter )
continue;
poBlock = PushBlock( psEntry->nBlockPtr );
if( poBlock == NULL )
return FALSE;
else if( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK )
return TRUE;
else
/* continue processing new index block */;
}
return m_poSpIndexLeaf != NULL;
}
/************************************************************************/
/* ResetReading() */
/* */
/* Ensure that any resources related to a spatial traversal of */
/* the file are recovered, and the state reinitialized to the */
/* initial conditions. */
/************************************************************************/
void TABMAPFile::ResetReading()
{
if (m_poSpIndex && m_eAccessMode == TABRead )
{
delete m_poSpIndex;
m_poSpIndex = NULL;
m_poSpIndexLeaf = NULL;
}
}
/************************************************************************/
/* GetNextFeatureId() */
/* */
/* Fetch the next feature id based on a traversal of the */
/* spatial index. */
/************************************************************************/
int TABMAPFile::GetNextFeatureId( int nPrevId )
{
/* -------------------------------------------------------------------- */
/* m_fp is NULL when all geometry are NONE and/or there's */
/* no .map file and/or there's no spatial indexes */
/* -------------------------------------------------------------------- */
if( m_fp == NULL )
return -1;
if( nPrevId == 0 )
nPrevId = -1;
/* -------------------------------------------------------------------- */
/* This should always be true if we are being called properly. */
/* -------------------------------------------------------------------- */
if( nPrevId != -1 && m_nCurObjId != nPrevId )
{
CPLError( CE_Failure, CPLE_AppDefined,
"TABMAPFile::GetNextFeatureId(%d) called out of sequence.",
nPrevId );
return -1;
}
CPLAssert( nPrevId == -1 || m_poCurObjBlock != NULL );
/* -------------------------------------------------------------------- */
/* Ensure things are initialized properly if this is a request */
/* for the first feature. */
/* -------------------------------------------------------------------- */
if( nPrevId == -1 )
{
m_nCurObjId = -1;
}
/* -------------------------------------------------------------------- */
/* Try to advance to the next object in the current object */
/* block. */
/* -------------------------------------------------------------------- */
if( nPrevId == -1
|| m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 )
{
// If not, try to advance to the next object block, and get
// first object from it. Note that some object blocks actually
// have no objects, so we may have to advance to additional
// object blocks till we find a non-empty one.
GBool bFirstCall = (nPrevId == -1);
do
{
if( !LoadNextMatchingObjectBlock( bFirstCall ) )
return -1;
bFirstCall = FALSE;
} while( m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 );
}
m_nCurObjType = m_poCurObjBlock->GetCurObjectType();
m_nCurObjId = m_poCurObjBlock->GetCurObjectId();
m_nCurObjPtr = m_poCurObjBlock->GetStartAddress()
+ m_poCurObjBlock->GetCurObjectOffset();
CPLAssert( m_nCurObjId != -1 );
return m_nCurObjId;
}
/**********************************************************************
* TABMAPFile::Int2Coordsys()
*
* Convert from long integer (internal) to coordinates system units
* as defined in the file's coordsys clause.
*
* Note that the false easting/northing and the conversion factor from
* datum to coordsys units are not included in the calculation.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY)
{
if (m_poHeader == NULL)
return -1;
return m_poHeader->Int2Coordsys(nX, nY, dX, dY);
}
/**********************************************************************
* TABMAPFile::Coordsys2Int()
*
* Convert from coordinates system units as defined in the file's
* coordsys clause to long integer (internal) coordinates.
*
* Note that the false easting/northing and the conversion factor from
* datum to coordsys units are not included in the calculation.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY,
GBool bIgnoreOverflow/*=FALSE*/)
{
if (m_poHeader == NULL)
return -1;
return m_poHeader->Coordsys2Int(dX, dY, nX, nY, bIgnoreOverflow);
}
/**********************************************************************
* TABMAPFile::Int2CoordsysDist()
*
* Convert a pair of X,Y size (or distance) values from long integer
* (internal) to coordinates system units as defined in the file's coordsys
* clause.
*
* The difference with Int2Coordsys() is that this function only applies
* the scaling factor: it does not apply the displacement.
*
* Since the calculations on the X and Y values are independent, either
* one can be omitted (i.e. passed as 0)
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY)
{
if (m_poHeader == NULL)
return -1;
return m_poHeader->Int2CoordsysDist(nX, nY, dX, dY);
}
/**********************************************************************
* TABMAPFile::Coordsys2IntDist()
*
* Convert a pair of X,Y size (or distance) values from coordinates
* system units as defined in the file's coordsys clause to long
* integer (internal) coordinate units.
*
* The difference with Int2Coordsys() is that this function only applies
* the scaling factor: it does not apply the displacement.
*
* Since the calculations on the X and Y values are independent, either
* one can be omitted (i.e. passed as 0)
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY)
{
if (m_poHeader == NULL)
return -1;
return m_poHeader->Coordsys2IntDist(dX, dY, nX, nY);
}
/**********************************************************************
* TABMAPFile::SetCoordsysBounds()
*
* Set projection coordinates bounds of the newly created dataset.
*
* This function must be called after creating a new dataset and before any
* feature can be written to it.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::SetCoordsysBounds(double dXMin, double dYMin,
double dXMax, double dYMax)
{
int nStatus = 0;
if (m_poHeader == NULL)
return -1;
nStatus = m_poHeader->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
if (nStatus == 0)
ResetCoordFilter();
return nStatus;
}
/**********************************************************************
* TABMAPFile::GetMaxObjId()
*
* Return the value of the biggest valid object id.
*
* Note that object ids are positive and start at 1.
*
* Returns a value >= 0 on success, -1 on error.
**********************************************************************/
GInt32 TABMAPFile::GetMaxObjId()
{
if (m_poIdIndex)
return m_poIdIndex->GetMaxObjId();
return -1;
}
/**********************************************************************
* TABMAPFile::MoveToObjId()
*
* Get ready to work with the object with the specified id. The object
* data pointer (inside m_poCurObjBlock) will be moved to the first byte
* of data for this map object.
*
* The object type and id (i.e. table row number) will be accessible
* using GetCurObjType() and GetCurObjId().
*
* Note that object ids are positive and start at 1.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::MoveToObjId(int nObjId)
{
int nFileOffset;
/*-----------------------------------------------------------------
* In read access mode, since the .MAP/.ID are optional, if the
* file is not opened then we can still act as if one existed and
* make any object id look like a TAB_GEOM_NONE
*----------------------------------------------------------------*/
if (m_fp == NULL && m_eAccessMode == TABRead)
{
CPLAssert(m_poIdIndex == NULL && m_poCurObjBlock == NULL);
m_nCurObjPtr = 0;
m_nCurObjId = nObjId;
m_nCurObjType = TAB_GEOM_NONE;
return 0;
}
if (m_poIdIndex == NULL || m_poCurObjBlock == NULL)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"MoveToObjId(): file not opened!");
m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
return -1;
}
/*-----------------------------------------------------------------
* Move map object pointer to the right location. Fetch location
* from the index file, unless we are already pointing at it.
*----------------------------------------------------------------*/
if( m_nCurObjId == nObjId )
nFileOffset = m_nCurObjPtr;
else
nFileOffset = m_poIdIndex->GetObjPtr(nObjId);
if (nFileOffset == 0)
{
/*---------------------------------------------------------
* Object with no geometry... this is a valid case.
*--------------------------------------------------------*/
m_nCurObjPtr = 0;
m_nCurObjId = nObjId;
m_nCurObjType = TAB_GEOM_NONE;
}
else if ( m_poCurObjBlock->GotoByteInFile(nFileOffset) == 0)
{
/*-------------------------------------------------------------
* OK, it worked, read the object type and row id.
*------------------------------------------------------------*/
m_nCurObjPtr = nFileOffset;
m_nCurObjType = m_poCurObjBlock->ReadByte();
m_nCurObjId = m_poCurObjBlock->ReadInt32();
// Do a consistency check...
if (m_nCurObjId != nObjId)
{
CPLError(CE_Failure, CPLE_FileIO,
"Object ID from the .ID file (%d) differs from the value "
"in the .MAP file (%d). File may be corrupt.",
nObjId, m_nCurObjId);
m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
return -1;
}
}
else
{
/*---------------------------------------------------------
* Failed positioning input file... CPLError has been called.
*--------------------------------------------------------*/
m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
return -1;
}
return 0;
}
/**********************************************************************
* TABMAPFile::UpdateMapHeaderInfo()
*
* Update .map header information (counter of objects by type and minimum
* required version) in light of a new object to be written to the file.
*
* Called only by PrepareNewObj() and by the TABCollection class.
**********************************************************************/
void TABMAPFile::UpdateMapHeaderInfo(GByte nObjType)
{
/*-----------------------------------------------------------------
* Update count of objects by type in the header block
*----------------------------------------------------------------*/
if (nObjType == TAB_GEOM_SYMBOL ||
nObjType == TAB_GEOM_FONTSYMBOL ||
nObjType == TAB_GEOM_CUSTOMSYMBOL ||
nObjType == TAB_GEOM_MULTIPOINT ||
nObjType == TAB_GEOM_SYMBOL_C ||
nObjType == TAB_GEOM_FONTSYMBOL_C ||
nObjType == TAB_GEOM_CUSTOMSYMBOL_C ||
nObjType == TAB_GEOM_MULTIPOINT_C )
{
m_poHeader->m_numPointObjects++;
}
else if (nObjType == TAB_GEOM_LINE ||
nObjType == TAB_GEOM_PLINE ||
nObjType == TAB_GEOM_MULTIPLINE ||
nObjType == TAB_GEOM_V450_MULTIPLINE ||
nObjType == TAB_GEOM_ARC ||
nObjType == TAB_GEOM_LINE_C ||
nObjType == TAB_GEOM_PLINE_C ||
nObjType == TAB_GEOM_MULTIPLINE_C ||
nObjType == TAB_GEOM_V450_MULTIPLINE_C ||
nObjType == TAB_GEOM_ARC_C)
{
m_poHeader->m_numLineObjects++;
}
else if (nObjType == TAB_GEOM_REGION ||
nObjType == TAB_GEOM_V450_REGION ||
nObjType == TAB_GEOM_RECT ||
nObjType == TAB_GEOM_ROUNDRECT ||
nObjType == TAB_GEOM_ELLIPSE ||
nObjType == TAB_GEOM_REGION_C ||
nObjType == TAB_GEOM_V450_REGION_C ||
nObjType == TAB_GEOM_RECT_C ||
nObjType == TAB_GEOM_ROUNDRECT_C ||
nObjType == TAB_GEOM_ELLIPSE_C)
{
m_poHeader->m_numRegionObjects++;
}
else if (nObjType == TAB_GEOM_TEXT ||
nObjType == TAB_GEOM_TEXT_C)
{
m_poHeader->m_numTextObjects++;
}
/*-----------------------------------------------------------------
* Check for V450-specific object types and minimum TAB file version number
*----------------------------------------------------------------*/
if (m_nMinTABVersion < 450 &&
(nObjType == TAB_GEOM_V450_REGION ||
nObjType == TAB_GEOM_V450_MULTIPLINE ||
nObjType == TAB_GEOM_V450_REGION_C ||
nObjType == TAB_GEOM_V450_MULTIPLINE_C) )
{
m_nMinTABVersion = 450;
}
/*-----------------------------------------------------------------
* Check for V460-specific object types (multipoint and collection)
*----------------------------------------------------------------*/
if (m_nMinTABVersion < 650 &&
(nObjType == TAB_GEOM_MULTIPOINT ||
nObjType == TAB_GEOM_MULTIPOINT_C ||
nObjType == TAB_GEOM_COLLECTION ||
nObjType == TAB_GEOM_COLLECTION_C ) )
{
m_nMinTABVersion = 650;
}
}
/**********************************************************************
* TABMAPFile::PrepareNewObj()
*
* Get ready to write a new object of the specified type and with the
* specified id.
*
* m_poCurObjBlock will be set to be ready to receive the new object, and
* a new block will be created if necessary (in which case the current
* block contents will be committed to disk, etc.) The object type and
* row ID will be written to the m_poCurObjBlock, so it will be ready to
* receive the first byte of data for this map object.
*
* If this object type uses coordinate blocks, then the coordinate block
* will be prepared to receive coordinates.
*
* This function will also take care of updating the .ID index entry for
* the new object.
*
* Note that object ids are positive and start at 1.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::PrepareNewObj(int nObjId, GByte nObjType)
{
int nObjSize;
m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
if (m_eAccessMode != TABWrite ||
m_poIdIndex == NULL || m_poHeader == NULL)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"PrepareNewObj() failed: file not opened for write access.");
return -1;
}
/*-----------------------------------------------------------------
* For objects with no geometry, we just update the .ID file and return
*----------------------------------------------------------------*/
if (nObjType == TAB_GEOM_NONE)
{
m_nCurObjType = nObjType;
m_nCurObjId = nObjId;
m_nCurObjPtr = 0;
m_poIdIndex->SetObjPtr(m_nCurObjId, 0);
return 0;
}
/*-----------------------------------------------------------------
* Update count of objects by type in the header block and minimum
* required version.
*----------------------------------------------------------------*/
UpdateMapHeaderInfo(nObjType);
/*-----------------------------------------------------------------
* OK, looks like we will need object block... check if it exists and
* create it if it has not been created yet (first time for this file).
* We do not create the object block in the open() call because
* files that contained only "NONE" geometries ended up with empty
* object and spatial index blocks.
* Note: A coord block will be created only if needed later.
*----------------------------------------------------------------*/
if (m_poCurObjBlock == NULL)
{
m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
int nBlockOffset = m_oBlockManager.AllocNewBlock();
m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset);
// The reference to the first object block should
// actually go through the index blocks... this will be
// updated when file is closed.
m_poHeader->m_nFirstIndexBlock = nBlockOffset;
}
/*-----------------------------------------------------------------
* Fetch new object size, make sure there is enough room in obj.
* block for new object, and save/create a new one if necessary.
*----------------------------------------------------------------*/
nObjSize = m_poHeader->GetMapObjectSize(nObjType);
if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize )
{
/*-------------------------------------------------------------
* OK, the new object won't fit in the current block. Commit
* the current block to disk, and init a new one.
*
* __TODO__ To create an optimum file, we should split the object
* block at this point and update the index blocks... however the
* file should still be usable even if we don't do it.
*------------------------------------------------------------*/
CommitObjBlock(TRUE);
}
/*-----------------------------------------------------------------
* Init member vars and write new object type/id
*----------------------------------------------------------------*/
m_nCurObjType = nObjType;
m_nCurObjId = nObjId;
m_nCurObjPtr = m_poCurObjBlock->GetFirstUnusedByteOffset();
m_poCurObjBlock->GotoByteInFile(m_nCurObjPtr);
m_poCurObjBlock->WriteByte(m_nCurObjType);
m_poCurObjBlock->WriteInt32(m_nCurObjId);
// Move write pointer to start location of next object (padding with zeros)
// Nothing will get written to the object block until CommitToFile()
// is called.
m_poCurObjBlock->WriteZeros(m_poHeader->GetMapObjectSize(nObjType) - 5);
/*-----------------------------------------------------------------
* Update .ID Index
*----------------------------------------------------------------*/
m_poIdIndex->SetObjPtr(m_nCurObjId, m_nCurObjPtr);
/*-----------------------------------------------------------------
* Prepare Coords block...
* create a new TABMAPCoordBlock if it was not done yet.
* Note that in write mode, TABCollections require read/write access
* to the coord block.
*----------------------------------------------------------------*/
if (m_poHeader->MapObjectUsesCoordBlock(m_nCurObjType))
{
if (m_poCurCoordBlock == NULL)
{
m_poCurCoordBlock = new TABMAPCoordBlock(m_eAccessMode==TABWrite?
TABReadWrite:
m_eAccessMode);
m_poCurCoordBlock->InitNewBlock(m_fp, 512,
m_oBlockManager.AllocNewBlock());
m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager);
// Set the references to this coord block in the MAPObjBlock
m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->
GetStartAddress());
}
if (m_poCurCoordBlock->GetNumUnusedBytes() < 4)
{
int nNewBlockOffset = m_oBlockManager.AllocNewBlock();
m_poCurCoordBlock->SetNextCoordBlock(nNewBlockOffset);
m_poCurCoordBlock->CommitToFile();
m_poCurCoordBlock->InitNewBlock(m_fp, 512, nNewBlockOffset);
}
}
if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure)
return -1;
return 0;
}
/**********************************************************************
* TABMAPFile::CommitObjBlock()
*
* Commit the TABMAPObjBlock and TABMAPCoordBlock to disk after updating
* the references to each other and in the TABMAPIndex block.
*
* After the Commit() call, the ObjBlock and others will be reinitialized
* and ready to receive new objects. (only if bInitNewBlock==TRUE)
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::CommitObjBlock(GBool bInitNewBlock /*=TRUE*/)
{
int nStatus = 0;
/*-----------------------------------------------------------------
* First check that a objBlock has been created. It is possible to have
* no object block in files that contain only "NONE" geometries.
*----------------------------------------------------------------*/
if (m_poCurObjBlock == NULL)
return 0;
if (m_eAccessMode != TABWrite)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"CommitObjBlock() failed: file not opened for write access.");
return -1;
}
/*-----------------------------------------------------------------
* We need to flush the coord block if there was one
* since a list of coord blocks can belong to only one obj. block
*----------------------------------------------------------------*/
if (m_poCurCoordBlock)
{
// Update the m_nMaxCoordBufSize member in the header block
//
int nTotalCoordSize = m_poCurCoordBlock->GetNumBlocksInChain()*512;
if (nTotalCoordSize > m_poHeader->m_nMaxCoordBufSize)
m_poHeader->m_nMaxCoordBufSize = nTotalCoordSize;
// Update the references to this coord block in the MAPObjBlock
//
m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->
GetStartAddress());
nStatus = m_poCurCoordBlock->CommitToFile();
delete m_poCurCoordBlock;
m_poCurCoordBlock = NULL;
}
/*-----------------------------------------------------------------
* Commit the obj block.
*----------------------------------------------------------------*/
if (nStatus == 0)
nStatus = m_poCurObjBlock->CommitToFile();
/*-----------------------------------------------------------------
* Update the spatial index
*
* Spatial index will be created here if it was not done yet.
*----------------------------------------------------------------*/
if (nStatus == 0)
{
GInt32 nXMin, nYMin, nXMax, nYMax;
if (m_poSpIndex == NULL)
{
// Spatial Index not created yet...
m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
m_poSpIndex->InitNewBlock(m_fp, 512,
m_oBlockManager.AllocNewBlock());
m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
}
m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
nStatus = m_poSpIndex->AddEntry(nXMin, nYMin, nXMax, nYMax,
m_poCurObjBlock->GetStartAddress());
m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
m_poSpIndex->GetCurMaxDepth()+1);
}
/*-----------------------------------------------------------------
* Reinitialize the obj block only if requested
*----------------------------------------------------------------*/
if (bInitNewBlock && nStatus == 0)
{
nStatus = m_poCurObjBlock->InitNewBlock(m_fp,512,
m_oBlockManager.AllocNewBlock());
}
return nStatus;
}
/**********************************************************************
* TABMAPFile::GetCurObjType()
*
* Return the MapInfo object type of the object that the m_poCurObjBlock
* is pointing to. This value is set after a call to MoveToObjId().
*
* Returns a value >= 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::GetCurObjType()
{
return m_nCurObjType;
}
/**********************************************************************
* TABMAPFile::GetCurObjId()
*
* Return the MapInfo object id of the object that the m_poCurObjBlock
* is pointing to. This value is set after a call to MoveToObjId().
*
* Returns a value >= 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::GetCurObjId()
{
return m_nCurObjId;
}
/**********************************************************************
* TABMAPFile::GetCurObjBlock()
*
* Return the m_poCurObjBlock. If MoveToObjId() has previously been
* called then m_poCurObjBlock points to the beginning of the current
* object data.
*
* Returns a reference to an object owned by this TABMAPFile object, or
* NULL on error.
**********************************************************************/
TABMAPObjectBlock *TABMAPFile::GetCurObjBlock()
{
return m_poCurObjBlock;
}
/**********************************************************************
* TABMAPFile::GetCurCoordBlock()
*
* Return the m_poCurCoordBlock. This function should be used after
* PrepareNewObj() to get the reference to the coord block that has
* just been initialized.
*
* Returns a reference to an object owned by this TABMAPFile object, or
* NULL on error.
**********************************************************************/
TABMAPCoordBlock *TABMAPFile::GetCurCoordBlock()
{
return m_poCurCoordBlock;
}
/**********************************************************************
* TABMAPFile::GetCoordBlock()
*
* Return a TABMAPCoordBlock object ready to read coordinates from it.
* The block that contains nFileOffset will automatically be
* loaded, and if nFileOffset is the beginning of a new block then the
* pointer will be moved to the beginning of the data.
*
* The contents of the returned object is only valid until the next call
* to GetCoordBlock().
*
* Returns a reference to an object owned by this TABMAPFile object, or
* NULL on error.
**********************************************************************/
TABMAPCoordBlock *TABMAPFile::GetCoordBlock(int nFileOffset)
{
if (m_eAccessMode != TABRead)
return NULL;
if (m_poCurCoordBlock == NULL)
{
m_poCurCoordBlock = new TABMAPCoordBlock(m_eAccessMode);
m_poCurCoordBlock->InitNewBlock(m_fp, 512);
}
/*-----------------------------------------------------------------
* Use GotoByteInFile() to go to the requested location. This will
* force loading the block if necessary and reading its header.
* If nFileOffset is at the beginning of the requested block, then
* we make sure to move the read pointer past the 8 bytes header
* to be ready to read coordinates data
*----------------------------------------------------------------*/
if ( m_poCurCoordBlock->GotoByteInFile(nFileOffset) != 0)
{
// Failed... an error has already been reported.
return NULL;
}
if (nFileOffset % 512 == 0)
m_poCurCoordBlock->GotoByteInBlock(8); // Skip Header
return m_poCurCoordBlock;
}
/**********************************************************************
* TABMAPFile::GetHeaderBlock()
*
* Return a reference to the MAP file's header block.
*
* The returned pointer is a reference to an object owned by this TABMAPFile
* object and should not be deleted by the caller.
*
* Return NULL if file has not been opened yet.
**********************************************************************/
TABMAPHeaderBlock *TABMAPFile::GetHeaderBlock()
{
return m_poHeader;
}
/**********************************************************************
* TABMAPFile::GetIDFileRef()
*
* Return a reference to the .ID file attached to this .MAP file
*
* The returned pointer is a reference to an object owned by this TABMAPFile
* object and should not be deleted by the caller.
*
* Return NULL if file has not been opened yet.
**********************************************************************/
TABIDFile *TABMAPFile::GetIDFileRef()
{
return m_poIdIndex;
}
/**********************************************************************
* TABMAPFile::GetIndexBlock()
*
* Return a reference to the requested index or object block..
*
* Ownership of the returned block is turned over to the caller, who should
* delete it when no longer needed. The type of the block can be determined
* with the GetBlockType() method.
*
* @param nFileOffset the offset in the map file of the spatial index
* block or object block to load.
*
* @return The requested TABMAPIndexBlock, TABMAPObjectBlock or NULL if the
* read fails for some reason.
**********************************************************************/
TABRawBinBlock *TABMAPFile::GetIndexObjectBlock( int nFileOffset )
{
/*----------------------------------------------------------------
* Read from the file
*---------------------------------------------------------------*/
GByte abyData[512];
if (VSIFSeek(m_fp, nFileOffset, SEEK_SET) != 0
|| VSIFRead(abyData, sizeof(GByte), 512, m_fp) != 512 )
{
CPLError(CE_Failure, CPLE_FileIO,
"GetIndexBlock() failed reading %d bytes at offset %d.",
512, nFileOffset);
return NULL;
}
/* -------------------------------------------------------------------- */
/* Create and initialize depending on the block type. */
/* -------------------------------------------------------------------- */
int nBlockType = abyData[0];
TABRawBinBlock *poBlock;
if( nBlockType == TABMAP_INDEX_BLOCK )
poBlock = new TABMAPIndexBlock();
else
poBlock = new TABMAPObjectBlock();
if( poBlock->InitBlockFromData(abyData,512,TRUE,m_fp,nFileOffset) == -1 )
{
delete poBlock;
poBlock = NULL;
}
return poBlock;
}
/**********************************************************************
* TABMAPFile::InitDrawingTools()
*
* Init the drawing tools for this file.
*
* In Read mode, this will load the drawing tools from the file.
*
* In Write mode, this function will init an empty the tool def table.
*
* Reutrns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::InitDrawingTools()
{
int nStatus = 0;
if (m_poHeader == NULL)
return -1; // File not opened yet!
/*-------------------------------------------------------------
* We want to perform this initialisation only ONCE
*------------------------------------------------------------*/
if (m_poToolDefTable != NULL)
return 0;
/*-------------------------------------------------------------
* Create a new ToolDefTable... no more initialization is required
* unless we want to read tool blocks from file.
*------------------------------------------------------------*/
m_poToolDefTable = new TABToolDefTable;
if (m_eAccessMode == TABRead && m_poHeader->m_nFirstToolBlock != 0)
{
TABMAPToolBlock *poBlock;
poBlock = new TABMAPToolBlock(m_eAccessMode);
poBlock->InitNewBlock(m_fp, 512);
/*-------------------------------------------------------------
* Use GotoByteInFile() to go to the first block's location. This will
* force loading the block if necessary and reading its header.
* Also make sure to move the read pointer past the 8 bytes header
* to be ready to read drawing tools data
*------------------------------------------------------------*/
if ( poBlock->GotoByteInFile(m_poHeader->m_nFirstToolBlock)!= 0)
{
// Failed... an error has already been reported.
delete poBlock;
return -1;
}
poBlock->GotoByteInBlock(8);
nStatus = m_poToolDefTable->ReadAllToolDefs(poBlock);
delete poBlock;
}
return nStatus;
}
/**********************************************************************
* TABMAPFile::CommitDrawingTools()
*
* Write the drawing tools for this file.
*
* This function applies only to write access mode.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::CommitDrawingTools()
{
int nStatus = 0;
if (m_eAccessMode != TABWrite || m_poHeader == NULL)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"CommitDrawingTools() failed: file not opened for write access.");
return -1;
}
if (m_poToolDefTable == NULL ||
(m_poToolDefTable->GetNumPen() +
m_poToolDefTable->GetNumBrushes() +
m_poToolDefTable->GetNumFonts() +
m_poToolDefTable->GetNumSymbols()) == 0)
{
return 0; // Nothing to do!
}
/*-------------------------------------------------------------
* Create a new TABMAPToolBlock and update header fields
*------------------------------------------------------------*/
TABMAPToolBlock *poBlock;
poBlock = new TABMAPToolBlock(m_eAccessMode);
poBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock());
poBlock->SetMAPBlockManagerRef(&m_oBlockManager);
m_poHeader->m_nFirstToolBlock = poBlock->GetStartAddress();
m_poHeader->m_numPenDefs = m_poToolDefTable->GetNumPen();
m_poHeader->m_numBrushDefs = m_poToolDefTable->GetNumBrushes();
m_poHeader->m_numFontDefs = m_poToolDefTable->GetNumFonts();
m_poHeader->m_numSymbolDefs = m_poToolDefTable->GetNumSymbols();
/*-------------------------------------------------------------
* Do the actual work and delete poBlock
* (Note that poBlock will have already been committed to the file
* by WriteAllToolDefs() )
*------------------------------------------------------------*/
nStatus = m_poToolDefTable->WriteAllToolDefs(poBlock);
m_poHeader->m_numMapToolBlocks = poBlock->GetNumBlocksInChain();
delete poBlock;
return nStatus;
}
/**********************************************************************
* TABMAPFile::ReadPenDef()
*
* Fill the TABPenDef structure with the definition of the specified pen
* index... (1-based pen index)
*
* If nPenIndex==0 or is invalid, then the structure is cleared.
*
* Returns 0 on success, -1 on error (i.e. Pen not found).
**********************************************************************/
int TABMAPFile::ReadPenDef(int nPenIndex, TABPenDef *psDef)
{
TABPenDef *psTmp;
if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
return -1;
if (psDef && m_poToolDefTable &&
(psTmp = m_poToolDefTable->GetPenDefRef(nPenIndex)) != NULL)
{
*psDef = *psTmp;
}
else if (psDef)
{
/* Init to MapInfo default */
static const TABPenDef csDefaultPen = MITAB_PEN_DEFAULT;
*psDef = csDefaultPen;
return -1;
}
return 0;
}
/**********************************************************************
* TABMAPFile::WritePenDef()
*
* Write a Pen Tool to the map file and return the pen index that has
* been attributed to this Pen tool definition, or -1 if something went
* wrong
*
* Note that the returned index is a 1-based index. A value of 0
* indicates "none" in MapInfo.
* Returns a value >= 0 on success, -1 on error
**********************************************************************/
int TABMAPFile::WritePenDef(TABPenDef *psDef)
{
if (psDef == NULL ||
(m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
m_poToolDefTable==NULL )
{
return -1;
}
return m_poToolDefTable->AddPenDefRef(psDef);
}
/**********************************************************************
* TABMAPFile::ReadBrushDef()
*
* Fill the TABBrushDef structure with the definition of the specified Brush
* index... (1-based Brush index)
*
* If nBrushIndex==0 or is invalid, then the structure is cleared.
*
* Returns 0 on success, -1 on error (i.e. Brush not found).
**********************************************************************/
int TABMAPFile::ReadBrushDef(int nBrushIndex, TABBrushDef *psDef)
{
TABBrushDef *psTmp;
if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
return -1;
if (psDef && m_poToolDefTable &&
(psTmp = m_poToolDefTable->GetBrushDefRef(nBrushIndex)) != NULL)
{
*psDef = *psTmp;
}
else if (psDef)
{
/* Init to MapInfo default */
static const TABBrushDef csDefaultBrush = MITAB_BRUSH_DEFAULT;
*psDef = csDefaultBrush;
return -1;
}
return 0;
}
/**********************************************************************
* TABMAPFile::WriteBrushDef()
*
* Write a Brush Tool to the map file and return the Brush index that has
* been attributed to this Brush tool definition, or -1 if something went
* wrong
*
* Note that the returned index is a 1-based index. A value of 0
* indicates "none" in MapInfo.
* Returns a value >= 0 on success, -1 on error
**********************************************************************/
int TABMAPFile::WriteBrushDef(TABBrushDef *psDef)
{
if (psDef == NULL ||
(m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
m_poToolDefTable==NULL )
{
return -1;
}
return m_poToolDefTable->AddBrushDefRef(psDef);
}
/**********************************************************************
* TABMAPFile::ReadFontDef()
*
* Fill the TABFontDef structure with the definition of the specified Font
* index... (1-based Font index)
*
* If nFontIndex==0 or is invalid, then the structure is cleared.
*
* Returns 0 on success, -1 on error (i.e. Font not found).
**********************************************************************/
int TABMAPFile::ReadFontDef(int nFontIndex, TABFontDef *psDef)
{
TABFontDef *psTmp;
if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
return -1;
if (psDef && m_poToolDefTable &&
(psTmp = m_poToolDefTable->GetFontDefRef(nFontIndex)) != NULL)
{
*psDef = *psTmp;
}
else if (psDef)
{
/* Init to MapInfo default */
static const TABFontDef csDefaultFont = MITAB_FONT_DEFAULT;
*psDef = csDefaultFont;
return -1;
}
return 0;
}
/**********************************************************************
* TABMAPFile::WriteFontDef()
*
* Write a Font Tool to the map file and return the Font index that has
* been attributed to this Font tool definition, or -1 if something went
* wrong
*
* Note that the returned index is a 1-based index. A value of 0
* indicates "none" in MapInfo.
* Returns a value >= 0 on success, -1 on error
**********************************************************************/
int TABMAPFile::WriteFontDef(TABFontDef *psDef)
{
if (psDef == NULL ||
(m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
m_poToolDefTable==NULL )
{
return -1;
}
return m_poToolDefTable->AddFontDefRef(psDef);
}
/**********************************************************************
* TABMAPFile::ReadSymbolDef()
*
* Fill the TABSymbolDef structure with the definition of the specified Symbol
* index... (1-based Symbol index)
*
* If nSymbolIndex==0 or is invalid, then the structure is cleared.
*
* Returns 0 on success, -1 on error (i.e. Symbol not found).
**********************************************************************/
int TABMAPFile::ReadSymbolDef(int nSymbolIndex, TABSymbolDef *psDef)
{
TABSymbolDef *psTmp;
if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
return -1;
if (psDef && m_poToolDefTable &&
(psTmp = m_poToolDefTable->GetSymbolDefRef(nSymbolIndex)) != NULL)
{
*psDef = *psTmp;
}
else if (psDef)
{
/* Init to MapInfo default */
static const TABSymbolDef csDefaultSymbol = MITAB_SYMBOL_DEFAULT;
*psDef = csDefaultSymbol;
return -1;
}
return 0;
}
/**********************************************************************
* TABMAPFile::WriteSymbolDef()
*
* Write a Symbol Tool to the map file and return the Symbol index that has
* been attributed to this Symbol tool definition, or -1 if something went
* wrong
*
* Note that the returned index is a 1-based index. A value of 0
* indicates "none" in MapInfo.
* Returns a value >= 0 on success, -1 on error
**********************************************************************/
int TABMAPFile::WriteSymbolDef(TABSymbolDef *psDef)
{
if (psDef == NULL ||
(m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
m_poToolDefTable==NULL )
{
return -1;
}
return m_poToolDefTable->AddSymbolDefRef(psDef);
}
#define ORDER_MIN_MAX(type,min,max) \
{ if( (max) < (min) ) \
{ type temp = (max); (max) = (min); (min) = temp; } }
/**********************************************************************
* TABMAPFile::SetCoordFilter()
*
* Set the MBR of the area of interest... only objects that at least
* overlap with that area will be returned.
*
* @param sMin minimum x/y the file's projection coord.
* @param sMax maximum x/y the file's projection coord.
**********************************************************************/
void TABMAPFile::SetCoordFilter(TABVertex sMin, TABVertex sMax)
{
m_sMinFilter = sMin;
m_sMaxFilter = sMax;
Coordsys2Int(sMin.x, sMin.y, m_XMinFilter, m_YMinFilter, TRUE);
Coordsys2Int(sMax.x, sMax.y, m_XMaxFilter, m_YMaxFilter, TRUE);
ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter);
ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter);
ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x);
ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y);
}
/**********************************************************************
* TABMAPFile::ResetCoordFilter()
*
* Reset the MBR of the area of interest to be the extents as defined
* in the header.
**********************************************************************/
void TABMAPFile::ResetCoordFilter()
{
m_XMinFilter = m_poHeader->m_nXMin;
m_YMinFilter = m_poHeader->m_nYMin;
m_XMaxFilter = m_poHeader->m_nXMax;
m_YMaxFilter = m_poHeader->m_nYMax;
Int2Coordsys(m_XMinFilter, m_YMinFilter,
m_sMinFilter.x, m_sMinFilter.y);
Int2Coordsys(m_XMaxFilter, m_YMaxFilter,
m_sMaxFilter.x, m_sMaxFilter.y);
ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter);
ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter);
ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x);
ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y);
}
/**********************************************************************
* TABMAPFile::GetCoordFilter()
*
* Get the MBR of the area of interest, as previously set by
* SetCoordFilter().
*
* @param sMin vertex into which the minimum x/y values put in coordsys space.
* @param sMax vertex into which the maximum x/y values put in coordsys space.
**********************************************************************/
void TABMAPFile::GetCoordFilter(TABVertex &sMin, TABVertex &sMax)
{
sMin = m_sMinFilter;
sMax = m_sMaxFilter;
}
/**********************************************************************
* TABMAPFile::CommitSpatialIndex()
*
* Write the spatial index blocks tree for this file.
*
* This function applies only to write access mode.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABMAPFile::CommitSpatialIndex()
{
if (m_eAccessMode != TABWrite || m_poHeader == NULL)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"CommitSpatialIndex() failed: file not opened for write access.");
return -1;
}
if (m_poSpIndex == NULL)
{
return 0; // Nothing to do!
}
/*-------------------------------------------------------------
* Update header fields and commit index block
* (it's children will be recursively committed as well)
*------------------------------------------------------------*/
// Add 1 to Spatial Index Depth to account to the MapObjectBlocks
m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
m_poSpIndex->GetCurMaxDepth()+1);
m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
m_poHeader->m_nXMax, m_poHeader->m_nYMax);
return m_poSpIndex->CommitToFile();
}
/**********************************************************************
* TABMAPFile::GetMinTABFileVersion()
*
* Returns the minimum TAB file version number that can contain all the
* objects stored in this file.
**********************************************************************/
int TABMAPFile::GetMinTABFileVersion()
{
int nToolVersion = 0;
if (m_poToolDefTable)
nToolVersion = m_poToolDefTable->GetMinVersionNumber();
return MAX(nToolVersion, m_nMinTABVersion);
}
/**********************************************************************
* TABMAPFile::Dump()
*
* Dump block contents... available only in DEBUG mode.
**********************************************************************/
#ifdef DEBUG
void TABMAPFile::Dump(FILE *fpOut /*=NULL*/)
{
if (fpOut == NULL)
fpOut = stdout;
fprintf(fpOut, "----- TABMAPFile::Dump() -----\n");
if (m_fp == NULL)
{
fprintf(fpOut, "File is not opened.\n");
}
else
{
fprintf(fpOut, "File is opened: %s\n", m_pszFname);
fprintf(fpOut, "Coordsys filter = (%g,%g)-(%g,%g)\n",
m_sMinFilter.x, m_sMinFilter.y, m_sMaxFilter.x,m_sMaxFilter.y);
fprintf(fpOut, "Int coord filter = (%d,%d)-(%d,%d)\n",
m_XMinFilter, m_YMinFilter, m_XMaxFilter,m_YMaxFilter);
fprintf(fpOut, "\nFile Header follows ...\n\n");
m_poHeader->Dump(fpOut);
fprintf(fpOut, "... end of file header.\n\n");
fprintf(fpOut, "Associated .ID file ...\n\n");
m_poIdIndex->Dump(fpOut);
fprintf(fpOut, "... end of ID file dump.\n\n");
}
fflush(fpOut);
}
#endif // DEBUG