www.pudn.com > mitab-1.5.1.zip > mitab_imapinfofile.cpp


/********************************************************************** 
 * $Id: mitab_imapinfofile.cpp,v 1.21 2005/05/19 21:10:50 fwarmerdam Exp $ 
 * 
 * Name:     mitab_imapinfo 
 * Project:  MapInfo mid/mif Tab Read/Write library 
 * Language: C++ 
 * Purpose:  Implementation of the IMapInfoFile class, super class of 
 *           of MIFFile and TABFile 
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca 
 * 
 ********************************************************************** 
 * Copyright (c) 1999-2001, 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_imapinfofile.cpp,v $ 
 * Revision 1.21  2005/05/19 21:10:50  fwarmerdam 
 * changed to use OGRLayers spatial filter support 
 * 
 * Revision 1.20  2005/05/19 15:27:00  jlacroix 
 * Implement a method to set the StyleString of a TABFeature. 
 * This is done via the ITABFeaturePen, Brush and Symbol classes. 
 * 
 * Revision 1.19  2004/06/30 20:29:04  dmorissette 
 * Fixed refs to old address danmo@videotron.ca 
 * 
 * Revision 1.18  2003/12/19 07:55:55  fwarmerdam 
 * treat 3D features as 2D on write 
 * 
 * Revision 1.17  2001/09/14 19:14:43  warmerda 
 * added attribute query support 
 * 
 * Revision 1.16  2001/09/14 03:23:55  warmerda 
 * Substantial upgrade to support spatial queries using spatial indexes 
 * 
 * Revision 1.15  2001/07/03 23:11:21  daniel 
 * Test for NULL geometries if spatial filter enabled in GetNextFeature(). 
 * 
 * Revision 1.14  2001/03/09 04:16:02  daniel 
 * Added TABSeamless for reading seamless TAB files 
 * 
 * Revision 1.13  2001/02/27 19:59:05  daniel 
 * Enabled spatial filter in IMapInfoFile::GetNextFeature(), and avoid 
 * unnecessary feature cloning in GetNextFeature() and GetFeature() 
 * 
 * Revision 1.12  2001/02/06 22:03:24  warmerda 
 * fixed memory leak of whole features in CreateFeature 
 * 
 * Revision 1.11  2001/01/23 21:23:42  daniel 
 * Added projection bounds lookup table, called from TABFile::SetProjInfo() 
 * 
 * Revision 1.10  2001/01/22 16:03:58  warmerda 
 * expanded tabs 
 * 
 * Revision 1.9  2000/11/30 20:27:56  warmerda 
 * make variable length string fields 254 wide, not 255 
 * 
 * Revision 1.8  2000/02/28 03:11:35  warmerda 
 * fix support for zero width fields 
 * 
 * Revision 1.7  2000/02/02 20:14:03  warmerda 
 * made safer when encountering geometryless features 
 * 
 * Revision 1.6  2000/01/26 18:17:35  warmerda 
 * added CreateField method 
 * 
 * Revision 1.5  2000/01/15 22:30:44  daniel 
 * Switch to MIT/X-Consortium OpenSource license 
 * 
 * Revision 1.4  2000/01/11 19:06:25  daniel 
 * Added support for conversion of collections in CreateFeature() 
 * 
 * Revision 1.3  1999/12/14 02:14:50  daniel 
 * Added static SmartOpen() method + TABView support 
 * 
 * Revision 1.2  1999/11/08 19:15:44  stephane 
 * Add headers method 
 * 
 * Revision 1.1  1999/11/08 04:17:27  stephane 
 * First Revision 
 * 
 **********************************************************************/ 
 
#include "mitab.h" 
#include "mitab_utils.h" 
 
#include       /* isspace() */ 
 
/********************************************************************** 
 *                   IMapInfoFile::IMapInfoFile() 
 * 
 * Constructor. 
 **********************************************************************/ 
