EVOLUTION-MANAGER
Edit File: mitab_miffile.cpp
/********************************************************************** * $Id: mitab_miffile.cpp,v 1.58 2011-09-22 21:57:46 dmorissette Exp $ * * Name: mitab_miffile.cpp * Project: MapInfo TAB Read/Write library * Language: C++ * Purpose: Implementation of the MIDFile class. * To be used by external programs to handle reading/writing of * features from/to MID/MIF datasets. * Author: Stephane Villeneuve, stephane.v@videotron.ca * ********************************************************************** * Copyright (c) 1999-2003, Stephane Villeneuve * Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org> * * 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_miffile.cpp,v $ * Revision 1.58 2011-09-22 21:57:46 dmorissette * Fixed problem with tab delimiter used in MIF files (GDAL #4257) * * Revision 1.57 2010-10-15 12:06:44 aboudreault * Fixed crash when trying to get the same mitab mif feature twice (GDAL #3765) * * Revision 1.56 2010-10-12 19:02:40 aboudreault * Fixed incomplet patch to handle differently indented lines in mif files (gdal #3694) * * Revision 1.55 2010-10-08 18:50:52 aboudreault * Fixed handle differently indented lines in mif files. (GDAL bug #3694) * * Revision 1.54 2010-07-07 19:00:15 aboudreault * Cleanup Win32 Compile Warnings (GDAL bug #2930) * * Revision 1.53 2010-05-07 19:39:19 aboudreault * Fixed MIF driver: doesn't create a layer defn at layer creation (bug 2180) * * Revision 1.52 2010-01-07 20:39:12 aboudreault * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) * * Revision 1.51 2009-07-27 14:08:41 dmorissette * Fixed dataset version check in AddFieldNative for type TABFDateTime * * Revision 1.50 2008-12-17 14:55:20 aboudreault * Fixed mitab mif/mid importer fails when a Text geometry have an empty * text value (bug 1978) * * Revision 1.49 2008/11/17 22:06:21 aboudreault * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with * OGR and fixed reading/writing support for these types. * * Revision 1.48 2008/09/26 14:40:24 aboudreault * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948) * * Revision 1.47 2008/03/05 20:35:39 dmorissette * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) * * Revision 1.46 2008/02/01 20:30:59 dmorissette * Use %.15g instead of %.16g as number precision in .MIF output * * Revision 1.45 2008/01/29 21:56:39 dmorissette * Update dataset version properly for Date/Time/DateTime field types (#1754) * * Revision 1.44 2008/01/29 20:46:32 dmorissette * Added support for v9 Time and DateTime fields (byg 1754) * * Revision 1.43 2007/09/14 15:35:21 dmorissette * Fixed problem with MIF parser being confused by special attribute * names (bug 1795) * * Revision 1.42 2007/06/12 13:52:37 dmorissette * Added IMapInfoFile::SetCharset() method (bug 1734) * * Revision 1.41 2005/10/13 20:12:03 fwarmerdam * layers with just regions can't be set as type wkbPolygon because they may * have multipolygons (bug GDAL:958) * http://bugzilla.remotesensing.org/show_bug.cgi?id=958 * * Revision 1.40 2005/10/12 14:03:02 fwarmerdam * Fixed problem with white space parsing in mitab_miffile.cpp (bug GDAL:954) * * Revision 1.39 2005/10/04 19:36:10 dmorissette * Added support for reading collections from MIF files (bug 1126) * * Revision 1.38 2004/02/27 21:04:14 fwarmerdam * dont write MIF header if file is readonly - gdal bugzilla 509 * * Revision 1.37 2003/12/19 07:54:50 fwarmerdam * write mif header on close if not already written out * * Revision 1.36 2003/08/13 02:49:02 dmorissette * Use tab as default delimiter if not explicitly specified (Anthony D, bug 37) * * Revision 1.35 2003/01/30 22:42:39 daniel * Fixed crash in ParseMIFHeader() when .mif doesn't contain a DATA line * * Revision 1.34 2002/09/23 12:53:29 warmerda * fix memory leak of m_pszIndex * * Revision 1.33 2002/05/08 15:10:48 julien * Implement MIFFile::SetMIFCoordSys in mitab_capi.cpp (Bug 984) * * Revision 1.32 2002/04/26 14:16:49 julien * Finishing the implementation of Multipoint (support for MIF) * * Revision 1.31 2001/09/19 21:39:15 warmerda * get extents efficiently * * Revision 1.30 2001/09/19 14:31:22 warmerda * added m_nPreloadedId to keep track of preloaded line * * Revision 1.29 2001/09/14 19:14:43 warmerda * added attribute query support * * Revision 1.28 2001/08/10 17:49:01 warmerda * fixed a few memory leaks * * Revision 1.27 2001/03/15 03:57:51 daniel * Added implementation for new OGRLayer::GetExtent(), returning data MBR. * * Revision 1.26 2001/03/09 04:14:19 daniel * Fixed problem creating new files with mixed case extensions (e.g. ".Tab") * * Revision 1.25 2001/03/09 03:51:48 daniel * Fixed writing MIF header: missing break; for decimal fields * * Revision 1.24 2001/02/27 19:59:05 daniel * Enabled spatial filter in IMapInfoFile::GetNextFeature(), and avoid * unnecessary feature cloning in GetNextFeature() and GetFeature() * * Revision 1.23 2001/01/23 21:23:42 daniel * Added projection bounds lookup table, called from TABFile::SetProjInfo() * * Revision 1.22 2001/01/22 16:03:58 warmerda * expanded tabs * * Revision 1.21 2000/12/15 05:38:38 daniel * Produce Warning instead of an error when nWidth>254 in AddFieldNative() * * Revision 1.20 2000/11/14 06:15:37 daniel * Handle '\t' as spaces in parsing, and fixed GotoFeature() to avoid calling * ResetReading() when reading forward. * * Revision 1.19 2000/07/04 01:50:40 warmerda * Removed unprotected debugging printf. * * Revision 1.18 2000/06/28 00:32:04 warmerda * Make GetFeatureCountByType() actually work if bForce is TRUE * Collect detailed (by feature type) feature counts in PreParse(). * * Revision 1.17 2000/04/27 15:46:25 daniel * Make SetFeatureDefn() use AddFieldNative(), scan field names for invalid * chars, and map field width=0 (variable length in OGR) to valid defaults * * Revision 1.16 2000/03/27 03:37:59 daniel * Handle bounds in CoordSys for read and write, + handle point SYMBOL line as * optional + fixed reading of bounds in PreParseFile() * * Revision 1.15 2000/02/28 17:05:06 daniel * Added support for index and unique directives for read and write * * Revision 1.14 2000/01/28 07:32:25 daniel * Validate char field width (must be <= 254 chars) * * Revision 1.13 2000/01/24 19:51:33 warmerda * AddFieldNative should not fail for read-only datasets * * Revision 1.12 2000/01/18 23:13:41 daniel * Implemented AddFieldNative() * * ... * * Revision 1.1 1999/11/08 04:16:07 stephane * First Revision * **********************************************************************/ #include "mitab.h" #include "mitab_utils.h" #include <ctype.h> /*===================================================================== * class MIFFile *====================================================================*/ /********************************************************************** * MIFFile::MIFFile() * * Constructor. **********************************************************************/ MIFFile::MIFFile() { m_pszFname = NULL; m_nVersion = 300; // Tab is default delimiter in MIF spec if not explicitly specified. Use // that by default for read mode. In write mode, we will use "," as // delimiter since it's more common than tab (we do this in Open()) m_pszDelimiter = CPLStrdup("\t"); m_pszUnique = NULL; m_pszIndex = NULL; m_pszCoordSys = NULL; m_paeFieldType = NULL; m_pabFieldIndexed = NULL; m_pabFieldUnique = NULL; m_dfXMultiplier = 1.0; m_dfYMultiplier = 1.0; m_dfXDisplacement = 0.0; m_dfYDisplacement = 0.0; m_poMIDFile = NULL; m_poMIFFile = NULL; m_nPreloadedId = 0; m_poDefn = NULL; m_poSpatialRef = NULL; m_nCurFeatureId = 0; m_nFeatureCount = 0; m_nWriteFeatureId = -1; m_poCurFeature = NULL; m_bPreParsed = FALSE; m_nAttribut = 0; m_bHeaderWrote = FALSE; m_nPoints = m_nLines = m_nRegions = m_nTexts = 0; m_bExtentsSet = FALSE; } /********************************************************************** * MIFFile::~MIFFile() * * Destructor. **********************************************************************/ MIFFile::~MIFFile() { Close(); } /********************************************************************** * MIFFile::Open() * * Returns 0 on success, -1 on error. **********************************************************************/ int MIFFile::Open(const char *pszFname, const char *pszAccess, GBool bTestOpenNoError /*=FALSE*/ ) { char *pszTmpFname = NULL; int nFnameLen = 0; CPLErrorReset(); if (m_poMIFFile) { CPLError(CE_Failure, CPLE_FileIO, "Open() failed: object already contains an open file"); return -1; } /*----------------------------------------------------------------- * Validate access mode *----------------------------------------------------------------*/ if (EQUALN(pszAccess, "r", 1)) { m_eAccessMode = TABRead; pszAccess = "rt"; } else if (EQUALN(pszAccess, "w", 1)) { m_eAccessMode = TABWrite; pszAccess = "wt"; // In write mode, use "," as delimiter since it's more common than tab CPLFree(m_pszDelimiter); m_pszDelimiter = CPLStrdup(","); } else { if (!bTestOpenNoError) CPLError(CE_Failure, CPLE_FileIO, "Open() failed: access mode \"%s\" not supported", pszAccess); else CPLErrorReset(); return -1; } /*----------------------------------------------------------------- * Make sure filename has a .MIF or .MID extension... *----------------------------------------------------------------*/ m_pszFname = CPLStrdup(pszFname); nFnameLen = strlen(m_pszFname); if (nFnameLen > 4 && (strcmp(m_pszFname+nFnameLen-4, ".MID")==0 || strcmp(m_pszFname+nFnameLen-4, ".MIF")==0 ) ) strcpy(m_pszFname+nFnameLen-4, ".MIF"); else if (nFnameLen > 4 && (EQUAL(m_pszFname+nFnameLen-4, ".mid") || EQUAL(m_pszFname+nFnameLen-4, ".mif") ) ) strcpy(m_pszFname+nFnameLen-4, ".mif"); else { if (!bTestOpenNoError) CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s: invalid filename extension", m_pszFname); else CPLErrorReset(); return -1; } pszTmpFname = CPLStrdup(m_pszFname); /*----------------------------------------------------------------- * Open .MIF file *----------------------------------------------------------------*/ #ifndef _WIN32 /*----------------------------------------------------------------- * On Unix, make sure extension uses the right cases * We do it even for write access because if a file with the same * extension already exists we want to overwrite it. *----------------------------------------------------------------*/ TABAdjustFilenameExtension(pszTmpFname); #endif m_poMIFFile = new MIDDATAFile; if (m_poMIFFile->Open(pszTmpFname, pszAccess) != 0) { if (!bTestOpenNoError) CPLError(CE_Failure, CPLE_NotSupported, "Unable to open %s.", pszTmpFname); else CPLErrorReset(); CPLFree(pszTmpFname); Close(); return -1; } /*----------------------------------------------------------------- * Read MIF File Header *----------------------------------------------------------------*/ if (m_eAccessMode == TABRead && ParseMIFHeader() != 0) { Close(); if (!bTestOpenNoError) CPLError(CE_Failure, CPLE_NotSupported, "Failed parsing header in %s.", m_pszFname); else CPLErrorReset(); CPLFree(pszTmpFname); return -1; } if ( m_nAttribut > 0 || m_eAccessMode == TABWrite ) { /*----------------------------------------------------------------- * Open .MID file *----------------------------------------------------------------*/ if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".MIF")==0) strcpy(pszTmpFname+nFnameLen-4, ".MID"); else strcpy(pszTmpFname+nFnameLen-4, ".mid"); #ifndef _WIN32 TABAdjustFilenameExtension(pszTmpFname); #endif m_poMIDFile = new MIDDATAFile; if (m_poMIDFile->Open(pszTmpFname, pszAccess) !=0) { if (!bTestOpenNoError) CPLError(CE_Failure, CPLE_NotSupported, "Unable to open %s.", pszTmpFname); else CPLErrorReset(); CPLFree(pszTmpFname); Close(); return -1; } } CPLFree(pszTmpFname); pszTmpFname = NULL; /*----------------------------------------------------------------- * In write access, set some defaults *----------------------------------------------------------------*/ if (m_eAccessMode == TABWrite) { m_nVersion = 300; m_pszCharset = CPLStrdup("Neutral"); } /* Put the MID file at the correct location, on the first feature */ if (m_eAccessMode == TABRead && (m_poMIDFile != NULL && m_poMIDFile->GetLine() == NULL)) { Close(); if (bTestOpenNoError) CPLErrorReset(); return -1; } m_poMIFFile->SetTranslation(m_dfXMultiplier,m_dfYMultiplier, m_dfXDisplacement, m_dfYDisplacement); if( m_poMIDFile != NULL ) m_poMIDFile->SetTranslation(m_dfXMultiplier,m_dfYMultiplier, m_dfXDisplacement, m_dfYDisplacement); m_poMIFFile->SetDelimiter(m_pszDelimiter); if( m_poMIDFile != NULL ) m_poMIDFile->SetDelimiter(m_pszDelimiter); /*------------------------------------------------------------- * Set geometry type if the geometry objects are uniform. *------------------------------------------------------------*/ int numPoints=0, numRegions=0, numTexts=0, numLines=0; if( GetFeatureCountByType( numPoints, numLines, numRegions, numTexts, FALSE ) == 0 ) { numPoints += numTexts; if( numPoints > 0 && numLines == 0 && numRegions == 0 ) m_poDefn->SetGeomType( wkbPoint ); else if( numPoints == 0 && numLines > 0 && numRegions == 0 ) m_poDefn->SetGeomType( wkbLineString ); else { /* we leave it unknown indicating a mixture */ } } /* A newly created layer should have OGRFeatureDefn */ if (m_poDefn == NULL) { char *pszFeatureClassName = TABGetBasename(m_pszFname); m_poDefn = new OGRFeatureDefn(pszFeatureClassName); CPLFree(pszFeatureClassName); // Ref count defaults to 0... set it to 1 m_poDefn->Reference(); } return 0; } /********************************************************************** * MIFFile::ParseMIFHeader() * * Scan the header of a MIF file, and store any useful information into * class members. The main piece of information being the fields * definition that we use to build the OGRFeatureDefn for this file. * * This private method should be used only during the Open() call. * * Returns 0 on success, -1 on error. **********************************************************************/ int MIFFile::ParseMIFHeader() { GBool bColumns = FALSE, bAllColumnsRead = FALSE; int nColumns = 0; GBool bCoordSys = FALSE; char *pszTmp; const char *pszLine; char **papszToken; char *pszFeatureClassName = TABGetBasename(m_pszFname); m_poDefn = new OGRFeatureDefn(pszFeatureClassName); CPLFree(pszFeatureClassName); // Ref count defaults to 0... set it to 1 m_poDefn->Reference(); if (m_eAccessMode != TABRead) { CPLError(CE_Failure, CPLE_NotSupported, "ParseMIDFile() can be used only with Read access."); return -1; } /*----------------------------------------------------------------- * Parse header until we find the "Data" line *----------------------------------------------------------------*/ while (((pszLine = m_poMIFFile->GetLine()) != NULL) && ((bAllColumnsRead == FALSE) || !EQUALN(pszLine,"Data",4))) { if (bColumns == TRUE && nColumns >0) { if (AddFields(pszLine) == 0) { nColumns--; if (nColumns == 0) { bAllColumnsRead = TRUE; bColumns = FALSE; } } else { bColumns = FALSE; } } else if (EQUALN(pszLine,"VERSION",7)) { papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); bColumns = FALSE; bCoordSys = FALSE; if (CSLCount(papszToken) == 2) m_nVersion = atoi(papszToken[1]); CSLDestroy(papszToken); } else if (EQUALN(pszLine,"CHARSET",7)) { papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); bColumns = FALSE; bCoordSys = FALSE; if (CSLCount(papszToken) == 2) { CPLFree(m_pszCharset); m_pszCharset = CPLStrdup(papszToken[1]); } CSLDestroy(papszToken); } else if (EQUALN(pszLine,"DELIMITER",9)) { papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); bColumns = FALSE; bCoordSys = FALSE; if (CSLCount(papszToken) == 2) { CPLFree(m_pszDelimiter); m_pszDelimiter = CPLStrdup(papszToken[1]); } CSLDestroy(papszToken); } else if (EQUALN(pszLine,"UNIQUE",6)) { bColumns = FALSE; bCoordSys = FALSE; m_pszUnique = CPLStrdup(pszLine + 6); } else if (EQUALN(pszLine,"INDEX",5)) { bColumns = FALSE; bCoordSys = FALSE; m_pszIndex = CPLStrdup(pszLine + 5); } else if (EQUALN(pszLine,"COORDSYS",8) ) { bCoordSys = TRUE; m_pszCoordSys = CPLStrdup(pszLine + 9); // Extract bounds if present char **papszFields; papszFields = CSLTokenizeStringComplex(m_pszCoordSys, " ,()\t", TRUE, FALSE ); int iBounds = CSLFindString( papszFields, "Bounds" ); if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields)) { m_dXMin = atof(papszFields[++iBounds]); m_dYMin = atof(papszFields[++iBounds]); m_dXMax = atof(papszFields[++iBounds]); m_dYMax = atof(papszFields[++iBounds]); m_bBoundsSet = TRUE; } CSLDestroy( papszFields ); } else if (EQUALN(pszLine,"TRANSFORM",9)) { papszToken = CSLTokenizeStringComplex(pszLine," ,\t",TRUE,FALSE); bColumns = FALSE; bCoordSys = FALSE; if (CSLCount(papszToken) == 5) { m_dfXMultiplier = atof(papszToken[1]); m_dfYMultiplier = atof(papszToken[2]); m_dfXDisplacement = atof(papszToken[3]); m_dfYDisplacement = atof(papszToken[4]); if (m_dfXMultiplier == 0.0) m_dfXMultiplier = 1.0; if (m_dfYMultiplier == 0.0) m_dfYMultiplier = 1.0; } CSLDestroy(papszToken); } else if (EQUALN(pszLine,"COLUMNS",7)) { papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); bCoordSys = FALSE; bColumns = TRUE; if (CSLCount(papszToken) == 2) { nColumns = atoi(papszToken[1]); m_nAttribut = nColumns; if (nColumns == 0) { // Permit to 0 columns bAllColumnsRead = TRUE; bColumns = FALSE; } } else { bColumns = FALSE; m_nAttribut = 0; } CSLDestroy(papszToken); } else if (bCoordSys == TRUE) { pszTmp = m_pszCoordSys; m_pszCoordSys = CPLStrdup(CPLSPrintf("%s %s",m_pszCoordSys, pszLine)); CPLFree(pszTmp); //printf("Reading CoordSys\n"); // Reading CoordSys } } if (!bAllColumnsRead) { CPLError(CE_Failure, CPLE_NotSupported, "COLUMNS keyword not found or invalid number of columns read in %s. File may be corrupt.", m_pszFname); return -1; } if ((pszLine = m_poMIFFile->GetLastLine()) == NULL || EQUALN(m_poMIFFile->GetLastLine(),"DATA",4) == FALSE) { CPLError(CE_Failure, CPLE_NotSupported, "DATA keyword not found in %s. File may be corrupt.", m_pszFname); return -1; } /*----------------------------------------------------------------- * Move pointer to first line of first object *----------------------------------------------------------------*/ while (((pszLine = m_poMIFFile->GetLine()) != NULL) && m_poMIFFile->IsValidFeature(pszLine) == FALSE) ; /*----------------------------------------------------------------- * Check for Unique and Indexed flags *----------------------------------------------------------------*/ if (m_pszIndex) { papszToken = CSLTokenizeStringComplex(m_pszIndex," ,\t",TRUE,FALSE); for(int i=0; papszToken && papszToken[i]; i++) { int nVal = atoi(papszToken[i]); if (nVal > 0 && nVal <= m_poDefn->GetFieldCount()) m_pabFieldIndexed[nVal-1] = TRUE; } CSLDestroy(papszToken); } if (m_pszUnique) { papszToken = CSLTokenizeStringComplex(m_pszUnique," ,\t",TRUE,FALSE); for(int i=0; papszToken && papszToken[i]; i++) { int nVal = atoi(papszToken[i]); if (nVal > 0 && nVal <= m_poDefn->GetFieldCount()) m_pabFieldUnique[nVal-1] = TRUE; } CSLDestroy(papszToken); } return 0; } /************************************************************************/ /* AddFields() */ /************************************************************************/ int MIFFile::AddFields(const char *pszLine) { char **papszToken; int nStatus = 0,numTok; CPLAssert(m_bHeaderWrote == FALSE); papszToken = CSLTokenizeStringComplex(pszLine," (,)\t",TRUE,FALSE); numTok = CSLCount(papszToken); if (numTok >= 3 && EQUAL(papszToken[1], "char")) { /*------------------------------------------------- * CHAR type *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFChar, atoi(papszToken[2])); } else if (numTok >= 2 && EQUAL(papszToken[1], "integer")) { if (numTok == 2) { /*------------------------------------------------- * INTEGER type without a specified width *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFInteger); } else if (numTok > 2) { /*------------------------------------------------- * INTEGER type with a specified width *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFInteger, atoi(papszToken[2])); } } else if (numTok >= 2 && EQUAL(papszToken[1], "smallint")) { if (numTok == 2) { /*------------------------------------------------- * SMALLINT type without a specified width *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFSmallInt); } else if (numTok > 2) { /*------------------------------------------------- * SMALLINT type with a specified width *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFSmallInt, atoi(papszToken[2])); } } else if (numTok >= 4 && EQUAL(papszToken[1], "decimal")) { /*------------------------------------------------- * DECIMAL type *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFDecimal, atoi(papszToken[2]), atoi(papszToken[3])); } else if (numTok >= 2 && EQUAL(papszToken[1], "float")) { /*------------------------------------------------- * FLOAT type *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFFloat); } else if (numTok >= 2 && EQUAL(papszToken[1], "date")) { /*------------------------------------------------- * DATE type (returned as a string: "DD/MM/YYYY" or "YYYYMMDD") *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFDate); } else if (numTok >= 2 && EQUAL(papszToken[1], "time")) { /*------------------------------------------------- * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm") *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFTime); } else if (numTok >= 2 && EQUAL(papszToken[1], "datetime")) { /*------------------------------------------------- * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS", * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm") *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFDateTime); } else if (numTok >= 2 && EQUAL(papszToken[1], "logical")) { /*------------------------------------------------- * LOGICAL type (value "T" or "F") *------------------------------------------------*/ nStatus = AddFieldNative(papszToken[0], TABFLogical); } else nStatus = -1; // Unrecognized field type or line corrupt CSLDestroy(papszToken); papszToken = NULL; if (nStatus != 0) { CPLError(CE_Failure, CPLE_FileIO, "Failed to parse field definition in file %s", m_pszFname); return -1; } return 0; } /************************************************************************/ /* GetFeatureCount() */ /************************************************************************/ int MIFFile::GetFeatureCount (int bForce) { if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) return OGRLayer::GetFeatureCount( bForce ); else { if (bForce == TRUE) PreParseFile(); if (m_bPreParsed) return m_nFeatureCount; else return -1; } } /************************************************************************/ /* ResetReading() */ /************************************************************************/ void MIFFile::ResetReading() { const char *pszLine; m_poMIFFile->Rewind(); while ((pszLine = m_poMIFFile->GetLine()) != NULL) if (EQUALN(pszLine,"DATA",4)) break; while ((pszLine = m_poMIFFile->GetLine()) != NULL) { if (m_poMIFFile->IsValidFeature(pszLine)) break; } if( m_poMIDFile != NULL ) { m_poMIDFile->Rewind(); m_poMIDFile->GetLine(); } // We're positioned on first feature. Feature Ids start at 1. if (m_poCurFeature) { delete m_poCurFeature; m_poCurFeature = NULL; } m_nCurFeatureId = 0; m_nPreloadedId = 1; } /************************************************************************/ /* PreParseFile() */ /************************************************************************/ void MIFFile::PreParseFile() { char **papszToken = NULL; const char *pszLine; GBool bPLine = FALSE; GBool bText = FALSE; if (m_bPreParsed == TRUE) return; m_poMIFFile->Rewind(); while ((pszLine = m_poMIFFile->GetLine()) != NULL) if (EQUALN(pszLine,"DATA",4)) break; m_nPoints = m_nLines = m_nRegions = m_nTexts = 0; while ((pszLine = m_poMIFFile->GetLine()) != NULL) { if (m_poMIFFile->IsValidFeature(pszLine)) { bPLine = FALSE; bText = FALSE; m_nFeatureCount++; } CSLDestroy(papszToken); papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS); if (EQUALN(pszLine,"POINT",5)) { m_nPoints++; if (CSLCount(papszToken) == 3) { UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[1])), m_poMIFFile->GetYTrans(atof(papszToken[2]))); } } else if (EQUALN(pszLine,"LINE",4) || EQUALN(pszLine,"RECT",4) || EQUALN(pszLine,"ROUNDRECT",9) || EQUALN(pszLine,"ARC",3) || EQUALN(pszLine,"ELLIPSE",7)) { if (CSLCount(papszToken) == 5) { m_nLines++; UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[1])), m_poMIFFile->GetYTrans(atof(papszToken[2]))); UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[3])), m_poMIFFile->GetYTrans(atof(papszToken[4]))); } } else if (EQUALN(pszLine,"REGION",6) ) { m_nRegions++; bPLine = TRUE; } else if( EQUALN(pszLine,"PLINE",5)) { m_nLines++; bPLine = TRUE; } else if (EQUALN(pszLine,"TEXT",4)) { m_nTexts++; bText = TRUE; } else if (bPLine == TRUE) { if (CSLCount(papszToken) == 2 && strchr("-.0123456789", papszToken[0][0]) != NULL) { UpdateExtents( m_poMIFFile->GetXTrans(atof(papszToken[0])), m_poMIFFile->GetYTrans(atof(papszToken[1]))); } } else if (bText == TRUE) { if (CSLCount(papszToken) == 4 && strchr("-.0123456789", papszToken[0][0]) != NULL) { UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[0])), m_poMIFFile->GetYTrans(atof(papszToken[1]))); UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[2])), m_poMIFFile->GetYTrans(atof(papszToken[3]))); } } } CSLDestroy(papszToken); m_poMIFFile->Rewind(); while ((pszLine = m_poMIFFile->GetLine()) != NULL) if (EQUALN(pszLine,"DATA",4)) break; while ((pszLine = m_poMIFFile->GetLine()) != NULL) { if (m_poMIFFile->IsValidFeature(pszLine)) break; } if( m_poMIDFile != NULL ) { m_poMIDFile->Rewind(); m_poMIDFile->GetLine(); } m_bPreParsed = TRUE; } /********************************************************************** * MIFFile::WriteMIFHeader() * * Generate the .MIF header. * * Returns 0 on success, -1 on error. **********************************************************************/ int MIFFile::WriteMIFHeader() { int iField; GBool bFound; if (m_eAccessMode != TABWrite) { CPLError(CE_Failure, CPLE_NotSupported, "WriteMIFHeader() can be used only with Write access."); return -1; } if (m_poDefn==NULL || m_poDefn->GetFieldCount() == 0) { CPLError(CE_Failure, CPLE_NotSupported, "File %s must contain at least 1 attribute field.", m_pszFname); return -1; } /*----------------------------------------------------------------- * Start writing header. *----------------------------------------------------------------*/ m_bHeaderWrote = TRUE; m_poMIFFile->WriteLine("Version %d\n", m_nVersion); m_poMIFFile->WriteLine("Charset \"%s\"\n", m_pszCharset); // Delimiter is not required if you use \t as delimiter if ( !EQUAL(m_pszDelimiter, "\t") ) m_poMIFFile->WriteLine("Delimiter \"%s\"\n", m_pszDelimiter); bFound = FALSE; for(iField=0; iField<m_poDefn->GetFieldCount(); iField++) { if (m_pabFieldUnique[iField]) { if (!bFound) m_poMIFFile->WriteLine("Unique %d", iField+1); else m_poMIFFile->WriteLine(",%d", iField+1); bFound = TRUE; } } if (bFound) m_poMIFFile->WriteLine("\n"); bFound = FALSE; for(iField=0; iField<m_poDefn->GetFieldCount(); iField++) { if (m_pabFieldIndexed[iField]) { if (!bFound) m_poMIFFile->WriteLine("Index %d", iField+1); else m_poMIFFile->WriteLine(",%d", iField+1); bFound = TRUE; } } if (bFound) m_poMIFFile->WriteLine("\n"); if (m_pszCoordSys && m_bBoundsSet) { m_poMIFFile->WriteLine("CoordSys %s " "Bounds (%.15g, %.15g) (%.15g, %.15g)\n", m_pszCoordSys, m_dXMin, m_dYMin, m_dXMax, m_dYMax); } else if (m_pszCoordSys) { m_poMIFFile->WriteLine("CoordSys %s\n",m_pszCoordSys); } /*----------------------------------------------------------------- * Column definitions *----------------------------------------------------------------*/ CPLAssert(m_paeFieldType); m_poMIFFile->WriteLine("Columns %d\n", m_poDefn->GetFieldCount()); for(iField=0; iField<m_poDefn->GetFieldCount(); iField++) { OGRFieldDefn *poFieldDefn; poFieldDefn = m_poDefn->GetFieldDefn(iField); switch(m_paeFieldType[iField]) { case TABFInteger: m_poMIFFile->WriteLine(" %s Integer\n", poFieldDefn->GetNameRef()); break; case TABFSmallInt: m_poMIFFile->WriteLine(" %s SmallInt\n", poFieldDefn->GetNameRef()); break; case TABFFloat: m_poMIFFile->WriteLine(" %s Float\n", poFieldDefn->GetNameRef()); break; case TABFDecimal: m_poMIFFile->WriteLine(" %s Decimal(%d,%d)\n", poFieldDefn->GetNameRef(), poFieldDefn->GetWidth(), poFieldDefn->GetPrecision()); break; case TABFLogical: m_poMIFFile->WriteLine(" %s Logical\n", poFieldDefn->GetNameRef()); break; case TABFDate: m_poMIFFile->WriteLine(" %s Date\n", poFieldDefn->GetNameRef()); break; case TABFTime: m_poMIFFile->WriteLine(" %s Time\n", poFieldDefn->GetNameRef()); break; case TABFDateTime: m_poMIFFile->WriteLine(" %s DateTime\n", poFieldDefn->GetNameRef()); break; case TABFChar: default: m_poMIFFile->WriteLine(" %s Char(%d)\n", poFieldDefn->GetNameRef(), poFieldDefn->GetWidth()); } } /*----------------------------------------------------------------- * Ready to write objects *----------------------------------------------------------------*/ m_poMIFFile->WriteLine("Data\n\n"); return 0; } /********************************************************************** * MIFFile::Close() * * Close current file, and release all memory used. * * Returns 0 on success, -1 on error. **********************************************************************/ int MIFFile::Close() { /* flush .mif header if not already written */ if ( m_poDefn != NULL && m_bHeaderWrote == FALSE && m_eAccessMode != TABRead ) { WriteMIFHeader(); } if (m_poMIDFile) { m_poMIDFile->Close(); delete m_poMIDFile; m_poMIDFile = NULL; } if (m_poMIFFile) { m_poMIFFile->Close(); delete m_poMIFFile; m_poMIFFile = NULL; } if (m_poCurFeature) { delete m_poCurFeature; m_poCurFeature = NULL; } /*----------------------------------------------------------------- * Note: we have to check the reference count before deleting * m_poSpatialRef and m_poDefn *----------------------------------------------------------------*/ if (m_poDefn && m_poDefn->Dereference() == 0) delete m_poDefn; m_poDefn = NULL; if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0) delete m_poSpatialRef; m_poSpatialRef = NULL; CPLFree(m_pszCoordSys); m_pszCoordSys = NULL; CPLFree(m_pszDelimiter); m_pszDelimiter = NULL; CPLFree(m_pszFname); m_pszFname = NULL; m_nVersion = 0; CPLFree(m_pszCharset); m_pszCharset = NULL; CPLFree(m_pabFieldIndexed); m_pabFieldIndexed = NULL; CPLFree(m_pabFieldUnique); m_pabFieldUnique = NULL; CPLFree( m_pszIndex ); m_pszIndex = NULL; CPLFree(m_paeFieldType); m_paeFieldType = NULL; m_nCurFeatureId = 0; m_nPreloadedId = 0; m_nFeatureCount =0; m_bBoundsSet = FALSE; return 0; } /********************************************************************** * MIFFile::GetNextFeatureId() * * Returns feature id that follows nPrevId, or -1 if it is the * last feature id. Pass nPrevId=-1 to fetch the first valid feature id. **********************************************************************/ int MIFFile::GetNextFeatureId(int nPrevId) { if (m_eAccessMode != TABRead) { CPLError(CE_Failure, CPLE_NotSupported, "GetNextFeatureId() can be used only with Read access."); return -1; } if (nPrevId <= 0 && m_poMIFFile->GetLastLine() != NULL) return 1; // Feature Ids start at 1 else if (nPrevId > 0 && m_poMIFFile->GetLastLine() != NULL) return nPrevId + 1; else return -1; } /********************************************************************** * MIFFile::GotoFeature() * * Private method to move MIF and MID pointers ready to read specified * feature. Note that Feature Ids start at 1. * * Returns 0 on success, -1 on error (likely request for invalid feature id) **********************************************************************/ int MIFFile::GotoFeature(int nFeatureId) { if (nFeatureId < 1) return -1; if (nFeatureId == m_nPreloadedId) // CorrectPosition { return 0; } else { if (nFeatureId < m_nPreloadedId || m_nCurFeatureId == 0) ResetReading(); while(m_nPreloadedId < nFeatureId) { if (NextFeature() == FALSE) return -1; } CPLAssert(m_nPreloadedId == nFeatureId); return 0; } } /********************************************************************** * MIFFile::NextFeature() **********************************************************************/ GBool MIFFile::NextFeature() { const char *pszLine; while ((pszLine = m_poMIFFile->GetLine()) != NULL) { if (m_poMIFFile->IsValidFeature(pszLine)) { if( m_poMIDFile != NULL ) m_poMIDFile->GetLine(); m_nPreloadedId++; return TRUE; } } return FALSE; } /********************************************************************** * MIFFile::GetFeatureRef() * * Fill and return a TABFeature object for the specified feature id. * * The retruned pointer is a reference to an object owned and maintained * by this MIFFile object. It should not be altered or freed by the * caller and its contents is guaranteed to be valid only until the next * call to GetFeatureRef() or Close(). * * Returns NULL if the specified feature id does not exist of if an * error happened. In any case, CPLError() will have been called to * report the reason of the failure. **********************************************************************/ TABFeature *MIFFile::GetFeatureRef(int nFeatureId) { const char *pszLine; if (m_eAccessMode != TABRead) { CPLError(CE_Failure, CPLE_NotSupported, "GetFeatureRef() can be used only with Read access."); return NULL; } /*----------------------------------------------------------------- * Make sure file is opened and Validate feature id by positioning * the read pointers for the .MAP and .DAT files to this feature id. *----------------------------------------------------------------*/ if (m_poMIFFile == NULL) { CPLError(CE_Failure, CPLE_IllegalArg, "GetFeatureRef() failed: file is not opened!"); return NULL; } if (GotoFeature(nFeatureId)!= 0 ) { CPLError(CE_Failure, CPLE_IllegalArg, "GetFeatureRef() failed: invalid feature id %d", nFeatureId); return NULL; } /*----------------------------------------------------------------- * Create new feature object of the right type *----------------------------------------------------------------*/ if ((pszLine = m_poMIFFile->GetLastLine()) != NULL) { // Delete previous feature... we'll start we a clean one. if (m_poCurFeature) delete m_poCurFeature; m_poCurFeature = NULL; m_nCurFeatureId = m_nPreloadedId; if (EQUALN(pszLine,"NONE",4)) { m_poCurFeature = new TABFeature(m_poDefn); } else if (EQUALN(pszLine,"POINT",5)) { // Special case, we need to know two lines to decide the type char **papszToken; papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS); if (CSLCount(papszToken) !=3) { CSLDestroy(papszToken); CPLError(CE_Failure, CPLE_NotSupported, "GetFeatureRef() failed: invalid point line: '%s'", pszLine); return NULL; } m_poMIFFile->SaveLine(pszLine); if ((pszLine = m_poMIFFile->GetLine()) != NULL) { CSLDestroy(papszToken); papszToken = CSLTokenizeStringComplex(pszLine," ,()\t", TRUE,FALSE); if (CSLCount(papszToken)> 0 &&EQUALN(papszToken[0],"SYMBOL",6)) { switch (CSLCount(papszToken)) { case 4: m_poCurFeature = new TABPoint(m_poDefn); break; case 7: m_poCurFeature = new TABFontPoint(m_poDefn); break; case 5: m_poCurFeature = new TABCustomPoint(m_poDefn); break; default: CSLDestroy(papszToken); CPLError(CE_Failure, CPLE_NotSupported, "GetFeatureRef() failed: invalid symbol " "line: '%s'", pszLine); return NULL; break; } } } CSLDestroy(papszToken); if (m_poCurFeature == NULL) { // No symbol clause... default to TABPoint m_poCurFeature = new TABPoint(m_poDefn); } } else if (EQUALN(pszLine,"LINE",4) || EQUALN(pszLine,"PLINE",5)) { m_poCurFeature = new TABPolyline(m_poDefn); } else if (EQUALN(pszLine,"REGION",6)) { m_poCurFeature = new TABRegion(m_poDefn); } else if (EQUALN(pszLine,"ARC",3)) { m_poCurFeature = new TABArc(m_poDefn); } else if (EQUALN(pszLine,"TEXT",4)) { m_poCurFeature = new TABText(m_poDefn); } else if (EQUALN(pszLine,"RECT",4) || EQUALN(pszLine,"ROUNDRECT",9)) { m_poCurFeature = new TABRectangle(m_poDefn); } else if (EQUALN(pszLine,"ELLIPSE",7)) { m_poCurFeature = new TABEllipse(m_poDefn); } else if (EQUALN(pszLine,"MULTIPOINT",10)) { m_poCurFeature = new TABMultiPoint(m_poDefn); } else if (EQUALN(pszLine,"COLLECTION",10)) { m_poCurFeature = new TABCollection(m_poDefn); } else { if (!EQUAL(pszLine,"")) CPLError(CE_Failure, CPLE_NotSupported, "Error during reading, unknown type %s.", pszLine); //m_poCurFeature = new TABDebugFeature(m_poDefn); return NULL; } } CPLAssert(m_poCurFeature); if (m_poCurFeature == NULL) return NULL; /*----------------------------------------------------------------- * Read fields from the .DAT file * GetRecordBlock() has already been called above... *----------------------------------------------------------------*/ if (m_poMIDFile != NULL && m_poCurFeature->ReadRecordFromMIDFile(m_poMIDFile) != 0) { CPLError(CE_Failure, CPLE_NotSupported, "Error during reading Record."); delete m_poCurFeature; m_poCurFeature = NULL; return NULL; } /*----------------------------------------------------------------- * Read geometry from the .MAP file * MoveToObjId() has already been called above... *----------------------------------------------------------------*/ if (m_poCurFeature->ReadGeometryFromMIFFile(m_poMIFFile) != 0) { CPLError(CE_Failure, CPLE_NotSupported, "Error during reading Geometry."); delete m_poCurFeature; m_poCurFeature = NULL; return NULL; } /* If the feature geometry is Text, and the value is empty(""), transform it to a geometry none */ if (m_poCurFeature->GetFeatureClass() == TABFCText) { TABFeature *poTmpFeature; TABText *poTextFeature = (TABText*)m_poCurFeature; if (strlen(poTextFeature->GetTextString()) == 0) { poTmpFeature = new TABFeature(m_poDefn); for( int i = 0; i < m_poDefn->GetFieldCount(); i++ ) { poTmpFeature->SetField( i, m_poCurFeature->GetRawFieldRef( i ) ); } delete m_poCurFeature; m_poCurFeature = poTmpFeature; } } /*--------------------------------------------------------------------- * The act of reading the geometry causes the first line of the * next object to be preloaded. Set the preloaded id appropriately. *--------------------------------------------------------------------- */ if( m_poMIFFile->GetLastLine() != NULL ) m_nPreloadedId++; else m_nPreloadedId = 0; /* Update the Current Feature ID */ m_poCurFeature->SetFID(m_nCurFeatureId); return m_poCurFeature; } /********************************************************************** * MIFFile::CreateFeature() * * Write a new feature to this dataset. The passed in feature is updated * with the new feature id. * * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an * error happened in which case, CPLError() will have been called to * report the reason of the failure. **********************************************************************/ OGRErr MIFFile::CreateFeature(TABFeature *poFeature) { int nFeatureId = -1; if (m_eAccessMode != TABWrite) { CPLError(CE_Failure, CPLE_NotSupported, "CreateFeature() can be used only with Write access."); return OGRERR_UNSUPPORTED_OPERATION; } /*----------------------------------------------------------------- * Make sure file is opened and establish new feature id. *----------------------------------------------------------------*/ if (m_poMIDFile == NULL) { CPLError(CE_Failure, CPLE_IllegalArg, "CreateFeature() failed: file is not opened!"); return OGRERR_FAILURE; } if (m_bHeaderWrote == FALSE) { /*------------------------------------------------------------- * OK, this is the first feature in the dataset... make sure the * .MID schema has been initialized. *------------------------------------------------------------*/ if (m_poDefn == NULL) SetFeatureDefn(poFeature->GetDefnRef(), NULL); WriteMIFHeader(); nFeatureId = 1; } else { nFeatureId = ++ m_nWriteFeatureId; } /*----------------------------------------------------------------- * Write geometry to the .Mif file *----------------------------------------------------------------*/ if (m_poMIFFile == NULL || poFeature->WriteGeometryToMIFFile(m_poMIFFile) != 0) { CPLError(CE_Failure, CPLE_FileIO, "Failed writing geometry for feature id %d in %s", nFeatureId, m_pszFname); return OGRERR_FAILURE; } if (m_poMIDFile == NULL || poFeature->WriteRecordToMIDFile(m_poMIDFile) != 0 ) { CPLError(CE_Failure, CPLE_FileIO, "Failed writing attributes for feature id %d in %s", nFeatureId, m_pszFname); return OGRERR_FAILURE; } poFeature->SetFID(nFeatureId); return OGRERR_NONE; } /********************************************************************** * MIFFile::GetLayerDefn() * * Returns a reference to the OGRFeatureDefn that will be used to create * features in this dataset. * * Returns a reference to an object that is maintained by this MIFFile * object (and thus should not be modified or freed by the caller) or * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file * opened yet) **********************************************************************/ OGRFeatureDefn *MIFFile::GetLayerDefn() { return m_poDefn; } /********************************************************************** * MIFFile::SetFeatureDefn() * * Pass a reference to the OGRFeatureDefn that will be used to create * features in this dataset. This function should be called after * creating a new dataset, but before writing the first feature. * All features that will be written to this dataset must share this same * OGRFeatureDefn. * * This function will use poFeatureDefn to create a local copy that * will be used to build the .MID file, etc. * * Returns 0 on success, -1 on error. **********************************************************************/ int MIFFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */) { int numFields; int nStatus = 0; /*----------------------------------------------------------------- * Check that call happens at the right time in dataset's life. *----------------------------------------------------------------*/ if ( m_eAccessMode == TABWrite && m_bHeaderWrote ) { CPLError(CE_Failure, CPLE_AssertionFailed, "SetFeatureDefn() must be called after opening a new " "dataset, but before writing the first feature to it."); return -1; } /*----------------------------------------------------------------- * Delete current feature defn if there is already one. * AddFieldNative() will take care of creating a new one for us. *----------------------------------------------------------------*/ if (m_poDefn && m_poDefn->Dereference() == 0) delete m_poDefn; m_poDefn = NULL; /*----------------------------------------------------------------- * Copy field information *----------------------------------------------------------------*/ numFields = poFeatureDefn->GetFieldCount(); for(int iField=0; iField<numFields; iField++) { TABFieldType eMapInfoType; OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField); if (paeMapInfoNativeFieldTypes) { eMapInfoType = paeMapInfoNativeFieldTypes[iField]; } else { /*--------------------------------------------------------- * Map OGRFieldTypes to MapInfo native types *--------------------------------------------------------*/ switch(poFieldDefn->GetType()) { case OFTInteger: eMapInfoType = TABFInteger; break; case OFTReal: eMapInfoType = TABFFloat; break; case OFTDateTime: eMapInfoType = TABFDateTime; break; case OFTDate: eMapInfoType = TABFDate; break; case OFTTime: eMapInfoType = TABFTime; break; case OFTString: default: eMapInfoType = TABFChar; } } nStatus = AddFieldNative(poFieldDefn->GetNameRef(), eMapInfoType, poFieldDefn->GetWidth(), poFieldDefn->GetPrecision(), FALSE, FALSE); } return nStatus; } /********************************************************************** * MIFFile::AddFieldNative() * * Create a new field using a native mapinfo data type... this is an * alternative to defining fields through the OGR interface. * This function should be called after creating a new dataset, but before * writing the first feature. * * This function will build/update the OGRFeatureDefn that will have to be * used when writing features to this dataset. * * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn(). * * Returns 0 on success, -1 on error. **********************************************************************/ int MIFFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType, int nWidth /*=0*/, int nPrecision /*=0*/, GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK ) { OGRFieldDefn *poFieldDefn; char *pszCleanName = NULL; int nStatus = 0; char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/ int nRenameNum = 1; /*----------------------------------------------------------------- * Check that call happens at the right time in dataset's life. *----------------------------------------------------------------*/ if ( m_eAccessMode == TABWrite && m_bHeaderWrote ) { CPLError(CE_Failure, CPLE_AssertionFailed, "AddFieldNative() must be called after opening a new " "dataset, but before writing the first feature to it."); return -1; } /*----------------------------------------------------------------- * Validate field width... must be <= 254 *----------------------------------------------------------------*/ if (nWidth > 254) { CPLError(CE_Warning, CPLE_IllegalArg, "Invalid size (%d) for field '%s'. " "Size must be 254 or less.", nWidth, pszName); nWidth = 254; } /*----------------------------------------------------------------- * Map fields with width=0 (variable length in OGR) to a valid default *----------------------------------------------------------------*/ if (eMapInfoType == TABFDecimal && nWidth == 0) nWidth=20; else if (eMapInfoType == TABFChar && nWidth == 0) nWidth=254; /* char fields */ /*----------------------------------------------------------------- * Create new OGRFeatureDefn if not done yet... *----------------------------------------------------------------*/ if (m_poDefn == NULL) { char *pszFeatureClassName = TABGetBasename(m_pszFname); m_poDefn = new OGRFeatureDefn(pszFeatureClassName); CPLFree(pszFeatureClassName); // Ref count defaults to 0... set it to 1 m_poDefn->Reference(); } /*----------------------------------------------------------------- * Make sure field name is valid... check for special chars, etc. * (pszCleanName will have to be freed.) *----------------------------------------------------------------*/ pszCleanName = TABCleanFieldName(pszName); if( !bApproxOK && ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 || !EQUAL(pszName, pszCleanName) ) ) { CPLError( CE_Failure, CPLE_NotSupported, "Failed to add field named '%s'", pszName ); } strncpy(szNewFieldName, pszCleanName, 31); szNewFieldName[31] = '\0'; while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10) sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ ); while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100) sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ ); if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0) { CPLError( CE_Failure, CPLE_NotSupported, "Too many field names like '%s' when truncated to 31 letters " "for MapInfo format.", pszCleanName ); } if( !EQUAL(pszCleanName,szNewFieldName) ) { CPLError( CE_Warning, CPLE_NotSupported, "Normalized/laundered field name: '%s' to '%s'", pszCleanName, szNewFieldName ); } /*----------------------------------------------------------------- * Map MapInfo native types to OGR types *----------------------------------------------------------------*/ poFieldDefn = NULL; switch(eMapInfoType) { case TABFChar: /*------------------------------------------------- * CHAR type *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString); poFieldDefn->SetWidth(nWidth); break; case TABFInteger: /*------------------------------------------------- * INTEGER type *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger); poFieldDefn->SetWidth(nWidth); break; case TABFSmallInt: /*------------------------------------------------- * SMALLINT type *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger); poFieldDefn->SetWidth(nWidth); break; case TABFDecimal: /*------------------------------------------------- * DECIMAL type *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal); poFieldDefn->SetWidth(nWidth); poFieldDefn->SetPrecision(nPrecision); break; case TABFFloat: /*------------------------------------------------- * FLOAT type *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal); break; case TABFDate: /*------------------------------------------------- * DATE type (V450, returned as a string: "DD/MM/YYYY" or "YYYYMMDD") *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, #ifdef MITAB_USE_OFTDATETIME OFTDate); #else OFTString); #endif poFieldDefn->SetWidth(10); m_nVersion = MAX(m_nVersion, 450); break; case TABFTime: /*------------------------------------------------- * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm") *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, #ifdef MITAB_USE_OFTDATETIME OFTTime); #else OFTString); #endif poFieldDefn->SetWidth(9); m_nVersion = MAX(m_nVersion, 900); break; case TABFDateTime: /*------------------------------------------------- * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS", * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm") *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, #ifdef MITAB_USE_OFTDATETIME OFTDateTime); #else OFTString); #endif poFieldDefn->SetWidth(19); m_nVersion = MAX(m_nVersion, 900); break; case TABFLogical: /*------------------------------------------------- * LOGICAL type (value "T" or "F") *------------------------------------------------*/ poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString); poFieldDefn->SetWidth(1); break; default: CPLError(CE_Failure, CPLE_NotSupported, "Unsupported type for field %s", pszName); return -1; } /*----------------------------------------------------- * Add the FieldDefn to the FeatureDefn *----------------------------------------------------*/ m_poDefn->AddFieldDefn(poFieldDefn); delete poFieldDefn; /*----------------------------------------------------------------- * Keep track of native field type *----------------------------------------------------------------*/ m_paeFieldType = (TABFieldType *)CPLRealloc(m_paeFieldType, m_poDefn->GetFieldCount()* sizeof(TABFieldType)); m_paeFieldType[m_poDefn->GetFieldCount()-1] = eMapInfoType; /*----------------------------------------------------------------- * Extend array of Indexed/Unique flags *----------------------------------------------------------------*/ m_pabFieldIndexed = (GBool *)CPLRealloc(m_pabFieldIndexed, m_poDefn->GetFieldCount()* sizeof(GBool)); m_pabFieldUnique = (GBool *)CPLRealloc(m_pabFieldUnique, m_poDefn->GetFieldCount()* sizeof(GBool)); m_pabFieldIndexed[m_poDefn->GetFieldCount()-1] = bIndexed; m_pabFieldUnique[m_poDefn->GetFieldCount()-1] = bUnique; CPLFree(pszCleanName); return nStatus; } /********************************************************************** * MIFFile::GetNativeFieldType() * * Returns the native MapInfo field type for the specified field. * * Returns TABFUnknown if file is not opened, or if specified field index is * invalid. **********************************************************************/ TABFieldType MIFFile::GetNativeFieldType(int nFieldId) { if ( m_poDefn==NULL || m_paeFieldType==NULL || nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) return TABFUnknown; return m_paeFieldType[nFieldId]; } /************************************************************************ * MIFFile::SetFieldIndexed() ************************************************************************/ int MIFFile::SetFieldIndexed( int nFieldId ) { if ( m_poDefn==NULL || m_pabFieldIndexed==NULL || nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) return -1; m_pabFieldIndexed[nFieldId] = TRUE; return 0; } /************************************************************************ * MIFFile::IsFieldIndexed() ************************************************************************/ GBool MIFFile::IsFieldIndexed( int nFieldId ) { if ( m_poDefn==NULL || m_pabFieldIndexed==NULL || nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) return FALSE; return m_pabFieldIndexed[nFieldId]; } /************************************************************************ * MIFFile::IsFieldUnique() ************************************************************************/ GBool MIFFile::IsFieldUnique( int nFieldId ) { if ( m_poDefn==NULL || m_pabFieldUnique==NULL || nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) return FALSE; return m_pabFieldUnique[nFieldId]; } /************************************************************************/ /* MIFFile::SetSpatialRef() */ /************************************************************************/ int MIFFile::SetSpatialRef( OGRSpatialReference * poSpatialRef ) { CPLFree( m_pszCoordSys ); m_pszCoordSys = MITABSpatialRef2CoordSys( poSpatialRef ); return( m_pszCoordSys != NULL ); } /************************************************************************/ /* MIFFile::SetMIFCoordSys() */ /************************************************************************/ int MIFFile::SetMIFCoordSys(const char * pszMIFCoordSys) { char **papszFields, *pszCoordSys; int iBounds; // Extract the word 'COORDSYS' if present if (EQUALN(pszMIFCoordSys,"COORDSYS",8) ) { pszCoordSys = CPLStrdup(pszMIFCoordSys + 9); } else { pszCoordSys = CPLStrdup(pszMIFCoordSys); } // Extract bounds if present papszFields = CSLTokenizeStringComplex(pszCoordSys, " ,()\t", TRUE, FALSE ); iBounds = CSLFindString( papszFields, "Bounds" ); if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields)) { m_dXMin = atof(papszFields[++iBounds]); m_dYMin = atof(papszFields[++iBounds]); m_dXMax = atof(papszFields[++iBounds]); m_dYMax = atof(papszFields[++iBounds]); m_bBoundsSet = TRUE; pszCoordSys[strstr(pszCoordSys, "Bounds") - pszCoordSys] = '\0'; } CSLDestroy( papszFields ); // Assign the CoordSys CPLFree( m_pszCoordSys ); m_pszCoordSys = CPLStrdup(pszCoordSys); CPLFree(pszCoordSys); return( m_pszCoordSys != NULL ); } /************************************************************************/ /* MIFFile::GetSpatialRef() */ /************************************************************************/ OGRSpatialReference *MIFFile::GetSpatialRef() { if( m_poSpatialRef == NULL ) m_poSpatialRef = MITABCoordSys2SpatialRef( m_pszCoordSys ); return m_poSpatialRef; } /********************************************************************** * MIFFile::UpdateExtents() * * Private Methode used to update the dataset extents **********************************************************************/ void MIFFile::UpdateExtents(double dfX, double dfY) { if (m_bExtentsSet == FALSE) { m_bExtentsSet = TRUE; m_sExtents.MinX = m_sExtents.MaxX = dfX; m_sExtents.MinY = m_sExtents.MaxY = dfY; } else { if (dfX < m_sExtents.MinX) m_sExtents.MinX = dfX; if (dfX > m_sExtents.MaxX) m_sExtents.MaxX = dfX; if (dfY < m_sExtents.MinY) m_sExtents.MinY = dfY; if (dfY > m_sExtents.MaxY) m_sExtents.MaxY = dfY; } } /********************************************************************** * MIFFile::SetBounds() * * 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 MIFFile::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax) { if (m_eAccessMode != TABWrite) { CPLError(CE_Failure, CPLE_NotSupported, "SetBounds() can be used only with Write access."); return -1; } m_dXMin = dXMin; m_dXMax = dXMax; m_dYMin = dYMin; m_dYMax = dYMax; m_bBoundsSet = TRUE; return 0; } /********************************************************************** * MIFFile::GetFeatureCountByType() * * Return number of features of each type. * * NOTE: The current implementation always returns -1 for MIF files * since this would require scanning the whole file. * * When properly implemented, the bForce flag will force scanning the * whole file by default. * * Returns 0 on success, or silently returns -1 (with no error) if this * information is not available. **********************************************************************/ int MIFFile::GetFeatureCountByType(int &numPoints, int &numLines, int &numRegions, int &numTexts, GBool bForce ) { if( m_bPreParsed || bForce ) { PreParseFile(); numPoints = m_nPoints; numLines = m_nLines; numRegions = m_nRegions; numTexts = m_nTexts; return 0; } else { numPoints = numLines = numRegions = numTexts = 0; return -1; } } /********************************************************************** * MIFFile::GetBounds() * * Fetch projection coordinates bounds of a dataset. * * Pass bForce=FALSE to avoid a scan of the whole file if the bounds * are not already available. * * Returns 0 on success, -1 on error or if bounds are not available and * bForce=FALSE. **********************************************************************/ int MIFFile::GetBounds(double &dXMin, double &dYMin, double &dXMax, double &dYMax, GBool bForce /*= TRUE*/ ) { if (m_bBoundsSet == FALSE && bForce == FALSE) { return -1; } else if (m_bBoundsSet == FALSE) { PreParseFile(); } if (m_bBoundsSet == FALSE) { return -1; } dXMin = m_dXMin; dXMax = m_dXMax; dYMin = m_dYMin; dYMax = m_dYMax; return 0; } /********************************************************************** * MIFFile::GetExtent() * * Fetch extent of the data currently stored in the dataset. We collect * this information while preparsing the file ... often already done for * other reasons, and if not it is still faster than fully reading all * the features just to count them. * * Returns OGRERR_NONE/OGRRERR_FAILURE. **********************************************************************/ OGRErr MIFFile::GetExtent (OGREnvelope *psExtent, int bForce) { if (bForce == TRUE) PreParseFile(); if (m_bPreParsed) { *psExtent = m_sExtents; return OGRERR_NONE; } else return OGRERR_FAILURE; } /************************************************************************/ /* TestCapability() */ /************************************************************************/ int MIFFile::TestCapability( const char * pszCap ) { if( EQUAL(pszCap,OLCRandomRead) ) return TRUE; else if( EQUAL(pszCap,OLCSequentialWrite) ) return TRUE; else if( EQUAL(pszCap,OLCFastFeatureCount) ) return m_bPreParsed; else if( EQUAL(pszCap,OLCFastSpatialFilter) ) return FALSE; else if( EQUAL(pszCap,OLCFastGetExtent) ) return m_bPreParsed; else if( EQUAL(pszCap,OLCCreateField) ) return TRUE; else return FALSE; }