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 12 : TABFile::TABFile()
119 : {
120 12 : m_eAccessMode = TABRead;
121 12 : m_pszFname = NULL;
122 12 : m_papszTABFile = NULL;
123 12 : m_nVersion = 300;
124 12 : m_eTableType = TABTableNative;
125 :
126 12 : m_poMAPFile = NULL;
127 12 : m_poDATFile = NULL;
128 12 : m_poINDFile = NULL;
129 12 : m_poDefn = NULL;
130 12 : m_poSpatialRef = NULL;
131 12 : m_poCurFeature = NULL;
132 12 : m_nCurFeatureId = 0;
133 12 : m_nLastFeatureId = 0;
134 12 : m_panIndexNo = NULL;
135 :
136 12 : bUseSpatialTraversal = FALSE;
137 :
138 12 : m_panMatchingFIDs = NULL;
139 12 : m_iMatchingFID = 0;
140 12 : }
141 :
142 : /**********************************************************************
143 : * TABFile::~TABFile()
144 : *
145 : * Destructor.
146 : **********************************************************************/
147 12 : TABFile::~TABFile()
148 : {
149 12 : Close();
150 12 : }
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 20 : void TABFile::ResetReading()
169 : {
170 20 : CPLFree(m_panMatchingFIDs);
171 20 : m_panMatchingFIDs = NULL;
172 20 : m_iMatchingFID = 0;
173 :
174 20 : m_nCurFeatureId = 0;
175 20 : if( m_poMAPFile != NULL )
176 20 : 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 20 : if( m_poMAPFile )
184 : {
185 20 : bUseSpatialTraversal = FALSE;
186 :
187 20 : m_poMAPFile->ResetCoordFilter();
188 :
189 20 : if( m_poFilterGeom != NULL )
190 : {
191 2 : OGREnvelope sEnvelope;
192 : TABVertex sMin, sMax;
193 : TABMAPHeaderBlock *poHeader;
194 :
195 2 : poHeader = m_poMAPFile->GetHeaderBlock();
196 :
197 2 : m_poFilterGeom->getEnvelope( &sEnvelope );
198 2 : m_poMAPFile->GetCoordFilter( sMin, sMax );
199 :
200 2 : if( sEnvelope.MinX > sMin.x
201 : || sEnvelope.MinY > sMin.y
202 : || sEnvelope.MaxX < sMax.x
203 : || sEnvelope.MaxY < sMax.y )
204 : {
205 2 : bUseSpatialTraversal = TRUE;
206 2 : sMin.x = sEnvelope.MinX;
207 2 : sMin.y = sEnvelope.MinY;
208 2 : sMax.x = sEnvelope.MaxX;
209 2 : sMax.y = sEnvelope.MaxY;
210 2 : m_poMAPFile->SetCoordFilter( sMin, sMax );
211 : }
212 : }
213 : }
214 20 : }
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 12 : int TABFile::Open(const char *pszFname, const char *pszAccess,
239 : GBool bTestOpenNoError /*=FALSE*/ )
240 : {
241 12 : char *pszTmpFname = NULL;
242 12 : int nFnameLen = 0;
243 :
244 12 : CPLErrorReset();
245 :
246 12 : 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 12 : if (EQUALN(pszAccess, "r", 1))
258 : {
259 6 : m_eAccessMode = TABRead;
260 6 : pszAccess = "rb";
261 : }
262 6 : else if (EQUALN(pszAccess, "w", 1))
263 : {
264 6 : m_eAccessMode = TABWrite;
265 6 : 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 12 : m_pszFname = CPLStrdup(pszFname);
282 12 : nFnameLen = strlen(m_pszFname);
283 :
284 12 : 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 24 : 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 12 : 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 12 : 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 12 : TABAdjustFilenameExtension(m_pszFname);
316 : #endif
317 :
318 : /*-----------------------------------------------------------------
319 : * Handle .TAB file... depends on access mode.
320 : *----------------------------------------------------------------*/
321 12 : 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 6 : m_papszTABFile = TAB_CSLLoad(m_pszFname);
328 6 : 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 6 : 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 6 : m_nVersion = 300;
369 6 : CPLFree(m_pszCharset);
370 6 : m_pszCharset = CPLStrdup("Neutral");
371 6 : m_eTableType = TABTableNative;
372 :
373 : /*-------------------------------------------------------------
374 : * Do initial setup of feature definition.
375 : *------------------------------------------------------------*/
376 6 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
377 6 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
378 6 : m_poDefn->Reference();
379 6 : CPLFree(pszFeatureClassName);
380 : }
381 :
382 :
383 : /*-----------------------------------------------------------------
384 : * Open .DAT file (or .DBF)
385 : *----------------------------------------------------------------*/
386 12 : 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 12 : if (m_eTableType == TABTableDBF)
396 0 : strcpy(pszTmpFname+nFnameLen-4, ".dbf");
397 : else // Default is NATIVE
398 12 : strcpy(pszTmpFname+nFnameLen-4, ".dat");
399 : }
400 :
401 : #ifndef _WIN32
402 12 : TABAdjustFilenameExtension(pszTmpFname);
403 : #endif
404 :
405 12 : m_poDATFile = new TABDATFile;
406 :
407 12 : 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 12 : m_nLastFeatureId = m_poDATFile->GetNumRecords();
419 :
420 :
421 : /*-----------------------------------------------------------------
422 : * Parse .TAB file field defs and build FeatureDefn (only in read access)
423 : *----------------------------------------------------------------*/
424 12 : 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 12 : if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".DAT")==0)
443 0 : strcpy(pszTmpFname+nFnameLen-4, ".MAP");
444 : else
445 12 : strcpy(pszTmpFname+nFnameLen-4, ".map");
446 :
447 : #ifndef _WIN32
448 12 : TABAdjustFilenameExtension(pszTmpFname);
449 : #endif
450 :
451 12 : m_poMAPFile = new TABMAPFile;
452 12 : 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 6 : 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 6 : int numPoints=0, numRegions=0, numTexts=0, numLines=0;
477 :
478 6 : GetFeatureCountByType( numPoints, numLines, numRegions, numTexts);
479 :
480 6 : numPoints += numTexts;
481 6 : if( numPoints > 0 && numLines == 0 && numRegions == 0 )
482 0 : m_poDefn->SetGeomType( wkbPoint );
483 6 : 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 6 : 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 12 : CPLXMLNode *psRoot = CPLCreateXMLNode( NULL, CXT_Element, "OGRMILayerAttrIndex" );
505 12 : CPLCreateXMLElementAndValue( psRoot, "MIIDFilename", CPLResetExtension( pszFname, "IND" ) );
506 12 : OGRFeatureDefn *poLayerDefn = GetLayerDefn();
507 12 : int iField, iIndexIndex, bHasIndex = 0;
508 28 : for( iField = 0; iField < poLayerDefn->GetFieldCount(); iField++ )
509 : {
510 16 : iIndexIndex = GetFieldIndexNumber(iField);
511 16 : 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 12 : if (bHasIndex)
523 : {
524 0 : char *pszRawXML = CPLSerializeXMLTree( psRoot );
525 0 : InitializeIndexSupport( pszRawXML );
526 0 : CPLFree( pszRawXML );
527 : }
528 :
529 12 : CPLDestroyXMLNode( psRoot );
530 :
531 12 : CPLFree(pszTmpFname);
532 12 : 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 12 : 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 6 : int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError)
555 : {
556 6 : int iLine, numLines, numFields = 0;
557 6 : char **papszTok=NULL;
558 6 : GBool bInsideTableDef = FALSE, bFoundTableFields=FALSE;
559 :
560 6 : 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 6 : numLines = CSLCount(m_papszTABFile);
568 :
569 64 : for(iLine=0; iLine<numLines; iLine++)
570 : {
571 : /*-------------------------------------------------------------
572 : * Tokenize the next .TAB line, and check first keyword
573 : *------------------------------------------------------------*/
574 58 : CSLDestroy(papszTok);
575 58 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
576 116 : TRUE, FALSE);
577 58 : if (CSLCount(papszTok) < 2)
578 12 : continue; // All interesting lines have at least 2 tokens
579 :
580 46 : if (EQUAL(papszTok[0], "!version"))
581 : {
582 6 : m_nVersion = atoi(papszTok[1]);
583 6 : 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 40 : 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 40 : else if (EQUAL(papszTok[0], "!charset"))
603 : {
604 6 : CPLFree(m_pszCharset);
605 6 : m_pszCharset = CPLStrdup(papszTok[1]);
606 : }
607 46 : else if (EQUAL(papszTok[0], "Definition") &&
608 6 : EQUAL(papszTok[1], "Table") )
609 : {
610 6 : bInsideTableDef = TRUE;
611 : }
612 52 : else if (bInsideTableDef && !bFoundTableFields &&
613 18 : (EQUAL(papszTok[0], "Type") || EQUAL(papszTok[0],"FORMAT:")) )
614 : {
615 12 : if (EQUAL(papszTok[1], "NATIVE") || EQUAL(papszTok[1], "LINKED"))
616 6 : 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 28 : else if (bInsideTableDef && !bFoundTableFields &&
632 6 : (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 6 : bFoundTableFields = TRUE;
640 6 : numFields = atoi(papszTok[1]);
641 :
642 6 : 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 6 : bInsideTableDef = FALSE;
654 : }/* end of fields section*/
655 : else
656 : {
657 : // Simply Ignore unrecognized lines
658 : }
659 : }
660 :
661 6 : CSLDestroy(papszTok);
662 :
663 6 : if (m_pszCharset == NULL)
664 0 : m_pszCharset = CPLStrdup("Neutral");
665 :
666 6 : 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 6 : 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 6 : int TABFile::ParseTABFileFields()
692 : {
693 6 : int iLine, numLines=0, numTok, nStatus;
694 6 : char **papszTok=NULL;
695 : OGRFieldDefn *poFieldDefn;
696 :
697 6 : 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 6 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
705 6 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
706 6 : CPLFree(pszFeatureClassName);
707 : // Ref count defaults to 0... set it to 1
708 6 : m_poDefn->Reference();
709 :
710 : /*-------------------------------------------------------------
711 : * Scan for fields.
712 : *------------------------------------------------------------*/
713 6 : numLines = CSLCount(m_papszTABFile);
714 42 : for(iLine=0; iLine<numLines; iLine++)
715 : {
716 : /*-------------------------------------------------------------
717 : * Tokenize the next .TAB line, and check first keyword
718 : *------------------------------------------------------------*/
719 42 : const char *pszStr = m_papszTABFile[iLine];
720 108 : while(*pszStr != '\0' && isspace((unsigned char)*pszStr))
721 24 : pszStr++;
722 :
723 42 : if (EQUALN(pszStr, "Fields", 6))
724 : {
725 : /*---------------------------------------------------------
726 : * We found the list of table fields
727 : *--------------------------------------------------------*/
728 : int iField, numFields;
729 6 : numFields = atoi(pszStr+7);
730 6 : 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 6 : m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int));
742 :
743 6 : iLine++;
744 6 : poFieldDefn = NULL;
745 22 : 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 16 : CSLDestroy(papszTok);
757 16 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine],
758 : " \t(),;",
759 32 : TRUE, FALSE);
760 16 : numTok = CSLCount(papszTok);
761 16 : nStatus = -1;
762 16 : CPLAssert(m_poDefn);
763 20 : if (numTok >= 3 && EQUAL(papszTok[1], "char"))
764 : {
765 : /*-------------------------------------------------
766 : * CHAR type
767 : *------------------------------------------------*/
768 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
769 4 : papszTok[0],
770 : TABFChar,
771 4 : atoi(papszTok[2]),
772 12 : 0);
773 4 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString);
774 4 : poFieldDefn->SetWidth(atoi(papszTok[2]));
775 : }
776 18 : else if (numTok >= 2 && EQUAL(papszTok[1], "integer"))
777 : {
778 : /*-------------------------------------------------
779 : * INTEGER type
780 : *------------------------------------------------*/
781 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
782 6 : papszTok[0],
783 : TABFInteger,
784 : 0,
785 6 : 0);
786 6 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger);
787 10 : if( numTok > 2 && atoi(papszTok[2]) > 0 )
788 4 : poFieldDefn->SetWidth( atoi(papszTok[2]) );
789 : }
790 6 : else if (numTok >= 2 && EQUAL(papszTok[1], "smallint"))
791 : {
792 : /*-------------------------------------------------
793 : * SMALLINT type
794 : *------------------------------------------------*/
795 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
796 0 : papszTok[0],
797 : TABFSmallInt,
798 : 0,
799 0 : 0);
800 0 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger);
801 0 : if( numTok > 2 && atoi(papszTok[2]) > 0 )
802 0 : poFieldDefn->SetWidth( atoi(papszTok[2]) );
803 : }
804 8 : else if (numTok >= 4 && EQUAL(papszTok[1], "decimal"))
805 : {
806 : /*-------------------------------------------------
807 : * DECIMAL type
808 : *------------------------------------------------*/
809 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
810 2 : papszTok[0],
811 : TABFDecimal,
812 2 : atoi(papszTok[2]),
813 6 : atoi(papszTok[3]));
814 2 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal);
815 2 : poFieldDefn->SetWidth(atoi(papszTok[2]));
816 2 : poFieldDefn->SetPrecision(atoi(papszTok[3]));
817 : }
818 8 : else if (numTok >= 2 && EQUAL(papszTok[1], "float"))
819 : {
820 : /*-------------------------------------------------
821 : * FLOAT type
822 : *------------------------------------------------*/
823 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
824 4 : papszTok[0],
825 : TABFFloat,
826 4 : 0, 0);
827 4 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal);
828 : }
829 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "date"))
830 : {
831 : /*-------------------------------------------------
832 : * DATE type (returned as a string: "DD/MM/YYYY")
833 : *------------------------------------------------*/
834 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
835 0 : papszTok[0],
836 : TABFDate,
837 : 0,
838 0 : 0);
839 0 : poFieldDefn = new OGRFieldDefn(papszTok[0],
840 : #ifdef MITAB_USE_OFTDATETIME
841 0 : OFTDate);
842 : #else
843 : OFTString);
844 : #endif
845 0 : poFieldDefn->SetWidth(10);
846 : }
847 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "time"))
848 : {
849 : /*-------------------------------------------------
850 : * TIME type (returned as a string: "HH:MM:SS")
851 : *------------------------------------------------*/
852 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
853 0 : papszTok[0],
854 : TABFTime,
855 : 0,
856 0 : 0);
857 0 : poFieldDefn = new OGRFieldDefn(papszTok[0],
858 : #ifdef MITAB_USE_OFTDATETIME
859 0 : OFTTime);
860 : #else
861 : OFTString);
862 : #endif
863 0 : poFieldDefn->SetWidth(9);
864 : }
865 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "datetime"))
866 : {
867 : /*-------------------------------------------------
868 : * DATETIME type (returned as a string: "DD/MM/YYYY HH:MM:SS")
869 : *------------------------------------------------*/
870 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
871 0 : papszTok[0],
872 : TABFDateTime,
873 : 0,
874 0 : 0);
875 0 : poFieldDefn = new OGRFieldDefn(papszTok[0],
876 : #ifdef MITAB_USE_OFTDATETIME
877 0 : OFTDateTime);
878 : #else
879 : OFTString);
880 : #endif
881 0 : poFieldDefn->SetWidth(19);
882 : }
883 0 : else if (numTok >= 2 && EQUAL(papszTok[1], "logical"))
884 : {
885 : /*-------------------------------------------------
886 : * LOGICAL type (value "T" or "F")
887 : *------------------------------------------------*/
888 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField,
889 0 : papszTok[0],
890 : TABFLogical,
891 : 0,
892 0 : 0);
893 0 : poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString);
894 0 : poFieldDefn->SetWidth(1);
895 : }
896 : else
897 0 : nStatus = -1; // Unrecognized field type or line corrupt
898 :
899 16 : if (nStatus != 0)
900 : {
901 : CPLError(CE_Failure, CPLE_FileIO,
902 : "Failed to parse field definition at line %d in file %s",
903 0 : iLine+1, m_pszFname);
904 0 : CSLDestroy(papszTok);
905 0 : return -1;
906 : }
907 : /*-----------------------------------------------------
908 : * Keep track of index number if present
909 : *----------------------------------------------------*/
910 16 : if (numTok >= 4 && EQUAL(papszTok[numTok-2], "index"))
911 : {
912 0 : m_panIndexNo[iField] = atoi(papszTok[numTok-1]);
913 : }
914 : else
915 : {
916 16 : m_panIndexNo[iField] = 0;
917 : }
918 :
919 : /*-----------------------------------------------------
920 : * Add the FieldDefn to the FeatureDefn and continue with
921 : * the next one.
922 : *----------------------------------------------------*/
923 16 : m_poDefn->AddFieldDefn(poFieldDefn);
924 : // AddFieldDenf() takes a copy, so we delete the original
925 16 : if (poFieldDefn) delete poFieldDefn;
926 16 : poFieldDefn = NULL;
927 : }
928 :
929 : /*---------------------------------------------------------
930 : * OK, we're done... end the loop now.
931 : *--------------------------------------------------------*/
932 6 : break;
933 : }/* end of fields section*/
934 : else
935 : {
936 : // Simply Ignore unrecognized lines
937 : }
938 :
939 : }
940 :
941 6 : CSLDestroy(papszTok);
942 :
943 6 : if (m_poDefn->GetFieldCount() == 0)
944 : {
945 : CPLError(CE_Failure, CPLE_NotSupported,
946 : "%s contains no table field definition. "
947 : "This type of .TAB file cannot be read by this library.",
948 0 : m_pszFname);
949 0 : return -1;
950 : }
951 :
952 6 : return 0;
953 : }
954 :
955 : /**********************************************************************
956 : * TABFile::WriteTABFile()
957 : *
958 : * Generate the .TAB file using mainly the attribute fields definition.
959 : *
960 : * This private method should be used only during the Close() call with
961 : * write access mode.
962 : *
963 : * Returns 0 on success, -1 on error.
964 : **********************************************************************/
965 6 : int TABFile::WriteTABFile()
966 : {
967 : FILE *fp;
968 :
969 6 : if (m_eAccessMode != TABWrite)
970 : {
971 : CPLError(CE_Failure, CPLE_NotSupported,
972 0 : "WriteTABFile() can be used only with Write access.");
973 0 : return -1;
974 : }
975 :
976 6 : if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL)
977 : {
978 6 : fprintf(fp, "!table\n");
979 6 : fprintf(fp, "!version %d\n", m_nVersion);
980 6 : fprintf(fp, "!charset %s\n", m_pszCharset);
981 6 : fprintf(fp, "\n");
982 :
983 6 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
984 : {
985 : int iField;
986 : OGRFieldDefn *poFieldDefn;
987 : const char *pszFieldType;
988 :
989 6 : fprintf(fp, "Definition Table\n");
990 6 : fprintf(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset);
991 6 : fprintf(fp, " Fields %d\n", m_poDefn->GetFieldCount());
992 :
993 22 : for(iField=0; iField<m_poDefn->GetFieldCount(); iField++)
994 : {
995 16 : poFieldDefn = m_poDefn->GetFieldDefn(iField);
996 16 : switch(GetNativeFieldType(iField))
997 : {
998 : case TABFChar:
999 : pszFieldType = CPLSPrintf("Char (%d)",
1000 4 : poFieldDefn->GetWidth());
1001 4 : break;
1002 : case TABFDecimal:
1003 : pszFieldType = CPLSPrintf("Decimal (%d,%d)",
1004 : poFieldDefn->GetWidth(),
1005 2 : poFieldDefn->GetPrecision());
1006 2 : break;
1007 : case TABFInteger:
1008 6 : if( poFieldDefn->GetWidth() == 0 )
1009 2 : pszFieldType = "Integer";
1010 : else
1011 : pszFieldType = CPLSPrintf("Integer (%d)",
1012 4 : poFieldDefn->GetWidth());
1013 6 : break;
1014 : case TABFSmallInt:
1015 0 : if( poFieldDefn->GetWidth() == 0 )
1016 0 : pszFieldType = "SmallInt";
1017 : else
1018 : pszFieldType = CPLSPrintf("SmallInt (%d)",
1019 0 : poFieldDefn->GetWidth());
1020 0 : break;
1021 : case TABFFloat:
1022 4 : pszFieldType = "Float";
1023 4 : break;
1024 : case TABFLogical:
1025 0 : pszFieldType = "Logical";
1026 0 : break;
1027 : case TABFDate:
1028 0 : pszFieldType = "Date";
1029 0 : break;
1030 : case TABFTime:
1031 0 : pszFieldType = "Time";
1032 0 : break;
1033 : case TABFDateTime:
1034 0 : pszFieldType = "DateTime";
1035 0 : break;
1036 : default:
1037 : // Unsupported field type!!! This should never happen.
1038 : CPLError(CE_Failure, CPLE_AssertionFailed,
1039 0 : "WriteTABFile(): Unsupported field type");
1040 0 : VSIFClose(fp);
1041 0 : return -1;
1042 : }
1043 :
1044 16 : if (GetFieldIndexNumber(iField) == 0)
1045 : {
1046 : fprintf(fp, " %s %s ;\n", poFieldDefn->GetNameRef(),
1047 16 : pszFieldType );
1048 : }
1049 : else
1050 : {
1051 : fprintf(fp, " %s %s Index %d ;\n",
1052 : poFieldDefn->GetNameRef(), pszFieldType,
1053 0 : GetFieldIndexNumber(iField) );
1054 : }
1055 :
1056 : }
1057 : }
1058 : else
1059 : {
1060 0 : fprintf(fp, "Definition Table\n");
1061 0 : fprintf(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset);
1062 0 : fprintf(fp, " Fields 1\n");
1063 0 : fprintf(fp, " FID Integer ;\n" );
1064 : }
1065 :
1066 6 : VSIFClose(fp);
1067 : }
1068 : else
1069 : {
1070 : CPLError(CE_Failure, CPLE_FileIO,
1071 0 : "Failed to create file `%s'", m_pszFname);
1072 0 : return -1;
1073 : }
1074 :
1075 6 : return 0;
1076 : }
1077 :
1078 : /**********************************************************************
1079 : * TABFile::Close()
1080 : *
1081 : * Close current file, and release all memory used.
1082 : *
1083 : * Returns 0 on success, -1 on error.
1084 : **********************************************************************/
1085 12 : int TABFile::Close()
1086 : {
1087 : // Commit the latest changes to the file...
1088 :
1089 : // In Write access, it's time to write the .TAB file.
1090 12 : if (m_eAccessMode == TABWrite && m_poMAPFile)
1091 : {
1092 : // First update file version number...
1093 6 : int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion();
1094 6 : m_nVersion = MAX(m_nVersion, nMapObjVersion);
1095 :
1096 6 : WriteTABFile();
1097 : }
1098 :
1099 12 : if (m_poMAPFile)
1100 : {
1101 12 : m_poMAPFile->Close();
1102 12 : delete m_poMAPFile;
1103 12 : m_poMAPFile = NULL;
1104 : }
1105 :
1106 12 : if (m_poDATFile)
1107 : {
1108 12 : m_poDATFile->Close();
1109 12 : delete m_poDATFile;
1110 12 : m_poDATFile = NULL;
1111 : }
1112 :
1113 12 : if (m_poINDFile)
1114 : {
1115 0 : m_poINDFile->Close();
1116 0 : delete m_poINDFile;
1117 0 : m_poINDFile = NULL;
1118 : }
1119 :
1120 12 : if (m_poCurFeature)
1121 : {
1122 2 : delete m_poCurFeature;
1123 2 : m_poCurFeature = NULL;
1124 : }
1125 :
1126 : /*-----------------------------------------------------------------
1127 : * Note: we have to check the reference count before deleting
1128 : * m_poSpatialRef and m_poDefn
1129 : *----------------------------------------------------------------*/
1130 12 : if (m_poDefn )
1131 : {
1132 12 : int nRefCount = m_poDefn->Dereference();
1133 :
1134 12 : CPLAssert( nRefCount >= 0 );
1135 :
1136 12 : if( nRefCount == 0 )
1137 10 : delete m_poDefn;
1138 12 : m_poDefn = NULL;
1139 : }
1140 :
1141 12 : if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
1142 4 : delete m_poSpatialRef;
1143 12 : m_poSpatialRef = NULL;
1144 :
1145 12 : CSLDestroy(m_papszTABFile);
1146 12 : m_papszTABFile = NULL;
1147 :
1148 12 : CPLFree(m_pszFname);
1149 12 : m_pszFname = NULL;
1150 :
1151 12 : CPLFree(m_pszCharset);
1152 12 : m_pszCharset = NULL;
1153 :
1154 12 : CPLFree(m_panIndexNo);
1155 12 : m_panIndexNo = NULL;
1156 :
1157 12 : CPLFree(m_panMatchingFIDs);
1158 12 : m_panMatchingFIDs = NULL;
1159 :
1160 12 : return 0;
1161 : }
1162 :
1163 : /**********************************************************************
1164 : * TABFile::SetQuickSpatialIndexMode()
1165 : *
1166 : * Select "quick spatial index mode".
1167 : *
1168 : * The default behavior of MITAB is to generate an optimized spatial index,
1169 : * but this results in slower write speed.
1170 : *
1171 : * Applications that want faster write speed and do not care
1172 : * about the performance of spatial queries on the resulting file can
1173 : * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
1174 : * spatial index (actually emulating the type of spatial index produced
1175 : * by MITAB before version 1.6.0). In this mode writing files can be
1176 : * about 5 times faster, but spatial queries can be up to 30 times slower.
1177 : *
1178 : * Returns 0 on success, -1 on error.
1179 : **********************************************************************/
1180 0 : int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
1181 : {
1182 0 : if (m_eAccessMode != TABWrite || m_poMAPFile == NULL)
1183 : {
1184 : CPLError(CE_Failure, CPLE_AssertionFailed,
1185 0 : "SetQuickSpatialIndexMode() failed: file not opened for write access.");
1186 0 : return -1;
1187 : }
1188 :
1189 :
1190 0 : return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode);
1191 : }
1192 :
1193 :
1194 :
1195 : /**********************************************************************
1196 : * TABFile::GetNextFeatureId()
1197 : *
1198 : * Returns feature id that follows nPrevId, or -1 if it is the
1199 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
1200 : **********************************************************************/
1201 114 : int TABFile::GetNextFeatureId(int nPrevId)
1202 : {
1203 114 : if (m_eAccessMode != TABRead)
1204 : {
1205 : CPLError(CE_Failure, CPLE_NotSupported,
1206 0 : "GetNextFeatureId() can be used only with Read access.");
1207 0 : return -1;
1208 : }
1209 :
1210 : /*-----------------------------------------------------------------
1211 : * Are we using spatial rather than .ID based traversal?
1212 : *----------------------------------------------------------------*/
1213 114 : if( bUseSpatialTraversal )
1214 22 : return m_poMAPFile->GetNextFeatureId( nPrevId );
1215 :
1216 : /*-----------------------------------------------------------------
1217 : * Should we use an attribute index traversal?
1218 : *----------------------------------------------------------------*/
1219 92 : if( m_poAttrQuery != NULL)
1220 : {
1221 64 : if( m_panMatchingFIDs == NULL )
1222 : {
1223 64 : m_iMatchingFID = 0;
1224 : m_panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this,
1225 64 : NULL );
1226 : }
1227 64 : if( m_panMatchingFIDs != NULL )
1228 : {
1229 0 : if( m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID )
1230 0 : return OGRNullFID;
1231 :
1232 0 : return m_panMatchingFIDs[m_iMatchingFID++] + 1;
1233 : }
1234 : }
1235 :
1236 : /*-----------------------------------------------------------------
1237 : * Establish what the next logical feature ID should be
1238 : *----------------------------------------------------------------*/
1239 92 : int nFeatureId = -1;
1240 :
1241 102 : if (nPrevId <= 0 && m_nLastFeatureId > 0)
1242 10 : nFeatureId = 1; // Feature Ids start at 1
1243 158 : else if (nPrevId > 0 && nPrevId < m_nLastFeatureId)
1244 76 : nFeatureId = nPrevId + 1;
1245 : else
1246 : {
1247 : // This was the last feature
1248 6 : return OGRNullFID;
1249 : }
1250 :
1251 : /*-----------------------------------------------------------------
1252 : * Skip any feature with NONE geometry and a deleted attribute record
1253 : *----------------------------------------------------------------*/
1254 172 : while(nFeatureId <= m_nLastFeatureId)
1255 : {
1256 86 : if ( m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
1257 : m_poDATFile->GetRecordBlock(nFeatureId) == NULL )
1258 : {
1259 : CPLError(CE_Failure, CPLE_IllegalArg,
1260 : "GetNextFeatureId() failed: unable to set read pointer "
1261 0 : "to feature id %d", nFeatureId);
1262 0 : return -1;
1263 : }
1264 :
1265 : // __TODO__ Add a test here to check if object is deleted,
1266 : // i.e. 0x40 set on object_id in object block
1267 86 : if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE ||
1268 : m_poDATFile->IsCurrentRecordDeleted() == FALSE)
1269 : {
1270 : // This feature contains at least a geometry or some attributes...
1271 : // return its id.
1272 86 : return nFeatureId;
1273 : }
1274 :
1275 0 : nFeatureId++;
1276 : }
1277 :
1278 : // If we reached this point, then we kept skipping deleted features
1279 : // and stopped when EOF was reached.
1280 0 : return -1;
1281 : }
1282 :
1283 : /**********************************************************************
1284 : * TABFile::GetNextFeatureId_Spatial()
1285 : *
1286 : * Returns feature id that follows nPrevId, or -1 if it is the
1287 : * last feature id, but by traversing the spatial tree instead of the
1288 : * direct object index. Generally speaking the feature id's will be
1289 : * returned in an unordered fashion.
1290 : **********************************************************************/
1291 0 : int TABFile::GetNextFeatureId_Spatial(int nPrevId)
1292 : {
1293 0 : if (m_eAccessMode != TABRead)
1294 : {
1295 : CPLError(CE_Failure, CPLE_NotSupported,
1296 0 : "GetNextFeatureId_Spatial() can be used only with Read access.");
1297 0 : return -1;
1298 : }
1299 :
1300 0 : if( m_poMAPFile == NULL )
1301 : {
1302 : CPLError(CE_Failure, CPLE_NotSupported,
1303 0 : "GetNextFeatureId_Spatial() requires availability of .MAP file." );
1304 0 : return -1;
1305 : }
1306 :
1307 0 : return m_poMAPFile->GetNextFeatureId( nPrevId );
1308 : }
1309 :
1310 : /**********************************************************************
1311 : * TABFile::GetFeatureRef()
1312 : *
1313 : * Fill and return a TABFeature object for the specified feature id.
1314 : *
1315 : * The retruned pointer is a reference to an object owned and maintained
1316 : * by this TABFile object. It should not be altered or freed by the
1317 : * caller and its contents is guaranteed to be valid only until the next
1318 : * call to GetFeatureRef() or Close().
1319 : *
1320 : * Returns NULL if the specified feature id does not exist of if an
1321 : * error happened. In any case, CPLError() will have been called to
1322 : * report the reason of the failure.
1323 : *
1324 : * If an unsupported object type is encountered (likely from a newer version
1325 : * of MapInfo) then a valid feature will be returned with a NONE geometry,
1326 : * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported
1327 : * CPLGetLastErrorNo() should be used to detect that case.
1328 : **********************************************************************/
1329 106 : TABFeature *TABFile::GetFeatureRef(int nFeatureId)
1330 : {
1331 106 : CPLErrorReset();
1332 :
1333 106 : if (m_eAccessMode != TABRead)
1334 : {
1335 : CPLError(CE_Failure, CPLE_NotSupported,
1336 0 : "GetFeatureRef() can be used only with Read access.");
1337 0 : return NULL;
1338 : }
1339 :
1340 : /*-----------------------------------------------------------------
1341 : * Make sure file is opened and Validate feature id by positioning
1342 : * the read pointers for the .MAP and .DAT files to this feature id.
1343 : *----------------------------------------------------------------*/
1344 106 : if (m_poMAPFile == NULL)
1345 : {
1346 : CPLError(CE_Failure, CPLE_IllegalArg,
1347 0 : "GetFeatureRef() failed: file is not opened!");
1348 0 : return NULL;
1349 : }
1350 :
1351 :
1352 106 : if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
1353 : m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
1354 : m_poDATFile->GetRecordBlock(nFeatureId) == NULL )
1355 : {
1356 : // CPLError(CE_Failure, CPLE_IllegalArg,
1357 : // "GetFeatureRef() failed: invalid feature id %d",
1358 : // nFeatureId);
1359 0 : return NULL;
1360 : }
1361 :
1362 : /*-----------------------------------------------------------------
1363 : * Flush current feature object
1364 : * __TODO__ try to reuse if it is already of the right type
1365 : *----------------------------------------------------------------*/
1366 106 : if (m_poCurFeature)
1367 : {
1368 62 : delete m_poCurFeature;
1369 62 : m_poCurFeature = NULL;
1370 : }
1371 :
1372 : /*-----------------------------------------------------------------
1373 : * Create new feature object of the right type
1374 : * Unsupported object types are returned as raw TABFeature (i.e. NONE
1375 : * geometry)
1376 : *----------------------------------------------------------------*/
1377 : m_poCurFeature = TABFeature::CreateFromMapInfoType(m_poMAPFile->GetCurObjType(),
1378 106 : m_poDefn);
1379 :
1380 : /*-----------------------------------------------------------------
1381 : * Read fields from the .DAT file
1382 : * GetRecordBlock() has already been called above...
1383 : *----------------------------------------------------------------*/
1384 106 : if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0)
1385 : {
1386 0 : delete m_poCurFeature;
1387 0 : m_poCurFeature = NULL;
1388 0 : return NULL;
1389 : }
1390 :
1391 : /*-----------------------------------------------------------------
1392 : * Read geometry from the .MAP file
1393 : * MoveToObjId() has already been called above...
1394 : *----------------------------------------------------------------*/
1395 : TABMAPObjHdr *poObjHdr =
1396 : TABMAPObjHdr::NewObj((GByte)m_poMAPFile->GetCurObjType(),
1397 106 : m_poMAPFile->GetCurObjId());
1398 : // Note that poObjHdr==NULL is a valid case if geometry type is NONE
1399 :
1400 212 : if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) ||
1401 106 : m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0)
1402 : {
1403 0 : delete m_poCurFeature;
1404 0 : m_poCurFeature = NULL;
1405 0 : if (poObjHdr)
1406 0 : delete poObjHdr;
1407 0 : return NULL;
1408 : }
1409 106 : if (poObjHdr) // May be NULL if feature geometry type is NONE
1410 106 : delete poObjHdr;
1411 :
1412 106 : m_nCurFeatureId = nFeatureId;
1413 106 : m_poCurFeature->SetFID(m_nCurFeatureId);
1414 :
1415 106 : m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted());
1416 :
1417 106 : return m_poCurFeature;
1418 : }
1419 :
1420 : /**********************************************************************
1421 : * TABFile::WriteFeature()
1422 : *
1423 : * Write a feature to this dataset.
1424 : *
1425 : * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1426 : * but eventually we should be able to do random access by specifying
1427 : * a value through nFeatureId.
1428 : *
1429 : * Returns the new featureId (> 0) on success, or -1 if an
1430 : * error happened in which case, CPLError() will have been called to
1431 : * report the reason of the failure.
1432 : **********************************************************************/
1433 28 : int TABFile::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1434 : {
1435 28 : if (m_eAccessMode != TABWrite)
1436 : {
1437 : CPLError(CE_Failure, CPLE_NotSupported,
1438 0 : "WriteFeature() can be used only with Write access.");
1439 0 : return -1;
1440 : }
1441 :
1442 28 : if (nFeatureId != -1)
1443 : {
1444 : CPLError(CE_Failure, CPLE_NotSupported,
1445 0 : "WriteFeature(): random access not implemented yet.");
1446 0 : return -1;
1447 : }
1448 :
1449 : /*-----------------------------------------------------------------
1450 : * Make sure file is opened and establish new feature id.
1451 : *----------------------------------------------------------------*/
1452 28 : if (m_poMAPFile == NULL)
1453 : {
1454 : CPLError(CE_Failure, CPLE_IllegalArg,
1455 0 : "WriteFeature() failed: file is not opened!");
1456 0 : return -1;
1457 : }
1458 :
1459 28 : if (m_nLastFeatureId < 1)
1460 : {
1461 : /*-------------------------------------------------------------
1462 : * OK, this is the first feature in the dataset... make sure the
1463 : * .DAT schema has been initialized.
1464 : *------------------------------------------------------------*/
1465 6 : if (m_poDefn == NULL)
1466 0 : SetFeatureDefn(poFeature->GetDefnRef(), NULL);
1467 :
1468 : /*-------------------------------------------------------------
1469 : * Special hack to write out at least one field if none are in
1470 : * OGRFeatureDefn.
1471 : *------------------------------------------------------------*/
1472 6 : if( m_poDATFile->GetNumFields() == 0 )
1473 : {
1474 : CPLError(CE_Warning, CPLE_IllegalArg,
1475 0 : "MapInfo tables must contain at least 1 column, adding dummy FID column.");
1476 0 : m_poDATFile->AddField("FID", TABFInteger, 10, 0 );
1477 : }
1478 :
1479 6 : nFeatureId = m_nLastFeatureId = 1;
1480 : }
1481 : else
1482 : {
1483 22 : nFeatureId = ++ m_nLastFeatureId;
1484 : }
1485 :
1486 :
1487 : /*-----------------------------------------------------------------
1488 : * Write fields to the .DAT file and update .IND if necessary
1489 : *----------------------------------------------------------------*/
1490 56 : if (m_poDATFile == NULL ||
1491 : m_poDATFile->GetRecordBlock(nFeatureId) == NULL ||
1492 : poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile,
1493 28 : m_panIndexNo) != 0 )
1494 : {
1495 : CPLError(CE_Failure, CPLE_FileIO,
1496 : "Failed writing attributes for feature id %d in %s",
1497 0 : nFeatureId, m_pszFname);
1498 0 : return -1;
1499 : }
1500 :
1501 : /*-----------------------------------------------------------------
1502 : * Write geometry to the .MAP file
1503 : * The call to PrepareNewObj() takes care of the .ID file.
1504 : *----------------------------------------------------------------*/
1505 : TABMAPObjHdr *poObjHdr =
1506 28 : TABMAPObjHdr::NewObj((GByte)poFeature->ValidateMapInfoType(m_poMAPFile),
1507 28 : nFeatureId);
1508 :
1509 : /*-----------------------------------------------------------------
1510 : * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature
1511 : * contained an invalid geometry for its class. Need to catch that
1512 : * case and return the error.
1513 : *----------------------------------------------------------------*/
1514 32 : if (poObjHdr->m_nType == TAB_GEOM_NONE &&
1515 4 : poFeature->GetFeatureClass() != TABFCNoGeomFeature )
1516 : {
1517 : CPLError(CE_Failure, CPLE_FileIO,
1518 : "Invalid geometry for feature id %d in %s",
1519 0 : nFeatureId, m_pszFname);
1520 0 : return -1;
1521 : }
1522 :
1523 : /*-----------------------------------------------------------------
1524 : * The ValidateMapInfoType() call above has forced calculation of the
1525 : * feature's IntMBR. Store that value in the ObjHdr for use by
1526 : * PrepareNewObj() to search the best node to insert the feature.
1527 : *----------------------------------------------------------------*/
1528 28 : if ( poObjHdr && poObjHdr->m_nType != TAB_GEOM_NONE)
1529 : {
1530 : poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
1531 24 : poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
1532 : }
1533 :
1534 56 : if ( poObjHdr == NULL || m_poMAPFile == NULL ||
1535 : m_poMAPFile->PrepareNewObj(poObjHdr) != 0 ||
1536 28 : poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr) != 0 ||
1537 : m_poMAPFile->CommitNewObj(poObjHdr) != 0 )
1538 : {
1539 : CPLError(CE_Failure, CPLE_FileIO,
1540 : "Failed writing geometry for feature id %d in %s",
1541 0 : nFeatureId, m_pszFname);
1542 0 : if (poObjHdr)
1543 0 : delete poObjHdr;
1544 0 : return -1;
1545 : }
1546 :
1547 28 : delete poObjHdr;
1548 :
1549 28 : return nFeatureId;
1550 : }
1551 :
1552 :
1553 : /**********************************************************************
1554 : * TABFile::CreateFeature()
1555 : *
1556 : * Write a new feature to this dataset. The passed in feature is updated
1557 : * with the new feature id.
1558 : *
1559 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
1560 : * error happened in which case, CPLError() will have been called to
1561 : * report the reason of the failure.
1562 : **********************************************************************/
1563 28 : OGRErr TABFile::CreateFeature(TABFeature *poFeature)
1564 : {
1565 28 : int nFeatureId = -1;
1566 :
1567 28 : nFeatureId = WriteFeature(poFeature, -1);
1568 :
1569 28 : if (nFeatureId == -1)
1570 0 : return OGRERR_FAILURE;
1571 :
1572 28 : poFeature->SetFID(nFeatureId);
1573 :
1574 28 : return OGRERR_NONE;
1575 : }
1576 :
1577 : /**********************************************************************
1578 : * TABFile::SetFeature()
1579 : *
1580 : * Implementation of OGRLayer's SetFeature(), enabled only for
1581 : * random write access
1582 : **********************************************************************/
1583 0 : OGRErr TABFile::SetFeature( OGRFeature *poFeature )
1584 :
1585 : {
1586 : //TODO: See CreateFeature()
1587 : // Need to convert OGRFeature to TABFeature, extract FID and then forward
1588 : // forward call to SetFeature(TABFeature, fid)
1589 0 : return OGRERR_UNSUPPORTED_OPERATION;
1590 : }
1591 :
1592 :
1593 : /**********************************************************************
1594 : * TABFile::GetLayerDefn()
1595 : *
1596 : * Returns a reference to the OGRFeatureDefn that will be used to create
1597 : * features in this dataset.
1598 : *
1599 : * Returns a reference to an object that is maintained by this TABFile
1600 : * object (and thus should not be modified or freed by the caller) or
1601 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
1602 : * opened yet)
1603 : **********************************************************************/
1604 98 : OGRFeatureDefn *TABFile::GetLayerDefn()
1605 : {
1606 98 : return m_poDefn;
1607 : }
1608 :
1609 : /**********************************************************************
1610 : * TABFile::SetFeatureDefn()
1611 : *
1612 : * Pass a reference to the OGRFeatureDefn that will be used to create
1613 : * features in this dataset. This function should be called after
1614 : * creating a new dataset, but before writing the first feature.
1615 : * All features that will be written to this dataset must share this same
1616 : * OGRFeatureDefn.
1617 : *
1618 : * A reference to the OGRFeatureDefn will be kept and will be used to
1619 : * build the .DAT file, etc.
1620 : *
1621 : * Returns 0 on success, -1 on error.
1622 : **********************************************************************/
1623 0 : int TABFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
1624 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1625 : {
1626 : int iField, numFields;
1627 : OGRFieldDefn *poFieldDefn;
1628 0 : TABFieldType eMapInfoType = TABFUnknown;
1629 0 : int nStatus = 0;
1630 :
1631 0 : if (m_eAccessMode != TABWrite)
1632 : {
1633 : CPLError(CE_Failure, CPLE_NotSupported,
1634 0 : "SetFeatureDefn() can be used only with Write access.");
1635 0 : return -1;
1636 : }
1637 :
1638 : /*-----------------------------------------------------------------
1639 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
1640 : * reference count into account when we are done with it.
1641 : *----------------------------------------------------------------*/
1642 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1643 0 : delete m_poDefn;
1644 :
1645 0 : m_poDefn = poFeatureDefn;
1646 0 : m_poDefn->Reference();
1647 :
1648 : /*-----------------------------------------------------------------
1649 : * Pass field information to the .DAT file, after making sure that
1650 : * it has been created and that it does not contain any field
1651 : * definition yet.
1652 : *----------------------------------------------------------------*/
1653 0 : if (m_poDATFile== NULL || m_poDATFile->GetNumFields() > 0 )
1654 : {
1655 : CPLError(CE_Failure, CPLE_AssertionFailed,
1656 : "SetFeatureDefn() can be called only once in a newly "
1657 0 : "created dataset.");
1658 0 : return -1;
1659 : }
1660 :
1661 0 : numFields = poFeatureDefn->GetFieldCount();
1662 0 : for(iField=0; nStatus==0 && iField < numFields; iField++)
1663 : {
1664 0 : poFieldDefn = m_poDefn->GetFieldDefn(iField);
1665 :
1666 : /*-------------------------------------------------------------
1667 : * Make sure field name is valid... check for special chars, etc.
1668 : *------------------------------------------------------------*/
1669 0 : char *pszCleanName = TABCleanFieldName(poFieldDefn->GetNameRef());
1670 0 : if (!EQUAL(pszCleanName, poFieldDefn->GetNameRef()))
1671 0 : poFieldDefn->SetName(pszCleanName);
1672 0 : CPLFree(pszCleanName);
1673 0 : pszCleanName = NULL;
1674 :
1675 0 : if (paeMapInfoNativeFieldTypes)
1676 : {
1677 0 : eMapInfoType = paeMapInfoNativeFieldTypes[iField];
1678 : }
1679 : else
1680 : {
1681 : /*---------------------------------------------------------
1682 : * Map OGRFieldTypes to MapInfo native types
1683 : *--------------------------------------------------------*/
1684 0 : switch(poFieldDefn->GetType())
1685 : {
1686 : case OFTInteger:
1687 0 : eMapInfoType = TABFInteger;
1688 0 : break;
1689 : case OFTReal:
1690 0 : if( poFieldDefn->GetWidth()>0 || poFieldDefn->GetPrecision()>0 )
1691 0 : eMapInfoType = TABFDecimal;
1692 : else
1693 0 : eMapInfoType = TABFFloat;
1694 0 : break;
1695 : case OFTDateTime:
1696 0 : eMapInfoType = TABFDateTime;
1697 0 : break;
1698 : case OFTDate:
1699 0 : eMapInfoType = TABFDate;
1700 0 : break;
1701 : case OFTTime:
1702 0 : eMapInfoType = TABFTime;
1703 0 : break;
1704 : case OFTString:
1705 : default:
1706 0 : eMapInfoType = TABFChar;
1707 : }
1708 : }
1709 :
1710 : nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(),
1711 : eMapInfoType,
1712 : poFieldDefn->GetWidth(),
1713 0 : poFieldDefn->GetPrecision());
1714 : }
1715 :
1716 : /*-----------------------------------------------------------------
1717 : * Alloc the array to keep track of indexed fields (default=NOT indexed)
1718 : *----------------------------------------------------------------*/
1719 0 : m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int));
1720 :
1721 0 : return nStatus;
1722 : }
1723 :
1724 : /**********************************************************************
1725 : * TABFile::AddFieldNative()
1726 : *
1727 : * Create a new field using a native mapinfo data type... this is an
1728 : * alternative to defining fields through the OGR interface.
1729 : * This function should be called after creating a new dataset, but before
1730 : * writing the first feature.
1731 : *
1732 : * This function will build/update the OGRFeatureDefn that will have to be
1733 : * used when writing features to this dataset.
1734 : *
1735 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1736 : *
1737 : * Note: The bUnique flag has no effect on TABFiles. See the TABView class.
1738 : *
1739 : * Returns 0 on success, -1 on error.
1740 : **********************************************************************/
1741 16 : int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1742 : int nWidth /*=0*/, int nPrecision /*=0*/,
1743 : GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/, int bApproxOK)
1744 : {
1745 : OGRFieldDefn *poFieldDefn;
1746 16 : int nStatus = 0;
1747 16 : char *pszCleanName = NULL;
1748 : char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/
1749 16 : int nRenameNum = 1;
1750 :
1751 16 : if (m_eAccessMode != TABWrite)
1752 : {
1753 : CPLError(CE_Failure, CPLE_NotSupported,
1754 0 : "AddFieldNative() can be used only with Write access.");
1755 0 : return -1;
1756 : }
1757 :
1758 : /*-----------------------------------------------------------------
1759 : * Check that call happens at the right time in dataset's life.
1760 : *----------------------------------------------------------------*/
1761 16 : if (m_eAccessMode != TABWrite ||
1762 : m_nLastFeatureId > 0 || m_poDATFile == NULL)
1763 : {
1764 : CPLError(CE_Failure, CPLE_AssertionFailed,
1765 : "AddFieldNative() must be called after opening a new "
1766 0 : "dataset, but before writing the first feature to it.");
1767 0 : return -1;
1768 : }
1769 :
1770 : /*-----------------------------------------------------------------
1771 : * Create new OGRFeatureDefn if not done yet...
1772 : *----------------------------------------------------------------*/
1773 16 : if (m_poDefn== NULL)
1774 : {
1775 0 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
1776 0 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
1777 0 : CPLFree(pszFeatureClassName);
1778 : // Ref count defaults to 0... set it to 1
1779 0 : m_poDefn->Reference();
1780 : }
1781 :
1782 : /*-----------------------------------------------------------------
1783 : * Validate field width... must be <= 254
1784 : *----------------------------------------------------------------*/
1785 16 : if (nWidth > 254)
1786 : {
1787 : CPLError(CE_Warning, CPLE_IllegalArg,
1788 : "Invalid size (%d) for field '%s'. "
1789 0 : "Size must be 254 or less.", nWidth, pszName);
1790 0 : nWidth=254;
1791 : }
1792 :
1793 : /*-----------------------------------------------------------------
1794 : * Map fields with width=0 (variable length in OGR) to a valid default
1795 : *----------------------------------------------------------------*/
1796 16 : if (eMapInfoType == TABFDecimal && nWidth == 0)
1797 0 : nWidth=20;
1798 16 : else if (nWidth == 0)
1799 0 : nWidth=254; /* char fields */
1800 :
1801 : /*-----------------------------------------------------------------
1802 : * Make sure field name is valid... check for special chars, etc.
1803 : * (pszCleanName will have to be freed.)
1804 : *----------------------------------------------------------------*/
1805 16 : pszCleanName = TABCleanFieldName(pszName);
1806 :
1807 16 : if( !bApproxOK &&
1808 : ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 ||
1809 : !EQUAL(pszName, pszCleanName) ) )
1810 : {
1811 : CPLError( CE_Failure, CPLE_NotSupported,
1812 : "Failed to add field named '%s'",
1813 0 : pszName );
1814 : }
1815 :
1816 16 : strncpy(szNewFieldName, pszCleanName, 31);
1817 16 : szNewFieldName[31] = '\0';
1818 :
1819 32 : while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10)
1820 0 : sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ );
1821 :
1822 32 : while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100)
1823 0 : sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ );
1824 :
1825 16 : if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0)
1826 : {
1827 : CPLError( CE_Failure, CPLE_NotSupported,
1828 : "Too many field names like '%s' when truncated to 31 letters "
1829 0 : "for MapInfo format.", pszCleanName );
1830 : }
1831 :
1832 16 : if( !EQUAL(pszCleanName,szNewFieldName) )
1833 : {
1834 : CPLError( CE_Warning, CPLE_NotSupported,
1835 : "Normalized/laundered field name: '%s' to '%s'",
1836 : pszCleanName,
1837 0 : szNewFieldName );
1838 : }
1839 :
1840 : /*-----------------------------------------------------------------
1841 : * Map MapInfo native types to OGR types
1842 : *----------------------------------------------------------------*/
1843 16 : poFieldDefn = NULL;
1844 :
1845 16 : switch(eMapInfoType)
1846 : {
1847 : case TABFChar:
1848 : /*-------------------------------------------------
1849 : * CHAR type
1850 : *------------------------------------------------*/
1851 4 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
1852 4 : poFieldDefn->SetWidth(nWidth);
1853 4 : break;
1854 : case TABFInteger:
1855 : /*-------------------------------------------------
1856 : * INTEGER type
1857 : *------------------------------------------------*/
1858 6 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
1859 6 : if (nWidth <= 10)
1860 4 : poFieldDefn->SetWidth(nWidth);
1861 6 : break;
1862 : case TABFSmallInt:
1863 : /*-------------------------------------------------
1864 : * SMALLINT type
1865 : *------------------------------------------------*/
1866 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
1867 0 : if (nWidth <= 5)
1868 0 : poFieldDefn->SetWidth(nWidth);
1869 0 : break;
1870 : case TABFDecimal:
1871 : /*-------------------------------------------------
1872 : * DECIMAL type
1873 : *------------------------------------------------*/
1874 2 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
1875 2 : poFieldDefn->SetWidth(nWidth);
1876 2 : poFieldDefn->SetPrecision(nPrecision);
1877 2 : break;
1878 : case TABFFloat:
1879 : /*-------------------------------------------------
1880 : * FLOAT type
1881 : *------------------------------------------------*/
1882 4 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
1883 4 : break;
1884 : case TABFDate:
1885 : /*-------------------------------------------------
1886 : * DATE type (V450, returned as a string: "DD/MM/YYYY")
1887 : *------------------------------------------------*/
1888 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1889 : #ifdef MITAB_USE_OFTDATETIME
1890 0 : OFTDate);
1891 : #else
1892 : OFTString);
1893 : #endif
1894 0 : poFieldDefn->SetWidth(10);
1895 0 : m_nVersion = MAX(m_nVersion, 450);
1896 0 : break;
1897 : case TABFTime:
1898 : /*-------------------------------------------------
1899 : * TIME type (V900, returned as a string: "HH:MM:SS")
1900 : *------------------------------------------------*/
1901 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1902 : #ifdef MITAB_USE_OFTDATETIME
1903 0 : OFTTime);
1904 : #else
1905 : OFTString);
1906 : #endif
1907 0 : poFieldDefn->SetWidth(8);
1908 0 : m_nVersion = MAX(m_nVersion, 900);
1909 0 : break;
1910 : case TABFDateTime:
1911 : /*-------------------------------------------------
1912 : * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS")
1913 : *------------------------------------------------*/
1914 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1915 : #ifdef MITAB_USE_OFTDATETIME
1916 0 : OFTDateTime);
1917 : #else
1918 : OFTString);
1919 : #endif
1920 0 : poFieldDefn->SetWidth(19);
1921 0 : m_nVersion = MAX(m_nVersion, 900);
1922 0 : break;
1923 : case TABFLogical:
1924 : /*-------------------------------------------------
1925 : * LOGICAL type (value "T" or "F")
1926 : *------------------------------------------------*/
1927 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
1928 0 : poFieldDefn->SetWidth(1);
1929 0 : break;
1930 : default:
1931 : CPLError(CE_Failure, CPLE_NotSupported,
1932 0 : "Unsupported type for field %s", szNewFieldName);
1933 0 : CPLFree(pszCleanName);
1934 0 : return -1;
1935 : }
1936 :
1937 : /*-----------------------------------------------------
1938 : * Add the FieldDefn to the FeatureDefn
1939 : *----------------------------------------------------*/
1940 16 : m_poDefn->AddFieldDefn(poFieldDefn);
1941 16 : delete poFieldDefn;
1942 :
1943 : /*-----------------------------------------------------
1944 : * ... and pass field info to the .DAT file.
1945 : *----------------------------------------------------*/
1946 : nStatus = m_poDATFile->AddField(szNewFieldName, eMapInfoType,
1947 16 : nWidth, nPrecision);
1948 :
1949 : /*-----------------------------------------------------------------
1950 : * Extend the array to keep track of indexed fields (default=NOT indexed)
1951 : *----------------------------------------------------------------*/
1952 : m_panIndexNo = (int *)CPLRealloc(m_panIndexNo,
1953 16 : m_poDefn->GetFieldCount()*sizeof(int));
1954 16 : m_panIndexNo[m_poDefn->GetFieldCount()-1] = 0;
1955 :
1956 : /*-----------------------------------------------------------------
1957 : * Index the field if requested
1958 : *----------------------------------------------------------------*/
1959 16 : if (nStatus == 0 && bIndexed)
1960 0 : nStatus = SetFieldIndexed(m_poDefn->GetFieldCount()-1);
1961 :
1962 16 : CPLFree(pszCleanName);
1963 16 : return nStatus;
1964 : }
1965 :
1966 :
1967 : /**********************************************************************
1968 : * TABFile::GetNativeFieldType()
1969 : *
1970 : * Returns the native MapInfo field type for the specified field.
1971 : *
1972 : * Returns TABFUnknown if file is not opened, or if specified field index is
1973 : * invalid.
1974 : *
1975 : * Note that field ids are positive and start at 0.
1976 : **********************************************************************/
1977 16 : TABFieldType TABFile::GetNativeFieldType(int nFieldId)
1978 : {
1979 16 : if (m_poDATFile)
1980 : {
1981 16 : return m_poDATFile->GetFieldType(nFieldId);
1982 : }
1983 0 : return TABFUnknown;
1984 : }
1985 :
1986 :
1987 :
1988 : /**********************************************************************
1989 : * TABFile::GetFieldIndexNumber()
1990 : *
1991 : * Returns the field's index number that was specified in the .TAB header
1992 : * or 0 if the specified field is not indexed.
1993 : *
1994 : * Note that field ids are positive and start at 0
1995 : * and valid index ids are positive and start at 1.
1996 : **********************************************************************/
1997 32 : int TABFile::GetFieldIndexNumber(int nFieldId)
1998 : {
1999 32 : if (m_panIndexNo == NULL || nFieldId < 0 ||
2000 : m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields())
2001 0 : return 0; // no index
2002 :
2003 32 : return m_panIndexNo[nFieldId];
2004 : }
2005 :
2006 : /************************************************************************
2007 : * TABFile::SetFieldIndexed()
2008 : *
2009 : * Request that a field be indexed. This will create the .IND file if
2010 : * necessary, etc.
2011 : *
2012 : * Note that field ids are positive and start at 0.
2013 : *
2014 : * Returns 0 on success, -1 on error.
2015 : ************************************************************************/
2016 0 : int TABFile::SetFieldIndexed( int nFieldId )
2017 : {
2018 : /*-----------------------------------------------------------------
2019 : * Make sure things are OK
2020 : *----------------------------------------------------------------*/
2021 0 : if (m_pszFname == NULL || m_eAccessMode != TABWrite || m_poDefn == NULL)
2022 : {
2023 : CPLError(CE_Failure, CPLE_AssertionFailed,
2024 : "SetFieldIndexed() must be called after opening a new "
2025 0 : "dataset, but before writing the first feature to it.");
2026 0 : return -1;
2027 : }
2028 :
2029 0 : if (m_panIndexNo == NULL || nFieldId < 0 ||
2030 : m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields())
2031 : {
2032 : CPLError(CE_Failure, CPLE_AssertionFailed,
2033 0 : "Invalid field number in SetFieldIndexed().");
2034 0 : return -1;
2035 : }
2036 :
2037 : /*-----------------------------------------------------------------
2038 : * If field is already indexed then just return
2039 : *----------------------------------------------------------------*/
2040 0 : if (m_panIndexNo[nFieldId] != 0)
2041 0 : return 0; // Nothing to do
2042 :
2043 :
2044 : /*-----------------------------------------------------------------
2045 : * Create .IND file if it's not done yet.
2046 : *
2047 : * Note: We can pass the .TAB's filename directly and the
2048 : * TABINDFile class will automagically adjust the extension.
2049 : *----------------------------------------------------------------*/
2050 0 : if (m_poINDFile == NULL)
2051 : {
2052 0 : m_poINDFile = new TABINDFile;
2053 :
2054 0 : if ( m_poINDFile->Open(m_pszFname, "w", TRUE) != 0)
2055 : {
2056 : // File could not be opened...
2057 0 : delete m_poINDFile;
2058 0 : m_poINDFile = NULL;
2059 0 : return -1;
2060 : }
2061 : }
2062 :
2063 : /*-----------------------------------------------------------------
2064 : * Init new index.
2065 : *----------------------------------------------------------------*/
2066 : int nNewIndexNo;
2067 0 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId);
2068 :
2069 0 : if (poFieldDefn == NULL ||
2070 0 : (nNewIndexNo = m_poINDFile->CreateIndex(GetNativeFieldType(nFieldId),
2071 : poFieldDefn->GetWidth()) ) < 1)
2072 : {
2073 : // Failed... an error has already been reported.
2074 0 : return -1;
2075 : }
2076 :
2077 0 : m_panIndexNo[nFieldId] = nNewIndexNo;
2078 :
2079 0 : return 0;
2080 : }
2081 :
2082 : /************************************************************************
2083 : * TABFile::IsFieldIndexed()
2084 : *
2085 : * Returns TRUE if field is indexed, or FALSE otherwise.
2086 : ************************************************************************/
2087 0 : GBool TABFile::IsFieldIndexed( int nFieldId )
2088 : {
2089 0 : return (GetFieldIndexNumber(nFieldId) > 0 ? TRUE:FALSE);
2090 : }
2091 :
2092 :
2093 :
2094 : /**********************************************************************
2095 : * TABFile::GetINDFileRef()
2096 : *
2097 : * Opens the .IND file for this dataset and returns a reference to
2098 : * the handle.
2099 : * If the .IND file has already been opened then the same handle is
2100 : * returned directly.
2101 : * If the .IND file does not exist then the function silently returns NULL.
2102 : *
2103 : * Note that the returned TABINDFile handle is only a reference to an
2104 : * object that is owned by this class. Callers can use it but cannot
2105 : * destroy the object. The object will remain valid for as long as
2106 : * the TABFile will remain open.
2107 : **********************************************************************/
2108 0 : TABINDFile *TABFile::GetINDFileRef()
2109 : {
2110 0 : if (m_pszFname == NULL)
2111 0 : return NULL;
2112 :
2113 0 : if (m_eAccessMode == TABRead && m_poINDFile == NULL)
2114 : {
2115 : /*-------------------------------------------------------------
2116 : * File is not opened yet... do it now.
2117 : *
2118 : * Note: We can pass the .TAB's filename directly and the
2119 : * TABINDFile class will automagically adjust the extension.
2120 : *------------------------------------------------------------*/
2121 0 : m_poINDFile = new TABINDFile;
2122 :
2123 0 : if ( m_poINDFile->Open(m_pszFname, "r", TRUE) != 0)
2124 : {
2125 : // File could not be opened... probably does not exist
2126 0 : delete m_poINDFile;
2127 0 : m_poINDFile = NULL;
2128 : }
2129 0 : else if (m_panIndexNo && m_poDATFile)
2130 : {
2131 : /*---------------------------------------------------------
2132 : * Pass type information for each indexed field.
2133 : *--------------------------------------------------------*/
2134 0 : for(int i=0; i<m_poDATFile->GetNumFields(); i++)
2135 : {
2136 0 : if (m_panIndexNo[i] > 0)
2137 : {
2138 : m_poINDFile->SetIndexFieldType(m_panIndexNo[i],
2139 0 : GetNativeFieldType(i));
2140 : }
2141 : }
2142 : }
2143 : }
2144 :
2145 0 : return m_poINDFile;
2146 : }
2147 :
2148 :
2149 : /**********************************************************************
2150 : * TABFile::SetBounds()
2151 : *
2152 : * Set projection coordinates bounds of the newly created dataset.
2153 : *
2154 : * This function must be called after creating a new dataset and before any
2155 : * feature can be written to it.
2156 : *
2157 : * Returns 0 on success, -1 on error.
2158 : **********************************************************************/
2159 6 : int TABFile::SetBounds(double dXMin, double dYMin,
2160 : double dXMax, double dYMax)
2161 : {
2162 6 : if (m_eAccessMode != TABWrite)
2163 : {
2164 : CPLError(CE_Failure, CPLE_NotSupported,
2165 0 : "SetBounds() can be used only with Write access.");
2166 0 : return -1;
2167 : }
2168 :
2169 : /*-----------------------------------------------------------------
2170 : * Check that dataset has been created but no feature set yet.
2171 : *----------------------------------------------------------------*/
2172 6 : if (m_poMAPFile && m_nLastFeatureId < 1)
2173 : {
2174 6 : m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
2175 :
2176 6 : m_bBoundsSet = TRUE;
2177 : }
2178 : else
2179 : {
2180 : CPLError(CE_Failure, CPLE_AssertionFailed,
2181 : "SetBounds() can be called only after dataset has been "
2182 0 : "created and before any feature is set.");
2183 0 : return -1;
2184 : }
2185 :
2186 6 : return 0;
2187 : }
2188 :
2189 :
2190 : /**********************************************************************
2191 : * TABFile::GetBounds()
2192 : *
2193 : * Fetch projection coordinates bounds of a dataset.
2194 : *
2195 : * The bForce flag has no effect on TAB files since the bounds are
2196 : * always in the header.
2197 : *
2198 : * Returns 0 on success, -1 on error.
2199 : **********************************************************************/
2200 0 : int TABFile::GetBounds(double &dXMin, double &dYMin,
2201 : double &dXMax, double &dYMax,
2202 : GBool /*bForce = TRUE*/)
2203 : {
2204 : TABMAPHeaderBlock *poHeader;
2205 :
2206 0 : if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
2207 : {
2208 : /*-------------------------------------------------------------
2209 : * Projection bounds correspond to the +/- 1e9 integer coord. limits
2210 : *------------------------------------------------------------*/
2211 : double dX0, dX1, dY0, dY1;
2212 : m_poMAPFile->Int2Coordsys(-1000000000, -1000000000,
2213 0 : dX0, dY0);
2214 : m_poMAPFile->Int2Coordsys(1000000000, 1000000000,
2215 0 : dX1, dY1);
2216 : /*-------------------------------------------------------------
2217 : * ... and make sure that Min < Max
2218 : *------------------------------------------------------------*/
2219 0 : dXMin = MIN(dX0, dX1);
2220 0 : dXMax = MAX(dX0, dX1);
2221 0 : dYMin = MIN(dY0, dY1);
2222 0 : dYMax = MAX(dY0, dY1);
2223 : }
2224 : else
2225 : {
2226 : CPLError(CE_Failure, CPLE_AppDefined,
2227 0 : "GetBounds() can be called only after dataset has been opened.");
2228 0 : return -1;
2229 : }
2230 :
2231 0 : return 0;
2232 : }
2233 :
2234 :
2235 : /**********************************************************************
2236 : * TABFile::GetExtent()
2237 : *
2238 : * Fetch extent of the data currently stored in the dataset.
2239 : *
2240 : * The bForce flag has no effect on TAB files since that value is
2241 : * always in the header.
2242 : *
2243 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
2244 : **********************************************************************/
2245 0 : OGRErr TABFile::GetExtent (OGREnvelope *psExtent, int bForce)
2246 : {
2247 : TABMAPHeaderBlock *poHeader;
2248 :
2249 0 : if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
2250 : {
2251 : double dX0, dX1, dY0, dY1;
2252 : /*-------------------------------------------------------------
2253 : * Fetch extent of the data from the .map header block
2254 : * this value is different from the projection bounds.
2255 : *------------------------------------------------------------*/
2256 : m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin,
2257 0 : dX0, dY0);
2258 : m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax,
2259 0 : dX1, dY1);
2260 :
2261 : /*-------------------------------------------------------------
2262 : * ... and make sure that Min < Max
2263 : *------------------------------------------------------------*/
2264 0 : psExtent->MinX = MIN(dX0, dX1);
2265 0 : psExtent->MaxX = MAX(dX0, dX1);
2266 0 : psExtent->MinY = MIN(dY0, dY1);
2267 0 : psExtent->MaxY = MAX(dY0, dY1);
2268 :
2269 0 : return OGRERR_NONE;
2270 : }
2271 :
2272 0 : return OGRERR_FAILURE;
2273 : }
2274 :
2275 : /**********************************************************************
2276 : * TABFile::GetFeatureCountByType()
2277 : *
2278 : * Return number of features of each type.
2279 : *
2280 : * Note that the sum of the 4 returned values may be different from
2281 : * the total number of features since features with NONE geometry
2282 : * are not taken into account here.
2283 : *
2284 : * Note: the bForce flag has nmo effect on .TAB files since the info
2285 : * is always in the header.
2286 : *
2287 : * Returns 0 on success, or silently returns -1 (with no error) if this
2288 : * information is not available.
2289 : **********************************************************************/
2290 6 : int TABFile::GetFeatureCountByType(int &numPoints, int &numLines,
2291 : int &numRegions, int &numTexts,
2292 : GBool /* bForce = TRUE*/ )
2293 : {
2294 : TABMAPHeaderBlock *poHeader;
2295 :
2296 6 : if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
2297 : {
2298 6 : numPoints = poHeader->m_numPointObjects;
2299 6 : numLines = poHeader->m_numLineObjects;
2300 6 : numRegions = poHeader->m_numRegionObjects;
2301 6 : numTexts = poHeader->m_numTextObjects;
2302 : }
2303 : else
2304 : {
2305 0 : numPoints = numLines = numRegions = numTexts = 0;
2306 0 : return -1;
2307 : }
2308 :
2309 6 : return 0;
2310 : }
2311 :
2312 :
2313 : /**********************************************************************
2314 : * TABFile::SetMIFCoordSys()
2315 : *
2316 : * Set projection for a new file using a MIF coordsys string.
2317 : *
2318 : * This function must be called after creating a new dataset and before any
2319 : * feature can be written to it.
2320 : *
2321 : * Returns 0 on success, -1 on error.
2322 : **********************************************************************/
2323 0 : int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys)
2324 : {
2325 0 : if (m_eAccessMode != TABWrite)
2326 : {
2327 : CPLError(CE_Failure, CPLE_NotSupported,
2328 0 : "SetMIFCoordSys() can be used only with Write access.");
2329 0 : return -1;
2330 : }
2331 :
2332 : /*-----------------------------------------------------------------
2333 : * Check that dataset has been created but no feature set yet.
2334 : *----------------------------------------------------------------*/
2335 0 : if (m_poMAPFile && m_nLastFeatureId < 1)
2336 : {
2337 : OGRSpatialReference *poSpatialRef;
2338 :
2339 0 : poSpatialRef = MITABCoordSys2SpatialRef( pszMIFCoordSys );
2340 :
2341 0 : if (poSpatialRef)
2342 : {
2343 : double dXMin, dYMin, dXMax, dYMax;
2344 0 : if (SetSpatialRef(poSpatialRef) == 0)
2345 : {
2346 0 : if (MITABExtractCoordSysBounds(pszMIFCoordSys,
2347 : dXMin, dYMin,
2348 : dXMax, dYMax) == TRUE)
2349 : {
2350 : // If the coordsys string contains bounds, then use them
2351 0 : if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0)
2352 : {
2353 : // Failed Setting Bounds... an error should have
2354 : // been already reported.
2355 0 : return -1;
2356 : }
2357 : }
2358 : }
2359 : else
2360 : {
2361 : // Failed setting poSpatialRef... and error should have
2362 : // been reported.
2363 0 : return -1;
2364 : }
2365 :
2366 : // Release our handle on poSpatialRef
2367 0 : if( poSpatialRef->Dereference() == 0 )
2368 0 : delete poSpatialRef;
2369 : }
2370 : }
2371 : else
2372 : {
2373 : CPLError(CE_Failure, CPLE_AssertionFailed,
2374 : "SetMIFCoordSys() can be called only after dataset has been "
2375 0 : "created and before any feature is set.");
2376 0 : return -1;
2377 : }
2378 :
2379 0 : return 0;
2380 : }
2381 :
2382 : /**********************************************************************
2383 : * TABFile::SetProjInfo()
2384 : *
2385 : * Set projection for a new file using a TABProjInfo structure.
2386 : *
2387 : * This function must be called after creating a new dataset and before any
2388 : * feature can be written to it.
2389 : *
2390 : * This call will also trigger a lookup of default bounds for the specified
2391 : * projection (except nonearth), and reset the m_bBoundsValid flag.
2392 : *
2393 : * Returns 0 on success, -1 on error.
2394 : **********************************************************************/
2395 2 : int TABFile::SetProjInfo(TABProjInfo *poPI)
2396 : {
2397 2 : if (m_eAccessMode != TABWrite)
2398 : {
2399 : CPLError(CE_Failure, CPLE_NotSupported,
2400 0 : "SetProjInfo() can be used only with Write access.");
2401 0 : return -1;
2402 : }
2403 :
2404 : /*-----------------------------------------------------------------
2405 : * Check that dataset has been created but no feature set yet.
2406 : *----------------------------------------------------------------*/
2407 2 : if (m_poMAPFile && m_nLastFeatureId < 1)
2408 : {
2409 2 : if (m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ) != 0)
2410 0 : return -1;
2411 : }
2412 : else
2413 : {
2414 : CPLError(CE_Failure, CPLE_AssertionFailed,
2415 : "SetProjInfo() can be called only after dataset has been "
2416 0 : "created and before any feature is set.");
2417 0 : return -1;
2418 : }
2419 :
2420 : /*-----------------------------------------------------------------
2421 : * Lookup default bounds and reset m_bBoundsSet flag
2422 : *----------------------------------------------------------------*/
2423 : double dXMin, dYMin, dXMax, dYMax;
2424 :
2425 2 : m_bBoundsSet = FALSE;
2426 2 : if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax) == TRUE)
2427 : {
2428 2 : SetBounds(dXMin, dYMin, dXMax, dYMax);
2429 : }
2430 :
2431 2 : return 0;
2432 : }
2433 :
2434 :
2435 : /************************************************************************/
2436 : /* TestCapability() */
2437 : /************************************************************************/
2438 :
2439 0 : int TABFile::TestCapability( const char * pszCap )
2440 :
2441 : {
2442 0 : if( EQUAL(pszCap,OLCRandomRead) )
2443 0 : return TRUE;
2444 :
2445 0 : else if( EQUAL(pszCap,OLCSequentialWrite) )
2446 0 : return m_eAccessMode == TABWrite;
2447 :
2448 0 : else if( EQUAL(pszCap,OLCRandomWrite) )
2449 0 : return FALSE;
2450 :
2451 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
2452 : return m_poFilterGeom == NULL
2453 0 : && m_poAttrQuery == NULL;
2454 :
2455 0 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
2456 0 : return TRUE;
2457 :
2458 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
2459 0 : return TRUE;
2460 :
2461 0 : else if( EQUAL(pszCap,OLCCreateField) )
2462 0 : return TRUE;
2463 :
2464 : else
2465 0 : return FALSE;
2466 : }
2467 :
2468 : /**********************************************************************
2469 : * TABFile::Dump()
2470 : *
2471 : * Dump block contents... available only in DEBUG mode.
2472 : **********************************************************************/
2473 : #ifdef DEBUG
2474 :
2475 0 : void TABFile::Dump(FILE *fpOut /*=NULL*/)
2476 : {
2477 0 : if (fpOut == NULL)
2478 0 : fpOut = stdout;
2479 :
2480 0 : fprintf(fpOut, "----- TABFile::Dump() -----\n");
2481 :
2482 0 : if (m_poMAPFile == NULL)
2483 : {
2484 0 : fprintf(fpOut, "File is not opened.\n");
2485 : }
2486 : else
2487 : {
2488 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
2489 0 : fprintf(fpOut, "Associated TABLE file ...\n\n");
2490 0 : m_poDATFile->Dump(fpOut);
2491 0 : fprintf(fpOut, "... end of TABLE file dump.\n\n");
2492 0 : if( GetSpatialRef() != NULL )
2493 : {
2494 : char *pszWKT;
2495 :
2496 0 : GetSpatialRef()->exportToWkt( &pszWKT );
2497 0 : fprintf( fpOut, "SRS = %s\n", pszWKT );
2498 0 : OGRFree( pszWKT );
2499 : }
2500 0 : fprintf(fpOut, "Associated .MAP file ...\n\n");
2501 0 : m_poMAPFile->Dump(fpOut);
2502 0 : fprintf(fpOut, "... end of .MAP file dump.\n\n");
2503 :
2504 : }
2505 :
2506 0 : fflush(fpOut);
2507 0 : }
2508 :
2509 : #endif // DEBUG
|