IMapInfoFile::IMapInfoFile() 
{ 
    m_nCurFeatureId = 0; 
    m_poCurFeature = NULL; 
    m_bBoundsSet = FALSE; 
} 
 
 
/********************************************************************** 
 *                   IMapInfoFile::~IMapInfoFile() 
 * 
 * Destructor. 
 **********************************************************************/ 
IMapInfoFile::~IMapInfoFile() 
{ 
    if (m_poCurFeature) 
    { 
        delete m_poCurFeature; 
        m_poCurFeature = NULL; 
    } 
} 
 
/********************************************************************** 
 *                   IMapInfoFile::SmartOpen() 
 * 
 * Use this static method to automatically open any flavour of MapInfo 
 * dataset.  This method will detect the file type, create an object 
 * of the right type, and open the file. 
 * 
 * Call GetFileClass() on the returned object if you need to find out 
 * its exact type.  (To access format-specific methods for instance) 
 * 
 * Returns the new object ptr. , or NULL if the open failed. 
 **********************************************************************/ 
IMapInfoFile *IMapInfoFile::SmartOpen(const char *pszFname, 
                                      GBool bTestOpenNoError /*=FALSE*/) 
{ 
    IMapInfoFile *poFile = NULL; 
    int nLen = 0; 
 
    if (pszFname) 
        nLen = strlen(pszFname); 
 
    if (nLen > 4 && (EQUAL(pszFname + nLen-4, ".MIF") || 
                     EQUAL(pszFname + nLen-4, ".MID") ) ) 
    { 
        /*------------------------------------------------------------- 
         * MIF/MID file 
         *------------------------------------------------------------*/ 
        poFile = new MIFFile; 
    } 
    else if (nLen > 4 && EQUAL(pszFname + nLen-4, ".TAB")) 
    { 
        /*------------------------------------------------------------- 
         * .TAB file ... is it a TABFileView or a TABFile? 
         * We have to read the .tab header to find out. 
         *------------------------------------------------------------*/ 
        FILE *fp; 
        const char *pszLine; 
        char *pszAdjFname = CPLStrdup(pszFname); 
        GBool bFoundFields = FALSE, bFoundView=FALSE, bFoundSeamless=FALSE; 
 
        TABAdjustFilenameExtension(pszAdjFname); 
        fp = VSIFOpen(pszAdjFname, "r"); 
        while(fp && (pszLine = CPLReadLine(fp)) != NULL) 
        { 
            while (isspace(*pszLine))  pszLine++; 
            if (EQUALN(pszLine, "Fields", 6)) 
                bFoundFields = TRUE; 
            else if (EQUALN(pszLine, "create view", 11)) 
                bFoundView = TRUE; 
            else if (EQUALN(pszLine, "\"\\IsSeamless\" = \"TRUE\"", 21)) 
                bFoundSeamless = TRUE; 
        } 
 
        if (bFoundView) 
            poFile = new TABView; 
        else if (bFoundFields && bFoundSeamless) 
            poFile = new TABSeamless; 
        else if (bFoundFields) 
            poFile = new TABFile; 
 
        if (fp) 
            VSIFClose(fp); 
 
        CPLFree(pszAdjFname); 
    } 
 
    /*----------------------------------------------------------------- 
     * Perform the open() call 
     *----------------------------------------------------------------*/ 
    if (poFile && poFile->Open(pszFname, "r", bTestOpenNoError) != 0) 
    { 
        delete poFile; 
        poFile = NULL; 
    } 
 
    if (!bTestOpenNoError && poFile == NULL) 
    { 
        CPLError(CE_Failure, CPLE_FileIO, 
                 "%s could not be opened as a MapInfo dataset.", pszFname); 
    } 
 
    return poFile; 
} 
 
 
 
/********************************************************************** 
 *                   IMapInfoFile::GetNextFeature() 
 * 
 * Standard OGR GetNextFeature implementation.  This methode is used 
 * to retreive the next OGRFeature. 
 **********************************************************************/ 
