1 : /**********************************************************************
2 : * $Id: mitab_tabfile.cpp,v 1.78 2010-10-08 18:40:12 aboudreault Exp $
3 : *
4 : * Name: mitab_tabfile.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABFile class, the main class of the lib.
8 : * To be used by external programs to handle reading/writing of
9 : * features from/to TAB datasets.
10 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
11 : *
12 : **********************************************************************
13 : * Copyright (c) 1999-2003, Daniel Morissette
14 : *
15 : * Permission is hereby granted, free of charge, to any person obtaining a
16 : * copy of this software and associated documentation files (the "Software"),
17 : * to deal in the Software without restriction, including without limitation
18 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 : * and/or sell copies of the Software, and to permit persons to whom the
20 : * Software is furnished to do so, subject to the following conditions:
21 : *
22 : * The above copyright notice and this permission notice shall be included
23 : * in all copies or substantial portions of the Software.
24 : *
25 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31 : * DEALINGS IN THE SOFTWARE.
32 : **********************************************************************
33 : *
34 : * $Log: mitab_tabfile.cpp,v $
35 : * Revision 1.78 2010-10-08 18:40:12 aboudreault
36 : * Fixed missing initializations that cause crashes
37 : *
38 : * Revision 1.77 2010-10-08 18:38:13 aboudreault
39 : * Added attribute index support for the sql queries in mapinfo tab format (GDAL bug #3687)
40 : *
41 : * Revision 1.76 2010-07-07 19:00:15 aboudreault
42 : * Cleanup Win32 Compile Warnings (GDAL bug #2930)
43 : *
44 : * Revision 1.75 2010-07-05 14:58:33 aboudreault
45 : * Fixed bad feature count after we deleted a feature in MapInfo (bug 2227)
46 : *
47 : * Revision 1.74 2010-01-07 20:39:12 aboudreault
48 : * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141)
49 : *
50 : * Revision 1.73 2008-11-27 20:50:23 aboudreault
51 : * Improved support for OGR date/time types. New Read/Write methods (bug 1948)
52 : * Added support of OGR date/time types for MIF features.
53 : *
54 : * Revision 1.72 2008/11/17 22:06:21 aboudreault
55 : * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with
56 : * OGR and fixed reading/writing support for these types.
57 : *
58 : * Revision 1.71 2008/09/26 14:40:24 aboudreault
59 : * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948)
60 : *
61 : * Revision 1.70 2008/06/13 18:39:21 aboudreault
62 : * Fixed problem with corrupt pointer if file not found (bug 1899) and
63 : * fixed tabdump build problem if DEBUG option not provided (bug 1898)
64 : *
65 : * Revision 1.69 2008/03/05 20:59:10 dmorissette
66 : * Purged CVS logs in header
67 : *
68 : * Revision 1.68 2008/03/05 20:35:39 dmorissette
69 : * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859)
70 : *
71 : * Revision 1.67 2008/01/29 21:56:39 dmorissette
72 : * Update dataset version properly for Date/Time/DateTime field types (#1754)
73 : *
74 : * Revision 1.66 2008/01/29 20:46:32 dmorissette
75 : * Added support for v9 Time and DateTime fields (byg 1754)
76 : *
77 : * Revision 1.65 2007/09/12 20:22:31 dmorissette
78 : * Added TABFeature::CreateFromMapInfoType()
79 : *
80 : * Revision 1.64 2007/06/21 14:00:23 dmorissette
81 : * Added missing cast in isspace() calls to avoid failed assertion on Windows
82 : * (MITAB bug 1737, GDAL ticket 1678))
83 : *
84 : * Revision 1.63 2007/06/12 13:52:38 dmorissette
85 : * Added IMapInfoFile::SetCharset() method (bug 1734)
86 : *
87 : * Revision 1.62 2007/06/12 12:50:40 dmorissette
88 : * Use Quick Spatial Index by default until bug 1732 is fixed (broken files
89 : * produced by current coord block splitting technique).
90 : *
91 : * Revision 1.61 2007/03/21 21:15:56 dmorissette
92 : * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial
93 : * index but results in faster write time (bug 1669)
94 : *
95 : * ...
96 : *
97 : * Revision 1.1 1999/07/12 04:18:25 daniel
98 : * Initial checkin
99 : *
100 : **********************************************************************/
101 :
102 : #include "mitab.h"
103 : #include "mitab_utils.h"
104 : #include "cpl_minixml.h"
105 :
106 : #include <ctype.h> /* isspace() */
107 :
108 : /*=====================================================================
109 : * class TABFile
110 : *====================================================================*/
111 :
112 :
113 : /**********************************************************************
114 : * TABFile::TABFile()
115 : *
116 : * Constructor.
117 : **********************************************************************/
118 4 : TABFile::TABFile()
119 : {
120 4 : m_eAccessMode = TABRead;
121 4 : m_pszFname = NULL;
122 4 : m_papszTABFile = NULL;
123 4 : m_nVersion = 300;
124 4 : m_eTableType = TABTableNative;
125 :
126 4 : m_poMAPFile = NULL;
127 4 : m_poDATFile = NULL;
128 4 : m_poINDFile = NULL;
129 4 : m_poDefn = NULL;
130 4 : m_poSpatialRef = NULL;
131 4 : m_poCurFeature = NULL;
132 4 : m_nCurFeatureId = 0;
133 4 : m_nLastFeatureId = 0;
134 4 : m_panIndexNo = NULL;
135 :
136 4 : bUseSpatialTraversal = FALSE;
137 :
138 4 : m_panMatchingFIDs = NULL;
139 4 : m_iMatchingFID = 0;
140 4 : }
141 :
142 : /**********************************************************************
143 : * TABFile::~TABFile()
144 : *
145 : * Destructor.
146 : **********************************************************************/
147 4 : TABFile::~TABFile()
148 : {
149 4 : Close();
150 4 : }
151 :
152 :
153 : /************************************************************************/
154 : /* GetFeatureCount() */
155 : /************************************************************************/
156 0 : int TABFile::GetFeatureCount (int bForce)
157 : {
158 :
159 0 : if( m_poFilterGeom != NULL || m_poAttrQuery != NULL || bForce)
160 0 : return OGRLayer::GetFeatureCount( bForce );
161 : else
162 0 : return m_nLastFeatureId;
163 : }
164 :
165 : /************************************************************************/
166 : /* ResetReading() */
167 : /************************************************************************/
168 10 : void TABFile::ResetReading()
169 : {
170 10 : CPLFree(m_panMatchingFIDs);
171 10 : m_panMatchingFIDs = NULL;
172 10 : m_iMatchingFID = 0;
173 :
174 10 : m_nCurFeatureId = 0;
175 10 : if( m_poMAPFile != NULL )
176 10 : m_poMAPFile->ResetReading();
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Decide whether to operate in spatial traversal mode or not, */
180 : /* and ensure the current spatial filter is applied to the map */
181 : /* file object. */
182 : /* -------------------------------------------------------------------- */
183 10 : if( m_poMAPFile )
184 : {
185 10 : bUseSpatialTraversal = FALSE;
186 :
187 10 : m_poMAPFile->ResetCoordFilter();
188 :
189 10 : if( m_poFilterGeom != NULL )
190 : {
191 1 : OGREnvelope sEnvelope;
192 : TABVertex sMin, sMax;
193 : TABMAPHeaderBlock *poHeader;
194 :
195 1 : poHeader = m_poMAPFile->GetHeaderBlock();
196 :
197 1 : m_poFilterGeom->getEnvelope( &sEnvelope );
198 1 : m_poMAPFile->GetCoordFilter( sMin, sMax );
199 :
200 1 : if( sEnvelope.MinX > sMin.x
201 : || sEnvelope.MinY > sMin.y
202 : || sEnvelope.MaxX < sMax.x
203 : || sEnvelope.MaxY < sMax.y )
204 : {
205 1 : bUseSpatialTraversal = TRUE;
206 1 : sMin.x = sEnvelope.MinX;
207 1 : sMin.y = sEnvelope.MinY;
208 1 : sMax.x = sEnvelope.MaxX;
209 1 : sMax.y = sEnvelope.MaxY;
210 1 : m_poMAPFile->SetCoordFilter( sMin, sMax );
211 : }
212 : }
213 : }
214 10 : }
215 :
216 : /**********************************************************************
217 : * TABFile::Open()
218 : *
219 : * Open a .TAB dataset and the associated files, and initialize the
220 : * structures to be ready to read features from (or write to) it.
221 : *
222 : * Supported access modes are "r" (read-only) and "w" (create new dataset).
223 : *
224 : * Set bTestOpenNoError=TRUE to silently return -1 with no error message
225 : * if the file cannot be opened. This is intended to be used in the
226 : * context of a TestOpen() function. The default value is FALSE which
227 : * means that an error is reported if the file cannot be opened.
228 : *
229 : * Note that dataset extents will have to be set using SetBounds() before
230 : * any feature can be written to a newly created dataset.
231 : *
232 : * In read mode, a valid dataset must have at least a .TAB and a .DAT file.
233 : * The .MAP and .ID files are optional and if they do not exist then
234 : * all features will be returned with NONE geometry.
235 : *
236 : * Returns 0 on success, -1 on error.
237 : **********************************************************************/
238 4 : int TABFile::Open(const char *pszFname, const char *pszAccess,
239 : GBool bTestOpenNoError /*=FALSE*/ )
240 : {
241 4 : char *pszTmpFname = NULL;
242 4 : int nFnameLen = 0;
243 :
244 4 : CPLErrorReset();
245 :
246 4 : if (m_poMAPFile)
247 : {
248 : CPLError(CE_Failure, CPLE_AssertionFailed,
249 0 : "Open() failed: object already contains an open file");
250 :
251 0 : return -1;
252 : }
253 :
254 : /*-----------------------------------------------------------------
255 : * Validate access mode
256 : *----------------------------------------------------------------*/
257 4 : if (EQUALN(pszAccess, "r", 1))
258 : {
259 2 : m_eAccessMode = TABRead;
260 2 : pszAccess = "rb";
261 : }
262 2 : else if (EQUALN(pszAccess, "w", 1))
263 : {
264 2 : m_eAccessMode = TABWrite;
265 2 : pszAccess = "wb";
266 : }
267 : else
268 : {
269 0 : if (!bTestOpenNoError)
270 : CPLError(CE_Failure, CPLE_FileIO,
271 0 : "Open() failed: access mode \"%s\" not supported", pszAccess);
272 : else
273 0 : CPLErrorReset();
274 :
275 0 : return -1;
276 : }
277 :
278 : /*-----------------------------------------------------------------
279 : * Make sure filename has a .TAB extension...
280 : *----------------------------------------------------------------*/
281 4 : m_pszFname = CPLStrdup(pszFname);
282 4 : nFnameLen = strlen(m_pszFname);
283 :
284 4 : if (nFnameLen > 4 && (strcmp(m_pszFname+nFnameLen-4, ".TAB")==0 ||
285 : strcmp(m_pszFname+nFnameLen-4, ".MAP")==0 ||
286 : strcmp(m_pszFname+nFnameLen-4, ".DAT")==0 ) )
287 0 : strcpy(m_pszFname+nFnameLen-4, ".TAB");
288 8 : else if (nFnameLen > 4 && (EQUAL(m_pszFname+nFnameLen-4, ".tab") ||
289 : EQUAL(m_pszFname+nFnameLen-4, ".map") ||
290 : EQUAL(m_pszFname+nFnameLen-4, ".dat") ) )
291 4 : strcpy(m_pszFname+nFnameLen-4, ".tab");
292 : else
293 : {
294 0 : if (!bTestOpenNoError)
295 : CPLError(CE_Failure, CPLE_FileIO,
296 : "Open() failed for %s: invalid filename extension",
297 0 : m_pszFname);
298 : else
299 0 : CPLErrorReset();
300 :
301 0 : CPLFree(m_pszFname);
302 0 : m_pszFname = NULL;
303 0 : return -1;
304 : }
305 :
306 4 : pszTmpFname = CPLStrdup(m_pszFname);
307 :
308 :
309 : #ifndef _WIN32
310 : /*-----------------------------------------------------------------
311 : * On Unix, make sure extension uses the right cases
312 : * We do it even for write access because if a file with the same
313 : * extension already exists we want to overwrite it.
314 : *----------------------------------------------------------------*/
315 4 : TABAdjustFilenameExtension(m_pszFname);
316 : #endif
317 :
318 : /*-----------------------------------------------------------------
319 : * Handle .TAB file... depends on access mode.
320 : *----------------------------------------------------------------*/
321 4 : if (m_eAccessMode == TABRead)
322 : {
323 : /*-------------------------------------------------------------
324 : * Open .TAB file... since it's a small text file, we will just load
325 : * it as a stringlist in memory.
326 : *------------------------------------------------------------*/
327 2 : m_papszTABFile = TAB_CSLLoad(m_pszFname);
328 2 : if (m_papszTABFile == NULL)
329 : {
330 0 : if (!bTestOpenNoError)
331 : {
332 : CPLError(CE_Failure, CPLE_FileIO,
333 0 : "Failed opening %s.", m_pszFname);
334 : }
335 0 : CPLFree(m_pszFname);
336 0 : m_pszFname = NULL;
337 0 : CSLDestroy(m_papszTABFile);
338 0 : m_papszTABFile = NULL;
339 0 : CPLFree( pszTmpFname );
340 0 : return -1;
341 : }
342 :
343 : /*-------------------------------------------------------------
344 : * Do a first pass on the TAB header to establish the type of
345 : * dataset we have (NATIVE, DBF, etc.)... and also to know if
346 : * it is a supported type.
347 : *------------------------------------------------------------*/
348 2 : if ( ParseTABFileFirstPass(bTestOpenNoError) != 0 )
349 : {
350 : // No need to produce an error... it's already been done if
351 : // necessary... just cleanup and exit.
352 :
353 0 : CPLFree(m_pszFname);
354 0 : m_pszFname = NULL;
355 0 : CSLDestroy(m_papszTABFile);
356 0 : m_papszTABFile = NULL;
357 0 : CPLFree( pszTmpFname );
358 :
359 0 : return -1;
360 : }
361 : }
362 : else
363 : {
364 : /*-------------------------------------------------------------
365 : * In Write access mode, the .TAB file will be written during the
366 : * Close() call... we will just set some defaults here.
367 : *------------------------------------------------------------*/
368 2 : m_nVersion = 300;
369 2 : CPLFree(m_pszCharset);
370 2 : m_pszCharset = CPLStrdup("Neutral");
371 2 : m_eTableType = TABTableNative;
372 :
373 : /*-------------------------------------------------------------
374 : * Do initial setup of feature definition.
375 : *------------------------------------------------------------*/
376 2 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
377 2 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
378 2 : m_poDefn->Reference();
379 2 : CPLFree(pszFeatureClassName);
380 : }
381 :
382 :
383 : /*-----------------------------------------------------------------
384 : * Open .DAT file (or .DBF)
385 : *----------------------------------------------------------------*/
386 4 : if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".TAB")==0)
387 : {
388 0 : if (m_eTableType == TABTableDBF)
389 0 : strcpy(pszTmpFname+nFnameLen-4, ".DBF");
390 : else // Default is NATIVE
391 0 : strcpy(pszTmpFname+nFnameLen-4, ".DAT");
392 : }
393 : else
394 : {
395 4 : if (m_eTableType == TABTableDBF)
396 0 : strcpy(pszTmpFname+nFnameLen-4, ".dbf");
397 : else // Default is NATIVE
398 4 : strcpy(pszTmpFname+nFnameLen-4, ".dat");
399 : }
400 :
401 : #ifndef _WIN32
402 4 : TABAdjustFilenameExtension(pszTmpFname);
403 : #endif
404 :
405 4 : m_poDATFile = new TABDATFile;
406 :
407 4 : if ( m_poDATFile->Open(pszTmpFname, pszAccess, m_eTableType) != 0)
408 : {
409 : // Open Failed... an error has already been reported, just return.
410 0 : CPLFree(pszTmpFname);
411 0 : Close();
412 0 : if (bTestOpenNoError)
413 0 : CPLErrorReset();
414 :
415 0 : return -1;
416 : }
417 :
418 4 : m_nLastFeatureId = m_poDATFile->GetNumRecords();
419 :
420 :
421 : /*-----------------------------------------------------------------
422 : * Parse .TAB file field defs and build FeatureDefn (only in read access)
423 : *----------------------------------------------------------------*/
424 4 : if (m_eAccessMode == TABRead && ParseTABFileFields() != 0)
425 : {
426 : // Failed... an error has already been reported, just return.
427 0 : CPLFree(pszTmpFname);
428 0 : Close();
429 0 : if (bTestOpenNoError)
430 0 : CPLErrorReset();
431 :
432 0 : return -1;
433 : }
434 :
435 :
436 : /*-----------------------------------------------------------------
437 : * Open .MAP (and .ID) file
438 : * Note that the .MAP and .ID files are optional. Failure to open them
439 : * is not an error... it simply means that all features will be returned
440 : * with NONE geometry.
441 : *----------------------------------------------------------------*/
442 4 : if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".DAT")==0)
443 0 : strcpy(pszTmpFname+nFnameLen-4, ".MAP");
444 : else
445 4 : strcpy(pszTmpFname+nFnameLen-4, ".map");
446 :
447 : #ifndef _WIN32
448 4 : TABAdjustFilenameExtension(pszTmpFname);
449 : #endif
450 :
451 4 : m_poMAPFile = new TABMAPFile;
452 4 : if (m_eAccessMode == TABRead)
453 : {
454 : /*-------------------------------------------------------------
455 : * Read access: .MAP/.ID are optional... try to open but return
456 : * no error if files do not exist.
457 : *------------------------------------------------------------*/
458 2 : if (m_poMAPFile->Open(pszTmpFname, pszAccess, TRUE) < 0)
459 : {
460 : // File exists, but Open Failed...
461 : // we have to produce an error message
462 0 : if (!bTestOpenNoError)
463 : CPLError(CE_Failure, CPLE_FileIO,
464 0 : "Open() failed for %s", pszTmpFname);
465 : else
466 0 : CPLErrorReset();
467 :
468 0 : CPLFree(pszTmpFname);
469 0 : Close();
470 0 : return -1;
471 : }
472 :
473 : /*-------------------------------------------------------------
474 : * Set geometry type if the geometry objects are uniform.
475 : *------------------------------------------------------------*/
476 2 : int numPoints=0, numRegions=0, numTexts=0, numLines=0;
477 :
478 2 : GetFeatureCountByType( numPoints, numLines, numRegions, numTexts);
479 :
480 2 : numPoints += numTexts;
481 2 : if( numPoints > 0 && numLines == 0 && numRegions == 0 )
482 0 : m_poDefn->SetGeomType( wkbPoint );
483 2 : else if( numPoints == 0 && numLines > 0 && numRegions == 0 )
484 0 : m_poDefn->SetGeomType( wkbLineString );
485 : else
486 : /* we leave it unknown indicating a mixture */;
487 : }
488 2 : else if (m_poMAPFile->Open(pszTmpFname, pszAccess) != 0)
489 : {
490 : // Open Failed for write...
491 : // an error has already been reported, just return.
492 0 : CPLFree(pszTmpFname);
493 0 : Close();
494 0 : if (bTestOpenNoError)
495 0 : CPLErrorReset();
496 :
497 0 : return -1;
498 : }
499 :
500 : /*-----------------------------------------------------------------
501 : * Initializing the attribute index (.IND) support
502 : *----------------------------------------------------------------*/
503 :
504 4 : CPLXMLNode *psRoot = CPLCreateXMLNode( NULL, CXT_Element, "OGRMILayerAttrIndex" );
505 4 : CPLCreateXMLElementAndValue( psRoot, "MIIDFilename", CPLResetExtension( pszFname, "IND" ) );
506 4 : OGRFeatureDefn *poLayerDefn = GetLayerDefn();
507 4 : int iField, iIndexIndex, bHasIndex = 0;
508 8 : for( iField = 0; iField < poLayerDefn->GetFieldCount(); iField++ )
509 : {
510 4 : iIndexIndex = GetFieldIndexNumber(iField);
511 4 : if (iIndexIndex > 0)
512 : {
513 0 : CPLXMLNode *psIndex = CPLCreateXMLNode( psRoot, CXT_Element, "OGRMIAttrIndex" );
514 0 : CPLCreateXMLElementAndValue( psIndex, "FieldIndex", CPLSPrintf( "%d", iField ) );
515 : CPLCreateXMLElementAndValue( psIndex, "FieldName",
516 0 : poLayerDefn->GetFieldDefn(iField)->GetNameRef() );
517 0 : CPLCreateXMLElementAndValue( psIndex, "IndexIndex", CPLSPrintf( "%d", iIndexIndex ) );
518 0 : bHasIndex = 1;
519 : }
520 : }
521 :
522 4 : if (bHasIndex)
523 : {
524 0 : char *pszRawXML = CPLSerializeXMLTree( psRoot );
525 0 : InitializeIndexSupport( pszRawXML );
526 0 : CPLFree( pszRawXML );
527 : }
528 :
529 4 : CPLDestroyXMLNode( psRoot );
530 :
531 4 : CPLFree(pszTmpFname);
532 4 : pszTmpFname = NULL;
533 :
534 : /*-----------------------------------------------------------------
535 : * __TODO__ we could probably call GetSpatialRef() here to force
536 : * parsing the projection information... this would allow us to
537 : * assignSpatialReference() on the geometries that we return.
538 : *----------------------------------------------------------------*/
539 :
540 4 : return 0;
541 : }
542 :
543 :
544 : /**********************************************************************
545 : * TABFile::ParseTABFileFirstPass()
546 : *
547 : * Do a first pass in the TAB header file to establish the table type, etc.
548 : * and store any useful information into class members.
549 : *
550 : * This private method should be used only during the Open() call.
551 : *
552 : * Returns 0 on success, -1 on error.
553 : **********************************************************************/
554 2 : int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError)
555 : {
556 2 : int iLine, numLines, numFields = 0;
557 2 : char **papszTok=NULL;
558 2 : GBool bInsideTableDef = FALSE, bFoundTableFields=FALSE;
559 :
560 2 : if (m_eAccessMode != TABRead)
561 : {
562 : CPLError(CE_Failure, CPLE_NotSupported,
563 0 : "ParseTABFile() can be used only with Read access.");
564 0 : return -1;
565 : }
566 :
567 2 : numLines = CSLCount(m_papszTABFile);
568 :
569 20 : for(iLine=0; iLine<numLines; iLine++)
570 : {
571 : /*-------------------------------------------------------------
572 : * Tokenize the next .TAB line, and check first keyword
573 : *------------------------------------------------------------*/
574 18 : CSLDestroy(papszTok);
575 18 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
576 36 : TRUE, FALSE);
577 18 : if (CSLCount(papszTok) < 2)
578 4 : continue; // All interesting lines have at least 2 tokens
579 :
580 14 : if (EQUAL(papszTok[0], "!version"))
581 : {
582 2 : m_nVersion = atoi(papszTok[1]);
583 2 : if (m_nVersion == 100)
584 : {
585 : /* Version 100 files contain only the fields definition,
586 : * so we set default values for the other params.
587 : */
588 0 : bInsideTableDef = TRUE;
589 0 : CPLFree(m_pszCharset);
590 0 : m_pszCharset = CPLStrdup("Neutral");
591 0 : m_eTableType = TABTableNative;
592 : }
593 :
594 : }
595 12 : else if (EQUAL(papszTok[0], "!edit_version"))
596 : {
597 : /* Sometimes, V450 files have version 300 + edit_version 450
598 : * for us version and edit_version are the same
599 : */
600 0 : m_nVersion = atoi(papszTok[1]);
601 : }
602 12 : else if (EQUAL(papszTok[0], "!charset"))
603 : {
604 2 : CPLFree(m_pszCharset);
605 2 : m_pszCharset = CPLStrdup(papszTok[1]);
606 : }
607 14 : else if (EQUAL(papszTok[0], "Definition") &&
608 2 : EQUAL(papszTok[1], "Table") )
609 : {
610 2 : bInsideTableDef = TRUE;
611 : }
612 16 : else if (bInsideTableDef && !bFoundTableFields &&
613 6 : (EQUAL(papszTok[0], "Type") || EQUAL(papszTok[0],"FORMAT:")) )
614 : {
615 4 : if (EQUAL(papszTok[1], "NATIVE") || EQUAL(papszTok[1], "LINKED"))
616 2 : m_eTableType = TABTableNative;
617 0 : else if (EQUAL(papszTok[1], "DBF"))
618 0 : m_eTableType = TABTableDBF;
619 : else
620 : {
621 : // Type=ACCESS, or other unsupported type... cannot open!
622 0 : if (!bTestOpenNoError)
623 : CPLError(CE_Failure, CPLE_NotSupported,
624 : "Unsupported table type '%s' in file %s. "
625 : "This type of .TAB file cannot be read by this library.",
626 0 : papszTok[1], m_pszFname);
627 0 : CSLDestroy(papszTok);
628 0 : return -1;
629 : }
630 : }
631 8 : else if (bInsideTableDef && !bFoundTableFields &&
632 2 : (EQUAL(papszTok[0],"Fields") || EQUAL(papszTok[0],"FIELDS:")))
633 : {
634 : /*---------------------------------------------------------
635 : * We found the list of table fields
636 : * Just remember number of fields... the field types will be
637 : * parsed inside ParseTABFileFields() later...
638 : *--------------------------------------------------------*/
639 2 : bFoundTableFields = TRUE;
640 2 : numFields = atoi(papszTok[1]);
641 :
642 2 : if (numFields < 1 || numFields>2048 || iLine+numFields >= numLines)
643 : {
644 0 : if (!bTestOpenNoError)
645 : CPLError(CE_Failure, CPLE_FileIO,
646 : "Invalid number of fields (%s) at line %d in file %s",
647 0 : papszTok[1], iLine+1, m_pszFname);
648 :
649 0 : CSLDestroy(papszTok);
650 0 : return -1;
651 : }
652 :
653 2 : bInsideTableDef = FALSE;
654 : }/* end of fields section*/
655 : else
656 : {
657 : // Simply Ignore unrecognized lines
658 : }
659 : }
660 :
661 2 : CSLDestroy(papszTok);
662 :
663 2 : if (m_pszCharset == NULL)
664 0 : m_pszCharset = CPLStrdup("Neutral");
665 :
666 2 : if (numFields == 0)
667 : {
668 0 : if (!bTestOpenNoError)
669 : CPLError(CE_Failure, CPLE_NotSupported,
670 : "%s contains no table field definition. "
671 : "This type of .TAB file cannot be read by this library.",
672 0 : m_pszFname);
673 0 : return -1;
674 : }
675 :
676 2 : return 0;
677 : }
678 :
679 : /**********************************************************************
680 : * TABFile::ParseTABFileFields()
681 : *
682 : * Extract the field definition from the TAB header file, validate
683 : * with what we have in the previously opened .DAT or .DBF file, and
684 : * finally build the m_poDefn OGRFeatureDefn for this dataset.
685 : *
686 : * This private method should be used only during the Open() call and after
687 : * ParseTABFileFirstPass() has been called.
688 : *
689 : * Returns 0 on success, -1 on error.
690 : **********************************************************************/
691 2 : int TABFile::ParseTABFileFields()
692 : {
693 2 : int iLine, numLines=0, numTok, nStatus;
694 2 : char **papszTok=NULL;
695 : OGRFieldDefn *poFieldDefn;
696 :
697 2 : if (m_eAccessMode != TABRead)
698 : {
699 : CPLError(CE_Failure, CPLE_NotSupported,
700 0 : "ParseTABFile() can be used only with Read access.");
701 0 : return -1;
702 : }
703 :
704 2 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
705 2 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
706 2 : CPLFree(pszFeatureClassName);
707 : // Ref count defaults to 0... set it to 1
708 2 : m_poDefn->Reference();
709 :
710 : /*-------------------------------------------------------------
711 : * Scan for fields.
712 : *------------------------------------------------------------*/
713 2 : numLines = CSLCount(m_papszTABFile);
714 14 : for(iLine=0; iLine<numLines; iLine++)
715 : {
716 : /*-------------------------------------------------------------
717 : * Tokenize the next .TAB line, and check first keyword
718 : *------------------------------------------------------------*/
719 14 : const char *pszStr = m_papszTABFile[iLine];
720 36 : while(*pszStr != '\0' && isspace((unsigned char)*pszStr))
721 8 : pszStr++;
722 :
723 14 : if (EQUALN(pszStr, "Fields", 6))
724 : {
725 : /*---------------------------------------------------------
726 : * We found the list of table fields
727 : *--------------------------------------------------------*/
728 : int iField, numFields;
729 2 : numFields = atoi(pszStr+7);
730 2 : if (numFields < 1 || numFields > 2048 ||
731 : iLine+numFields >= numLines)
732 : {
733 : CPLError(CE_Failure, CPLE_FileIO,
734 : "Invalid number of fields (%s) at line %d in file %s",
735 0 : pszStr+7, iLine+1, m_pszFname);
736 0 : CSLDestroy(papszTok);
737 0 : return -1;
738 : }
739 :
740 : // Alloc the array to keep track of indexed fields
741 2 : m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int));
742 :
743 2 : iLine++;
744 2 : poFieldDefn = NULL;
745 6 : for(iField=0; iField<numFields; iField++, iLine++)
746 : {
747 : /*-----------------------------------------------------
748 : * For each field definition found in the .TAB:
749 : * Pass the info to the DAT file object. It will validate
750 : * the info with what is found in the .DAT header, and will
751 : * also use this info later to interpret field values.
752 : *
753 : * We also create the OGRFieldDefn at the same time to
754 : * initialize the OGRFeatureDefn
755 : *----------------------------------------------------*/
756 4 : CSLDestroy(papszTok);
757 4 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine],
758 : " \t(),;",
759 8 : TRUE, FALSE);
760 4 : numTok = CSLCount(papszTok);
761 4 : nStatus = -1;
762 4 : CPLAssert(m_poDefn);
763 6 : if (numTok >= 3 && EQUAL(papszTok[1], "char"))
764 : {
765 : /*-------------------------------------------------
766 : * CHAR type
767 : *------------------------------------------------*/
768 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
769 2 : papszTok[0],
770 : TABFChar,
771 2 : atoi(papszTok[2]),
772 6 : 0);
773 2 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString);
774 2 : poFieldDefn->SetWidth(atoi(papszTok[2]));
775 : }
776 3 : else if (numTok >= 2 && EQUAL(papszTok[1], "integer"))
777 : {
778 : /*-------------------------------------------------
779 : * INTEGER type
780 : *------------------------------------------------*/
781 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
782 1 : papszTok[0],
783 : TABFInteger,
784 : 0,
785 1 : 0);
786 1 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger);
787 : }
788 1 : else if (numTok >= 2 && EQUAL(papszTok[1], "smallint"))
789 : {
790 : /*-------------------------------------------------
791 : * SMALLINT type
792 : *------------------------------------------------*/
793 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
794 0 : papszTok[0],
795 : TABFSmallInt,
796 : 0,
797 0 : 0);
798 0 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger);
799 : }
800 1 : else if (numTok >= 4 && EQUAL(papszTok[1], "decimal"))
801 : {
802 : /*-------------------------------------------------
803 : * DECIMAL type
804 : *------------------------------------------------*/
805 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
806 0 : papszTok[0],
807 : TABFDecimal,
808 0 : atoi(papszTok[2]),
809 0 : atoi(papszTok[3]));
810 0 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal);
811 0 : poFieldDefn->SetWidth(atoi(papszTok[2]));
812 0 : poFieldDefn->SetPrecision(atoi(papszTok[3]));
813 : }
814 2 : else if (numTok >= 2 && EQUAL(papszTok[1], "float"))
815 : {
816 : /*-------------------------------------------------
817 : * FLOAT type
818 : *------------------------------------------------*/
819 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
820 1 : papszTok[0],
821 : TABFFloat,
822 1 : 0, 0);
823 1 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal);
824 : }
825 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "date"))
826 : {
827 : /*-------------------------------------------------
828 : * DATE type (returned as a string: "DD/MM/YYYY")
829 : *------------------------------------------------*/
830 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
831 0 : papszTok[0],
832 : TABFDate,
833 : 0,
834 0 : 0);
835 0 : poFieldDefn = new OGRFieldDefn(papszTok[0],
836 : #ifdef MITAB_USE_OFTDATETIME
837 0 : OFTDate);
838 : #else
839 : OFTString);
840 : #endif
841 0 : poFieldDefn->SetWidth(10);
842 : }
843 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "time"))
844 : {
845 : /*-------------------------------------------------
846 : * TIME type (returned as a string: "HH:MM:SS")
847 : *------------------------------------------------*/
848 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
849 0 : papszTok[0],
850 : TABFTime,
851 : 0,
852 0 : 0);
853 0 : poFieldDefn = new OGRFieldDefn(papszTok[0],
854 : #ifdef MITAB_USE_OFTDATETIME
855 0 : OFTTime);
856 : #else
857 : OFTString);
858 : #endif
859 0 : poFieldDefn->SetWidth(9);
860 : }
861 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "datetime"))
862 : {
863 : /*-------------------------------------------------
864 : * DATETIME type (returned as a string: "DD/MM/YYYY HH:MM:SS")
865 : *------------------------------------------------*/
866 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
867 0 : papszTok[0],
868 : TABFDateTime,
869 : 0,
870 0 : 0);
871 0 : poFieldDefn = new OGRFieldDefn(papszTok[0],
872 : #ifdef MITAB_USE_OFTDATETIME
873 0 : OFTDateTime);
874 : #else
875 : OFTString);
876 : #endif
877 0 : poFieldDefn->SetWidth(19);
878 : }
879 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "logical"))
880 : {
881 : /*-------------------------------------------------
882 : * LOGICAL type (value "T" or "F")
883 : *------------------------------------------------*/
884 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
885 0 : papszTok[0],
886 : TABFLogical,
887 : 0,
888 0 : 0);
889 0 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString);
890 0 : poFieldDefn->SetWidth(1);
891 : }
892 : else
893 0 : nStatus = -1; // Unrecognized field type or line corrupt
894 :
895 4 : if (nStatus != 0)
896 : {
897 : CPLError(CE_Failure, CPLE_FileIO,
898 : "Failed to parse field definition at line %d in file %s",
899 0 : iLine+1, m_pszFname);
900 0 : CSLDestroy(papszTok);
901 0 : return -1;
902 : }
903 : /*-----------------------------------------------------
904 : * Keep track of index number if present
905 : *----------------------------------------------------*/
906 4 : if (numTok >= 4 && EQUAL(papszTok[numTok-2], "index"))
907 : {
908 0 : m_panIndexNo[iField] = atoi(papszTok[numTok-1]);
909 : }
910 : else
911 : {
912 4 : m_panIndexNo[iField] = 0;
913 : }
914 :
915 : /*-----------------------------------------------------
916 : * Add the FieldDefn to the FeatureDefn and continue with
917 : * the next one.
918 : *----------------------------------------------------*/
919 4 : m_poDefn->AddFieldDefn(poFieldDefn);
920 : // AddFieldDenf() takes a copy, so we delete the original
921 4 : if (poFieldDefn) delete poFieldDefn;
922 4 : poFieldDefn = NULL;
923 : }
924 :
925 : /*---------------------------------------------------------
926 : * OK, we're done... end the loop now.
927 : *--------------------------------------------------------*/
928 2 : break;
929 : }/* end of fields section*/
930 : else
931 : {
932 : // Simply Ignore unrecognized lines
933 : }
934 :
935 : }
936 :
937 2 : CSLDestroy(papszTok);
938 :
939 2 : if (m_poDefn->GetFieldCount() == 0)
940 : {
941 : CPLError(CE_Failure, CPLE_NotSupported,
942 : "%s contains no table field definition. "
943 : "This type of .TAB file cannot be read by this library.",
944 0 : m_pszFname);
945 0 : return -1;
946 : }
947 :
948 2 : return 0;
949 : }
950 :
951 : /**********************************************************************
952 : * TABFile::WriteTABFile()
953 : *
954 : * Generate the .TAB file using mainly the attribute fields definition.
955 : *
956 : * This private method should be used only during the Close() call with
957 : * write access mode.
958 : *
959 : * Returns 0 on success, -1 on error.
960 : **********************************************************************/
961 2 : int TABFile::WriteTABFile()
962 : {
963 : FILE *fp;
964 :
965 2 : if (m_eAccessMode != TABWrite)
966 : {
967 : CPLError(CE_Failure, CPLE_NotSupported,
968 0 : "WriteTABFile() can be used only with Write access.");
969 0 : return -1;
970 : }
971 :
972 2 : if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL)
973 : {
974 2 : fprintf(fp, "!table\n");
975 2 : fprintf(fp, "!version %d\n", m_nVersion);
976 2 : fprintf(fp, "!charset %s\n", m_pszCharset);
977 2 : fprintf(fp, "\n");
978 :
979 2 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
980 : {
981 : int iField;
982 : OGRFieldDefn *poFieldDefn;
983 : const char *pszFieldType;
984 :
985 2 : fprintf(fp, "Definition Table\n");
986 2 : fprintf(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset);
987 2 : fprintf(fp, " Fields %d\n", m_poDefn->GetFieldCount());
988 :
989 6 : for(iField=0; iField<m_poDefn->GetFieldCount(); iField++)
990 : {
991 4 : poFieldDefn = m_poDefn->GetFieldDefn(iField);
992 4 : switch(GetNativeFieldType(iField))
993 : {
994 : case TABFChar:
995 : pszFieldType = CPLSPrintf("Char (%d)",
996 2 : poFieldDefn->GetWidth());
997 2 : break;
998 : case TABFDecimal:
999 : pszFieldType = CPLSPrintf("Decimal (%d,%d)",
1000 : poFieldDefn->GetWidth(),
1001 0 : poFieldDefn->GetPrecision());
1002 0 : break;
1003 : case TABFInteger:
1004 1 : pszFieldType = "Integer";
1005 1 : break;
1006 : case TABFSmallInt:
1007 0 : pszFieldType = "SmallInt";
1008 0 : break;
1009 : case TABFFloat:
1010 1 : pszFieldType = "Float";
1011 1 : break;
1012 : case TABFLogical:
1013 0 : pszFieldType = "Logical";
1014 0 : break;
1015 : case TABFDate:
1016 0 : pszFieldType = "Date";
1017 0 : break;
1018 : case TABFTime:
1019 0 : pszFieldType = "Time";
1020 0 : break;
1021 : case TABFDateTime:
1022 0 : pszFieldType = "DateTime";
1023 0 : break;
1024 : default:
1025 : // Unsupported field type!!! This should never happen.
1026 : CPLError(CE_Failure, CPLE_AssertionFailed,
1027 0 : "WriteTABFile(): Unsupported field type");
1028 0 : VSIFClose(fp);
1029 0 : return -1;
1030 : }
1031 :
1032 4 : if (GetFieldIndexNumber(iField) == 0)
1033 : {
1034 : fprintf(fp, " %s %s ;\n", poFieldDefn->GetNameRef(),
1035 4 : pszFieldType );
1036 : }
1037 : else
1038 : {
1039 : fprintf(fp, " %s %s Index %d ;\n",
1040 : poFieldDefn->GetNameRef(), pszFieldType,
1041 0 : GetFieldIndexNumber(iField) );
1042 : }
1043 :
1044 : }
1045 : }
1046 : else
1047 : {
1048 0 : fprintf(fp, "Definition Table\n");
1049 0 : fprintf(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset);
1050 0 : fprintf(fp, " Fields 1\n");
1051 0 : fprintf(fp, " FID Integer ;\n" );
1052 : }
1053 :
1054 2 : VSIFClose(fp);
1055 : }
1056 : else
1057 : {
1058 : CPLError(CE_Failure, CPLE_FileIO,
1059 0 : "Failed to create file `%s'", m_pszFname);
1060 0 : return -1;
1061 : }
1062 :
1063 2 : return 0;
1064 : }
1065 :
1066 : /**********************************************************************
1067 : * TABFile::Close()
1068 : *
1069 : * Close current file, and release all memory used.
1070 : *
1071 : * Returns 0 on success, -1 on error.
1072 : **********************************************************************/
1073 4 : int TABFile::Close()
1074 : {
1075 : // Commit the latest changes to the file...
1076 :
1077 : // In Write access, it's time to write the .TAB file.
1078 4 : if (m_eAccessMode == TABWrite && m_poMAPFile)
1079 : {
1080 : // First update file version number...
1081 2 : int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion();
1082 2 : m_nVersion = MAX(m_nVersion, nMapObjVersion);
1083 :
1084 2 : WriteTABFile();
1085 : }
1086 :
1087 4 : if (m_poMAPFile)
1088 : {
1089 4 : m_poMAPFile->Close();
1090 4 : delete m_poMAPFile;
1091 4 : m_poMAPFile = NULL;
1092 : }
1093 :
1094 4 : if (m_poDATFile)
1095 : {
1096 4 : m_poDATFile->Close();
1097 4 : delete m_poDATFile;
1098 4 : m_poDATFile = NULL;
1099 : }
1100 :
1101 4 : if (m_poINDFile)
1102 : {
1103 0 : m_poINDFile->Close();
1104 0 : delete m_poINDFile;
1105 0 : m_poINDFile = NULL;
1106 : }
1107 :
1108 4 : if (m_poCurFeature)
1109 : {
1110 1 : delete m_poCurFeature;
1111 1 : m_poCurFeature = NULL;
1112 : }
1113 :
1114 : /*-----------------------------------------------------------------
1115 : * Note: we have to check the reference count before deleting
1116 : * m_poSpatialRef and m_poDefn
1117 : *----------------------------------------------------------------*/
1118 4 : if (m_poDefn )
1119 : {
1120 4 : int nRefCount = m_poDefn->Dereference();
1121 :
1122 4 : CPLAssert( nRefCount >= 0 );
1123 :
1124 4 : if( nRefCount == 0 )
1125 3 : delete m_poDefn;
1126 4 : m_poDefn = NULL;
1127 : }
1128 :
1129 4 : if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
1130 2 : delete m_poSpatialRef;
1131 4 : m_poSpatialRef = NULL;
1132 :
1133 4 : CSLDestroy(m_papszTABFile);
1134 4 : m_papszTABFile = NULL;
1135 :
1136 4 : CPLFree(m_pszFname);
1137 4 : m_pszFname = NULL;
1138 :
1139 4 : CPLFree(m_pszCharset);
1140 4 : m_pszCharset = NULL;
1141 :
1142 4 : CPLFree(m_panIndexNo);
1143 4 : m_panIndexNo = NULL;
1144 :
1145 4 : CPLFree(m_panMatchingFIDs);
1146 4 : m_panMatchingFIDs = NULL;
1147 :
1148 4 : return 0;
1149 : }
1150 :
1151 : /**********************************************************************
1152 : * TABFile::SetQuickSpatialIndexMode()
1153 : *
1154 : * Select "quick spatial index mode".
1155 : *
1156 : * The default behavior of MITAB is to generate an optimized spatial index,
1157 : * but this results in slower write speed.
1158 : *
1159 : * Applications that want faster write speed and do not care
1160 : * about the performance of spatial queries on the resulting file can
1161 : * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
1162 : * spatial index (actually emulating the type of spatial index produced
1163 : * by MITAB before version 1.6.0). In this mode writing files can be
1164 : * about 5 times faster, but spatial queries can be up to 30 times slower.
1165 : *
1166 : * Returns 0 on success, -1 on error.
1167 : **********************************************************************/
1168 0 : int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
1169 : {
1170 0 : if (m_eAccessMode != TABWrite || m_poMAPFile == NULL)
1171 : {
1172 : CPLError(CE_Failure, CPLE_AssertionFailed,
1173 0 : "SetQuickSpatialIndexMode() failed: file not opened for write access.");
1174 0 : return -1;
1175 : }
1176 :
1177 :
1178 0 : return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode);
1179 : }
1180 :
1181 :
1182 :
1183 : /**********************************************************************
1184 : * TABFile::GetNextFeatureId()
1185 : *
1186 : * Returns feature id that follows nPrevId, or -1 if it is the
1187 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
1188 : **********************************************************************/
1189 57 : int TABFile::GetNextFeatureId(int nPrevId)
1190 : {
1191 57 : if (m_eAccessMode != TABRead)
1192 : {
1193 : CPLError(CE_Failure, CPLE_NotSupported,
1194 0 : "GetNextFeatureId() can be used only with Read access.");
1195 0 : return -1;
1196 : }
1197 :
1198 : /*-----------------------------------------------------------------
1199 : * Are we using spatial rather than .ID based traversal?
1200 : *----------------------------------------------------------------*/
1201 57 : if( bUseSpatialTraversal )
1202 11 : return m_poMAPFile->GetNextFeatureId( nPrevId );
1203 :
1204 : /*-----------------------------------------------------------------
1205 : * Should we use an attribute index traversal?
1206 : *----------------------------------------------------------------*/
1207 46 : if( m_poAttrQuery != NULL)
1208 : {
1209 32 : if( m_panMatchingFIDs == NULL )
1210 : {
1211 32 : m_iMatchingFID = 0;
1212 : m_panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this,
1213 32 : NULL );
1214 : }
1215 32 : if( m_panMatchingFIDs != NULL )
1216 : {
1217 0 : if( m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID )
1218 0 : return OGRNullFID;
1219 :
1220 0 : return m_panMatchingFIDs[m_iMatchingFID++] + 1;
1221 : }
1222 : }
1223 :
1224 : /*-----------------------------------------------------------------
1225 : * Establish what the next logical feature ID should be
1226 : *----------------------------------------------------------------*/
1227 46 : int nFeatureId = -1;
1228 :
1229 51 : if (nPrevId <= 0 && m_nLastFeatureId > 0)
1230 5 : nFeatureId = 1; // Feature Ids start at 1
1231 79 : else if (nPrevId > 0 && nPrevId < m_nLastFeatureId)
1232 38 : nFeatureId = nPrevId + 1;
1233 : else
1234 : {
1235 : // This was the last feature
1236 3 : return OGRNullFID;
1237 : }
1238 :
1239 : /*-----------------------------------------------------------------
1240 : * Skip any feature with NONE geometry and a deleted attribute record
1241 : *----------------------------------------------------------------*/
1242 86 : while(nFeatureId <= m_nLastFeatureId)
1243 : {
1244 43 : if ( m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
1245 : m_poDATFile->GetRecordBlock(nFeatureId) == NULL )
1246 : {
1247 : CPLError(CE_Failure, CPLE_IllegalArg,
1248 : "GetNextFeatureId() failed: unable to set read pointer "
1249 0 : "to feature id %d", nFeatureId);
1250 0 : return -1;
1251 : }
1252 :
1253 : // __TODO__ Add a test here to check if object is deleted,
1254 : // i.e. 0x40 set on object_id in object block
1255 43 : if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE ||
1256 : m_poDATFile->IsCurrentRecordDeleted() == FALSE)
1257 : {
1258 : // This feature contains at least a geometry or some attributes...
1259 : // return its id.
1260 43 : return nFeatureId;
1261 : }
1262 :
1263 0 : nFeatureId++;
1264 : }
1265 :
1266 : // If we reached this point, then we kept skipping deleted features
1267 : // and stopped when EOF was reached.
1268 0 : return -1;
1269 : }
1270 :
1271 : /**********************************************************************
1272 : * TABFile::GetNextFeatureId_Spatial()
1273 : *
1274 : * Returns feature id that follows nPrevId, or -1 if it is the
1275 : * last feature id, but by traversing the spatial tree instead of the
1276 : * direct object index. Generally speaking the feature id's will be
1277 : * returned in an unordered fashion.
1278 : **********************************************************************/
1279 0 : int TABFile::GetNextFeatureId_Spatial(int nPrevId)
1280 : {
1281 0 : if (m_eAccessMode != TABRead)
1282 : {
1283 : CPLError(CE_Failure, CPLE_NotSupported,
1284 0 : "GetNextFeatureId_Spatial() can be used only with Read access.");
1285 0 : return -1;
1286 : }
1287 :
1288 0 : if( m_poMAPFile == NULL )
1289 : {
1290 : CPLError(CE_Failure, CPLE_NotSupported,
1291 0 : "GetNextFeatureId_Spatial() requires availability of .MAP file." );
1292 0 : return -1;
1293 : }
1294 :
1295 0 : return m_poMAPFile->GetNextFeatureId( nPrevId );
1296 : }
1297 :
1298 : /**********************************************************************
1299 : * TABFile::GetFeatureRef()
1300 : *
1301 : * Fill and return a TABFeature object for the specified feature id.
1302 : *
1303 : * The retruned pointer is a reference to an object owned and maintained
1304 : * by this TABFile object. It should not be altered or freed by the
1305 : * caller and its contents is guaranteed to be valid only until the next
1306 : * call to GetFeatureRef() or Close().
1307 : *
1308 : * Returns NULL if the specified feature id does not exist of if an
1309 : * error happened. In any case, CPLError() will have been called to
1310 : * report the reason of the failure.
1311 : *
1312 : * If an unsupported object type is encountered (likely from a newer version
1313 : * of MapInfo) then a valid feature will be returned with a NONE geometry,
1314 : * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported
1315 : * CPLGetLastErrorNo() should be used to detect that case.
1316 : **********************************************************************/
1317 53 : TABFeature *TABFile::GetFeatureRef(int nFeatureId)
1318 : {
1319 53 : CPLErrorReset();
1320 :
1321 53 : if (m_eAccessMode != TABRead)
1322 : {
1323 : CPLError(CE_Failure, CPLE_NotSupported,
1324 0 : "GetFeatureRef() can be used only with Read access.");
1325 0 : return NULL;
1326 : }
1327 :
1328 : /*-----------------------------------------------------------------
1329 : * Make sure file is opened and Validate feature id by positioning
1330 : * the read pointers for the .MAP and .DAT files to this feature id.
1331 : *----------------------------------------------------------------*/
1332 53 : if (m_poMAPFile == NULL)
1333 : {
1334 : CPLError(CE_Failure, CPLE_IllegalArg,
1335 0 : "GetFeatureRef() failed: file is not opened!");
1336 0 : return NULL;
1337 : }
1338 :
1339 :
1340 53 : if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
1341 : m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
1342 : m_poDATFile->GetRecordBlock(nFeatureId) == NULL )
1343 : {
1344 : // CPLError(CE_Failure, CPLE_IllegalArg,
1345 : // "GetFeatureRef() failed: invalid feature id %d",
1346 : // nFeatureId);
1347 0 : return NULL;
1348 : }
1349 :
1350 : /*-----------------------------------------------------------------
1351 : * Flush current feature object
1352 : * __TODO__ try to reuse if it is already of the right type
1353 : *----------------------------------------------------------------*/
1354 53 : if (m_poCurFeature)
1355 : {
1356 31 : delete m_poCurFeature;
1357 31 : m_poCurFeature = NULL;
1358 : }
1359 :
1360 : /*-----------------------------------------------------------------
1361 : * Create new feature object of the right type
1362 : * Unsupported object types are returned as raw TABFeature (i.e. NONE
1363 : * geometry)
1364 : *----------------------------------------------------------------*/
1365 : m_poCurFeature = TABFeature::CreateFromMapInfoType(m_poMAPFile->GetCurObjType(),
1366 53 : m_poDefn);
1367 :
1368 : /*-----------------------------------------------------------------
1369 : * Read fields from the .DAT file
1370 : * GetRecordBlock() has already been called above...
1371 : *----------------------------------------------------------------*/
1372 53 : if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0)
1373 : {
1374 0 : delete m_poCurFeature;
1375 0 : m_poCurFeature = NULL;
1376 0 : return NULL;
1377 : }
1378 :
1379 : /*-----------------------------------------------------------------
1380 : * Read geometry from the .MAP file
1381 : * MoveToObjId() has already been called above...
1382 : *----------------------------------------------------------------*/
1383 : TABMAPObjHdr *poObjHdr =
1384 : TABMAPObjHdr::NewObj((GByte)m_poMAPFile->GetCurObjType(),
1385 53 : m_poMAPFile->GetCurObjId());
1386 : // Note that poObjHdr==NULL is a valid case if geometry type is NONE
1387 :
1388 106 : if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) ||
1389 53 : m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0)
1390 : {
1391 0 : delete m_poCurFeature;
1392 0 : m_poCurFeature = NULL;
1393 0 : if (poObjHdr)
1394 0 : delete poObjHdr;
1395 0 : return NULL;
1396 : }
1397 53 : if (poObjHdr) // May be NULL if feature geometry type is NONE
1398 53 : delete poObjHdr;
1399 :
1400 53 : m_nCurFeatureId = nFeatureId;
1401 53 : m_poCurFeature->SetFID(m_nCurFeatureId);
1402 :
1403 53 : m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted());
1404 :
1405 53 : return m_poCurFeature;
1406 : }
1407 :
1408 : /**********************************************************************
1409 : * TABFile::WriteFeature()
1410 : *
1411 : * Write a feature to this dataset.
1412 : *
1413 : * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1414 : * but eventually we should be able to do random access by specifying
1415 : * a value through nFeatureId.
1416 : *
1417 : * Returns the new featureId (> 0) on success, or -1 if an
1418 : * error happened in which case, CPLError() will have been called to
1419 : * report the reason of the failure.
1420 : **********************************************************************/
1421 13 : int TABFile::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1422 : {
1423 13 : if (m_eAccessMode != TABWrite)
1424 : {
1425 : CPLError(CE_Failure, CPLE_NotSupported,
1426 0 : "WriteFeature() can be used only with Write access.");
1427 0 : return -1;
1428 : }
1429 :
1430 13 : if (nFeatureId != -1)
1431 : {
1432 : CPLError(CE_Failure, CPLE_NotSupported,
1433 0 : "WriteFeature(): random access not implemented yet.");
1434 0 : return -1;
1435 : }
1436 :
1437 : /*-----------------------------------------------------------------
1438 : * Make sure file is opened and establish new feature id.
1439 : *----------------------------------------------------------------*/
1440 13 : if (m_poMAPFile == NULL)
1441 : {
1442 : CPLError(CE_Failure, CPLE_IllegalArg,
1443 0 : "WriteFeature() failed: file is not opened!");
1444 0 : return -1;
1445 : }
1446 :
1447 13 : if (m_nLastFeatureId < 1)
1448 : {
1449 : /*-------------------------------------------------------------
1450 : * OK, this is the first feature in the dataset... make sure the
1451 : * .DAT schema has been initialized.
1452 : *------------------------------------------------------------*/
1453 2 : if (m_poDefn == NULL)
1454 0 : SetFeatureDefn(poFeature->GetDefnRef(), NULL);
1455 :
1456 : /*-------------------------------------------------------------
1457 : * Special hack to write out at least one field if none are in
1458 : * OGRFeatureDefn.
1459 : *------------------------------------------------------------*/
1460 2 : if( m_poDATFile->GetNumFields() == 0 )
1461 : {
1462 : CPLError(CE_Warning, CPLE_IllegalArg,
1463 0 : "MapInfo tables must contain at least 1 column, adding dummy FID column.");
1464 0 : m_poDATFile->AddField("FID", TABFInteger, 10, 0 );
1465 : }
1466 :
1467 2 : nFeatureId = m_nLastFeatureId = 1;
1468 : }
1469 : else
1470 : {
1471 11 : nFeatureId = ++ m_nLastFeatureId;
1472 : }
1473 :
1474 :
1475 : /*-----------------------------------------------------------------
1476 : * Write fields to the .DAT file and update .IND if necessary
1477 : *----------------------------------------------------------------*/
1478 26 : if (m_poDATFile == NULL ||
1479 : m_poDATFile->GetRecordBlock(nFeatureId) == NULL ||
1480 : poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile,
1481 13 : m_panIndexNo) != 0 )
1482 : {
1483 : CPLError(CE_Failure, CPLE_FileIO,
1484 : "Failed writing attributes for feature id %d in %s",
1485 0 : nFeatureId, m_pszFname);
1486 0 : return -1;
1487 : }
1488 :
1489 : /*-----------------------------------------------------------------
1490 : * Write geometry to the .MAP file
1491 : * The call to PrepareNewObj() takes care of the .ID file.
1492 : *----------------------------------------------------------------*/
1493 : TABMAPObjHdr *poObjHdr =
1494 13 : TABMAPObjHdr::NewObj((GByte)poFeature->ValidateMapInfoType(m_poMAPFile),
1495 13 : nFeatureId);
1496 :
1497 : /*-----------------------------------------------------------------
1498 : * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature
1499 : * contained an invalid geometry for its class. Need to catch that
1500 : * case and return the error.
1501 : *----------------------------------------------------------------*/
1502 14 : if (poObjHdr->m_nType == TAB_GEOM_NONE &&
1503 1 : poFeature->GetFeatureClass() != TABFCNoGeomFeature )
1504 : {
1505 : CPLError(CE_Failure, CPLE_FileIO,
1506 : "Invalid geometry for feature id %d in %s",
1507 0 : nFeatureId, m_pszFname);
1508 0 : return -1;
1509 : }
1510 :
1511 : /*-----------------------------------------------------------------
1512 : * The ValidateMapInfoType() call above has forced calculation of the
1513 : * feature's IntMBR. Store that value in the ObjHdr for use by
1514 : * PrepareNewObj() to search the best node to insert the feature.
1515 : *----------------------------------------------------------------*/
1516 13 : if ( poObjHdr && poObjHdr->m_nType != TAB_GEOM_NONE)
1517 : {
1518 : poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
1519 12 : poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
1520 : }
1521 :
1522 26 : if ( poObjHdr == NULL || m_poMAPFile == NULL ||
1523 : m_poMAPFile->PrepareNewObj(poObjHdr) != 0 ||
1524 13 : poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr) != 0 ||
1525 : m_poMAPFile->CommitNewObj(poObjHdr) != 0 )
1526 : {
1527 : CPLError(CE_Failure, CPLE_FileIO,
1528 : "Failed writing geometry for feature id %d in %s",
1529 0 : nFeatureId, m_pszFname);
1530 0 : if (poObjHdr)
1531 0 : delete poObjHdr;
1532 0 : return -1;
1533 : }
1534 :
1535 13 : delete poObjHdr;
1536 :
1537 13 : return nFeatureId;
1538 : }
1539 :
1540 :
1541 : /**********************************************************************
1542 : * TABFile::CreateFeature()
1543 : *
1544 : * Write a new feature to this dataset. The passed in feature is updated
1545 : * with the new feature id.
1546 : *
1547 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
1548 : * error happened in which case, CPLError() will have been called to
1549 : * report the reason of the failure.
1550 : **********************************************************************/
1551 13 : OGRErr TABFile::CreateFeature(TABFeature *poFeature)
1552 : {
1553 13 : int nFeatureId = -1;
1554 :
1555 13 : nFeatureId = WriteFeature(poFeature, -1);
1556 :
1557 13 : if (nFeatureId == -1)
1558 0 : return OGRERR_FAILURE;
1559 :
1560 13 : poFeature->SetFID(nFeatureId);
1561 :
1562 13 : return OGRERR_NONE;
1563 : }
1564 :
1565 : /**********************************************************************
1566 : * TABFile::SetFeature()
1567 : *
1568 : * Implementation of OGRLayer's SetFeature(), enabled only for
1569 : * random write access
1570 : **********************************************************************/
1571 0 : OGRErr TABFile::SetFeature( OGRFeature *poFeature )
1572 :
1573 : {
1574 : //TODO: See CreateFeature()
1575 : // Need to convert OGRFeature to TABFeature, extract FID and then forward
1576 : // forward call to SetFeature(TABFeature, fid)
1577 0 : return OGRERR_UNSUPPORTED_OPERATION;
1578 : }
1579 :
1580 :
1581 : /**********************************************************************
1582 : * TABFile::GetLayerDefn()
1583 : *
1584 : * Returns a reference to the OGRFeatureDefn that will be used to create
1585 : * features in this dataset.
1586 : *
1587 : * Returns a reference to an object that is maintained by this TABFile
1588 : * object (and thus should not be modified or freed by the caller) or
1589 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
1590 : * opened yet)
1591 : **********************************************************************/
1592 43 : OGRFeatureDefn *TABFile::GetLayerDefn()
1593 : {
1594 43 : return m_poDefn;
1595 : }
1596 :
1597 : /**********************************************************************
1598 : * TABFile::SetFeatureDefn()
1599 : *
1600 : * Pass a reference to the OGRFeatureDefn that will be used to create
1601 : * features in this dataset. This function should be called after
1602 : * creating a new dataset, but before writing the first feature.
1603 : * All features that will be written to this dataset must share this same
1604 : * OGRFeatureDefn.
1605 : *
1606 : * A reference to the OGRFeatureDefn will be kept and will be used to
1607 : * build the .DAT file, etc.
1608 : *
1609 : * Returns 0 on success, -1 on error.
1610 : **********************************************************************/
1611 0 : int TABFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
1612 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1613 : {
1614 : int iField, numFields;
1615 : OGRFieldDefn *poFieldDefn;
1616 0 : TABFieldType eMapInfoType = TABFUnknown;
1617 0 : int nStatus = 0;
1618 :
1619 0 : if (m_eAccessMode != TABWrite)
1620 : {
1621 : CPLError(CE_Failure, CPLE_NotSupported,
1622 0 : "SetFeatureDefn() can be used only with Write access.");
1623 0 : return -1;
1624 : }
1625 :
1626 : /*-----------------------------------------------------------------
1627 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
1628 : * reference count into account when we are done with it.
1629 : *----------------------------------------------------------------*/
1630 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1631 0 : delete m_poDefn;
1632 :
1633 0 : m_poDefn = poFeatureDefn;
1634 0 : m_poDefn->Reference();
1635 :
1636 : /*-----------------------------------------------------------------
1637 : * Pass field information to the .DAT file, after making sure that
1638 : * it has been created and that it does not contain any field
1639 : * definition yet.
1640 : *----------------------------------------------------------------*/
1641 0 : if (m_poDATFile== NULL || m_poDATFile->GetNumFields() > 0 )
1642 : {
1643 : CPLError(CE_Failure, CPLE_AssertionFailed,
1644 : "SetFeatureDefn() can be called only once in a newly "
1645 0 : "created dataset.");
1646 0 : return -1;
1647 : }
1648 :
1649 0 : numFields = poFeatureDefn->GetFieldCount();
1650 0 : for(iField=0; nStatus==0 && iField < numFields; iField++)
1651 : {
1652 0 : poFieldDefn = m_poDefn->GetFieldDefn(iField);
1653 :
1654 : /*-------------------------------------------------------------
1655 : * Make sure field name is valid... check for special chars, etc.
1656 : *------------------------------------------------------------*/
1657 0 : char *pszCleanName = TABCleanFieldName(poFieldDefn->GetNameRef());
1658 0 : if (!EQUAL(pszCleanName, poFieldDefn->GetNameRef()))
1659 0 : poFieldDefn->SetName(pszCleanName);
1660 0 : CPLFree(pszCleanName);
1661 0 : pszCleanName = NULL;
1662 :
1663 0 : if (paeMapInfoNativeFieldTypes)
1664 : {
1665 0 : eMapInfoType = paeMapInfoNativeFieldTypes[iField];
1666 : }
1667 : else
1668 : {
1669 : /*---------------------------------------------------------
1670 : * Map OGRFieldTypes to MapInfo native types
1671 : *--------------------------------------------------------*/
1672 0 : switch(poFieldDefn->GetType())
1673 : {
1674 : case OFTInteger:
1675 0 : eMapInfoType = TABFInteger;
1676 0 : break;
1677 : case OFTReal:
1678 0 : eMapInfoType = TABFFloat;
1679 0 : break;
1680 : case OFTDateTime:
1681 0 : eMapInfoType = TABFDateTime;
1682 0 : break;
1683 : case OFTDate:
1684 0 : eMapInfoType = TABFDate;
1685 0 : break;
1686 : case OFTTime:
1687 0 : eMapInfoType = TABFTime;
1688 0 : break;
1689 : case OFTString:
1690 : default:
1691 0 : eMapInfoType = TABFChar;
1692 : }
1693 : }
1694 :
1695 : nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(),
1696 : eMapInfoType,
1697 : poFieldDefn->GetWidth(),
1698 0 : poFieldDefn->GetPrecision());
1699 : }
1700 :
1701 : /*-----------------------------------------------------------------
1702 : * Alloc the array to keep track of indexed fields (default=NOT indexed)
1703 : *----------------------------------------------------------------*/
1704 0 : m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int));
1705 :
1706 0 : return nStatus;
1707 : }
1708 :
1709 : /**********************************************************************
1710 : * TABFile::AddFieldNative()
1711 : *
1712 : * Create a new field using a native mapinfo data type... this is an
1713 : * alternative to defining fields through the OGR interface.
1714 : * This function should be called after creating a new dataset, but before
1715 : * writing the first feature.
1716 : *
1717 : * This function will build/update the OGRFeatureDefn that will have to be
1718 : * used when writing features to this dataset.
1719 : *
1720 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1721 : *
1722 : * Note: The bUnique flag has no effect on TABFiles. See the TABView class.
1723 : *
1724 : * Returns 0 on success, -1 on error.
1725 : **********************************************************************/
1726 4 : int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1727 : int nWidth /*=0*/, int nPrecision /*=0*/,
1728 : GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/, int bApproxOK)
1729 : {
1730 : OGRFieldDefn *poFieldDefn;
1731 4 : int nStatus = 0;
1732 4 : char *pszCleanName = NULL;
1733 : char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/
1734 4 : int nRenameNum = 1;
1735 :
1736 4 : if (m_eAccessMode != TABWrite)
1737 : {
1738 : CPLError(CE_Failure, CPLE_NotSupported,
1739 0 : "AddFieldNative() can be used only with Write access.");
1740 0 : return -1;
1741 : }
1742 :
1743 : /*-----------------------------------------------------------------
1744 : * Check that call happens at the right time in dataset's life.
1745 : *----------------------------------------------------------------*/
1746 4 : if (m_eAccessMode != TABWrite ||
1747 : m_nLastFeatureId > 0 || m_poDATFile == NULL)
1748 : {
1749 : CPLError(CE_Failure, CPLE_AssertionFailed,
1750 : "AddFieldNative() must be called after opening a new "
1751 0 : "dataset, but before writing the first feature to it.");
1752 0 : return -1;
1753 : }
1754 :
1755 : /*-----------------------------------------------------------------
1756 : * Create new OGRFeatureDefn if not done yet...
1757 : *----------------------------------------------------------------*/
1758 4 : if (m_poDefn== NULL)
1759 : {
1760 0 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
1761 0 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
1762 0 : CPLFree(pszFeatureClassName);
1763 : // Ref count defaults to 0... set it to 1
1764 0 : m_poDefn->Reference();
1765 : }
1766 :
1767 : /*-----------------------------------------------------------------
1768 : * Validate field width... must be <= 254
1769 : *----------------------------------------------------------------*/
1770 4 : if (nWidth > 254)
1771 : {
1772 : CPLError(CE_Warning, CPLE_IllegalArg,
1773 : "Invalid size (%d) for field '%s'. "
1774 0 : "Size must be 254 or less.", nWidth, pszName);
1775 0 : nWidth=254;
1776 : }
1777 :
1778 : /*-----------------------------------------------------------------
1779 : * Map fields with width=0 (variable length in OGR) to a valid default
1780 : *----------------------------------------------------------------*/
1781 4 : if (eMapInfoType == TABFDecimal && nWidth == 0)
1782 0 : nWidth=20;
1783 4 : else if (nWidth == 0)
1784 0 : nWidth=254; /* char fields */
1785 :
1786 : /*-----------------------------------------------------------------
1787 : * Make sure field name is valid... check for special chars, etc.
1788 : * (pszCleanName will have to be freed.)
1789 : *----------------------------------------------------------------*/
1790 4 : pszCleanName = TABCleanFieldName(pszName);
1791 :
1792 4 : if( !bApproxOK &&
1793 : ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 ||
1794 : !EQUAL(pszName, pszCleanName) ) )
1795 : {
1796 : CPLError( CE_Failure, CPLE_NotSupported,
1797 : "Failed to add field named '%s'",
1798 0 : pszName );
1799 : }
1800 :
1801 4 : strncpy(szNewFieldName, pszCleanName, 31);
1802 4 : szNewFieldName[31] = '\0';
1803 :
1804 8 : while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10)
1805 0 : sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ );
1806 :
1807 8 : while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100)
1808 0 : sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ );
1809 :
1810 4 : if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0)
1811 : {
1812 : CPLError( CE_Failure, CPLE_NotSupported,
1813 : "Too many field names like '%s' when truncated to 31 letters "
1814 0 : "for MapInfo format.", pszCleanName );
1815 : }
1816 :
1817 4 : if( !EQUAL(pszCleanName,szNewFieldName) )
1818 : {
1819 : CPLError( CE_Warning, CPLE_NotSupported,
1820 : "Normalized/laundered field name: '%s' to '%s'",
1821 : pszCleanName,
1822 0 : szNewFieldName );
1823 : }
1824 :
1825 : /*-----------------------------------------------------------------
1826 : * Map MapInfo native types to OGR types
1827 : *----------------------------------------------------------------*/
1828 4 : poFieldDefn = NULL;
1829 :
1830 4 : switch(eMapInfoType)
1831 : {
1832 : case TABFChar:
1833 : /*-------------------------------------------------
1834 : * CHAR type
1835 : *------------------------------------------------*/
1836 2 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
1837 2 : poFieldDefn->SetWidth(nWidth);
1838 2 : break;
1839 : case TABFInteger:
1840 : /*-------------------------------------------------
1841 : * INTEGER type
1842 : *------------------------------------------------*/
1843 1 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
1844 1 : break;
1845 : case TABFSmallInt:
1846 : /*-------------------------------------------------
1847 : * SMALLINT type
1848 : *------------------------------------------------*/
1849 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
1850 0 : break;
1851 : case TABFDecimal:
1852 : /*-------------------------------------------------
1853 : * DECIMAL type
1854 : *------------------------------------------------*/
1855 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
1856 0 : poFieldDefn->SetWidth(nWidth);
1857 0 : poFieldDefn->SetPrecision(nPrecision);
1858 0 : break;
1859 : case TABFFloat:
1860 : /*-------------------------------------------------
1861 : * FLOAT type
1862 : *------------------------------------------------*/
1863 1 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
1864 1 : break;
1865 : case TABFDate:
1866 : /*-------------------------------------------------
1867 : * DATE type (V450, returned as a string: "DD/MM/YYYY")
1868 : *------------------------------------------------*/
1869 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1870 : #ifdef MITAB_USE_OFTDATETIME
1871 0 : OFTDate);
1872 : #else
1873 : OFTString);
1874 : #endif
1875 0 : poFieldDefn->SetWidth(10);
1876 0 : m_nVersion = MAX(m_nVersion, 450);
1877 0 : break;
1878 : case TABFTime:
1879 : /*-------------------------------------------------
1880 : * TIME type (V900, returned as a string: "HH:MM:SS")
1881 : *------------------------------------------------*/
1882 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1883 : #ifdef MITAB_USE_OFTDATETIME
1884 0 : OFTTime);
1885 : #else
1886 : OFTString);
1887 : #endif
1888 0 : poFieldDefn->SetWidth(8);
1889 0 : m_nVersion = MAX(m_nVersion, 900);
1890 0 : break;
1891 : case TABFDateTime:
1892 : /*-------------------------------------------------
1893 : * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS")
1894 : *------------------------------------------------*/
1895 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1896 : #ifdef MITAB_USE_OFTDATETIME
1897 0 : OFTDateTime);
1898 : #else
1899 : OFTString);
1900 : #endif
1901 0 : poFieldDefn->SetWidth(19);
1902 0 : m_nVersion = MAX(m_nVersion, 900);
1903 0 : break;
1904 : case TABFLogical:
1905 : /*-------------------------------------------------
1906 : * LOGICAL type (value "T" or "F")
1907 : *------------------------------------------------*/
1908 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
1909 0 : poFieldDefn->SetWidth(1);
1910 0 : break;
1911 : default:
1912 : CPLError(CE_Failure, CPLE_NotSupported,
1913 0 : "Unsupported type for field %s", szNewFieldName);
1914 0 : CPLFree(pszCleanName);
1915 0 : return -1;
1916 : }
1917 :
1918 : /*-----------------------------------------------------
1919 : * Add the FieldDefn to the FeatureDefn
1920 : *----------------------------------------------------*/
1921 4 : m_poDefn->AddFieldDefn(poFieldDefn);
1922 4 : delete poFieldDefn;
1923 :
1924 : /*-----------------------------------------------------
1925 : * ... and pass field info to the .DAT file.
1926 : *----------------------------------------------------*/
1927 : nStatus = m_poDATFile->AddField(szNewFieldName, eMapInfoType,
1928 4 : nWidth, nPrecision);
1929 :
1930 : /*-----------------------------------------------------------------
1931 : * Extend the array to keep track of indexed fields (default=NOT indexed)
1932 : *----------------------------------------------------------------*/
1933 : m_panIndexNo = (int *)CPLRealloc(m_panIndexNo,
1934 4 : m_poDefn->GetFieldCount()*sizeof(int));
1935 4 : m_panIndexNo[m_poDefn->GetFieldCount()-1] = 0;
1936 :
1937 : /*-----------------------------------------------------------------
1938 : * Index the field if requested
1939 : *----------------------------------------------------------------*/
1940 4 : if (nStatus == 0 && bIndexed)
1941 0 : nStatus = SetFieldIndexed(m_poDefn->GetFieldCount()-1);
1942 :
1943 4 : CPLFree(pszCleanName);
1944 4 : return nStatus;
1945 : }
1946 :
1947 :
1948 : /**********************************************************************
1949 : * TABFile::GetNativeFieldType()
1950 : *
1951 : * Returns the native MapInfo field type for the specified field.
1952 : *
1953 : * Returns TABFUnknown if file is not opened, or if specified field index is
1954 : * invalid.
1955 : *
1956 : * Note that field ids are positive and start at 0.
1957 : **********************************************************************/
1958 4 : TABFieldType TABFile::GetNativeFieldType(int nFieldId)
1959 : {
1960 4 : if (m_poDATFile)
1961 : {
1962 4 : return m_poDATFile->GetFieldType(nFieldId);
1963 : }
1964 0 : return TABFUnknown;
1965 : }
1966 :
1967 :
1968 :
1969 : /**********************************************************************
1970 : * TABFile::GetFieldIndexNumber()
1971 : *
1972 : * Returns the field's index number that was specified in the .TAB header
1973 : * or 0 if the specified field is not indexed.
1974 : *
1975 : * Note that field ids are positive and start at 0
1976 : * and valid index ids are positive and start at 1.
1977 : **********************************************************************/
1978 8 : int TABFile::GetFieldIndexNumber(int nFieldId)
1979 : {
1980 8 : if (m_panIndexNo == NULL || nFieldId < 0 ||
1981 : m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields())
1982 0 : return 0; // no index
1983 :
1984 8 : return m_panIndexNo[nFieldId];
1985 : }
1986 :
1987 : /************************************************************************
1988 : * TABFile::SetFieldIndexed()
1989 : *
1990 : * Request that a field be indexed. This will create the .IND file if
1991 : * necessary, etc.
1992 : *
1993 : * Note that field ids are positive and start at 0.
1994 : *
1995 : * Returns 0 on success, -1 on error.
1996 : ************************************************************************/
1997 0 : int TABFile::SetFieldIndexed( int nFieldId )
1998 : {
1999 : /*-----------------------------------------------------------------
2000 : * Make sure things are OK
2001 : *----------------------------------------------------------------*/
2002 0 : if (m_pszFname == NULL || m_eAccessMode != TABWrite || m_poDefn == NULL)
2003 : {
2004 : CPLError(CE_Failure, CPLE_AssertionFailed,
2005 : "SetFieldIndexed() must be called after opening a new "
2006 0 : "dataset, but before writing the first feature to it.");
2007 0 : return -1;
2008 : }
2009 :
2010 0 : if (m_panIndexNo == NULL || nFieldId < 0 ||
2011 : m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields())
2012 : {
2013 : CPLError(CE_Failure, CPLE_AssertionFailed,
2014 0 : "Invalid field number in SetFieldIndexed().");
2015 0 : return -1;
2016 : }
2017 :
2018 : /*-----------------------------------------------------------------
2019 : * If field is already indexed then just return
2020 : *----------------------------------------------------------------*/
2021 0 : if (m_panIndexNo[nFieldId] != 0)
2022 0 : return 0; // Nothing to do
2023 :
2024 :
2025 : /*-----------------------------------------------------------------
2026 : * Create .IND file if it's not done yet.
2027 : *
2028 : * Note: We can pass the .TAB's filename directly and the
2029 : * TABINDFile class will automagically adjust the extension.
2030 : *----------------------------------------------------------------*/
2031 0 : if (m_poINDFile == NULL)
2032 : {
2033 0 : m_poINDFile = new TABINDFile;
2034 :
2035 0 : if ( m_poINDFile->Open(m_pszFname, "w", TRUE) != 0)
2036 : {
2037 : // File could not be opened...
2038 0 : delete m_poINDFile;
2039 0 : m_poINDFile = NULL;
2040 0 : return -1;
2041 : }
2042 : }
2043 :
2044 : /*-----------------------------------------------------------------
2045 : * Init new index.
2046 : *----------------------------------------------------------------*/
2047 : int nNewIndexNo;
2048 0 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId);
2049 :
2050 0 : if (poFieldDefn == NULL ||
2051 0 : (nNewIndexNo = m_poINDFile->CreateIndex(GetNativeFieldType(nFieldId),
2052 : poFieldDefn->GetWidth()) ) < 1)
2053 : {
2054 : // Failed... an error has already been reported.
2055 0 : return -1;
2056 : }
2057 :
2058 0 : m_panIndexNo[nFieldId] = nNewIndexNo;
2059 :
2060 0 : return 0;
2061 : }
2062 :
2063 : /************************************************************************
2064 : * TABFile::IsFieldIndexed()
2065 : *
2066 : * Returns TRUE if field is indexed, or FALSE otherwise.
2067 : ************************************************************************/
2068 0 : GBool TABFile::IsFieldIndexed( int nFieldId )
2069 : {
2070 0 : return (GetFieldIndexNumber(nFieldId) > 0 ? TRUE:FALSE);
2071 : }
2072 :
2073 :
2074 :
2075 : /**********************************************************************
2076 : * TABFile::GetINDFileRef()
2077 : *
2078 : * Opens the .IND file for this dataset and returns a reference to
2079 : * the handle.
2080 : * If the .IND file has already been opened then the same handle is
2081 : * returned directly.
2082 : * If the .IND file does not exist then the function silently returns NULL.
2083 : *
2084 : * Note that the returned TABINDFile handle is only a reference to an
2085 : * object that is owned by this class. Callers can use it but cannot
2086 : * destroy the object. The object will remain valid for as long as
2087 : * the TABFile will remain open.
2088 : **********************************************************************/
2089 0 : TABINDFile *TABFile::GetINDFileRef()
2090 : {
2091 0 : if (m_pszFname == NULL)
2092 0 : return NULL;
2093 :
2094 0 : if (m_eAccessMode == TABRead && m_poINDFile == NULL)
2095 : {
2096 : /*-------------------------------------------------------------
2097 : * File is not opened yet... do it now.
2098 : *
2099 : * Note: We can pass the .TAB's filename directly and the
2100 : * TABINDFile class will automagically adjust the extension.
2101 : *------------------------------------------------------------*/
2102 0 : m_poINDFile = new TABINDFile;
2103 :
2104 0 : if ( m_poINDFile->Open(m_pszFname, "r", TRUE) != 0)
2105 : {
2106 : // File could not be opened... probably does not exist
2107 0 : delete m_poINDFile;
2108 0 : m_poINDFile = NULL;
2109 : }
2110 0 : else if (m_panIndexNo && m_poDATFile)
2111 : {
2112 : /*---------------------------------------------------------
2113 : * Pass type information for each indexed field.
2114 : *--------------------------------------------------------*/
2115 0 : for(int i=0; i<m_poDATFile->GetNumFields(); i++)
2116 : {
2117 0 : if (m_panIndexNo[i] > 0)
2118 : {
2119 : m_poINDFile->SetIndexFieldType(m_panIndexNo[i],
2120 0 : GetNativeFieldType(i));
2121 : }
2122 : }
2123 : }
2124 : }
2125 :
2126 0 : return m_poINDFile;
2127 : }
2128 :
2129 :
2130 : /**********************************************************************
2131 : * TABFile::SetBounds()
2132 : *
2133 : * Set projection coordinates bounds of the newly created dataset.
2134 : *
2135 : * This function must be called after creating a new dataset and before any
2136 : * feature can be written to it.
2137 : *
2138 : * Returns 0 on success, -1 on error.
2139 : **********************************************************************/
2140 2 : int TABFile::SetBounds(double dXMin, double dYMin,
2141 : double dXMax, double dYMax)
2142 : {
2143 2 : if (m_eAccessMode != TABWrite)
2144 : {
2145 : CPLError(CE_Failure, CPLE_NotSupported,
2146 0 : "SetBounds() can be used only with Write access.");
2147 0 : return -1;
2148 : }
2149 :
2150 : /*-----------------------------------------------------------------
2151 : * Check that dataset has been created but no feature set yet.
2152 : *----------------------------------------------------------------*/
2153 2 : if (m_poMAPFile && m_nLastFeatureId < 1)
2154 : {
2155 2 : m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
2156 :
2157 2 : m_bBoundsSet = TRUE;
2158 : }
2159 : else
2160 : {
2161 : CPLError(CE_Failure, CPLE_AssertionFailed,
2162 : "SetBounds() can be called only after dataset has been "
2163 0 : "created and before any feature is set.");
2164 0 : return -1;
2165 : }
2166 :
2167 2 : return 0;
2168 : }
2169 :
2170 :
2171 : /**********************************************************************
2172 : * TABFile::GetBounds()
2173 : *
2174 : * Fetch projection coordinates bounds of a dataset.
2175 : *
2176 : * The bForce flag has no effect on TAB files since the bounds are
2177 : * always in the header.
2178 : *
2179 : * Returns 0 on success, -1 on error.
2180 : **********************************************************************/
2181 0 : int TABFile::GetBounds(double &dXMin, double &dYMin,
2182 : double &dXMax, double &dYMax,
2183 : GBool /*bForce = TRUE*/)
2184 : {
2185 : TABMAPHeaderBlock *poHeader;
2186 :
2187 0 : if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
2188 : {
2189 : /*-------------------------------------------------------------
2190 : * Projection bounds correspond to the +/- 1e9 integer coord. limits
2191 : *------------------------------------------------------------*/
2192 : double dX0, dX1, dY0, dY1;
2193 : m_poMAPFile->Int2Coordsys(-1000000000, -1000000000,
2194 0 : dX0, dY0);
2195 : m_poMAPFile->Int2Coordsys(1000000000, 1000000000,
2196 0 : dX1, dY1);
2197 : /*-------------------------------------------------------------
2198 : * ... and make sure that Min < Max
2199 : *------------------------------------------------------------*/
2200 0 : dXMin = MIN(dX0, dX1);
2201 0 : dXMax = MAX(dX0, dX1);
2202 0 : dYMin = MIN(dY0, dY1);
2203 0 : dYMax = MAX(dY0, dY1);
2204 : }
2205 : else
2206 : {
2207 : CPLError(CE_Failure, CPLE_AppDefined,
2208 0 : "GetBounds() can be called only after dataset has been opened.");
2209 0 : return -1;
2210 : }
2211 :
2212 0 : return 0;
2213 : }
2214 :
2215 :
2216 : /**********************************************************************
2217 : * TABFile::GetExtent()
2218 : *
2219 : * Fetch extent of the data currently stored in the dataset.
2220 : *
2221 : * The bForce flag has no effect on TAB files since that value is
2222 : * always in the header.
2223 : *
2224 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
2225 : **********************************************************************/
2226 0 : OGRErr TABFile::GetExtent (OGREnvelope *psExtent, int bForce)
2227 : {
2228 : TABMAPHeaderBlock *poHeader;
2229 :
2230 0 : if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
2231 : {
2232 : double dX0, dX1, dY0, dY1;
2233 : /*-------------------------------------------------------------
2234 : * Fetch extent of the data from the .map header block
2235 : * this value is different from the projection bounds.
2236 : *------------------------------------------------------------*/
2237 : m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin,
2238 0 : dX0, dY0);
2239 : m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax,
2240 0 : dX1, dY1);
2241 :
2242 : /*-------------------------------------------------------------
2243 : * ... and make sure that Min < Max
2244 : *------------------------------------------------------------*/
2245 0 : psExtent->MinX = MIN(dX0, dX1);
2246 0 : psExtent->MaxX = MAX(dX0, dX1);
2247 0 : psExtent->MinY = MIN(dY0, dY1);
2248 0 : psExtent->MaxY = MAX(dY0, dY1);
2249 :
2250 0 : return OGRERR_NONE;
2251 : }
2252 :
2253 0 : return OGRERR_FAILURE;
2254 : }
2255 :
2256 : /**********************************************************************
2257 : * TABFile::GetFeatureCountByType()
2258 : *
2259 : * Return number of features of each type.
2260 : *
2261 : * Note that the sum of the 4 returned values may be different from
2262 : * the total number of features since features with NONE geometry
2263 : * are not taken into account here.
2264 : *
2265 : * Note: the bForce flag has nmo effect on .TAB files since the info
2266 : * is always in the header.
2267 : *
2268 : * Returns 0 on success, or silently returns -1 (with no error) if this
2269 : * information is not available.
2270 : **********************************************************************/
2271 2 : int TABFile::GetFeatureCountByType(int &numPoints, int &numLines,
2272 : int &numRegions, int &numTexts,
2273 : GBool /* bForce = TRUE*/ )
2274 : {
2275 : TABMAPHeaderBlock *poHeader;
2276 :
2277 2 : if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
2278 : {
2279 2 : numPoints = poHeader->m_numPointObjects;
2280 2 : numLines = poHeader->m_numLineObjects;
2281 2 : numRegions = poHeader->m_numRegionObjects;
2282 2 : numTexts = poHeader->m_numTextObjects;
2283 : }
2284 : else
2285 : {
2286 0 : numPoints = numLines = numRegions = numTexts = 0;
2287 0 : return -1;
2288 : }
2289 :
2290 2 : return 0;
2291 : }
2292 :
2293 :
2294 : /**********************************************************************
2295 : * TABFile::SetMIFCoordSys()
2296 : *
2297 : * Set projection for a new file using a MIF coordsys string.
2298 : *
2299 : * This function must be called after creating a new dataset and before any
2300 : * feature can be written to it.
2301 : *
2302 : * Returns 0 on success, -1 on error.
2303 : **********************************************************************/
2304 0 : int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys)
2305 : {
2306 0 : if (m_eAccessMode != TABWrite)
2307 : {
2308 : CPLError(CE_Failure, CPLE_NotSupported,
2309 0 : "SetMIFCoordSys() can be used only with Write access.");
2310 0 : return -1;
2311 : }
2312 :
2313 : /*-----------------------------------------------------------------
2314 : * Check that dataset has been created but no feature set yet.
2315 : *----------------------------------------------------------------*/
2316 0 : if (m_poMAPFile && m_nLastFeatureId < 1)
2317 : {
2318 : OGRSpatialReference *poSpatialRef;
2319 :
2320 0 : poSpatialRef = MITABCoordSys2SpatialRef( pszMIFCoordSys );
2321 :
2322 0 : if (poSpatialRef)
2323 : {
2324 : double dXMin, dYMin, dXMax, dYMax;
2325 0 : if (SetSpatialRef(poSpatialRef) == 0)
2326 : {
2327 0 : if (MITABExtractCoordSysBounds(pszMIFCoordSys,
2328 : dXMin, dYMin,
2329 : dXMax, dYMax) == TRUE)
2330 : {
2331 : // If the coordsys string contains bounds, then use them
2332 0 : if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0)
2333 : {
2334 : // Failed Setting Bounds... an error should have
2335 : // been already reported.
2336 0 : return -1;
2337 : }
2338 : }
2339 : }
2340 : else
2341 : {
2342 : // Failed setting poSpatialRef... and error should have
2343 : // been reported.
2344 0 : return -1;
2345 : }
2346 :
2347 : // Release our handle on poSpatialRef
2348 0 : if( poSpatialRef->Dereference() == 0 )
2349 0 : delete poSpatialRef;
2350 : }
2351 : }
2352 : else
2353 : {
2354 : CPLError(CE_Failure, CPLE_AssertionFailed,
2355 : "SetMIFCoordSys() can be called only after dataset has been "
2356 0 : "created and before any feature is set.");
2357 0 : return -1;
2358 : }
2359 :
2360 0 : return 0;
2361 : }
2362 :
2363 : /**********************************************************************
2364 : * TABFile::SetProjInfo()
2365 : *
2366 : * Set projection for a new file using a TABProjInfo structure.
2367 : *
2368 : * This function must be called after creating a new dataset and before any
2369 : * feature can be written to it.
2370 : *
2371 : * This call will also trigger a lookup of default bounds for the specified
2372 : * projection (except nonearth), and reset the m_bBoundsValid flag.
2373 : *
2374 : * Returns 0 on success, -1 on error.
2375 : **********************************************************************/
2376 1 : int TABFile::SetProjInfo(TABProjInfo *poPI)
2377 : {
2378 1 : if (m_eAccessMode != TABWrite)
2379 : {
2380 : CPLError(CE_Failure, CPLE_NotSupported,
2381 0 : "SetProjInfo() can be used only with Write access.");
2382 0 : return -1;
2383 : }
2384 :
2385 : /*-----------------------------------------------------------------
2386 : * Check that dataset has been created but no feature set yet.
2387 : *----------------------------------------------------------------*/
2388 1 : if (m_poMAPFile && m_nLastFeatureId < 1)
2389 : {
2390 1 : if (m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ) != 0)
2391 0 : return -1;
2392 : }
2393 : else
2394 : {
2395 : CPLError(CE_Failure, CPLE_AssertionFailed,
2396 : "SetProjInfo() can be called only after dataset has been "
2397 0 : "created and before any feature is set.");
2398 0 : return -1;
2399 : }
2400 :
2401 : /*-----------------------------------------------------------------
2402 : * Lookup default bounds and reset m_bBoundsSet flag
2403 : *----------------------------------------------------------------*/
2404 : double dXMin, dYMin, dXMax, dYMax;
2405 :
2406 1 : m_bBoundsSet = FALSE;
2407 1 : if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax) == TRUE)
2408 : {
2409 1 : SetBounds(dXMin, dYMin, dXMax, dYMax);
2410 : }
2411 :
2412 1 : return 0;
2413 : }
2414 :
2415 :
2416 : /************************************************************************/
2417 : /* TestCapability() */
2418 : /************************************************************************/
2419 :
2420 0 : int TABFile::TestCapability( const char * pszCap )
2421 :
2422 : {
2423 0 : if( EQUAL(pszCap,OLCRandomRead) )
2424 0 : return TRUE;
2425 :
2426 0 : else if( EQUAL(pszCap,OLCSequentialWrite) )
2427 0 : return m_eAccessMode == TABWrite;
2428 :
2429 0 : else if( EQUAL(pszCap,OLCRandomWrite) )
2430 0 : return FALSE;
2431 :
2432 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
2433 : return m_poFilterGeom == NULL
2434 0 : && m_poAttrQuery == NULL;
2435 :
2436 0 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
2437 0 : return TRUE;
2438 :
2439 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
2440 0 : return TRUE;
2441 :
2442 0 : else if( EQUAL(pszCap,OLCCreateField) )
2443 0 : return TRUE;
2444 :
2445 : else
2446 0 : return FALSE;
2447 : }
2448 :
2449 : /**********************************************************************
2450 : * TABFile::Dump()
2451 : *
2452 : * Dump block contents... available only in DEBUG mode.
2453 : **********************************************************************/
2454 : #ifdef DEBUG
2455 :
2456 0 : void TABFile::Dump(FILE *fpOut /*=NULL*/)
2457 : {
2458 0 : if (fpOut == NULL)
2459 0 : fpOut = stdout;
2460 :
2461 0 : fprintf(fpOut, "----- TABFile::Dump() -----\n");
2462 :
2463 0 : if (m_poMAPFile == NULL)
2464 : {
2465 0 : fprintf(fpOut, "File is not opened.\n");
2466 : }
2467 : else
2468 : {
2469 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
2470 0 : fprintf(fpOut, "Associated TABLE file ...\n\n");
2471 0 : m_poDATFile->Dump(fpOut);
2472 0 : fprintf(fpOut, "... end of TABLE file dump.\n\n");
2473 0 : if( GetSpatialRef() != NULL )
2474 : {
2475 : char *pszWKT;
2476 :
2477 0 : GetSpatialRef()->exportToWkt( &pszWKT );
2478 0 : fprintf( fpOut, "SRS = %s\n", pszWKT );
2479 0 : OGRFree( pszWKT );
2480 : }
2481 0 : fprintf(fpOut, "Associated .MAP file ...\n\n");
2482 0 : m_poMAPFile->Dump(fpOut);
2483 0 : fprintf(fpOut, "... end of .MAP file dump.\n\n");
2484 :
2485 : }
2486 :
2487 0 : fflush(fpOut);
2488 0 : }
2489 :
2490 : #endif // DEBUG
|