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