OGRFeature *IMapInfoFile::GetNextFeature() 
{ 
    OGRFeature *poFeatureRef; 
    OGRGeometry *poGeom; 
    int nFeatureId; 
 
    while( (nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1 ) 
    { 
        poFeatureRef = GetFeatureRef(nFeatureId); 
        if (poFeatureRef == NULL) 
            return NULL; 
        else if( (m_poFilterGeom == NULL || 
                  ((poGeom = poFeatureRef->GetGeometryRef()) != NULL && 
                   FilterGeometry( poGeom ))) 
                 && (m_poAttrQuery == NULL 
                     || m_poAttrQuery->Evaluate( poFeatureRef )) ) 
        { 
            // Avoid cloning feature... return the copy owned by the class 
            CPLAssert(poFeatureRef == m_poCurFeature); 
            m_poCurFeature = NULL;   
            return poFeatureRef; 
        } 
    } 
    return NULL; 
} 
 
/********************************************************************** 
 *                   IMapInfoFile::CreateFeature() 
 * 
 * Standard OGR CreateFeature implementation.  This methode is used 
 * to create a new feature in current dataset  
 **********************************************************************/ 
OGRErr     IMapInfoFile::CreateFeature(OGRFeature *poFeature) 
{ 
    TABFeature *poTABFeature; 
    OGRGeometry   *poGeom; 
    OGRwkbGeometryType eGType; 
    OGRErr  eErr; 
    TABPoint *poTABPointFeature = NULL; 
    TABRegion *poTABRegionFeature = NULL; 
    TABPolyline *poTABPolylineFeature = NULL; 
 
    /*----------------------------------------------------------------- 
     * MITAB won't accept new features unless they are in a type derived 
     * from TABFeature... so we have to do our best to map to the right 
     * feature type based on the geometry type. 
     *----------------------------------------------------------------*/ 
    poGeom = poFeature->GetGeometryRef(); 
    if( poGeom != NULL ) 
        eGType = poGeom->getGeometryType(); 
    else 
        eGType = wkbNone; 
 
    switch( wkbFlatten(eGType) ) 
    { 
      /*------------------------------------------------------------- 
       * POINT 
       *------------------------------------------------------------*/ 
      case wkbPoint: 
        poTABFeature = new TABPoint(poFeature->GetDefnRef()); 
        if(poFeature->GetStyleString()) 
        { 
            poTABPointFeature = (TABPoint*)poTABFeature; 
            poTABPointFeature->SetSymbolFromStyleString( 
                poFeature->GetStyleString()); 
        } 
        break; 
      /*------------------------------------------------------------- 
       * REGION 
       *------------------------------------------------------------*/ 
      case wkbPolygon: 
      case wkbMultiPolygon: 
        poTABFeature = new TABRegion(poFeature->GetDefnRef()); 
        if(poFeature->GetStyleString()) 
        { 
            poTABRegionFeature = (TABRegion*)poTABFeature; 
            poTABRegionFeature->SetPenFromStyleString( 
                poFeature->GetStyleString()); 
 
            poTABRegionFeature->SetBrushFromStyleString( 
                poFeature->GetStyleString()); 
        } 
        break; 
      /*------------------------------------------------------------- 
       * LINE/PLINE/MULTIPLINE 
       *------------------------------------------------------------*/ 
      case wkbLineString: 
      case wkbMultiLineString: 
        poTABFeature = new TABPolyline(poFeature->GetDefnRef()); 
        if(poFeature->GetStyleString()) 
        { 
            poTABPolylineFeature = (TABPolyline*)poTABFeature; 
            poTABPolylineFeature->SetPenFromStyleString( 
                poFeature->GetStyleString()); 
        } 
        break; 
      /*------------------------------------------------------------- 
       * Collection types that are not directly supported... convert 
       * to multiple features in output file through recursive calls. 
       *------------------------------------------------------------*/ 
      case wkbGeometryCollection: 
      case wkbMultiPoint: 
      { 
          OGRErr eStatus = OGRERR_NONE; 
          int i; 
          OGRGeometryCollection *poColl = (OGRGeometryCollection*)poGeom; 
          OGRFeature *poTmpFeature = poFeature->Clone(); 
 
          for (i=0; eStatus==OGRERR_NONE && igetNumGeometries(); i++) 
          { 
              poTmpFeature->SetGeometry(poColl->getGeometryRef(i)); 
              eStatus = CreateFeature(poTmpFeature); 
          } 
          delete poTmpFeature; 
          return eStatus; 
        } 
        break; 
      /*------------------------------------------------------------- 
       * Unsupported type.... convert to MapInfo geometry NONE 
       *------------------------------------------------------------*/ 
      case wkbUnknown: 
      default: 
         poTABFeature = new TABFeature(poFeature->GetDefnRef());  
        break; 
    } 
 
    if( poGeom != NULL ) 
        poTABFeature->SetGeometryDirectly(poGeom->clone()); 
     
    for (int i=0; i< poFeature->GetDefnRef()->GetFieldCount();i++) 
    { 
        poTABFeature->SetField(i,poFeature->GetRawFieldRef( i )); 
    } 
     
 
    if (SetFeature(poTABFeature) > -1) 
        eErr = OGRERR_NONE; 
    else 
        eErr = OGRERR_FAILURE; 
 
    delete poTABFeature; 
     
    return eErr; 
} 
 
/********************************************************************** 
 *                   IMapInfoFile::GetFeature() 
 * 
 * Standard OGR GetFeature implementation.  This methode is used 
 * to get the wanted (nFeatureId) feature, a NULL value will be  
 * returned on error. 
 **********************************************************************/ 
OGRFeature *IMapInfoFile::GetFeature(long nFeatureId) 
{ 
    OGRFeature *poFeatureRef; 
 
    poFeatureRef = GetFeatureRef(nFeatureId); 
    if (poFeatureRef) 
    { 
        // Avoid cloning feature... return the copy owned by the class 
        CPLAssert(poFeatureRef == m_poCurFeature); 
        m_poCurFeature = NULL;   
 
        return poFeatureRef; 
    } 
    else 
      return NULL; 
} 
 
/************************************************************************/ 
/*                            CreateField()                             */ 
/*                                                                      */ 
/*      Create a native field based on a generic OGR definition.        */ 
/************************************************************************/ 
 
OGRErr IMapInfoFile::CreateField( OGRFieldDefn *poField, int bApproxOK ) 
 
{ 
    TABFieldType        eTABType; 
    int                 nWidth = poField->GetWidth(); 
 
    if( poField->GetType() == OFTInteger ) 
    { 
        eTABType = TABFInteger; 
        if( nWidth == 0 ) 
            nWidth = 12; 
    } 
    else if( poField->GetType() == OFTReal ) 
    { 
        eTABType = TABFFloat; 
        if( nWidth == 0 ) 
            nWidth = 32; 
    } 
    else if( poField->GetType() == OFTString ) 
    { 
        eTABType = TABFChar; 
        if( nWidth == 0 ) 
            nWidth = 254; 
        else 
            nWidth = MIN(254,nWidth); 
    } 
    else 
    { 
        CPLError( CE_Failure, CPLE_AppDefined, 
                  "IMapInfoFile::CreateField() called with unsupported field" 
                  " type %d.\n" 
                  "Note that Mapinfo files don't support list field types.\n", 
                  poField->GetType() ); 
 
        return OGRERR_FAILURE; 
    } 
 
    if( AddFieldNative( poField->GetNameRef(), eTABType, 
                        nWidth, poField->GetPrecision() ) > -1 ) 
        return OGRERR_NONE; 
    else 
        return OGRERR_FAILURE; 
}