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