Coverage Report

Created: 2025-11-15 08:43

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