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
|