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