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