LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_tabseamless.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 156 273 57.1 %
Date: 2025-09-10 17:48:50 Functions: 17 27 63.0 %

          Line data    Source code
       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           1 : TABSeamless::TABSeamless(GDALDataset *poDS)
      55             :     : IMapInfoFile(poDS), m_pszFname(nullptr), m_pszPath(nullptr),
      56             :       m_eAccessMode(TABRead), m_poFeatureDefnRef(nullptr),
      57             :       m_poIndexTable(nullptr), m_nTableNameField(-1), m_nCurBaseTableId(-1),
      58           1 :       m_poCurBaseTable(nullptr), m_bEOF(FALSE)
      59             : {
      60           1 :     m_poCurFeature = nullptr;
      61           1 :     m_nCurFeatureId = -1;
      62           1 : }
      63             : 
      64             : /**********************************************************************
      65             :  *                   TABSeamless::~TABSeamless()
      66             :  *
      67             :  * Destructor.
      68             :  **********************************************************************/
      69           2 : TABSeamless::~TABSeamless()
      70             : {
      71           1 :     TABSeamless::Close();
      72           2 : }
      73             : 
      74           2 : void TABSeamless::ResetReading()
      75             : {
      76           2 :     if (m_poIndexTable)
      77           2 :         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           2 :     m_nCurFeatureId = -1;
      82           2 : }
      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           1 : int TABSeamless::Open(const char *pszFname, TABAccess eAccess,
     101             :                       GBool bTestOpenNoError /*= FALSE*/,
     102             :                       const char * /*pszCharset = NULL */)
     103             : {
     104           1 :     char nStatus = 0;
     105             : 
     106           1 :     if (m_poIndexTable)
     107             :     {
     108           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     109             :                  "Open() failed: object already contains an open file");
     110           0 :         return -1;
     111             :     }
     112             : 
     113             :     /*-----------------------------------------------------------------
     114             :      * Validate access mode and call the right open method
     115             :      *----------------------------------------------------------------*/
     116           1 :     if (eAccess == TABRead)
     117             :     {
     118           1 :         m_eAccessMode = TABRead;
     119           1 :         nStatus = static_cast<char>(OpenForRead(pszFname, bTestOpenNoError));
     120             :     }
     121             :     else
     122             :     {
     123           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     124             :                  "Open() failed: access mode \"%d\" not supported", eAccess);
     125           0 :         return -1;
     126             :     }
     127             : 
     128           1 :     return nStatus;
     129             : }
     130             : 
     131             : /**********************************************************************
     132             :  *                   TABSeamless::OpenForRead()
     133             :  *
     134             :  * Open for reading
     135             :  *
     136             :  * Returns 0 on success, -1 on error.
     137             :  **********************************************************************/
     138           1 : int TABSeamless::OpenForRead(const char *pszFname,
     139             :                              GBool bTestOpenNoError /*= FALSE*/)
     140             : {
     141           1 :     int nFnameLen = 0;
     142             : 
     143           1 :     m_eAccessMode = TABRead;
     144             : 
     145             :     /*-----------------------------------------------------------------
     146             :      * Read main .TAB (text) file
     147             :      *----------------------------------------------------------------*/
     148           1 :     m_pszFname = CPLStrdup(pszFname);
     149             : 
     150             : #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           1 :     TABAdjustFilenameExtension(m_pszFname);
     157             : #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           1 :     char **papszTABFile = TAB_CSLLoad(m_pszFname);
     164           1 :     if (papszTABFile == nullptr)
     165             :     {
     166           0 :         if (!bTestOpenNoError)
     167             :         {
     168           0 :             CPLError(CE_Failure, CPLE_FileIO, "Failed opening %s.", m_pszFname);
     169             :         }
     170             : 
     171           0 :         CPLFree(m_pszFname);
     172           0 :         m_pszFname = nullptr;
     173           0 :         CSLDestroy(papszTABFile);
     174           0 :         return -1;
     175             :     }
     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           1 :     GBool bSeamlessFound = FALSE;
     183          11 :     for (int i = 0; !bSeamlessFound && papszTABFile[i]; i++)
     184             :     {
     185          10 :         const char *pszStr = papszTABFile[i];
     186          24 :         while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr)))
     187          14 :             pszStr++;
     188          10 :         if (STARTS_WITH_CI(pszStr, "\"\\IsSeamless\" = \"TRUE\""))
     189           1 :             bSeamlessFound = TRUE;
     190             :     }
     191           1 :     CSLDestroy(papszTABFile);
     192             : 
     193           1 :     if (!bSeamlessFound)
     194             :     {
     195           0 :         if (!bTestOpenNoError)
     196           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     197             :                      "%s does not appear to be a Seamless TAB File.  "
     198             :                      "This type of .TAB file cannot be read by this library.",
     199             :                      m_pszFname);
     200             :         else
     201           0 :             CPLErrorReset();
     202             : 
     203           0 :         CPLFree(m_pszFname);
     204           0 :         m_pszFname = nullptr;
     205             : 
     206           0 :         return -1;
     207             :     }
     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           1 :     m_pszPath = CPLStrdup(m_pszFname);
     215           1 :     nFnameLen = static_cast<int>(strlen(m_pszPath));
     216          13 :     for (; nFnameLen > 0; nFnameLen--)
     217             :     {
     218          13 :         if (m_pszPath[nFnameLen - 1] == '/' || m_pszPath[nFnameLen - 1] == '\\')
     219             :         {
     220             :             break;
     221             :         }
     222          12 :         m_pszPath[nFnameLen - 1] = '\0';
     223             :     }
     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           1 :     m_poIndexTable = new TABFile(m_poDS);
     230           1 :     if (m_poIndexTable->Open(m_pszFname, m_eAccessMode, bTestOpenNoError) != 0)
     231             :     {
     232             :         // Open Failed... an error has already been reported, just return.
     233           0 :         if (bTestOpenNoError)
     234           0 :             CPLErrorReset();
     235           0 :         Close();
     236           0 :         return -1;
     237             :     }
     238             : 
     239           1 :     const OGRFeatureDefn *poDefn = m_poIndexTable->GetLayerDefn();
     240           2 :     if (poDefn == nullptr ||
     241           1 :         (m_nTableNameField = poDefn->GetFieldIndex("Table")) == -1)
     242             :     {
     243           0 :         if (!bTestOpenNoError)
     244           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     245             :                      "Open Failed: Field 'Table' not found in Seamless "
     246             :                      "Dataset '%s'.  This is type of file not currently "
     247             :                      "supported.",
     248             :                      m_pszFname);
     249           0 :         Close();
     250           0 :         return -1;
     251             :     }
     252             : 
     253             :     /*-----------------------------------------------------------------
     254             :      * We need to open the first table to get its FeatureDefn
     255             :      *----------------------------------------------------------------*/
     256           1 :     if (OpenBaseTable(-1, bTestOpenNoError) != 0)
     257             :     {
     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             :     }
     264             : 
     265           1 :     CPLAssert(m_poCurBaseTable);
     266           1 :     OGRLayer *poCurBaseTable = m_poCurBaseTable;
     267           1 :     m_poFeatureDefnRef = poCurBaseTable->GetLayerDefn();
     268           1 :     m_poFeatureDefnRef->Reference();
     269             : 
     270           1 :     return 0;
     271             : }
     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           1 : int TABSeamless::Close()
     281             : {
     282           1 :     if (m_poIndexTable)
     283           1 :         delete m_poIndexTable;  // Automatically closes.
     284           1 :     m_poIndexTable = nullptr;
     285             : 
     286           1 :     if (m_poFeatureDefnRef)
     287           1 :         m_poFeatureDefnRef->Release();
     288           1 :     m_poFeatureDefnRef = nullptr;
     289             : 
     290           1 :     if (m_poCurFeature)
     291           0 :         delete m_poCurFeature;
     292           1 :     m_poCurFeature = nullptr;
     293           1 :     m_nCurFeatureId = -1;
     294             : 
     295           1 :     CPLFree(m_pszFname);
     296           1 :     m_pszFname = nullptr;
     297             : 
     298           1 :     CPLFree(m_pszPath);
     299           1 :     m_pszPath = nullptr;
     300             : 
     301           1 :     m_nTableNameField = -1;
     302           1 :     m_nCurBaseTableId = -1;
     303             : 
     304           1 :     if (m_poCurBaseTable)
     305           1 :         delete m_poCurBaseTable;
     306           1 :     m_poCurBaseTable = nullptr;
     307             : 
     308           1 :     return 0;
     309             : }
     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           9 : int TABSeamless::OpenBaseTable(TABFeature *poIndexFeature,
     319             :                                GBool bTestOpenNoError /*=FALSE*/)
     320             : {
     321           9 :     CPLAssert(poIndexFeature);
     322             : 
     323             :     /*-----------------------------------------------------------------
     324             :      * Fetch table id.  We actually use the index feature's ids as the
     325             :      * base table ids.
     326             :      *----------------------------------------------------------------*/
     327           9 :     GIntBig nTableId64 = poIndexFeature->GetFID();
     328           9 :     int nTableId = static_cast<int>(nTableId64);
     329           9 :     CPLAssert(static_cast<GIntBig>(nTableId) == nTableId64);
     330             : 
     331           9 :     if (m_nCurBaseTableId == nTableId && m_poCurBaseTable != nullptr)
     332             :     {
     333             :         // The right table is already opened.  Not much to do!
     334           3 :         m_poCurBaseTable->ResetReading();
     335           3 :         return 0;
     336             :     }
     337             : 
     338             :     // Close current base table
     339           6 :     if (m_poCurBaseTable)
     340           5 :         delete m_poCurBaseTable;
     341           6 :     m_nCurBaseTableId = -1;
     342             : 
     343           6 :     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           6 :     const char *pszName = poIndexFeature->GetFieldAsString(m_nTableNameField);
     351           6 :     char *pszFname = CPLStrdup(CPLSPrintf("%s%s", m_pszPath, pszName));
     352             : 
     353             : #ifndef _WIN32
     354             :     // On Unix, replace any '\\' in path with '/'
     355           6 :     char *pszPtr = pszFname;
     356           6 :     while ((pszPtr = strchr(pszPtr, '\\')) != nullptr)
     357             :     {
     358           0 :         *pszPtr = '/';
     359           0 :         pszPtr++;
     360             :     }
     361             : #endif
     362             : 
     363           6 :     m_poCurBaseTable = new TABFile(m_poDS);
     364           6 :     if (m_poCurBaseTable->Open(pszFname, m_eAccessMode, bTestOpenNoError) != 0)
     365             :     {
     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             :     }
     374             : 
     375             :     // Set the spatial filter to the new table
     376           6 :     if (m_poFilterGeom != nullptr)
     377             :     {
     378           0 :         m_poCurBaseTable->SetSpatialFilter(m_poFilterGeom);
     379             :     }
     380             : 
     381           6 :     m_nCurBaseTableId = nTableId;
     382           6 :     CPLFree(pszFname);
     383             : 
     384           6 :     return 0;
     385             : }
     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           7 : int TABSeamless::OpenBaseTable(int nTableId, GBool bTestOpenNoError /*=FALSE*/)
     395             : {
     396             : 
     397           7 :     if (nTableId == -1)
     398             :     {
     399             :         // Open first table from dataset
     400           5 :         m_poIndexTable->ResetReading();
     401           5 :         if (OpenNextBaseTable(bTestOpenNoError) != 0)
     402             :         {
     403             :             // Open Failed... an error has already been reported.
     404           0 :             if (bTestOpenNoError)
     405           0 :                 CPLErrorReset();
     406           0 :             return -1;
     407             :         }
     408             :     }
     409           2 :     else if (nTableId == m_nCurBaseTableId && m_poCurBaseTable != nullptr)
     410             :     {
     411             :         // The right table is already opened.  Not much to do!
     412           0 :         m_poCurBaseTable->ResetReading();
     413           0 :         return 0;
     414             :     }
     415             :     else
     416             :     {
     417           2 :         TABFeature *poIndexFeature = m_poIndexTable->GetFeatureRef(nTableId);
     418             : 
     419           2 :         if (poIndexFeature)
     420             :         {
     421           2 :             if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0)
     422             :             {
     423             :                 // Open Failed... an error has already been reported.
     424           0 :                 if (bTestOpenNoError)
     425           0 :                     CPLErrorReset();
     426           0 :                 return -1;
     427             :             }
     428             :         }
     429             :     }
     430             : 
     431           7 :     return 0;
     432             : }
     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           8 : int TABSeamless::OpenNextBaseTable(GBool bTestOpenNoError /*=FALSE*/)
     445             : {
     446           8 :     CPLAssert(m_poIndexTable);
     447             : 
     448             :     TABFeature *poIndexFeature =
     449           8 :         cpl::down_cast<TABFeature *>(m_poIndexTable->GetNextFeature());
     450             : 
     451           8 :     if (poIndexFeature)
     452             :     {
     453           7 :         if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0)
     454             :         {
     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             :         }
     461           7 :         delete poIndexFeature;
     462           7 :         m_bEOF = FALSE;
     463             :     }
     464             :     else
     465             :     {
     466             :         // Reached EOF
     467           1 :         m_bEOF = TRUE;
     468             :     }
     469             : 
     470           8 :     return 0;
     471             : }
     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           8 : GIntBig TABSeamless::EncodeFeatureId(int nTableId, int nBaseFeatureId)
     480             : {
     481           8 :     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           8 :     return (static_cast<GIntBig>(nTableId) << 32) + nBaseFeatureId;
     488             : }
     489             : 
     490          23 : int TABSeamless::ExtractBaseTableId(GIntBig nEncodedFeatureId)
     491             : {
     492          23 :     if (nEncodedFeatureId == -1)
     493           2 :         return -1;
     494             : 
     495          21 :     return static_cast<int>(nEncodedFeatureId >> 32);
     496             : }
     497             : 
     498          21 : int TABSeamless::ExtractBaseFeatureId(GIntBig nEncodedFeatureId)
     499             : {
     500          21 :     if (nEncodedFeatureId == -1)
     501           2 :         return -1;
     502             : 
     503          19 :     return static_cast<int>(nEncodedFeatureId & 0xffffffff);
     504             : }
     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           9 : GIntBig TABSeamless::GetNextFeatureId(GIntBig nPrevId)
     513             : {
     514           9 :     if (m_poIndexTable == nullptr || m_poCurBaseTable == nullptr)
     515           0 :         return -1;  // File is not opened yet
     516             : 
     517           9 :     if (nPrevId == -1 || m_nCurBaseTableId != ExtractBaseTableId(nPrevId))
     518             :     {
     519           2 :         if (OpenBaseTable(ExtractBaseTableId(nPrevId)) != 0)
     520           0 :             return -1;
     521             :     }
     522             : 
     523           9 :     int nId = ExtractBaseFeatureId(nPrevId);
     524           2 :     do
     525             :     {
     526          11 :         nId = static_cast<int>(m_poCurBaseTable->GetNextFeatureId(nId));
     527          11 :         if (nId != -1)
     528           8 :             return EncodeFeatureId(m_nCurBaseTableId, nId);  // Found one!
     529             :         else
     530           3 :             OpenNextBaseTable();  // Skip to next tile and loop again
     531           3 :     } while (nId == -1 && !m_bEOF && m_poCurBaseTable);
     532             : 
     533           1 :     return -1;
     534             : }
     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          12 : TABFeature *TABSeamless::GetFeatureRef(GIntBig nFeatureId)
     551             : {
     552          12 :     if (m_poIndexTable == nullptr)
     553           0 :         return nullptr;  // File is not opened yet
     554             : 
     555          12 :     if (nFeatureId == m_nCurFeatureId && m_poCurFeature)
     556           0 :         return m_poCurFeature;
     557             : 
     558          12 :     if (m_nCurBaseTableId != ExtractBaseTableId(nFeatureId))
     559             :     {
     560           2 :         if (OpenBaseTable(ExtractBaseTableId(nFeatureId)) != 0)
     561           0 :             return nullptr;
     562             :     }
     563             : 
     564          12 :     if (m_poCurBaseTable)
     565             :     {
     566          12 :         if (m_poCurFeature)
     567           0 :             delete m_poCurFeature;
     568          12 :         m_poCurFeature = nullptr;
     569             : 
     570             :         TABFeature *poCurFeature = static_cast<TABFeature *>(
     571          12 :             m_poCurBaseTable->GetFeature(ExtractBaseFeatureId(nFeatureId)));
     572          12 :         if (poCurFeature == nullptr)
     573           2 :             return nullptr;
     574          10 :         m_poCurFeature = new TABFeature(m_poFeatureDefnRef);
     575          10 :         m_poCurFeature->SetFrom(poCurFeature);
     576          10 :         delete poCurFeature;
     577             : 
     578          10 :         m_nCurFeatureId = nFeatureId;
     579             : 
     580          10 :         m_poCurFeature->SetFID(nFeatureId);
     581             : 
     582          10 :         return m_poCurFeature;
     583             :     }
     584             : 
     585           0 :     return nullptr;
     586             : }
     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           1 : const OGRFeatureDefn *TABSeamless::GetLayerDefn() const
     600             : {
     601           1 :     return m_poFeatureDefnRef;
     602             : }
     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           0 : TABFieldType TABSeamless::GetNativeFieldType(int nFieldId)
     615             : {
     616           0 :     if (m_poCurBaseTable)
     617           0 :         return m_poCurBaseTable->GetNativeFieldType(nFieldId);
     618             : 
     619           0 :     return TABFUnknown;
     620             : }
     621             : 
     622             : /**********************************************************************
     623             :  *                   TABSeamless::IsFieldIndexed()
     624             :  *
     625             :  * Returns TRUE if field is indexed, or FALSE otherwise.
     626             :  **********************************************************************/
     627           0 : GBool TABSeamless::IsFieldIndexed(int nFieldId)
     628             : {
     629           0 :     if (m_poCurBaseTable)
     630           0 :         return m_poCurBaseTable->IsFieldIndexed(nFieldId);
     631             : 
     632           0 :     return FALSE;
     633             : }
     634             : 
     635             : /**********************************************************************
     636             :  *                   TABSeamless::IsFieldUnique()
     637             :  *
     638             :  * Returns TRUE if field is in the Unique table, or FALSE otherwise.
     639             :  **********************************************************************/
     640           0 : GBool TABSeamless::IsFieldUnique(int nFieldId)
     641             : {
     642           0 :     if (m_poCurBaseTable)
     643           0 :         return m_poCurBaseTable->IsFieldUnique(nFieldId);
     644             : 
     645           0 :     return FALSE;
     646             : }
     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           0 : int TABSeamless::GetBounds(double &dXMin, double &dYMin, double &dXMax,
     659             :                            double &dYMax, GBool bForce /*= TRUE*/)
     660             : {
     661           0 :     if (m_poIndexTable == nullptr)
     662             :     {
     663           0 :         CPLError(
     664             :             CE_Failure, CPLE_AppDefined,
     665             :             "GetBounds() can be called only after dataset has been opened.");
     666           0 :         return -1;
     667             :     }
     668             : 
     669           0 :     return m_poIndexTable->GetBounds(dXMin, dYMin, dXMax, dYMax, bForce);
     670             : }
     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           0 : OGRErr TABSeamless::IGetExtent(int iGeomField, OGREnvelope *psExtent,
     683             :                                bool bForce)
     684             : {
     685           0 :     if (m_poIndexTable == nullptr)
     686             :     {
     687           0 :         CPLError(
     688             :             CE_Failure, CPLE_AppDefined,
     689             :             "GetExtent() can be called only after dataset has been opened.");
     690           0 :         return OGRERR_FAILURE;
     691             :     }
     692             : 
     693           0 :     return m_poIndexTable->GetExtent(iGeomField, psExtent, bForce);
     694             : }
     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           0 : 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             : {
     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             : }
     721             : 
     722           1 : GIntBig TABSeamless::GetFeatureCount(int bForce)
     723             : {
     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           1 :     return OGRLayer::GetFeatureCount(bForce);
     730             : }
     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           0 : const OGRSpatialReference *TABSeamless::GetSpatialRef() const
     745             : {
     746           0 :     if (m_poIndexTable == nullptr)
     747             :     {
     748           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     749             :                  "GetSpatialRef() failed: file has not been opened yet.");
     750           0 :         return nullptr;
     751             :     }
     752             : 
     753           0 :     return m_poIndexTable->GetSpatialRef();
     754             : }
     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           0 : OGRErr TABSeamless::ISetSpatialFilter(int /*iGeomField*/,
     763             :                                       const OGRGeometry *poGeomIn)
     764             : 
     765             : {
     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             : }
     776             : 
     777             : /************************************************************************/
     778             : /*                           TestCapability()                           */
     779             : /************************************************************************/
     780             : 
     781           0 : int TABSeamless::TestCapability(const char *pszCap) const
     782             : 
     783             : {
     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             :     else
     803           0 :         return FALSE;
     804             : }
     805             : 
     806             : /**********************************************************************
     807             :  *                   TABSeamless::Dump()
     808             :  *
     809             :  * Dump block contents... available only in DEBUG mode.
     810             :  **********************************************************************/
     811             : #ifdef DEBUG
     812             : 
     813           0 : void TABSeamless::Dump(FILE *fpOut /*=NULL*/)
     814             : {
     815           0 :     if (fpOut == nullptr)
     816           0 :         fpOut = stdout;
     817             : 
     818           0 :     fprintf(fpOut, "----- TABSeamless::Dump() -----\n");
     819             : 
     820           0 :     if (m_poIndexTable == nullptr)
     821             :     {
     822           0 :         fprintf(fpOut, "File is not opened.\n");
     823             :     }
     824             :     else
     825             :     {
     826           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
     827             :     }
     828             : 
     829           0 :     fflush(fpOut);
     830           0 : }
     831             : 
     832             : #endif  // DEBUG

Generated by: LCOV version 1.14