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