1 /**********************************************************************
2  * $Id: mitab_tabseamless.cpp,v 1.10 2010-07-07 19:00:15 aboudreault Exp $
3  *
4  * Name:     mitab_tabseamless.cpp
5  * Project:  MapInfo TAB Read/Write library
6  * Language: C++
7  * Purpose:  Implementation of the TABSeamless class, used to handle seamless
8  *           .TAB datasets.
9  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
10  *
11  **********************************************************************
12  * Copyright (c) 1999-2004, Daniel Morissette
13  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
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_tabseamless.cpp,v $
35  * Revision 1.10  2010-07-07 19:00:15  aboudreault
36  * Cleanup Win32 Compile Warnings (GDAL bug #2930)
37  *
38  * Revision 1.9  2009-03-10 13:50:02  aboudreault
39  * Fixed Overflow of FIDs in Seamless tables (bug 2015)
40  *
41  * Revision 1.8  2009-03-04 21:22:44  dmorissette
42  * Set m_nCurFeatureId=-1 in TABSeamless::ResetReading() (bug 2017)
43  *
44  * Revision 1.7  2007-06-21 14:00:23  dmorissette
45  * Added missing cast in isspace() calls to avoid failed assertion on Windows
46  * (MITAB bug 1737, GDAL ticket 1678))
47  *
48  * Revision 1.6  2004/06/30 20:29:04  dmorissette
49  * Fixed refs to old address danmo@videotron.ca
50  *
51  * Revision 1.5  2004/03/12 16:29:05  dmorissette
52  * Fixed 2 memory leaks (bug 283)
53  *
54  * Revision 1.4  2002/06/28 18:32:37  julien
55  * Add SetSpatialFilter() in TABSeamless class (Bug 164, MapServer)
56  * Use double for comparison in Coordsys2Int() in mitab_mapheaderblock.cpp
57  *
58  * Revision 1.3  2001/09/19 14:21:36  daniel
59  * On Unix: replace '\\' in file path read from tab index with '/'
60  *
61  * Revision 1.2  2001/03/15 03:57:51  daniel
62  * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
63  *
64  * Revision 1.1  2001/03/09 04:38:04  danmo
65  * Update from master - version 1.1.0
66  *
67  * Revision 1.1  2001/03/09 04:16:02  daniel
68  * Added TABSeamless for reading seamless TAB files
69  *
70  **********************************************************************/
71 
72 #include "mitab.h"
73 #include "mitab_utils.h"
74 
75 #include <ctype.h>      /* isspace() */
76 
77 /*=====================================================================
78  *                      class TABSeamless
79  *
80  * Support for seamless vector datasets.
81  *
82  * The current implementation has some limitations (base assumptions):
83  *  - Read-only
84  *  - Base tables can only be of type TABFile
85  *  - Feature Ids are build using the id of the base table in the main
86  *    index table (upper 32 bits) and the actual feature id of each object
87  *    inside the base tables (lower 32 bits).
88  *  - Only relative paths are supported for base tables names.
89  *
90  *====================================================================*/
91 
92 
93 /**********************************************************************
94  *                   TABSeamless::TABSeamless()
95  *
96  * Constructor.
97  **********************************************************************/
TABSeamless()98 TABSeamless::TABSeamless()
99 {
100     m_pszFname = NULL;
101     m_pszPath = NULL;
102     m_eAccessMode = TABRead;
103     m_poFeatureDefnRef = NULL;
104     m_poCurFeature = NULL;
105     m_nCurFeatureId = -1;
106 
107     m_poIndexTable = NULL;
108     m_nTableNameField = -1;
109     m_nCurBaseTableId = -1;
110     m_poCurBaseTable = NULL;
111     m_bEOF = FALSE;
112 }
113 
114 /**********************************************************************
115  *                   TABSeamless::~TABSeamless()
116  *
117  * Destructor.
118  **********************************************************************/
~TABSeamless()119 TABSeamless::~TABSeamless()
120 {
121     Close();
122 }
123 
124 
ResetReading()125 void TABSeamless::ResetReading()
126 {
127     if (m_poIndexTable)
128         OpenBaseTable(-1);  // Asking for first table resets everything
129 
130     // Reset m_nCurFeatureId so that next pass via GetNextFeatureId()
131     // will start from the beginning
132     m_nCurFeatureId = -1;
133 }
134 
135 
136 /**********************************************************************
137  *                   TABSeamless::Open()
138  *
139  * Open a seamless .TAB dataset and initialize the structures to be ready
140  * to read features from it.
141  *
142  * Seamless .TAB files are composed of a main .TAB file in which each
143  * feature is the MBR of a base table.
144  *
145  * Set bTestOpenNoError=TRUE to silently return -1 with no error message
146  * if the file cannot be opened.  This is intended to be used in the
147  * context of a TestOpen() function.  The default value is FALSE which
148  * means that an error is reported if the file cannot be opened.
149  *
150  * Returns 0 on success, -1 on error.
151  **********************************************************************/
Open(const char * pszFname,TABAccess eAccess,GBool bTestOpenNoError)152 int TABSeamless::Open(const char *pszFname, TABAccess eAccess,
153                       GBool bTestOpenNoError /*= FALSE*/ )
154 {
155     char nStatus = 0;
156 
157     if (m_poIndexTable)
158     {
159         CPLError(CE_Failure, CPLE_AssertionFailed,
160                  "Open() failed: object already contains an open file");
161         return -1;
162     }
163 
164     /*-----------------------------------------------------------------
165      * Validate access mode and call the right open method
166      *----------------------------------------------------------------*/
167     if (eAccess == TABRead)
168     {
169         m_eAccessMode = TABRead;
170         nStatus = (char)OpenForRead(pszFname, bTestOpenNoError);
171     }
172     else
173     {
174         CPLError(CE_Failure, CPLE_NotSupported,
175                  "Open() failed: access mode \"%d\" not supported", eAccess);
176         return -1;
177     }
178 
179     return nStatus;
180 }
181 
182 
183 /**********************************************************************
184  *                   TABSeamless::OpenForRead()
185  *
186  * Open for reading
187  *
188  * Returns 0 on success, -1 on error.
189  **********************************************************************/
OpenForRead(const char * pszFname,GBool bTestOpenNoError)190 int TABSeamless::OpenForRead(const char *pszFname,
191                              GBool bTestOpenNoError /*= FALSE*/ )
192 {
193     int nFnameLen = 0;
194 
195     m_eAccessMode = TABRead;
196 
197     /*-----------------------------------------------------------------
198      * Read main .TAB (text) file
199      *----------------------------------------------------------------*/
200     m_pszFname = CPLStrdup(pszFname);
201 
202 #ifndef _WIN32
203     /*-----------------------------------------------------------------
204      * On Unix, make sure extension uses the right cases
205      * We do it even for write access because if a file with the same
206      * extension already exists we want to overwrite it.
207      *----------------------------------------------------------------*/
208     TABAdjustFilenameExtension(m_pszFname);
209 #endif
210 
211     /*-----------------------------------------------------------------
212      * Open .TAB file... since it's a small text file, we will just load
213      * it as a stringlist in memory.
214      *----------------------------------------------------------------*/
215     char **papszTABFile = TAB_CSLLoad(m_pszFname);
216     if (papszTABFile == NULL)
217     {
218         if (!bTestOpenNoError)
219         {
220             CPLError(CE_Failure, CPLE_FileIO,
221                      "Failed opening %s.", m_pszFname);
222         }
223 
224         CPLFree(m_pszFname);
225         CSLDestroy(papszTABFile);
226         return -1;
227     }
228 
229     /*-------------------------------------------------------------
230      * Look for a metadata line with "\IsSeamless" = "TRUE".
231      * If there is no such line, then we may have a valid .TAB file,
232      * but we do not support it in this class.
233      *------------------------------------------------------------*/
234     GBool bSeamlessFound = FALSE;
235     for (int i=0; !bSeamlessFound && papszTABFile && papszTABFile[i]; i++)
236     {
237         const char *pszStr = papszTABFile[i];
238         while(*pszStr != '\0' && isspace((unsigned char)*pszStr))
239             pszStr++;
240         if (EQUALN(pszStr, "\"\\IsSeamless\" = \"TRUE\"", 21))
241             bSeamlessFound = TRUE;
242     }
243     CSLDestroy(papszTABFile);
244 
245     if ( !bSeamlessFound )
246     {
247         if (!bTestOpenNoError)
248             CPLError(CE_Failure, CPLE_NotSupported,
249                      "%s does not appear to be a Seamless TAB File.  "
250                      "This type of .TAB file cannot be read by this library.",
251                      m_pszFname);
252         else
253             CPLErrorReset();
254 
255         CPLFree(m_pszFname);
256 
257         return -1;
258     }
259 
260     /*-----------------------------------------------------------------
261      * OK, this appears to be a valid seamless TAB dataset...
262      * Extract the path component from the main .TAB filename
263      * to build the filename of the base tables
264      *----------------------------------------------------------------*/
265     m_pszPath = CPLStrdup(m_pszFname);
266     nFnameLen = strlen(m_pszPath);
267     for( ; nFnameLen > 0; nFnameLen--)
268     {
269         if (m_pszPath[nFnameLen-1] == '/' ||
270             m_pszPath[nFnameLen-1] == '\\' )
271         {
272             break;
273         }
274         m_pszPath[nFnameLen-1] = '\0';
275     }
276 
277     /*-----------------------------------------------------------------
278      * Open the main Index table and look for the "Table" field that
279      * should contain the path to the base table for each rectangle MBR
280      *----------------------------------------------------------------*/
281     m_poIndexTable = new TABFile;
282     if (m_poIndexTable->Open(m_pszFname, m_eAccessMode, bTestOpenNoError) != 0)
283     {
284         // Open Failed... an error has already been reported, just return.
285         if (bTestOpenNoError)
286             CPLErrorReset();
287         Close();
288         return -1;
289     }
290 
291     OGRFeatureDefn *poDefn = m_poIndexTable->GetLayerDefn();
292     if (poDefn == NULL ||
293         (m_nTableNameField = poDefn->GetFieldIndex("Table")) == -1)
294     {
295         if (!bTestOpenNoError)
296             CPLError(CE_Failure, CPLE_NotSupported,
297                      "Open Failed: Field 'Table' not found in Seamless "
298                      "Dataset '%s'.  This is type of file not currently "
299                      "supported.",
300                      m_pszFname);
301         Close();
302         return -1;
303     }
304 
305     /*-----------------------------------------------------------------
306      * We need to open the first table to get its FeatureDefn
307      *----------------------------------------------------------------*/
308     if (OpenBaseTable(-1, bTestOpenNoError) != 0 )
309     {
310         // Open Failed... an error has already been reported, just return.
311         if (bTestOpenNoError)
312             CPLErrorReset();
313         Close();
314         return -1;
315     }
316 
317     CPLAssert(m_poCurBaseTable);
318     m_poFeatureDefnRef = m_poCurBaseTable->GetLayerDefn();
319     m_poFeatureDefnRef->Reference();
320 
321     return 0;
322 }
323 
324 
325 /**********************************************************************
326  *                   TABSeamless::Close()
327  *
328  * Close current file, and release all memory used.
329  *
330  * Returns 0 on success, -1 on error.
331  **********************************************************************/
Close()332 int TABSeamless::Close()
333 {
334     if (m_poIndexTable)
335         delete m_poIndexTable;  // Automatically closes.
336     m_poIndexTable = NULL;
337 
338     if (m_poFeatureDefnRef )
339         m_poFeatureDefnRef->Release();
340     m_poFeatureDefnRef = NULL;
341 
342     if (m_poCurFeature)
343         delete m_poCurFeature;
344     m_poCurFeature = NULL;
345     m_nCurFeatureId = -1;
346 
347     CPLFree(m_pszFname);
348     m_pszFname = NULL;
349 
350     CPLFree(m_pszPath);
351     m_pszPath = NULL;
352 
353     m_nTableNameField = -1;
354     m_nCurBaseTableId = -1;
355 
356     if (m_poCurBaseTable)
357         delete m_poCurBaseTable;
358     m_poCurBaseTable = NULL;
359 
360     return 0;
361 }
362 
363 /**********************************************************************
364  *                   TABSeamless::OpenBaseTable()
365  *
366  * Open the base table for specified IndexFeature.
367  *
368  * Returns 0 on success, -1 on error.
369  **********************************************************************/
OpenBaseTable(TABFeature * poIndexFeature,GBool bTestOpenNoError)370 int TABSeamless::OpenBaseTable(TABFeature *poIndexFeature,
371                                GBool bTestOpenNoError /*=FALSE*/)
372 {
373     CPLAssert(poIndexFeature);
374 
375     /*-----------------------------------------------------------------
376      * Fetch table id.  We actually use the index feature's ids as the
377      * base table ids.
378      *----------------------------------------------------------------*/
379     GIntBig nTableId64 = poIndexFeature->GetFID();
380     int nTableId = (int)nTableId64;
381     CPLAssert((GIntBig)nTableId == nTableId64);
382 
383     if (m_nCurBaseTableId == nTableId && m_poCurBaseTable != NULL)
384     {
385         // The right table is already opened.  Not much to do!
386         m_poCurBaseTable->ResetReading();
387         return 0;
388     }
389 
390     // Close current base table
391     if (m_poCurBaseTable)
392         delete m_poCurBaseTable;
393     m_nCurBaseTableId = -1;
394 
395     m_bEOF = FALSE;
396 
397     /*-----------------------------------------------------------------
398      * Build full path to the table and open it.
399      * __TODO__ For now we assume that all table filename paths are relative
400      *          but we may have to deal with absolute filenames as well.
401      *----------------------------------------------------------------*/
402     const char *pszName = poIndexFeature->GetFieldAsString(m_nTableNameField);
403     char *pszFname = CPLStrdup(CPLSPrintf("%s%s", m_pszPath, pszName));
404 
405 #ifndef _WIN32
406     // On Unix, replace any '\\' in path with '/'
407     char *pszPtr = pszFname;
408     while((pszPtr = strchr(pszPtr, '\\')) != NULL)
409     {
410         *pszPtr = '/';
411         pszPtr++;
412     }
413 #endif
414 
415     m_poCurBaseTable = new TABFile;
416     if (m_poCurBaseTable->Open(pszFname, m_eAccessMode, bTestOpenNoError) != 0)
417     {
418         // Open Failed... an error has already been reported, just return.
419         if (bTestOpenNoError)
420             CPLErrorReset();
421         delete m_poCurBaseTable;
422         m_poCurBaseTable = NULL;
423         CPLFree(pszFname);
424         return -1;
425     }
426 
427     // Set the spatial filter to the new table
428     if( m_poFilterGeom != NULL &&  m_poCurBaseTable )
429     {
430         m_poCurBaseTable->SetSpatialFilter( m_poFilterGeom );
431     }
432 
433     m_nCurBaseTableId = nTableId;
434     CPLFree(pszFname);
435 
436     return 0;
437 }
438 
439 /**********************************************************************
440  *                   TABSeamless::OpenBaseTable()
441  *
442  * Open the base table for specified IndexFeature.
443  *
444  * Returns 0 on success, -1 on error.
445  **********************************************************************/
OpenBaseTable(int nTableId,GBool bTestOpenNoError)446 int TABSeamless::OpenBaseTable(int nTableId, GBool bTestOpenNoError /*=FALSE*/)
447 {
448 
449     if (nTableId == -1)
450     {
451         // Open first table from dataset
452         m_poIndexTable->ResetReading();
453         if (OpenNextBaseTable(bTestOpenNoError) != 0)
454         {
455             // Open Failed... an error has already been reported.
456             if (bTestOpenNoError)
457                 CPLErrorReset();
458             return -1;
459         }
460     }
461     else if (nTableId == m_nCurBaseTableId && m_poCurBaseTable != NULL)
462     {
463         // The right table is already opened.  Not much to do!
464         m_poCurBaseTable->ResetReading();
465         return 0;
466     }
467     else
468     {
469         TABFeature *poIndexFeature = m_poIndexTable->GetFeatureRef(nTableId);
470 
471         if (poIndexFeature)
472         {
473             if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0)
474             {
475                 // Open Failed... an error has already been reported.
476                 if (bTestOpenNoError)
477                     CPLErrorReset();
478                 return -1;
479             }
480         }
481     }
482 
483     return 0;
484 }
485 
486 /**********************************************************************
487  *                   TABSeamless::OpenNextBaseTable()
488  *
489  * Open the next base table in the dataset, using GetNextFeature() so that
490  * the spatial filter is respected.
491  *
492  * m_bEOF will be set if there are no more base tables to read.
493  *
494  * Returns 0 on success, -1 on error.
495  **********************************************************************/
OpenNextBaseTable(GBool bTestOpenNoError)496 int TABSeamless::OpenNextBaseTable(GBool bTestOpenNoError /*=FALSE*/)
497 {
498     CPLAssert(m_poIndexTable);
499 
500     TABFeature *poIndexFeature = (TABFeature*)m_poIndexTable->GetNextFeature();
501 
502     if (poIndexFeature)
503     {
504         if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0)
505         {
506             // Open Failed... an error has already been reported.
507             if (bTestOpenNoError)
508                 CPLErrorReset();
509             delete poIndexFeature;
510             return -1;
511         }
512         delete poIndexFeature;
513         m_bEOF = FALSE;
514     }
515     else
516     {
517         // Reached EOF
518         m_bEOF = TRUE;
519     }
520 
521     return 0;
522 }
523 
524 
525 /**********************************************************************
526  *                   TABSeamless::EncodeFeatureId()
527  *
528  * Combine the table id + feature id into a single feature id that should
529  * be unique amongst all base tables in this seamless dataset.
530  **********************************************************************/
EncodeFeatureId(int nTableId,int nBaseFeatureId)531 GIntBig TABSeamless::EncodeFeatureId(int nTableId, int nBaseFeatureId)
532 {
533     if (nTableId == -1 || nBaseFeatureId == -1)
534         return -1;
535 
536     /* Feature encoding is now based on the numbers of bits on the number
537        of features in the index table. */
538 
539     return (((GIntBig)nTableId<<32) + nBaseFeatureId);
540 }
541 
ExtractBaseTableId(GIntBig nEncodedFeatureId)542 int TABSeamless::ExtractBaseTableId(GIntBig nEncodedFeatureId)
543 {
544     if (nEncodedFeatureId == -1)
545         return -1;
546 
547     return ((int)(nEncodedFeatureId>>32));
548 }
549 
ExtractBaseFeatureId(GIntBig nEncodedFeatureId)550 int TABSeamless::ExtractBaseFeatureId(GIntBig nEncodedFeatureId)
551 {
552     if (nEncodedFeatureId == -1)
553         return -1;
554 
555     return ((int)(nEncodedFeatureId & 0xffffffff));
556 }
557 
558 /**********************************************************************
559  *                   TABSeamless::GetNextFeatureId()
560  *
561  * Returns feature id that follows nPrevId, or -1 if it is the
562  * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
563  **********************************************************************/
GetNextFeatureId(GIntBig nPrevId)564 GIntBig TABSeamless::GetNextFeatureId(GIntBig nPrevId)
565 {
566     if (m_poIndexTable == NULL)
567         return -1; // File is not opened yet
568 
569     if (nPrevId == -1 || m_nCurBaseTableId != ExtractBaseTableId(nPrevId))
570     {
571         if (OpenBaseTable(ExtractBaseTableId(nPrevId)) != 0)
572             return -1;
573     }
574 
575     int nId = ExtractBaseFeatureId(nPrevId);
576     do
577     {
578         nId = (int) m_poCurBaseTable->GetNextFeatureId(nId);
579         if (nId != -1)
580             return EncodeFeatureId(m_nCurBaseTableId, nId);  // Found one!
581         else
582             OpenNextBaseTable();  // Skip to next tile and loop again
583 
584     } while (nId == -1 && !m_bEOF && m_poCurBaseTable);
585 
586     return -1;
587 }
588 
589 /**********************************************************************
590  *                   TABSeamless::GetFeatureRef()
591  *
592  * Fill and return a TABFeature object for the specified feature id.
593  *
594  * The returned pointer is a reference to an object owned and maintained
595  * by this TABSeamless object.  It should not be altered or freed by the
596  * caller and its contents is guaranteed to be valid only until the next
597  * call to GetFeatureRef() or Close().
598  *
599  * Returns NULL if the specified feature id does not exist of if an
600  * error happened.  In any case, CPLError() will have been called to
601  * report the reason of the failure.
602  **********************************************************************/
GetFeatureRef(GIntBig nFeatureId)603 TABFeature *TABSeamless::GetFeatureRef(GIntBig nFeatureId)
604 {
605     if (m_poIndexTable == NULL)
606         return NULL; // File is not opened yet
607 
608     if (nFeatureId == m_nCurFeatureId && m_poCurFeature)
609         return m_poCurFeature;
610 
611     if (m_nCurBaseTableId != ExtractBaseTableId(nFeatureId))
612     {
613         if (OpenBaseTable(ExtractBaseTableId(nFeatureId)) != 0)
614             return NULL;
615     }
616 
617     if (m_poCurBaseTable)
618     {
619         if (m_poCurFeature)
620             delete m_poCurFeature;
621         m_poCurFeature = NULL;
622 
623         TABFeature* poCurFeature = (TABFeature*)m_poCurBaseTable->GetFeature(ExtractBaseFeatureId(nFeatureId));
624         if( poCurFeature == NULL )
625             return NULL;
626         m_poCurFeature = new TABFeature(m_poFeatureDefnRef);
627         m_poCurFeature->SetFrom(poCurFeature);
628         delete poCurFeature;
629 
630         m_nCurFeatureId = nFeatureId;
631 
632         m_poCurFeature->SetFID(nFeatureId);
633 
634         return m_poCurFeature;
635     }
636 
637     return NULL;
638 }
639 
640 
641 /**********************************************************************
642  *                   TABSeamless::GetLayerDefn()
643  *
644  * Returns a reference to the OGRFeatureDefn that will be used to create
645  * features in this dataset.
646  *
647  * Returns a reference to an object that is maintained by this TABSeamless
648  * object (and thus should not be modified or freed by the caller) or
649  * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
650  * opened yet)
651  **********************************************************************/
GetLayerDefn()652 OGRFeatureDefn *TABSeamless::GetLayerDefn()
653 {
654     return m_poFeatureDefnRef;
655 }
656 
657 /**********************************************************************
658  *                   TABSeamless::GetNativeFieldType()
659  *
660  * Returns the native MapInfo field type for the specified field.
661  *
662  * Returns TABFUnknown if file is not opened, or if specified field index is
663  * invalid.
664  *
665  * Note that field ids are positive and start at 0.
666  **********************************************************************/
GetNativeFieldType(int nFieldId)667 TABFieldType TABSeamless::GetNativeFieldType(int nFieldId)
668 {
669     if (m_poCurBaseTable)
670         return m_poCurBaseTable->GetNativeFieldType(nFieldId);
671 
672     return TABFUnknown;
673 }
674 
675 
676 
677 /**********************************************************************
678  *                   TABSeamless::IsFieldIndexed()
679  *
680  * Returns TRUE if field is indexed, or FALSE otherwise.
681  **********************************************************************/
IsFieldIndexed(int nFieldId)682 GBool TABSeamless::IsFieldIndexed(int nFieldId)
683 {
684     if (m_poCurBaseTable)
685         return m_poCurBaseTable->IsFieldIndexed(nFieldId);
686 
687     return FALSE;
688 }
689 
690 /**********************************************************************
691  *                   TABSeamless::IsFieldUnique()
692  *
693  * Returns TRUE if field is in the Unique table, or FALSE otherwise.
694  **********************************************************************/
IsFieldUnique(int nFieldId)695 GBool TABSeamless::IsFieldUnique(int nFieldId)
696 {
697     if (m_poCurBaseTable)
698         return m_poCurBaseTable->IsFieldUnique(nFieldId);
699 
700     return FALSE;
701 }
702 
703 
704 /**********************************************************************
705  *                   TABSeamless::GetBounds()
706  *
707  * Fetch projection coordinates bounds of a dataset.
708  *
709  * The bForce flag has no effect on TAB files since the bounds are
710  * always in the header.
711  *
712  * Returns 0 on success, -1 on error.
713  **********************************************************************/
GetBounds(double & dXMin,double & dYMin,double & dXMax,double & dYMax,GBool bForce)714 int TABSeamless::GetBounds(double &dXMin, double &dYMin,
715                        double &dXMax, double &dYMax,
716                        GBool bForce /*= TRUE*/)
717 {
718     if (m_poIndexTable == NULL)
719     {
720         CPLError(CE_Failure, CPLE_AppDefined,
721              "GetBounds() can be called only after dataset has been opened.");
722         return -1;
723     }
724 
725     return m_poIndexTable->GetBounds(dXMin, dYMin, dXMax, dYMax, bForce);
726 }
727 
728 /**********************************************************************
729  *                   TABSeamless::GetExtent()
730  *
731  * Fetch extent of the data currently stored in the dataset.
732  *
733  * The bForce flag has no effect on TAB files since that value is
734  * always in the header.
735  *
736  * Returns OGRERR_NONE/OGRRERR_FAILURE.
737  **********************************************************************/
GetExtent(OGREnvelope * psExtent,int bForce)738 OGRErr TABSeamless::GetExtent (OGREnvelope *psExtent, int bForce)
739 {
740     if (m_poIndexTable == NULL)
741     {
742         CPLError(CE_Failure, CPLE_AppDefined,
743              "GetExtent() can be called only after dataset has been opened.");
744         return OGRERR_FAILURE;
745     }
746 
747     return m_poIndexTable->GetExtent(psExtent, bForce);
748 
749 }
750 
751 /**********************************************************************
752  *                   TABSeamless::GetFeatureCountByType()
753  *
754  * Return number of features of each type.
755  *
756  * Note that the sum of the 4 returned values may be different from
757  * the total number of features since features with NONE geometry
758  * are not taken into account here.
759  *
760  * Returns 0 on success, or silently returns -1 (with no error) if this
761  * information is not available.
762  **********************************************************************/
GetFeatureCountByType(CPL_UNUSED int & numPoints,CPL_UNUSED int & numLines,CPL_UNUSED int & numRegions,CPL_UNUSED int & numTexts,CPL_UNUSED GBool bForce)763 int TABSeamless::GetFeatureCountByType(CPL_UNUSED int &numPoints,
764                                        CPL_UNUSED int &numLines,
765                                        CPL_UNUSED int &numRegions,
766                                        CPL_UNUSED int &numTexts,
767                                        CPL_UNUSED GBool bForce /*= TRUE*/)
768 {
769     /*-----------------------------------------------------------------
770      * __TODO__  This should be implemented to return -1 if force=false,
771      * or scan all the base tables if force=true
772      *----------------------------------------------------------------*/
773 
774     return -1;
775 }
776 
GetFeatureCount(int bForce)777 GIntBig TABSeamless::GetFeatureCount(int bForce)
778 {
779     /*-----------------------------------------------------------------
780      * __TODO__  This should be implemented to return -1 if force=false,
781      * or scan all the base tables if force=true
782      *----------------------------------------------------------------*/
783 
784     return OGRLayer::GetFeatureCount(bForce);
785 }
786 
787 
788 /**********************************************************************
789  *                   TABSeamless::GetSpatialRef()
790  *
791  * Returns a reference to an OGRSpatialReference for this dataset.
792  * If the projection parameters have not been parsed yet, then we will
793  * parse them before returning.
794  *
795  * The returned object is owned and maintained by this TABFile and
796  * should not be modified or freed by the caller.
797  *
798  * Returns NULL if the SpatialRef cannot be accessed.
799  **********************************************************************/
GetSpatialRef()800 OGRSpatialReference *TABSeamless::GetSpatialRef()
801 {
802     if (m_poIndexTable == NULL)
803     {
804         CPLError(CE_Failure, CPLE_AssertionFailed,
805                  "GetSpatialRef() failed: file has not been opened yet.");
806         return NULL;
807     }
808 
809     return m_poIndexTable->GetSpatialRef();
810 }
811 
812 
813 
814 /**********************************************************************
815  *                   IMapInfoFile::SetSpatialFilter()
816  *
817  * Standard OGR SetSpatialFiltere implementation.  This methode is used
818  * to set a SpatialFilter for this OGRLayer
819  **********************************************************************/
SetSpatialFilter(OGRGeometry * poGeomIn)820 void TABSeamless::SetSpatialFilter (OGRGeometry * poGeomIn )
821 
822 {
823     IMapInfoFile::SetSpatialFilter( poGeomIn );
824 
825     if( m_poIndexTable )
826         m_poIndexTable->SetSpatialFilter( poGeomIn );
827 
828     if( m_poCurBaseTable )
829         m_poCurBaseTable->SetSpatialFilter( poGeomIn );
830 }
831 
832 
833 
834 /************************************************************************/
835 /*                           TestCapability()                           */
836 /************************************************************************/
837 
TestCapability(const char * pszCap)838 int TABSeamless::TestCapability( const char * pszCap )
839 
840 {
841     if( EQUAL(pszCap,OLCRandomRead) )
842         return TRUE;
843 
844     else if( EQUAL(pszCap,OLCSequentialWrite)
845              || EQUAL(pszCap,OLCRandomWrite) )
846         return FALSE;
847 
848     else if( EQUAL(pszCap,OLCFastFeatureCount) )
849         return FALSE;
850 
851     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
852         return FALSE;
853 
854     else if( EQUAL(pszCap,OLCFastGetExtent) )
855         return TRUE;
856 
857     else
858         return FALSE;
859 }
860 
861 
862 
863 /**********************************************************************
864  *                   TABSeamless::Dump()
865  *
866  * Dump block contents... available only in DEBUG mode.
867  **********************************************************************/
868 #ifdef DEBUG
869 
Dump(FILE * fpOut)870 void TABSeamless::Dump(FILE *fpOut /*=NULL*/)
871 {
872     if (fpOut == NULL)
873         fpOut = stdout;
874 
875     fprintf(fpOut, "----- TABSeamless::Dump() -----\n");
876 
877     if (m_poIndexTable == NULL)
878     {
879         fprintf(fpOut, "File is not opened.\n");
880     }
881     else
882     {
883         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
884 
885     }
886 
887     fflush(fpOut);
888 }
889 
890 #endif // DEBUG
891