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