/src/gdal/ogr/ogrsf_frmts/mitab/mitab_tabfile.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * Name: mitab_tabfile.cpp |
4 | | * Project: MapInfo TAB Read/Write library |
5 | | * Language: C++ |
6 | | * Purpose: Implementation of the TABFile class, the main class of the lib. |
7 | | * To be used by external programs to handle reading/writing of |
8 | | * features from/to TAB datasets. |
9 | | * Author: Daniel Morissette, dmorissette@dmsolutions.ca |
10 | | * |
11 | | ********************************************************************** |
12 | | * Copyright (c) 1999-2003, Daniel Morissette |
13 | | * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com> |
14 | | * |
15 | | * SPDX-License-Identifier: MIT |
16 | | **********************************************************************/ |
17 | | |
18 | | #include "cpl_port.h" |
19 | | #include "mitab.h" |
20 | | |
21 | | #include <cctype> |
22 | | #include <climits> |
23 | | #include <cstdio> |
24 | | #include <cstdlib> |
25 | | #include <cstring> |
26 | | #include <algorithm> |
27 | | #include <memory> |
28 | | |
29 | | #include "cpl_conv.h" |
30 | | #include "cpl_error.h" |
31 | | #include "cpl_minixml.h" |
32 | | #include "cpl_string.h" |
33 | | #include "cpl_vsi.h" |
34 | | #include "mitab_priv.h" |
35 | | #include "mitab_utils.h" |
36 | | #include "ogr_core.h" |
37 | | #include "ogr_feature.h" |
38 | | #include "ogr_geometry.h" |
39 | | #include "ogr_p.h" |
40 | | #include "ogr_spatialref.h" |
41 | | #include "ogrsf_frmts.h" |
42 | | |
43 | | static const char UNSUPPORTED_OP_READ_ONLY[] = |
44 | | "%s : unsupported operation on a read-only datasource."; |
45 | | |
46 | | constexpr const char *DESCRIPTION_KEY = "DESCRIPTION"; |
47 | | // Tab file allow to store description longer than 255 characters. |
48 | | // But only 255 will shown in MapInfo layer list. |
49 | | constexpr int MAX_DESCRIPTION_LEN = 254 * 2; |
50 | | |
51 | | static char *EscapeString(const char *pszInput, |
52 | | bool bEscapeDoubleQuotes = false) |
53 | 226 | { |
54 | 226 | if (nullptr == pszInput) |
55 | 226 | { |
56 | 226 | return nullptr; |
57 | 226 | } |
58 | | |
59 | 0 | auto nLength = CPLStrnlen(pszInput, MAX_DESCRIPTION_LEN); |
60 | 0 | char *pszOutput = static_cast<char *>(CPLMalloc(nLength * 2 + 1)); |
61 | 0 | int iOut = 0; |
62 | 0 | int nDoubleQuotesCount = 0; |
63 | 0 | for (int iIn = 0; iIn < static_cast<int>(nLength + 1); ++iIn) |
64 | 0 | { |
65 | 0 | if (pszInput[iIn] == '"') |
66 | 0 | { |
67 | 0 | if (bEscapeDoubleQuotes) |
68 | 0 | { |
69 | 0 | pszOutput[iOut++] = '"'; |
70 | 0 | pszOutput[iOut++] = '"'; |
71 | 0 | } |
72 | 0 | else |
73 | 0 | { |
74 | 0 | nDoubleQuotesCount++; |
75 | 0 | pszOutput[iOut++] = pszInput[iIn]; |
76 | 0 | } |
77 | 0 | } |
78 | 0 | else if (pszInput[iIn] == '\n' || pszInput[iIn] == '\r') |
79 | 0 | { |
80 | 0 | pszOutput[iOut++] = ' '; |
81 | 0 | } |
82 | 0 | else |
83 | 0 | { |
84 | 0 | if ((pszInput[iIn] & 0xc0) != 0x80) |
85 | 0 | { |
86 | | // Stop at the start of the character just beyond the maximum |
87 | | // accepted |
88 | 0 | if (iOut >= MAX_DESCRIPTION_LEN - nDoubleQuotesCount) |
89 | 0 | { |
90 | 0 | break; |
91 | 0 | } |
92 | 0 | } |
93 | 0 | pszOutput[iOut++] = pszInput[iIn]; |
94 | 0 | } |
95 | 0 | } |
96 | |
|
97 | 0 | pszOutput[iOut] = '\0'; |
98 | 0 | return pszOutput; |
99 | 226 | } |
100 | | |
101 | | static char *UnescapeString(const char *pszInput) |
102 | 0 | { |
103 | 0 | if (nullptr == pszInput) |
104 | 0 | { |
105 | 0 | return nullptr; |
106 | 0 | } |
107 | | |
108 | 0 | auto nLength = CPLStrnlen(pszInput, MAX_DESCRIPTION_LEN); |
109 | 0 | char *pszOutput = static_cast<char *>(CPLMalloc(nLength * 2 + 1)); |
110 | 0 | int iOut = 0; |
111 | 0 | for (int iIn = 0; iIn < static_cast<int>(nLength + 1); ++iIn) |
112 | 0 | { |
113 | 0 | if (pszInput[iIn] == '"' && pszInput[iIn + 1] == '"') |
114 | 0 | { |
115 | 0 | ++iIn; |
116 | 0 | pszOutput[iOut++] = pszInput[iIn]; |
117 | 0 | } |
118 | 0 | else |
119 | 0 | { |
120 | 0 | if ((pszInput[iIn] & 0xc0) != 0x80) |
121 | 0 | { |
122 | | // Stop at the start of the character just beyond the maximum |
123 | | // accepted |
124 | 0 | if (iOut >= MAX_DESCRIPTION_LEN) |
125 | 0 | { |
126 | 0 | break; |
127 | 0 | } |
128 | 0 | } |
129 | 0 | pszOutput[iOut++] = pszInput[iIn]; |
130 | 0 | } |
131 | 0 | } |
132 | 0 | pszOutput[iOut] = '\0'; |
133 | 0 | return pszOutput; |
134 | 0 | } |
135 | | |
136 | | static std::string GetTabDescription(const char *pszLine) |
137 | 0 | { |
138 | 0 | CPLString osDescriptionLine(pszLine); |
139 | 0 | auto nStart = osDescriptionLine.find_first_of('"') + 1; |
140 | 0 | if (nStart != std::string::npos) |
141 | 0 | { |
142 | 0 | auto nEnd = osDescriptionLine.find_last_of('"'); |
143 | 0 | auto nLen = nEnd == std::string::npos ? nEnd : nEnd - nStart; |
144 | 0 | return osDescriptionLine.substr(nStart, nLen); |
145 | 0 | } |
146 | 0 | return ""; |
147 | 0 | } |
148 | | |
149 | | /*===================================================================== |
150 | | * class TABFile |
151 | | *====================================================================*/ |
152 | | |
153 | | /********************************************************************** |
154 | | * TABFile::TABFile() |
155 | | * |
156 | | * Constructor. |
157 | | **********************************************************************/ |
158 | | TABFile::TABFile(GDALDataset *poDS) |
159 | 2.81k | : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead), |
160 | 2.81k | m_papszTABFile(nullptr), m_nVersion(300), m_panIndexNo(nullptr), |
161 | 2.81k | m_eTableType(TABTableNative), m_poDATFile(nullptr), m_poMAPFile(nullptr), |
162 | 2.81k | m_poINDFile(nullptr), m_poDefn(nullptr), m_poSpatialRef(nullptr), |
163 | 2.81k | bUseSpatialTraversal(FALSE), m_nLastFeatureId(0), |
164 | 2.81k | m_panMatchingFIDs(nullptr), m_iMatchingFID(0), m_bNeedTABRewrite(FALSE), |
165 | 2.81k | m_bLastOpWasRead(FALSE), m_bLastOpWasWrite(FALSE) |
166 | 2.81k | { |
167 | 2.81k | m_poCurFeature = nullptr; |
168 | 2.81k | m_nCurFeatureId = 0; |
169 | 2.81k | } |
170 | | |
171 | | /********************************************************************** |
172 | | * TABFile::~TABFile() |
173 | | * |
174 | | * Destructor. |
175 | | **********************************************************************/ |
176 | | TABFile::~TABFile() |
177 | 2.81k | { |
178 | 2.81k | TABFile::Close(); |
179 | 2.81k | } |
180 | | |
181 | | /************************************************************************/ |
182 | | /* GetFeatureCount() */ |
183 | | /************************************************************************/ |
184 | | |
185 | | GIntBig TABFile::GetFeatureCount(int bForce) |
186 | 0 | { |
187 | |
|
188 | 0 | if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr || bForce) |
189 | 0 | return OGRLayer::GetFeatureCount(bForce); |
190 | 0 | else |
191 | 0 | return m_nLastFeatureId; |
192 | 0 | } |
193 | | |
194 | | /************************************************************************/ |
195 | | /* ResetReading() */ |
196 | | /************************************************************************/ |
197 | | void TABFile::ResetReading() |
198 | 0 | { |
199 | 0 | CPLFree(m_panMatchingFIDs); |
200 | 0 | m_panMatchingFIDs = nullptr; |
201 | 0 | m_iMatchingFID = 0; |
202 | |
|
203 | 0 | m_nCurFeatureId = 0; |
204 | 0 | if (m_poMAPFile != nullptr) |
205 | 0 | m_poMAPFile->ResetReading(); |
206 | | |
207 | | /* -------------------------------------------------------------------- */ |
208 | | /* Decide whether to operate in spatial traversal mode or not, */ |
209 | | /* and ensure the current spatial filter is applied to the map */ |
210 | | /* file object. */ |
211 | | /* -------------------------------------------------------------------- */ |
212 | 0 | if (m_poMAPFile) |
213 | 0 | { |
214 | 0 | bUseSpatialTraversal = FALSE; |
215 | |
|
216 | 0 | m_poMAPFile->ResetCoordFilter(); |
217 | |
|
218 | 0 | if (m_poFilterGeom != nullptr) |
219 | 0 | { |
220 | | // TABMAPHeaderBlock *poHeader = m_poMAPFile->GetHeaderBlock(); |
221 | |
|
222 | 0 | OGREnvelope sEnvelope; |
223 | 0 | m_poFilterGeom->getEnvelope(&sEnvelope); |
224 | |
|
225 | 0 | TABVertex sMin; |
226 | 0 | TABVertex sMax; |
227 | 0 | m_poMAPFile->GetCoordFilter(sMin, sMax); |
228 | |
|
229 | 0 | if (sEnvelope.MinX > sMin.x || sEnvelope.MinY > sMin.y || |
230 | 0 | sEnvelope.MaxX < sMax.x || sEnvelope.MaxY < sMax.y) |
231 | 0 | { |
232 | 0 | bUseSpatialTraversal = TRUE; |
233 | 0 | sMin.x = sEnvelope.MinX; |
234 | 0 | sMin.y = sEnvelope.MinY; |
235 | 0 | sMax.x = sEnvelope.MaxX; |
236 | 0 | sMax.y = sEnvelope.MaxY; |
237 | 0 | m_poMAPFile->SetCoordFilter(sMin, sMax); |
238 | 0 | } |
239 | 0 | } |
240 | 0 | } |
241 | |
|
242 | 0 | m_bLastOpWasRead = FALSE; |
243 | 0 | m_bLastOpWasWrite = FALSE; |
244 | 0 | } |
245 | | |
246 | | /********************************************************************** |
247 | | * TABFile::Open() |
248 | | * |
249 | | * Open a .TAB dataset and the associated files, and initialize the |
250 | | * structures to be ready to read features from (or write to) it. |
251 | | * |
252 | | * Supported access modes are "r" (read-only) and "w" (create new dataset or |
253 | | * update). |
254 | | * |
255 | | * Set bTestOpenNoError=TRUE to silently return -1 with no error message |
256 | | * if the file cannot be opened. This is intended to be used in the |
257 | | * context of a TestOpen() function. The default value is FALSE which |
258 | | * means that an error is reported if the file cannot be opened. |
259 | | * |
260 | | * Note that dataset extents will have to be set using SetBounds() before |
261 | | * any feature can be written to a newly created dataset. |
262 | | * |
263 | | * In read mode, a valid dataset must have at least a .TAB and a .DAT file. |
264 | | * The .MAP and .ID files are optional and if they do not exist then |
265 | | * all features will be returned with NONE geometry. |
266 | | * |
267 | | * Returns 0 on success, -1 on error. |
268 | | **********************************************************************/ |
269 | | int TABFile::Open(const char *pszFname, TABAccess eAccess, |
270 | | GBool bTestOpenNoError /*=FALSE*/, int nBlockSizeForCreate, |
271 | | const char *pszCharset /* = NULL */) |
272 | 2.81k | { |
273 | 2.81k | char *pszTmpFname = nullptr; |
274 | 2.81k | int nFnameLen = 0; |
275 | | |
276 | 2.81k | CPLErrorReset(); |
277 | | |
278 | 2.81k | if (m_poMAPFile) |
279 | 0 | { |
280 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
281 | 0 | "Open() failed: object already contains an open file"); |
282 | |
|
283 | 0 | return -1; |
284 | 0 | } |
285 | | |
286 | 2.81k | m_eAccessMode = eAccess; |
287 | | |
288 | | /*----------------------------------------------------------------- |
289 | | * Make sure filename has a .TAB extension... |
290 | | *----------------------------------------------------------------*/ |
291 | 2.81k | m_pszFname = CPLStrdup(pszFname); |
292 | 2.81k | nFnameLen = static_cast<int>(strlen(m_pszFname)); |
293 | | |
294 | 2.81k | if (nFnameLen > 4 && (strcmp(m_pszFname + nFnameLen - 4, ".TAB") == 0 || |
295 | 2.72k | strcmp(m_pszFname + nFnameLen - 4, ".MAP") == 0 || |
296 | 2.72k | strcmp(m_pszFname + nFnameLen - 4, ".DAT") == 0)) |
297 | 88 | strcpy(m_pszFname + nFnameLen - 4, ".TAB"); |
298 | 2.72k | else if (nFnameLen > 4 && (EQUAL(m_pszFname + nFnameLen - 4, ".tab") || |
299 | 0 | EQUAL(m_pszFname + nFnameLen - 4, ".map") || |
300 | 0 | EQUAL(m_pszFname + nFnameLen - 4, ".dat"))) |
301 | 2.72k | strcpy(m_pszFname + nFnameLen - 4, ".tab"); |
302 | 0 | else |
303 | 0 | { |
304 | 0 | if (!bTestOpenNoError) |
305 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
306 | 0 | "Open() failed for %s: invalid filename extension", |
307 | 0 | m_pszFname); |
308 | 0 | else |
309 | 0 | CPLErrorReset(); |
310 | |
|
311 | 0 | CPLFree(m_pszFname); |
312 | 0 | m_pszFname = nullptr; |
313 | 0 | return -1; |
314 | 0 | } |
315 | | |
316 | 2.81k | pszTmpFname = CPLStrdup(m_pszFname); |
317 | | |
318 | 2.81k | #ifndef _WIN32 |
319 | | /*----------------------------------------------------------------- |
320 | | * On Unix, make sure extension uses the right cases |
321 | | * We do it even for write access because if a file with the same |
322 | | * extension already exists we want to overwrite it. |
323 | | *----------------------------------------------------------------*/ |
324 | 2.81k | TABAdjustFilenameExtension(m_pszFname); |
325 | 2.81k | #endif |
326 | | |
327 | | /*----------------------------------------------------------------- |
328 | | * Handle .TAB file... depends on access mode. |
329 | | *----------------------------------------------------------------*/ |
330 | 2.81k | if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) |
331 | 2.58k | { |
332 | | /*------------------------------------------------------------- |
333 | | * Open .TAB file... since it is a small text file, we will just load |
334 | | * it as a stringlist in memory. |
335 | | *------------------------------------------------------------*/ |
336 | 2.58k | m_papszTABFile = TAB_CSLLoad(m_pszFname); |
337 | 2.58k | if (m_papszTABFile == nullptr) |
338 | 37 | { |
339 | 37 | if (!bTestOpenNoError) |
340 | 0 | { |
341 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Failed opening %s.", |
342 | 0 | m_pszFname); |
343 | 0 | } |
344 | 37 | CPLFree(m_pszFname); |
345 | 37 | m_pszFname = nullptr; |
346 | 37 | CSLDestroy(m_papszTABFile); |
347 | 37 | m_papszTABFile = nullptr; |
348 | 37 | CPLFree(pszTmpFname); |
349 | 37 | return -1; |
350 | 37 | } |
351 | | |
352 | | /*------------------------------------------------------------- |
353 | | * Do a first pass on the TAB header to establish the type of |
354 | | * dataset we have (NATIVE, DBF, etc.)... and also to know if |
355 | | * it is a supported type. |
356 | | *------------------------------------------------------------*/ |
357 | 2.54k | if (ParseTABFileFirstPass(bTestOpenNoError) != 0) |
358 | 311 | { |
359 | | // No need to produce an error... it is already been done if |
360 | | // necessary... just cleanup and exit. |
361 | | |
362 | 311 | CPLFree(m_pszFname); |
363 | 311 | m_pszFname = nullptr; |
364 | 311 | CSLDestroy(m_papszTABFile); |
365 | 311 | m_papszTABFile = nullptr; |
366 | 311 | CPLFree(pszTmpFname); |
367 | | |
368 | 311 | return -1; |
369 | 311 | } |
370 | 2.54k | } |
371 | 231 | else |
372 | 231 | { |
373 | | /*------------------------------------------------------------- |
374 | | * In Write access mode, the .TAB file will be written during the |
375 | | * Close() call... we will just set some defaults here. |
376 | | *------------------------------------------------------------*/ |
377 | 231 | m_nVersion = 300; |
378 | 231 | if (pszCharset != nullptr) |
379 | 231 | SetCharset(pszCharset); |
380 | 0 | else |
381 | 0 | SetCharset("Neutral"); |
382 | 231 | m_eTableType = TABTableNative; |
383 | | |
384 | | /*------------------------------------------------------------- |
385 | | * Do initial setup of feature definition. |
386 | | *------------------------------------------------------------*/ |
387 | 231 | char *pszFeatureClassName = TABGetBasename(m_pszFname); |
388 | 231 | m_poDefn = new OGRFeatureDefn(pszFeatureClassName); |
389 | 231 | m_poDefn->Reference(); |
390 | 231 | CPLFree(pszFeatureClassName); |
391 | | |
392 | 231 | m_bNeedTABRewrite = TRUE; |
393 | 231 | } |
394 | | |
395 | | /*----------------------------------------------------------------- |
396 | | * Open .DAT file (or .DBF) |
397 | | *----------------------------------------------------------------*/ |
398 | 2.46k | if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".TAB") == 0) |
399 | 23 | { |
400 | 23 | if (m_eTableType == TABTableDBF) |
401 | 0 | strcpy(pszTmpFname + nFnameLen - 4, ".DBF"); |
402 | 23 | else // Default is NATIVE |
403 | 23 | strcpy(pszTmpFname + nFnameLen - 4, ".DAT"); |
404 | 23 | } |
405 | 2.44k | else |
406 | 2.44k | { |
407 | 2.44k | if (m_eTableType == TABTableDBF) |
408 | 0 | strcpy(pszTmpFname + nFnameLen - 4, ".dbf"); |
409 | 2.44k | else // Default is NATIVE |
410 | 2.44k | strcpy(pszTmpFname + nFnameLen - 4, ".dat"); |
411 | 2.44k | } |
412 | | |
413 | 2.46k | #ifndef _WIN32 |
414 | 2.46k | TABAdjustFilenameExtension(pszTmpFname); |
415 | 2.46k | #endif |
416 | | |
417 | 2.46k | CPLString oEncoding; |
418 | | |
419 | 2.46k | if (eAccess == TABRead || eAccess == TABReadWrite) |
420 | 2.23k | { |
421 | 2.23k | oEncoding = CharsetToEncoding(GetCharset()); |
422 | 2.23k | } |
423 | 231 | else if (eAccess == TABWrite) |
424 | 231 | { |
425 | 231 | oEncoding = CharsetToEncoding(pszCharset); |
426 | 231 | } |
427 | | |
428 | 2.46k | m_poDATFile = new TABDATFile(oEncoding); |
429 | | |
430 | 2.46k | if (m_poDATFile->Open(pszTmpFname, eAccess, m_eTableType) != 0) |
431 | 160 | { |
432 | | // Open Failed... an error has already been reported, just return. |
433 | 160 | CPLFree(pszTmpFname); |
434 | 160 | Close(); |
435 | 160 | if (bTestOpenNoError) |
436 | 160 | CPLErrorReset(); |
437 | | |
438 | 160 | return -1; |
439 | 160 | } |
440 | | |
441 | 2.30k | m_nLastFeatureId = m_poDATFile->GetNumRecords(); |
442 | | |
443 | | /*----------------------------------------------------------------- |
444 | | * Parse .TAB file field defs and build FeatureDefn (only in read access) |
445 | | *----------------------------------------------------------------*/ |
446 | 2.30k | if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) && |
447 | 2.07k | ParseTABFileFields() != 0) |
448 | 125 | { |
449 | | // Failed... an error has already been reported, just return. |
450 | 125 | CPLFree(pszTmpFname); |
451 | 125 | Close(); |
452 | 125 | if (bTestOpenNoError) |
453 | 125 | CPLErrorReset(); |
454 | | |
455 | 125 | return -1; |
456 | 125 | } |
457 | | |
458 | | /*----------------------------------------------------------------- |
459 | | * Open .MAP (and .ID) file |
460 | | * Note that the .MAP and .ID files are optional. Failure to open them |
461 | | * is not an error... it simply means that all features will be returned |
462 | | * with NONE geometry. |
463 | | *----------------------------------------------------------------*/ |
464 | 2.18k | bool bUpperCase = false; |
465 | 2.18k | if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".DAT") == 0) |
466 | 0 | { |
467 | 0 | bUpperCase = true; |
468 | 0 | strcpy(pszTmpFname + nFnameLen - 4, ".MAP"); |
469 | 0 | } |
470 | 2.18k | else |
471 | 2.18k | strcpy(pszTmpFname + nFnameLen - 4, ".map"); |
472 | | |
473 | 2.18k | #ifndef _WIN32 |
474 | 2.18k | TABAdjustFilenameExtension(pszTmpFname); |
475 | 2.18k | #endif |
476 | | |
477 | 2.18k | m_poMAPFile = new TABMAPFile(oEncoding); |
478 | 2.18k | if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) |
479 | 1.95k | { |
480 | | /*------------------------------------------------------------- |
481 | | * Read access: .MAP/.ID are optional... try to open but return |
482 | | * no error if files do not exist. |
483 | | *------------------------------------------------------------*/ |
484 | 1.95k | if (m_poMAPFile->Open(pszTmpFname, eAccess, TRUE) < 0) |
485 | 78 | { |
486 | | // File exists, but Open Failed... |
487 | | // we have to produce an error message |
488 | 78 | if (!bTestOpenNoError) |
489 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s", |
490 | 0 | pszTmpFname); |
491 | 78 | else |
492 | 78 | CPLErrorReset(); |
493 | | |
494 | 78 | CPLFree(pszTmpFname); |
495 | 78 | Close(); |
496 | 78 | return -1; |
497 | 78 | } |
498 | | |
499 | | /*------------------------------------------------------------- |
500 | | * Set geometry type if the geometry objects are uniform. |
501 | | *------------------------------------------------------------*/ |
502 | 1.87k | int numPoints = 0, numRegions = 0, numTexts = 0, numLines = 0; |
503 | | |
504 | 1.87k | GetFeatureCountByType(numPoints, numLines, numRegions, numTexts); |
505 | | |
506 | 1.87k | if (numPoints >= 0 && numTexts >= 0 && numPoints < INT_MAX - numTexts) |
507 | 1.64k | numPoints += numTexts; |
508 | 1.87k | if (numPoints > 0 && numLines == 0 && numRegions == 0) |
509 | 119 | m_poDefn->SetGeomType(wkbPoint); |
510 | 1.75k | else if (numPoints == 0 && numLines > 0 && numRegions == 0) |
511 | 17 | m_poDefn->SetGeomType(wkbLineString); |
512 | 1.73k | else if (m_eAccessMode == TABRead && numPoints == 0 && numLines == 0 && |
513 | 1.03k | numRegions == 0) |
514 | | /* No geometries present; this is an aspatial dataset */ |
515 | 739 | m_poDefn->SetGeomType(wkbNone); |
516 | 998 | else |
517 | 998 | { |
518 | | /* we leave it unknown indicating a mixture */ |
519 | 998 | } |
520 | 1.87k | } |
521 | 231 | else if (m_poMAPFile->Open(pszTmpFname, eAccess, FALSE, |
522 | 231 | nBlockSizeForCreate) != 0) |
523 | 0 | { |
524 | | // Open Failed for write... |
525 | | // an error has already been reported, just return. |
526 | |
|
527 | 0 | m_poMAPFile->Close(); |
528 | 0 | delete m_poMAPFile; |
529 | 0 | m_poMAPFile = nullptr; |
530 | |
|
531 | 0 | CPLFree(pszTmpFname); |
532 | 0 | Close(); |
533 | 0 | if (bTestOpenNoError) |
534 | 0 | CPLErrorReset(); |
535 | |
|
536 | 0 | return -1; |
537 | 0 | } |
538 | | |
539 | | /*----------------------------------------------------------------- |
540 | | * Set BOUNDS metadata |
541 | | *----------------------------------------------------------------*/ |
542 | 2.10k | { |
543 | 2.10k | OGREnvelope e; |
544 | 2.10k | GetBounds(e.MinX, e.MinY, e.MaxX, e.MaxY, 1); |
545 | 2.10k | const char *pszBounds = CPLSPrintf("%.17g,%.17g,%.17g,%.17g", e.MinX, |
546 | 2.10k | e.MinY, e.MaxX, e.MaxY); |
547 | 2.10k | SetMetadataItem("BOUNDS", pszBounds); |
548 | 2.10k | } |
549 | | |
550 | | /*----------------------------------------------------------------- |
551 | | * Initializing the attribute index (.IND) support |
552 | | *----------------------------------------------------------------*/ |
553 | 2.10k | bool bHasIndex = false; |
554 | | |
555 | 2.10k | CPLXMLNode *psRoot = |
556 | 2.10k | CPLCreateXMLNode(nullptr, CXT_Element, "OGRMILayerAttrIndex"); |
557 | 2.10k | const OGRFeatureDefn *poLayerDefn = GetLayerDefn(); |
558 | 8.27k | for (int iField = 0; iField < poLayerDefn->GetFieldCount(); iField++) |
559 | 6.64k | { |
560 | 6.64k | int iIndexIndex = GetFieldIndexNumber(iField); |
561 | 6.64k | if (iIndexIndex > 0) |
562 | 1.35k | { |
563 | 1.35k | if (!bHasIndex) |
564 | 1.35k | { |
565 | 1.35k | const std::string osIndFilename = |
566 | 1.35k | CPLFormCIFilenameSafe(CPLGetPathSafe(pszFname).c_str(), |
567 | 1.35k | CPLGetBasenameSafe(pszFname).c_str(), |
568 | 1.35k | (bUpperCase) ? "IND" : "ind"); |
569 | 1.35k | VSIStatBufL sStat; |
570 | 1.35k | if (VSIStatL(osIndFilename.c_str(), &sStat) == 0) |
571 | 884 | { |
572 | 884 | CPLCreateXMLElementAndValue(psRoot, "MIIDFilename", |
573 | 884 | osIndFilename.c_str()); |
574 | 884 | } |
575 | 466 | else |
576 | 466 | { |
577 | 466 | CPLDebug("MITAB", |
578 | 466 | "At least one field is supposed to be indexed, " |
579 | 466 | "but index file is missing"); |
580 | 466 | break; |
581 | 466 | } |
582 | 1.35k | } |
583 | | |
584 | 884 | CPLXMLNode *psIndex = |
585 | 884 | CPLCreateXMLNode(psRoot, CXT_Element, "OGRMIAttrIndex"); |
586 | 884 | CPLCreateXMLElementAndValue(psIndex, "FieldIndex", |
587 | 884 | CPLSPrintf("%d", iField)); |
588 | 884 | CPLCreateXMLElementAndValue( |
589 | 884 | psIndex, "FieldName", |
590 | 884 | poLayerDefn->GetFieldDefn(iField)->GetNameRef()); |
591 | 884 | CPLCreateXMLElementAndValue(psIndex, "IndexIndex", |
592 | 884 | CPLSPrintf("%d", iIndexIndex)); |
593 | 884 | bHasIndex = true; |
594 | 884 | } |
595 | 6.64k | } |
596 | | |
597 | 2.10k | if (bHasIndex) |
598 | 884 | { |
599 | 884 | char *pszRawXML = CPLSerializeXMLTree(psRoot); |
600 | 884 | InitializeIndexSupport(pszRawXML); |
601 | 884 | CPLFree(pszRawXML); |
602 | 884 | } |
603 | | |
604 | 2.10k | CPLDestroyXMLNode(psRoot); |
605 | | |
606 | 2.10k | CPLFree(pszTmpFname); |
607 | 2.10k | pszTmpFname = nullptr; |
608 | | |
609 | 2.10k | if (m_poDefn != nullptr && m_eAccessMode != TABWrite && |
610 | 1.87k | m_poDefn->GetGeomFieldCount() != 0) |
611 | 1.13k | m_poDefn->GetGeomFieldDefn(0)->SetSpatialRef(GetSpatialRef()); |
612 | | |
613 | 2.10k | if (m_poDefn) |
614 | 2.10k | m_poDefn->Seal(/* bSealFields = */ true); |
615 | | |
616 | 2.10k | return 0; |
617 | 2.18k | } |
618 | | |
619 | | /********************************************************************** |
620 | | * TABFile::ParseTABFileFirstPass() |
621 | | * |
622 | | * Do a first pass in the TAB header file to establish the table type, etc. |
623 | | * and store any useful information into class members. |
624 | | * |
625 | | * This private method should be used only during the Open() call. |
626 | | * |
627 | | * Returns 0 on success, -1 on error. |
628 | | **********************************************************************/ |
629 | | int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError) |
630 | 2.54k | { |
631 | 2.54k | int iLine, numLines, numFields = 0; |
632 | 2.54k | char **papszTok = nullptr; |
633 | 2.54k | GBool bInsideTableDef = FALSE, bFoundTableFields = FALSE; |
634 | | |
635 | 2.54k | if (m_eAccessMode == TABWrite) |
636 | 0 | { |
637 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
638 | 0 | "ParseTABFile() can be used only with Read access."); |
639 | 0 | return -1; |
640 | 0 | } |
641 | | |
642 | 2.54k | numLines = CSLCount(m_papszTABFile); |
643 | | |
644 | 1.94M | for (iLine = 0; iLine < numLines; iLine++) |
645 | 1.94M | { |
646 | | /*------------------------------------------------------------- |
647 | | * Tokenize the next .TAB line, and check first keyword |
648 | | *------------------------------------------------------------*/ |
649 | 1.94M | CSLDestroy(papszTok); |
650 | 1.94M | papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;", |
651 | 1.94M | TRUE, FALSE); |
652 | 1.94M | if (CSLCount(papszTok) < 2) |
653 | 1.77M | continue; // All interesting lines have at least 2 tokens |
654 | | |
655 | 165k | if (EQUAL(papszTok[0], "!version")) |
656 | 9.45k | { |
657 | 9.45k | m_nVersion = atoi(papszTok[1]); |
658 | 9.45k | if (m_nVersion == 100) |
659 | 474 | { |
660 | | /* Version 100 files contain only the fields definition, |
661 | | * so we set default values for the other params. |
662 | | */ |
663 | 474 | bInsideTableDef = TRUE; |
664 | 474 | SetCharset("Neutral"); |
665 | 474 | m_eTableType = TABTableNative; |
666 | 474 | } |
667 | 9.45k | } |
668 | 155k | else if (EQUAL(papszTok[0], "!edit_version")) |
669 | 0 | { |
670 | | /* Sometimes, V450 files have version 300 + edit_version 450 |
671 | | * for us version and edit_version are the same |
672 | | */ |
673 | 0 | m_nVersion = atoi(papszTok[1]); |
674 | 0 | } |
675 | 155k | else if (EQUAL(papszTok[0], "!charset")) |
676 | 7.88k | { |
677 | 7.88k | SetCharset(papszTok[1]); |
678 | 7.88k | } |
679 | 147k | else if (EQUAL(papszTok[0], "Definition") && |
680 | 8.59k | EQUAL(papszTok[1], "Table")) |
681 | 5.18k | { |
682 | 5.18k | bInsideTableDef = TRUE; |
683 | 5.18k | } |
684 | 142k | else if (bInsideTableDef && !bFoundTableFields && |
685 | 34.4k | (EQUAL(papszTok[0], "Type") || EQUAL(papszTok[0], "FORMAT:"))) |
686 | 2.76k | { |
687 | 2.76k | if (EQUAL(papszTok[1], "NATIVE") || EQUAL(papszTok[1], "LINKED")) |
688 | 2.76k | m_eTableType = TABTableNative; |
689 | 5 | else if (EQUAL(papszTok[1], "DBF")) |
690 | 0 | m_eTableType = TABTableDBF; |
691 | 5 | else |
692 | 5 | { |
693 | | // Type=ACCESS, or other unsupported type... cannot open! |
694 | 5 | if (!bTestOpenNoError) |
695 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
696 | 0 | "Unsupported table type '%s' in file %s. " |
697 | 0 | "This type of .TAB file cannot be read by this " |
698 | 0 | "library.", |
699 | 0 | papszTok[1], m_pszFname); |
700 | 5 | CSLDestroy(papszTok); |
701 | 5 | return -1; |
702 | 5 | } |
703 | 2.76k | } |
704 | 139k | else if (bInsideTableDef && !bFoundTableFields && |
705 | 31.6k | EQUAL(papszTok[0], "Description")) |
706 | 0 | { |
707 | 0 | auto osDescription = GetTabDescription(m_papszTABFile[iLine]); |
708 | 0 | if (!osDescription.empty()) |
709 | 0 | { |
710 | 0 | const char *pszEncoding = GetEncoding(); |
711 | 0 | if (pszEncoding == nullptr || EQUAL(pszEncoding, "")) |
712 | 0 | { |
713 | 0 | std::shared_ptr<char> oUnescapedDescription( |
714 | 0 | UnescapeString(osDescription.c_str()), CPLFree); |
715 | 0 | IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY, |
716 | 0 | oUnescapedDescription.get()); |
717 | 0 | } |
718 | 0 | else |
719 | 0 | { |
720 | 0 | std::shared_ptr<char> oEncodedDescription( |
721 | 0 | CPLRecode(osDescription.c_str(), pszEncoding, |
722 | 0 | CPL_ENC_UTF8), |
723 | 0 | CPLFree); |
724 | |
|
725 | 0 | std::shared_ptr<char> oUnescapedDescription( |
726 | 0 | UnescapeString(oEncodedDescription.get()), CPLFree); |
727 | 0 | IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY, |
728 | 0 | oUnescapedDescription.get()); |
729 | 0 | } |
730 | 0 | } |
731 | 0 | } |
732 | 139k | else if (bInsideTableDef && !bFoundTableFields && |
733 | 31.6k | (EQUAL(papszTok[0], "Fields") || |
734 | 29.4k | EQUAL(papszTok[0], "FIELDS:"))) |
735 | 2.26k | { |
736 | | /*--------------------------------------------------------- |
737 | | * We found the list of table fields |
738 | | * Just remember number of fields... the field types will be |
739 | | * parsed inside ParseTABFileFields() later... |
740 | | *--------------------------------------------------------*/ |
741 | 2.26k | bFoundTableFields = TRUE; |
742 | 2.26k | numFields = atoi(papszTok[1]); |
743 | | |
744 | 2.26k | if (numFields < 1 || numFields > 2048 || |
745 | 2.25k | iLine + numFields >= numLines) |
746 | 24 | { |
747 | 24 | if (!bTestOpenNoError) |
748 | 0 | CPLError( |
749 | 0 | CE_Failure, CPLE_FileIO, |
750 | 0 | "Invalid number of fields (%s) at line %d in file %s", |
751 | 0 | papszTok[1], iLine + 1, m_pszFname); |
752 | | |
753 | 24 | CSLDestroy(papszTok); |
754 | 24 | return -1; |
755 | 24 | } |
756 | | |
757 | 2.23k | bInsideTableDef = FALSE; |
758 | 2.23k | } /* end of fields section*/ |
759 | 137k | else |
760 | 137k | { |
761 | | // Simply Ignore unrecognized lines |
762 | 137k | } |
763 | 165k | } |
764 | | |
765 | 2.51k | CSLDestroy(papszTok); |
766 | | |
767 | 2.51k | if (m_pszCharset == nullptr) |
768 | 714 | SetCharset("Neutral"); |
769 | | |
770 | 2.51k | if (numFields == 0) |
771 | 282 | { |
772 | 282 | if (!bTestOpenNoError) |
773 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
774 | 0 | "%s contains no table field definition. " |
775 | 0 | "This type of .TAB file cannot be read by this library.", |
776 | 0 | m_pszFname); |
777 | 282 | return -1; |
778 | 282 | } |
779 | | |
780 | 2.23k | return 0; |
781 | 2.51k | } |
782 | | |
783 | | /********************************************************************** |
784 | | * TABFile::ParseTABFileFields() |
785 | | * |
786 | | * Extract the field definition from the TAB header file, validate |
787 | | * with what we have in the previously opened .DAT or .DBF file, and |
788 | | * finally build the m_poDefn OGRFeatureDefn for this dataset. |
789 | | * |
790 | | * This private method should be used only during the Open() call and after |
791 | | * ParseTABFileFirstPass() has been called. |
792 | | * |
793 | | * Returns 0 on success, -1 on error. |
794 | | **********************************************************************/ |
795 | | int TABFile::ParseTABFileFields() |
796 | 2.07k | { |
797 | | |
798 | 2.07k | if (m_eAccessMode == TABWrite) |
799 | 0 | { |
800 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
801 | 0 | "ParseTABFile() can be used only with Read access."); |
802 | 0 | return -1; |
803 | 0 | } |
804 | | |
805 | 2.07k | char *pszFeatureClassName = TABGetBasename(m_pszFname); |
806 | 2.07k | m_poDefn = new OGRFeatureDefn(pszFeatureClassName); |
807 | 2.07k | CPLFree(pszFeatureClassName); |
808 | | // Ref count defaults to 0... set it to 1 |
809 | 2.07k | m_poDefn->Reference(); |
810 | | |
811 | | /*------------------------------------------------------------- |
812 | | * Scan for fields. |
813 | | *------------------------------------------------------------*/ |
814 | 2.07k | OGRFieldDefn *poFieldDefn = nullptr; |
815 | 2.07k | char **papszTok = nullptr; |
816 | | |
817 | 2.07k | const int numLines = CSLCount(m_papszTABFile); |
818 | 60.2k | for (int iLine = 0; iLine < numLines; iLine++) |
819 | 60.2k | { |
820 | | /*------------------------------------------------------------- |
821 | | * Tokenize the next .TAB line, and check first keyword |
822 | | *------------------------------------------------------------*/ |
823 | 60.2k | const char *pszStr = m_papszTABFile[iLine]; |
824 | 69.4k | while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr))) |
825 | 9.25k | pszStr++; |
826 | | |
827 | 60.2k | if (STARTS_WITH_CI(pszStr, "Fields") && CPLStrnlen(pszStr, 7) >= 7) |
828 | 2.06k | { |
829 | | /*--------------------------------------------------------- |
830 | | * We found the list of table fields |
831 | | *--------------------------------------------------------*/ |
832 | 2.06k | int iField = 0; |
833 | 2.06k | int numFields = atoi(pszStr + 7); |
834 | 2.06k | if (numFields < 1 || numFields > 2048 || |
835 | 2.06k | iLine + numFields >= numLines) |
836 | 1 | { |
837 | 1 | CPLError(CE_Failure, CPLE_FileIO, |
838 | 1 | "Invalid number of fields (%s) at line %d in file %s", |
839 | 1 | pszStr + 7, iLine + 1, m_pszFname); |
840 | 1 | CSLDestroy(papszTok); |
841 | 1 | return -1; |
842 | 1 | } |
843 | | |
844 | | // Alloc the array to keep track of indexed fields |
845 | 2.06k | m_panIndexNo = |
846 | 2.06k | static_cast<int *>(CPLCalloc(numFields, sizeof(int))); |
847 | | |
848 | 2.06k | iLine++; |
849 | 2.06k | poFieldDefn = nullptr; |
850 | 9.41k | for (iField = 0; iField < numFields; iField++, iLine++) |
851 | 7.46k | { |
852 | | /*----------------------------------------------------- |
853 | | * For each field definition found in the .TAB: |
854 | | * Pass the info to the DAT file object. It will validate |
855 | | * the info with what is found in the .DAT header, and will |
856 | | * also use this info later to interpret field values. |
857 | | * |
858 | | * We also create the OGRFieldDefn at the same time to |
859 | | * initialize the OGRFeatureDefn |
860 | | *----------------------------------------------------*/ |
861 | 7.46k | CSLDestroy(papszTok); |
862 | 7.46k | papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], |
863 | 7.46k | " \t(),;", TRUE, FALSE); |
864 | 7.46k | const int numTok = CSLCount(papszTok); |
865 | | |
866 | 7.46k | CPLAssert(m_poDefn); |
867 | 7.46k | poFieldDefn = nullptr; |
868 | | |
869 | 7.46k | CPLString osFieldName; |
870 | 7.46k | if (numTok > 0) |
871 | 7.45k | { |
872 | 7.45k | osFieldName = papszTok[0]; |
873 | 7.45k | if (strlen(GetEncoding()) > 0) |
874 | 0 | osFieldName.Recode(GetEncoding(), CPL_ENC_UTF8); |
875 | 7.45k | } |
876 | | |
877 | 7.46k | int nStatus = -1; |
878 | 7.46k | if (numTok >= 3 && EQUAL(papszTok[1], "char")) |
879 | 5.03k | { |
880 | | /*------------------------------------------------- |
881 | | * CHAR type |
882 | | *------------------------------------------------*/ |
883 | 5.03k | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
884 | 5.03k | iField, osFieldName, TABFChar, atoi(papszTok[2]), 0); |
885 | 5.03k | poFieldDefn = new OGRFieldDefn(osFieldName, OFTString); |
886 | 5.03k | poFieldDefn->SetWidth(atoi(papszTok[2])); |
887 | 5.03k | } |
888 | 2.43k | else if (numTok >= 2 && EQUAL(papszTok[1], "integer")) |
889 | 2.24k | { |
890 | | /*------------------------------------------------- |
891 | | * INTEGER type |
892 | | *------------------------------------------------*/ |
893 | 2.24k | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
894 | 2.24k | iField, osFieldName, TABFInteger, 0, 0); |
895 | 2.24k | poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger); |
896 | 2.24k | if (numTok > 2 && atoi(papszTok[2]) > 0) |
897 | 1 | poFieldDefn->SetWidth(atoi(papszTok[2])); |
898 | 2.24k | } |
899 | 190 | else if (numTok >= 2 && EQUAL(papszTok[1], "smallint")) |
900 | 0 | { |
901 | | /*------------------------------------------------- |
902 | | * SMALLINT type |
903 | | *------------------------------------------------*/ |
904 | 0 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
905 | 0 | iField, osFieldName, TABFSmallInt, 0, 0); |
906 | 0 | poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger); |
907 | 0 | if (numTok > 2 && atoi(papszTok[2]) > 0) |
908 | 0 | poFieldDefn->SetWidth(atoi(papszTok[2])); |
909 | 0 | } |
910 | 190 | else if (numTok >= 2 && EQUAL(papszTok[1], "largeint")) |
911 | 0 | { |
912 | | /*------------------------------------------------- |
913 | | * LargeInt type |
914 | | *------------------------------------------------*/ |
915 | 0 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
916 | 0 | iField, osFieldName, TABFLargeInt, 0, 0); |
917 | 0 | poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger64); |
918 | 0 | if (numTok > 2 && atoi(papszTok[2]) > 0) |
919 | 0 | poFieldDefn->SetWidth(atoi(papszTok[2])); |
920 | 0 | } |
921 | 190 | else if (numTok >= 4 && EQUAL(papszTok[1], "decimal")) |
922 | 82 | { |
923 | | /*------------------------------------------------- |
924 | | * DECIMAL type |
925 | | *------------------------------------------------*/ |
926 | 82 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
927 | 82 | iField, osFieldName, TABFDecimal, atoi(papszTok[2]), |
928 | 82 | atoi(papszTok[3])); |
929 | 82 | poFieldDefn = new OGRFieldDefn(osFieldName, OFTReal); |
930 | 82 | poFieldDefn->SetWidth(atoi(papszTok[2])); |
931 | 82 | poFieldDefn->SetPrecision(atoi(papszTok[3])); |
932 | 82 | } |
933 | 108 | else if (numTok >= 2 && EQUAL(papszTok[1], "float")) |
934 | 8 | { |
935 | | /*------------------------------------------------- |
936 | | * FLOAT type |
937 | | *------------------------------------------------*/ |
938 | 8 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
939 | 8 | iField, osFieldName, TABFFloat, 0, 0); |
940 | 8 | poFieldDefn = new OGRFieldDefn(osFieldName, OFTReal); |
941 | 8 | } |
942 | 100 | else if (numTok >= 2 && EQUAL(papszTok[1], "date")) |
943 | 32 | { |
944 | | /*------------------------------------------------- |
945 | | * DATE type (returned as a string: "DD/MM/YYYY") |
946 | | *------------------------------------------------*/ |
947 | 32 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
948 | 32 | iField, osFieldName, TABFDate, 0, 0); |
949 | 32 | poFieldDefn = new OGRFieldDefn(osFieldName, |
950 | 32 | #ifdef MITAB_USE_OFTDATETIME |
951 | 32 | OFTDate); |
952 | | #else |
953 | | OFTString); |
954 | | #endif |
955 | 32 | poFieldDefn->SetWidth(10); |
956 | 32 | } |
957 | 68 | else if (numTok >= 2 && EQUAL(papszTok[1], "time")) |
958 | 18 | { |
959 | | /*------------------------------------------------- |
960 | | * TIME type (returned as a string: "HH:MM:SS") |
961 | | *------------------------------------------------*/ |
962 | 18 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
963 | 18 | iField, osFieldName, TABFTime, 0, 0); |
964 | 18 | poFieldDefn = new OGRFieldDefn(osFieldName, |
965 | 18 | #ifdef MITAB_USE_OFTDATETIME |
966 | 18 | OFTTime); |
967 | | #else |
968 | | OFTString); |
969 | | #endif |
970 | 18 | poFieldDefn->SetWidth(9); |
971 | 18 | } |
972 | 50 | else if (numTok >= 2 && EQUAL(papszTok[1], "datetime")) |
973 | 0 | { |
974 | | /*------------------------------------------------- |
975 | | * DATETIME type (returned as a string: "DD/MM/YYYY |
976 | | *HH:MM:SS") |
977 | | *------------------------------------------------*/ |
978 | 0 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
979 | 0 | iField, osFieldName, TABFDateTime, 0, 0); |
980 | 0 | poFieldDefn = new OGRFieldDefn(osFieldName, |
981 | 0 | #ifdef MITAB_USE_OFTDATETIME |
982 | 0 | OFTDateTime); |
983 | | #else |
984 | | OFTString); |
985 | | #endif |
986 | 0 | poFieldDefn->SetWidth(19); |
987 | 0 | } |
988 | 50 | else if (numTok >= 2 && EQUAL(papszTok[1], "logical")) |
989 | 0 | { |
990 | | /*------------------------------------------------- |
991 | | * LOGICAL type (value "T" or "F") |
992 | | *------------------------------------------------*/ |
993 | 0 | nStatus = m_poDATFile->ValidateFieldInfoFromTAB( |
994 | 0 | iField, osFieldName, TABFLogical, 0, 0); |
995 | 0 | poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger); |
996 | 0 | poFieldDefn->SetSubType(OFSTBoolean); |
997 | 0 | poFieldDefn->SetWidth(1); |
998 | 0 | } |
999 | 50 | else |
1000 | 50 | { |
1001 | | // Unrecognized field type or line corrupt |
1002 | 50 | } |
1003 | | |
1004 | 7.46k | if (nStatus != 0) |
1005 | 117 | { |
1006 | 117 | CPLError(CE_Failure, CPLE_FileIO, |
1007 | 117 | "Failed to parse field definition at line %d in " |
1008 | 117 | "file %s", |
1009 | 117 | iLine + 1, m_pszFname); |
1010 | 117 | CSLDestroy(papszTok); |
1011 | 117 | delete poFieldDefn; |
1012 | 117 | return -1; |
1013 | 117 | } |
1014 | | /*----------------------------------------------------- |
1015 | | * Keep track of index number if present |
1016 | | *----------------------------------------------------*/ |
1017 | 7.34k | if (numTok >= 4 && EQUAL(papszTok[numTok - 2], "index")) |
1018 | 1.50k | { |
1019 | 1.50k | m_panIndexNo[iField] = atoi(papszTok[numTok - 1]); |
1020 | 1.50k | } |
1021 | 5.84k | else |
1022 | 5.84k | { |
1023 | 5.84k | m_panIndexNo[iField] = 0; |
1024 | 5.84k | } |
1025 | | |
1026 | | /*----------------------------------------------------- |
1027 | | * Add the FieldDefn to the FeatureDefn and continue with |
1028 | | * the next one. |
1029 | | *----------------------------------------------------*/ |
1030 | 7.34k | m_poDefn->AddFieldDefn(poFieldDefn); |
1031 | 7.34k | m_oSetFields.insert( |
1032 | 7.34k | CPLString(poFieldDefn->GetNameRef()).toupper()); |
1033 | | // AddFieldDenf() takes a copy, so we delete the original |
1034 | 7.34k | delete poFieldDefn; |
1035 | 7.34k | poFieldDefn = nullptr; |
1036 | 7.34k | } |
1037 | | |
1038 | | /*--------------------------------------------------------- |
1039 | | * OK, we're done... end the loop now. |
1040 | | *--------------------------------------------------------*/ |
1041 | 1.95k | break; |
1042 | 2.06k | } /* end of fields section*/ |
1043 | 58.1k | else |
1044 | 58.1k | { |
1045 | | // Simply Ignore unrecognized lines |
1046 | 58.1k | } |
1047 | 60.2k | } |
1048 | | |
1049 | 1.95k | CSLDestroy(papszTok); |
1050 | | |
1051 | 1.95k | if (m_poDefn->GetFieldCount() == 0) |
1052 | 7 | { |
1053 | 7 | CPLError(CE_Failure, CPLE_NotSupported, |
1054 | 7 | "%s contains no table field definition. " |
1055 | 7 | "This type of .TAB file cannot be read by this library.", |
1056 | 7 | m_pszFname); |
1057 | 7 | return -1; |
1058 | 7 | } |
1059 | | |
1060 | 1.95k | return 0; |
1061 | 1.95k | } |
1062 | | |
1063 | | /********************************************************************** |
1064 | | * TABFile::WriteTABFile() |
1065 | | * |
1066 | | * Generate the .TAB file using mainly the attribute fields definition. |
1067 | | * |
1068 | | * |
1069 | | * Returns 0 on success, -1 on error. |
1070 | | **********************************************************************/ |
1071 | | int TABFile::WriteTABFile() |
1072 | 457 | { |
1073 | 457 | if (!m_bNeedTABRewrite) |
1074 | 226 | { |
1075 | 226 | return 0; |
1076 | 226 | } |
1077 | | |
1078 | 231 | if (m_poMAPFile == nullptr || m_eAccessMode == TABRead) |
1079 | 0 | { |
1080 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1081 | 0 | "WriteTABFile() can be used only with Write access."); |
1082 | 0 | return -1; |
1083 | 0 | } |
1084 | | |
1085 | | // First update file version number... |
1086 | 231 | int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion(); |
1087 | 231 | m_nVersion = std::max(m_nVersion, nMapObjVersion); |
1088 | | |
1089 | 231 | VSILFILE *fp = VSIFOpenL(m_pszFname, "wt"); |
1090 | 231 | if (fp != nullptr) |
1091 | 231 | { |
1092 | 231 | VSIFPrintfL(fp, "!table\n"); |
1093 | 231 | VSIFPrintfL(fp, "!version %d\n", m_nVersion); |
1094 | 231 | VSIFPrintfL(fp, "!charset %s\n", m_pszCharset); |
1095 | 231 | VSIFPrintfL(fp, "\n"); |
1096 | | |
1097 | 231 | if (m_poDefn && m_poDefn->GetFieldCount() > 0) |
1098 | 223 | { |
1099 | 223 | VSIFPrintfL(fp, "Definition Table\n"); |
1100 | 223 | VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); |
1101 | 223 | const char *pszDescription = GetMetadataItem(DESCRIPTION_KEY); |
1102 | 223 | if (nullptr != pszDescription) |
1103 | 0 | { |
1104 | 0 | std::shared_ptr<char> oEscapedDescription( |
1105 | 0 | EscapeString(pszDescription, true), CPLFree); |
1106 | 0 | const char *pszEncoding = GetEncoding(); |
1107 | 0 | if (nullptr == pszEncoding || EQUAL(pszEncoding, "")) |
1108 | 0 | { |
1109 | 0 | VSIFPrintfL(fp, " Description \"%s\"\n", |
1110 | 0 | oEscapedDescription.get()); |
1111 | 0 | } |
1112 | 0 | else |
1113 | 0 | { |
1114 | 0 | std::shared_ptr<char> oEncodedDescription( |
1115 | 0 | CPLRecode(oEscapedDescription.get(), CPL_ENC_UTF8, |
1116 | 0 | pszEncoding), |
1117 | 0 | CPLFree); |
1118 | 0 | VSIFPrintfL(fp, " Description \"%s\"\n", |
1119 | 0 | oEncodedDescription.get()); |
1120 | 0 | } |
1121 | 0 | } |
1122 | 223 | VSIFPrintfL(fp, " Fields %d\n", m_poDefn->GetFieldCount()); |
1123 | | |
1124 | 12.6k | for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++) |
1125 | 12.4k | { |
1126 | 12.4k | const char *pszFieldType = nullptr; |
1127 | 12.4k | OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField); |
1128 | 12.4k | switch (GetNativeFieldType(iField)) |
1129 | 12.4k | { |
1130 | 12.4k | case TABFChar: |
1131 | 12.4k | pszFieldType = |
1132 | 12.4k | CPLSPrintf("Char (%d)", poFieldDefn->GetWidth()); |
1133 | 12.4k | break; |
1134 | 0 | case TABFDecimal: |
1135 | 0 | pszFieldType = CPLSPrintf("Decimal (%d,%d)", |
1136 | 0 | poFieldDefn->GetWidth(), |
1137 | 0 | poFieldDefn->GetPrecision()); |
1138 | 0 | break; |
1139 | 5 | case TABFInteger: |
1140 | 5 | if (poFieldDefn->GetWidth() == 0) |
1141 | 5 | pszFieldType = "Integer"; |
1142 | 0 | else |
1143 | 0 | pszFieldType = CPLSPrintf("Integer (%d)", |
1144 | 0 | poFieldDefn->GetWidth()); |
1145 | 5 | break; |
1146 | 0 | case TABFSmallInt: |
1147 | 0 | if (poFieldDefn->GetWidth() == 0) |
1148 | 0 | pszFieldType = "SmallInt"; |
1149 | 0 | else |
1150 | 0 | pszFieldType = CPLSPrintf("SmallInt (%d)", |
1151 | 0 | poFieldDefn->GetWidth()); |
1152 | 0 | break; |
1153 | 0 | case TABFLargeInt: |
1154 | 0 | if (poFieldDefn->GetWidth() == 0) |
1155 | 0 | pszFieldType = "LargeInt"; |
1156 | 0 | else |
1157 | 0 | pszFieldType = CPLSPrintf("LargeInt (%d)", |
1158 | 0 | poFieldDefn->GetWidth()); |
1159 | 0 | break; |
1160 | 5 | case TABFFloat: |
1161 | 5 | pszFieldType = "Float"; |
1162 | 5 | break; |
1163 | 0 | case TABFLogical: |
1164 | 0 | pszFieldType = "Logical"; |
1165 | 0 | break; |
1166 | 0 | case TABFDate: |
1167 | 0 | pszFieldType = "Date"; |
1168 | 0 | break; |
1169 | 0 | case TABFTime: |
1170 | 0 | pszFieldType = "Time"; |
1171 | 0 | break; |
1172 | 0 | case TABFDateTime: |
1173 | 0 | pszFieldType = "DateTime"; |
1174 | 0 | break; |
1175 | 0 | default: |
1176 | | // Unsupported field type!!! This should never happen. |
1177 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1178 | 0 | "WriteTABFile(): Unsupported field type"); |
1179 | 0 | VSIFCloseL(fp); |
1180 | 0 | return -1; |
1181 | 12.4k | } |
1182 | | |
1183 | 12.4k | CPLString osFieldName(poFieldDefn->GetNameRef()); |
1184 | | |
1185 | 12.4k | if (strlen(GetEncoding()) > 0) |
1186 | 0 | osFieldName.Recode(CPL_ENC_UTF8, GetEncoding()); |
1187 | | |
1188 | 12.4k | char *pszCleanName = TABCleanFieldName( |
1189 | 12.4k | osFieldName, GetEncoding(), m_bStrictLaundering); |
1190 | 12.4k | osFieldName = pszCleanName; |
1191 | 12.4k | CPLFree(pszCleanName); |
1192 | | |
1193 | 12.4k | if (GetFieldIndexNumber(iField) == 0) |
1194 | 12.4k | { |
1195 | 12.4k | VSIFPrintfL(fp, " %s %s ;\n", osFieldName.c_str(), |
1196 | 12.4k | pszFieldType); |
1197 | 12.4k | } |
1198 | 0 | else |
1199 | 0 | { |
1200 | 0 | VSIFPrintfL(fp, " %s %s Index %d ;\n", |
1201 | 0 | osFieldName.c_str(), pszFieldType, |
1202 | 0 | GetFieldIndexNumber(iField)); |
1203 | 0 | } |
1204 | 12.4k | } |
1205 | 223 | } |
1206 | 8 | else |
1207 | 8 | { |
1208 | 8 | VSIFPrintfL(fp, "Definition Table\n"); |
1209 | 8 | VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); |
1210 | 8 | VSIFPrintfL(fp, " Fields 1\n"); |
1211 | 8 | VSIFPrintfL(fp, " FID Integer ;\n"); |
1212 | 8 | } |
1213 | | |
1214 | 231 | VSIFCloseL(fp); |
1215 | | |
1216 | 231 | m_bNeedTABRewrite = FALSE; |
1217 | 231 | } |
1218 | 0 | else |
1219 | 0 | { |
1220 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'", |
1221 | 0 | m_pszFname); |
1222 | 0 | return -1; |
1223 | 0 | } |
1224 | | |
1225 | 231 | return 0; |
1226 | 231 | } |
1227 | | |
1228 | | /********************************************************************** |
1229 | | * TABFile::Close() |
1230 | | * |
1231 | | * Close current file, and release all memory used. |
1232 | | * |
1233 | | * Returns 0 on success, -1 on error. |
1234 | | **********************************************************************/ |
1235 | | int TABFile::Close() |
1236 | 3.17k | { |
1237 | 3.17k | CPLErrorReset(); |
1238 | | |
1239 | | // Commit the latest changes to the file... |
1240 | | |
1241 | 3.17k | if (m_poMAPFile) |
1242 | 2.18k | { |
1243 | | // In Write access, it is time to write the .TAB file. |
1244 | 2.18k | if (m_eAccessMode != TABRead) |
1245 | 231 | { |
1246 | 231 | WriteTABFile(); |
1247 | 231 | } |
1248 | | |
1249 | 2.18k | m_poMAPFile->Close(); |
1250 | 2.18k | delete m_poMAPFile; |
1251 | 2.18k | m_poMAPFile = nullptr; |
1252 | 2.18k | } |
1253 | | |
1254 | 3.17k | if (m_poDATFile) |
1255 | 2.46k | { |
1256 | 2.46k | m_poDATFile->Close(); |
1257 | 2.46k | delete m_poDATFile; |
1258 | 2.46k | m_poDATFile = nullptr; |
1259 | 2.46k | } |
1260 | | |
1261 | 3.17k | if (m_poINDFile) |
1262 | 315 | { |
1263 | 315 | m_poINDFile->Close(); |
1264 | 315 | delete m_poINDFile; |
1265 | 315 | m_poINDFile = nullptr; |
1266 | 315 | } |
1267 | | |
1268 | 3.17k | if (m_poCurFeature) |
1269 | 153 | { |
1270 | 153 | delete m_poCurFeature; |
1271 | 153 | m_poCurFeature = nullptr; |
1272 | 153 | } |
1273 | | |
1274 | 3.17k | if (m_poDefn) |
1275 | 2.30k | m_poDefn->Release(); |
1276 | 3.17k | m_poDefn = nullptr; |
1277 | | |
1278 | 3.17k | if (m_poSpatialRef) |
1279 | 1.15k | m_poSpatialRef->Release(); |
1280 | 3.17k | m_poSpatialRef = nullptr; |
1281 | | |
1282 | 3.17k | CSLDestroy(m_papszTABFile); |
1283 | 3.17k | m_papszTABFile = nullptr; |
1284 | | |
1285 | 3.17k | CPLFree(m_pszFname); |
1286 | 3.17k | m_pszFname = nullptr; |
1287 | | |
1288 | 3.17k | CPLFree(m_pszCharset); |
1289 | 3.17k | m_pszCharset = nullptr; |
1290 | | |
1291 | 3.17k | CPLFree(m_panIndexNo); |
1292 | 3.17k | m_panIndexNo = nullptr; |
1293 | | |
1294 | 3.17k | CPLFree(m_panMatchingFIDs); |
1295 | 3.17k | m_panMatchingFIDs = nullptr; |
1296 | | |
1297 | 3.17k | return 0; |
1298 | 3.17k | } |
1299 | | |
1300 | | /********************************************************************** |
1301 | | * TABFile::SetQuickSpatialIndexMode() |
1302 | | * |
1303 | | * Select "quick spatial index mode". |
1304 | | * |
1305 | | * The default behavior of MITAB is to generate an optimized spatial index, |
1306 | | * but this results in slower write speed. |
1307 | | * |
1308 | | * Applications that want faster write speed and do not care |
1309 | | * about the performance of spatial queries on the resulting file can |
1310 | | * use SetQuickSpatialIndexMode() to require the creation of a non-optimal |
1311 | | * spatial index (actually emulating the type of spatial index produced |
1312 | | * by MITAB before version 1.6.0). In this mode writing files can be |
1313 | | * about 5 times faster, but spatial queries can be up to 30 times slower. |
1314 | | * |
1315 | | * Returns 0 on success, -1 on error. |
1316 | | **********************************************************************/ |
1317 | | int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode /*=TRUE*/) |
1318 | 0 | { |
1319 | 0 | if (m_eAccessMode != TABWrite || m_poMAPFile == nullptr) |
1320 | 0 | { |
1321 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1322 | 0 | "SetQuickSpatialIndexMode() failed: file not opened for write " |
1323 | 0 | "access."); |
1324 | 0 | return -1; |
1325 | 0 | } |
1326 | | |
1327 | 0 | return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode); |
1328 | 0 | } |
1329 | | |
1330 | | /********************************************************************** |
1331 | | * TABFile::GetNextFeatureId() |
1332 | | * |
1333 | | * Returns feature id that follows nPrevId, or -1 if it is the |
1334 | | * last feature id. Pass nPrevId=-1 to fetch the first valid feature id. |
1335 | | **********************************************************************/ |
1336 | | GIntBig TABFile::GetNextFeatureId(GIntBig nPrevId) |
1337 | 28.4k | { |
1338 | 28.4k | if (m_bLastOpWasWrite) |
1339 | 0 | ResetReading(); |
1340 | 28.4k | m_bLastOpWasRead = TRUE; |
1341 | | |
1342 | 28.4k | if (!CPL_INT64_FITS_ON_INT32(nPrevId)) |
1343 | 0 | return -1; |
1344 | | |
1345 | | /*----------------------------------------------------------------- |
1346 | | * Are we using spatial rather than .ID based traversal? |
1347 | | *----------------------------------------------------------------*/ |
1348 | 28.4k | if (bUseSpatialTraversal) |
1349 | 0 | return m_poMAPFile->GetNextFeatureId(static_cast<int>(nPrevId)); |
1350 | | |
1351 | | /*----------------------------------------------------------------- |
1352 | | * Should we use an attribute index traversal? |
1353 | | *----------------------------------------------------------------*/ |
1354 | 28.4k | if (m_poAttrQuery != nullptr) |
1355 | 0 | { |
1356 | 0 | if (m_panMatchingFIDs == nullptr) |
1357 | 0 | { |
1358 | 0 | m_iMatchingFID = 0; |
1359 | 0 | m_panMatchingFIDs = |
1360 | 0 | m_poAttrQuery->EvaluateAgainstIndices(this, nullptr); |
1361 | 0 | } |
1362 | 0 | if (m_panMatchingFIDs != nullptr) |
1363 | 0 | { |
1364 | 0 | if (m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID) |
1365 | 0 | return OGRNullFID; |
1366 | | |
1367 | 0 | return m_panMatchingFIDs[m_iMatchingFID++] + 1; |
1368 | 0 | } |
1369 | 0 | } |
1370 | | |
1371 | | /*----------------------------------------------------------------- |
1372 | | * Establish what the next logical feature ID should be |
1373 | | *----------------------------------------------------------------*/ |
1374 | 28.4k | int nFeatureId = -1; |
1375 | | |
1376 | 28.4k | if (nPrevId <= 0 && m_nLastFeatureId > 0) |
1377 | 339 | nFeatureId = 1; // Feature Ids start at 1 |
1378 | 28.1k | else if (nPrevId > 0 && nPrevId < m_nLastFeatureId) |
1379 | 28.0k | nFeatureId = static_cast<int>(nPrevId) + 1; |
1380 | 18 | else |
1381 | 18 | { |
1382 | | // This was the last feature |
1383 | 18 | return OGRNullFID; |
1384 | 18 | } |
1385 | | |
1386 | | /*----------------------------------------------------------------- |
1387 | | * Skip any feature with NONE geometry and a deleted attribute record |
1388 | | *----------------------------------------------------------------*/ |
1389 | 759k | while (nFeatureId <= m_nLastFeatureId) |
1390 | 759k | { |
1391 | 759k | if (m_poMAPFile->MoveToObjId(nFeatureId) != 0 || |
1392 | 759k | m_poDATFile->GetRecordBlock(nFeatureId) == nullptr) |
1393 | 277 | { |
1394 | 277 | CPLError(CE_Failure, CPLE_IllegalArg, |
1395 | 277 | "GetNextFeatureId() failed: unable to set read pointer " |
1396 | 277 | "to feature id %d", |
1397 | 277 | nFeatureId); |
1398 | 277 | return -1; |
1399 | 277 | } |
1400 | | |
1401 | | // __TODO__ Add a test here to check if object is deleted, |
1402 | | // i.e. 0x40 set on object_id in object block |
1403 | 759k | if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE || |
1404 | 759k | m_poDATFile->IsCurrentRecordDeleted() == FALSE) |
1405 | 28.1k | { |
1406 | | // This feature contains at least a geometry or some attributes... |
1407 | | // return its id. |
1408 | 28.1k | return nFeatureId; |
1409 | 28.1k | } |
1410 | | |
1411 | 731k | nFeatureId++; |
1412 | 731k | } |
1413 | | |
1414 | | // If we reached this point, then we kept skipping deleted features |
1415 | | // and stopped when EOF was reached. |
1416 | 5 | return -1; |
1417 | 28.4k | } |
1418 | | |
1419 | | /********************************************************************** |
1420 | | * TABFile::GetNextFeatureId_Spatial() |
1421 | | * |
1422 | | * Returns feature id that follows nPrevId, or -1 if it is the |
1423 | | * last feature id, but by traversing the spatial tree instead of the |
1424 | | * direct object index. Generally speaking the feature id's will be |
1425 | | * returned in an unordered fashion. |
1426 | | **********************************************************************/ |
1427 | | int TABFile::GetNextFeatureId_Spatial(int nPrevId) |
1428 | 0 | { |
1429 | 0 | if (m_eAccessMode != TABRead) |
1430 | 0 | { |
1431 | 0 | CPLError( |
1432 | 0 | CE_Failure, CPLE_NotSupported, |
1433 | 0 | "GetNextFeatureId_Spatial() can be used only with Read access."); |
1434 | 0 | return -1; |
1435 | 0 | } |
1436 | | |
1437 | 0 | if (m_poMAPFile == nullptr) |
1438 | 0 | { |
1439 | 0 | CPLError( |
1440 | 0 | CE_Failure, CPLE_NotSupported, |
1441 | 0 | "GetNextFeatureId_Spatial() requires availability of .MAP file."); |
1442 | 0 | return -1; |
1443 | 0 | } |
1444 | | |
1445 | 0 | return m_poMAPFile->GetNextFeatureId(nPrevId); |
1446 | 0 | } |
1447 | | |
1448 | | /********************************************************************** |
1449 | | * TABFile::GetFeatureRef() |
1450 | | * |
1451 | | * Fill and return a TABFeature object for the specified feature id. |
1452 | | * |
1453 | | * The returned pointer is a reference to an object owned and maintained |
1454 | | * by this TABFile object. It should not be altered or freed by the |
1455 | | * caller and its contents is guaranteed to be valid only until the next |
1456 | | * call to GetFeatureRef() or Close(). |
1457 | | * |
1458 | | * Returns NULL if the specified feature id does not exist of if an |
1459 | | * error happened. In any case, CPLError() will have been called to |
1460 | | * report the reason of the failure. |
1461 | | * |
1462 | | * If an unsupported object type is encountered (likely from a newer version |
1463 | | * of MapInfo) then a valid feature will be returned with a NONE geometry, |
1464 | | * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported |
1465 | | * CPLGetLastErrorNo() should be used to detect that case. |
1466 | | **********************************************************************/ |
1467 | | TABFeature *TABFile::GetFeatureRef(GIntBig nFeatureId) |
1468 | 29.8k | { |
1469 | 29.8k | CPLErrorReset(); |
1470 | | |
1471 | | /*----------------------------------------------------------------- |
1472 | | * Make sure file is opened and Validate feature id by positioning |
1473 | | * the read pointers for the .MAP and .DAT files to this feature id. |
1474 | | *----------------------------------------------------------------*/ |
1475 | 29.8k | if (m_poMAPFile == nullptr) |
1476 | 0 | { |
1477 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1478 | 0 | "GetFeatureRef() failed: file is not opened!"); |
1479 | 0 | return nullptr; |
1480 | 0 | } |
1481 | | |
1482 | 29.8k | if (m_bLastOpWasWrite) |
1483 | 0 | ResetReading(); |
1484 | 29.8k | m_bLastOpWasRead = TRUE; |
1485 | | |
1486 | 29.8k | if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId || |
1487 | 29.6k | m_poMAPFile->MoveToObjId(static_cast<int>(nFeatureId)) != 0 || |
1488 | 29.6k | m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr) |
1489 | 453 | { |
1490 | | // CPLError(CE_Failure, CPLE_IllegalArg, |
1491 | | // "GetFeatureRef() failed: invalid feature id %d", |
1492 | | // nFeatureId); |
1493 | 453 | return nullptr; |
1494 | 453 | } |
1495 | | |
1496 | 29.3k | if (m_poDATFile->IsCurrentRecordDeleted()) |
1497 | 1.23k | { |
1498 | 1.23k | if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE) |
1499 | 1 | { |
1500 | 1 | CPLError( |
1501 | 1 | CE_Failure, CPLE_AppDefined, |
1502 | 1 | "Valid .MAP record " CPL_FRMT_GIB |
1503 | 1 | " found, but .DAT is marked as deleted. File likely corrupt", |
1504 | 1 | nFeatureId); |
1505 | 1 | } |
1506 | 1.23k | return nullptr; |
1507 | 1.23k | } |
1508 | | |
1509 | | /*----------------------------------------------------------------- |
1510 | | * Flush current feature object |
1511 | | * __TODO__ try to reuse if it is already of the right type |
1512 | | *----------------------------------------------------------------*/ |
1513 | 28.1k | if (m_poCurFeature) |
1514 | 10.0k | { |
1515 | 10.0k | delete m_poCurFeature; |
1516 | 10.0k | m_poCurFeature = nullptr; |
1517 | 10.0k | } |
1518 | | |
1519 | | /*----------------------------------------------------------------- |
1520 | | * Create new feature object of the right type |
1521 | | * Unsupported object types are returned as raw TABFeature (i.e. NONE |
1522 | | * geometry) |
1523 | | *----------------------------------------------------------------*/ |
1524 | 28.1k | m_poCurFeature = TABFeature::CreateFromMapInfoType( |
1525 | 28.1k | m_poMAPFile->GetCurObjType(), m_poDefn); |
1526 | | |
1527 | | /*----------------------------------------------------------------- |
1528 | | * Read fields from the .DAT file |
1529 | | * GetRecordBlock() has already been called above... |
1530 | | *----------------------------------------------------------------*/ |
1531 | 28.1k | if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0) |
1532 | 0 | { |
1533 | 0 | delete m_poCurFeature; |
1534 | 0 | m_poCurFeature = nullptr; |
1535 | 0 | return nullptr; |
1536 | 0 | } |
1537 | | |
1538 | | /*----------------------------------------------------------------- |
1539 | | * Read geometry from the .MAP file |
1540 | | * MoveToObjId() has already been called above... |
1541 | | *----------------------------------------------------------------*/ |
1542 | 28.1k | TABMAPObjHdr *poObjHdr = TABMAPObjHdr::NewObj(m_poMAPFile->GetCurObjType(), |
1543 | 28.1k | m_poMAPFile->GetCurObjId()); |
1544 | | // Note that poObjHdr==NULL is a valid case if geometry type is NONE |
1545 | | |
1546 | 28.1k | if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) || |
1547 | 28.1k | m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0) |
1548 | 33 | { |
1549 | 33 | delete m_poCurFeature; |
1550 | 33 | m_poCurFeature = nullptr; |
1551 | 33 | if (poObjHdr) |
1552 | 33 | delete poObjHdr; |
1553 | 33 | return nullptr; |
1554 | 33 | } |
1555 | 28.1k | if (poObjHdr) // May be NULL if feature geometry type is NONE |
1556 | 28.1k | delete poObjHdr; |
1557 | | |
1558 | 28.1k | m_nCurFeatureId = nFeatureId; |
1559 | 28.1k | m_poCurFeature->SetFID(m_nCurFeatureId); |
1560 | | |
1561 | 28.1k | m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted()); |
1562 | | |
1563 | 28.1k | return m_poCurFeature; |
1564 | 28.1k | } |
1565 | | |
1566 | | /********************************************************************** |
1567 | | * TABFile::DeleteFeature() |
1568 | | * |
1569 | | * Standard OGR DeleteFeature implementation. |
1570 | | **********************************************************************/ |
1571 | | OGRErr TABFile::DeleteFeature(GIntBig nFeatureId) |
1572 | 0 | { |
1573 | 0 | CPLErrorReset(); |
1574 | |
|
1575 | 0 | if (m_eAccessMode == TABRead) |
1576 | 0 | { |
1577 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1578 | 0 | "DeleteFeature() cannot be used in read-only access."); |
1579 | 0 | return OGRERR_FAILURE; |
1580 | 0 | } |
1581 | | |
1582 | | /*----------------------------------------------------------------- |
1583 | | * Make sure file is opened and establish new feature id. |
1584 | | *----------------------------------------------------------------*/ |
1585 | 0 | if (m_poMAPFile == nullptr) |
1586 | 0 | { |
1587 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1588 | 0 | "DeleteFeature() failed: file is not opened!"); |
1589 | 0 | return OGRERR_FAILURE; |
1590 | 0 | } |
1591 | | |
1592 | 0 | if (m_bLastOpWasWrite) |
1593 | 0 | ResetReading(); |
1594 | |
|
1595 | 0 | if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId || |
1596 | 0 | m_poMAPFile->MoveToObjId(static_cast<int>(nFeatureId)) != 0 || |
1597 | 0 | m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr) |
1598 | 0 | { |
1599 | | /*CPLError(CE_Failure, CPLE_IllegalArg, |
1600 | | "DeleteFeature() failed: invalid feature id " CPL_FRMT_GIB, |
1601 | | nFeatureId);*/ |
1602 | 0 | return OGRERR_NON_EXISTING_FEATURE; |
1603 | 0 | } |
1604 | | |
1605 | 0 | if (m_poDATFile->IsCurrentRecordDeleted()) |
1606 | 0 | { |
1607 | | /*CPLError(CE_Failure, CPLE_IllegalArg, |
1608 | | "DeleteFeature() failed: record is already deleted!");*/ |
1609 | 0 | return OGRERR_NON_EXISTING_FEATURE; |
1610 | 0 | } |
1611 | | |
1612 | 0 | if (m_poCurFeature) |
1613 | 0 | { |
1614 | 0 | delete m_poCurFeature; |
1615 | 0 | m_poCurFeature = nullptr; |
1616 | 0 | } |
1617 | |
|
1618 | 0 | if (m_poMAPFile->MarkAsDeleted() != 0 || m_poDATFile->MarkAsDeleted() != 0) |
1619 | 0 | { |
1620 | 0 | return OGRERR_FAILURE; |
1621 | 0 | } |
1622 | | |
1623 | 0 | return OGRERR_NONE; |
1624 | 0 | } |
1625 | | |
1626 | | /********************************************************************** |
1627 | | * TABFile::WriteFeature() |
1628 | | * |
1629 | | * Write a feature to this dataset. |
1630 | | * |
1631 | | * Returns 0 on success, or -1 if an error happened in which case, |
1632 | | * CPLError() will have been called to |
1633 | | * report the reason of the failure. |
1634 | | **********************************************************************/ |
1635 | | int TABFile::WriteFeature(TABFeature *poFeature) |
1636 | 315k | { |
1637 | | |
1638 | 315k | m_bLastOpWasWrite = TRUE; |
1639 | | |
1640 | | /*----------------------------------------------------------------- |
1641 | | * Make sure file is opened and establish new feature id. |
1642 | | *----------------------------------------------------------------*/ |
1643 | 315k | if (m_poMAPFile == nullptr) |
1644 | 0 | { |
1645 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1646 | 0 | "WriteFeature() failed: file is not opened!"); |
1647 | 0 | return -1; |
1648 | 0 | } |
1649 | | |
1650 | 315k | int nFeatureId = 0; |
1651 | 315k | if (poFeature->GetFID() >= 0) |
1652 | 0 | { |
1653 | 0 | nFeatureId = static_cast<int>(poFeature->GetFID()); |
1654 | 0 | } |
1655 | 315k | else if (m_nLastFeatureId < 1) |
1656 | 211 | { |
1657 | | /*------------------------------------------------------------- |
1658 | | * Special hack to write out at least one field if none are in |
1659 | | * OGRFeatureDefn. |
1660 | | *------------------------------------------------------------*/ |
1661 | 211 | if (m_poDATFile->GetNumFields() == 0) |
1662 | 3 | { |
1663 | 3 | CPLError(CE_Warning, CPLE_IllegalArg, |
1664 | 3 | "MapInfo tables must contain at least 1 column, adding " |
1665 | 3 | "dummy FID column."); |
1666 | 3 | CPLErrorReset(); |
1667 | 3 | m_poDATFile->AddField("FID", TABFInteger, 10, 0); |
1668 | 3 | } |
1669 | | |
1670 | 211 | nFeatureId = 1; |
1671 | 211 | } |
1672 | 315k | else |
1673 | 315k | { |
1674 | 315k | nFeatureId = m_nLastFeatureId + 1; |
1675 | 315k | } |
1676 | | |
1677 | 315k | poFeature->SetFID(nFeatureId); |
1678 | | |
1679 | | /*----------------------------------------------------------------- |
1680 | | * Write fields to the .DAT file and update .IND if necessary |
1681 | | *----------------------------------------------------------------*/ |
1682 | 315k | if (m_poDATFile->GetRecordBlock(nFeatureId) == nullptr || |
1683 | 315k | poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile, |
1684 | 315k | m_panIndexNo) != 0) |
1685 | 0 | { |
1686 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1687 | 0 | "Failed writing attributes for feature id %d in %s", |
1688 | 0 | nFeatureId, m_pszFname); |
1689 | 0 | return -1; |
1690 | 0 | } |
1691 | | |
1692 | | /*----------------------------------------------------------------- |
1693 | | * Write geometry to the .MAP file |
1694 | | * The call to PrepareNewObj() takes care of the .ID file. |
1695 | | *----------------------------------------------------------------*/ |
1696 | 315k | std::unique_ptr<TABMAPObjHdr> poObjHdr(TABMAPObjHdr::NewObj( |
1697 | 315k | poFeature->ValidateMapInfoType(m_poMAPFile), nFeatureId)); |
1698 | | |
1699 | 315k | if (poObjHdr == nullptr) |
1700 | 0 | { |
1701 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1702 | 0 | "Failed writing geometry for feature id %d in %s", nFeatureId, |
1703 | 0 | m_pszFname); |
1704 | 0 | return -1; |
1705 | 0 | } |
1706 | | |
1707 | | /*----------------------------------------------------------------- |
1708 | | * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature |
1709 | | * contained an invalid geometry for its class. Need to catch that |
1710 | | * case and return the error. |
1711 | | *----------------------------------------------------------------*/ |
1712 | 315k | if (poObjHdr->m_nType == TAB_GEOM_NONE && |
1713 | 302k | poFeature->GetFeatureClass() != TABFCNoGeomFeature) |
1714 | 4.26k | { |
1715 | 4.26k | CPLError(CE_Failure, CPLE_FileIO, |
1716 | 4.26k | "Invalid geometry for feature id %d in %s", nFeatureId, |
1717 | 4.26k | m_pszFname); |
1718 | 4.26k | return -1; |
1719 | 4.26k | } |
1720 | | |
1721 | | /*----------------------------------------------------------------- |
1722 | | * The ValidateMapInfoType() call above has forced calculation of the |
1723 | | * feature's IntMBR. Store that value in the ObjHdr for use by |
1724 | | * PrepareNewObj() to search the best node to insert the feature. |
1725 | | *----------------------------------------------------------------*/ |
1726 | 311k | if (poObjHdr->m_nType != TAB_GEOM_NONE) |
1727 | 13.2k | { |
1728 | 13.2k | poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, |
1729 | 13.2k | poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); |
1730 | 13.2k | } |
1731 | | |
1732 | | /* |
1733 | | if( m_nCurFeatureId < m_nLastFeatureId ) |
1734 | | { |
1735 | | delete GetFeatureRef(m_nLastFeatureId); |
1736 | | m_poCurFeature = NULL; |
1737 | | }*/ |
1738 | | |
1739 | 311k | if (m_poMAPFile->PrepareNewObj(poObjHdr.get()) != 0 || |
1740 | 311k | poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr.get()) != 0 || |
1741 | 310k | m_poMAPFile->CommitNewObj(poObjHdr.get()) != 0) |
1742 | 874 | { |
1743 | 874 | CPLError(CE_Failure, CPLE_FileIO, |
1744 | 874 | "Failed writing geometry for feature id %d in %s", nFeatureId, |
1745 | 874 | m_pszFname); |
1746 | 874 | return -1; |
1747 | 874 | } |
1748 | | |
1749 | 310k | m_nLastFeatureId = std::max(m_nLastFeatureId, nFeatureId); |
1750 | 310k | m_nCurFeatureId = nFeatureId; |
1751 | | |
1752 | 310k | return 0; |
1753 | 311k | } |
1754 | | |
1755 | | int TABFile::SetCharset(const char *pszCharset) |
1756 | 9.30k | { |
1757 | 9.30k | if (0 != IMapInfoFile::SetCharset(pszCharset)) |
1758 | 0 | { |
1759 | 0 | return -1; |
1760 | 0 | } |
1761 | 9.30k | if (m_poDATFile != nullptr) |
1762 | 0 | { |
1763 | 0 | m_poDATFile->SetEncoding(CharsetToEncoding(pszCharset)); |
1764 | 0 | } |
1765 | 9.30k | if (m_poMAPFile != nullptr) |
1766 | 0 | { |
1767 | 0 | m_poMAPFile->SetEncoding(CharsetToEncoding(pszCharset)); |
1768 | 0 | } |
1769 | 9.30k | if (EQUAL(pszCharset, "UTF-8")) |
1770 | 0 | { |
1771 | 0 | m_nVersion = std::max(m_nVersion, 1520); |
1772 | 0 | } |
1773 | 9.30k | return 0; |
1774 | 9.30k | } |
1775 | | |
1776 | | void TABFile::SetStrictLaundering(bool bStrictLaundering) |
1777 | 457 | { |
1778 | 457 | IMapInfoFile::SetStrictLaundering(bStrictLaundering); |
1779 | 457 | if (!bStrictLaundering) |
1780 | 0 | { |
1781 | 0 | m_nVersion = std::max(m_nVersion, 1520); |
1782 | 0 | } |
1783 | 457 | } |
1784 | | |
1785 | | /********************************************************************** |
1786 | | * TABFile::CreateFeature() |
1787 | | * |
1788 | | * Write a new feature to this dataset. The passed in feature is updated |
1789 | | * with the new feature id. |
1790 | | * |
1791 | | * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an |
1792 | | * error happened in which case, CPLError() will have been called to |
1793 | | * report the reason of the failure. |
1794 | | **********************************************************************/ |
1795 | | OGRErr TABFile::CreateFeature(TABFeature *poFeature) |
1796 | 315k | { |
1797 | 315k | CPLErrorReset(); |
1798 | | |
1799 | 315k | if (m_eAccessMode == TABRead) |
1800 | 0 | { |
1801 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1802 | 0 | "CreateFeature() cannot be used in read-only access."); |
1803 | 0 | return OGRERR_FAILURE; |
1804 | 0 | } |
1805 | | |
1806 | 315k | GIntBig nFeatureId = poFeature->GetFID(); |
1807 | 315k | if (nFeatureId != OGRNullFID) |
1808 | 0 | { |
1809 | 0 | if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId) |
1810 | 0 | { |
1811 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1812 | 0 | "CreateFeature() failed: invalid feature id " CPL_FRMT_GIB, |
1813 | 0 | nFeatureId); |
1814 | 0 | return OGRERR_FAILURE; |
1815 | 0 | } |
1816 | | |
1817 | 0 | if (m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == |
1818 | 0 | nullptr || |
1819 | 0 | !m_poDATFile->IsCurrentRecordDeleted()) |
1820 | 0 | { |
1821 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1822 | 0 | "CreateFeature() failed: cannot re-write already existing " |
1823 | 0 | "feature " CPL_FRMT_GIB, |
1824 | 0 | nFeatureId); |
1825 | 0 | return OGRERR_FAILURE; |
1826 | 0 | } |
1827 | 0 | } |
1828 | | |
1829 | 315k | if (WriteFeature(poFeature) < 0) |
1830 | 5.13k | return OGRERR_FAILURE; |
1831 | | |
1832 | 310k | return OGRERR_NONE; |
1833 | 315k | } |
1834 | | |
1835 | | /********************************************************************** |
1836 | | * TABFile::ISetFeature() |
1837 | | * |
1838 | | * Implementation of OGRLayer's SetFeature() |
1839 | | **********************************************************************/ |
1840 | | OGRErr TABFile::ISetFeature(OGRFeature *poFeature) |
1841 | | |
1842 | 0 | { |
1843 | 0 | CPLErrorReset(); |
1844 | |
|
1845 | 0 | if (m_eAccessMode == TABRead) |
1846 | 0 | { |
1847 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1848 | 0 | "SetFeature() cannot be used in read-only access."); |
1849 | 0 | return OGRERR_FAILURE; |
1850 | 0 | } |
1851 | | |
1852 | | /*----------------------------------------------------------------- |
1853 | | * Make sure file is opened. |
1854 | | *----------------------------------------------------------------*/ |
1855 | 0 | if (m_poMAPFile == nullptr) |
1856 | 0 | { |
1857 | 0 | CPLError(CE_Failure, CPLE_IllegalArg, |
1858 | 0 | "SetFeature() failed: file is not opened!"); |
1859 | 0 | return OGRERR_FAILURE; |
1860 | 0 | } |
1861 | | |
1862 | 0 | GIntBig nFeatureId = poFeature->GetFID(); |
1863 | 0 | if (nFeatureId == OGRNullFID) |
1864 | 0 | { |
1865 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1866 | 0 | "SetFeature() must be used on a feature with a FID."); |
1867 | 0 | return OGRERR_FAILURE; |
1868 | 0 | } |
1869 | 0 | if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId) |
1870 | 0 | { |
1871 | | /*CPLError(CE_Failure, CPLE_IllegalArg, |
1872 | | "SetFeature() failed: invalid feature id " CPL_FRMT_GIB, |
1873 | | nFeatureId);*/ |
1874 | 0 | return OGRERR_NON_EXISTING_FEATURE; |
1875 | 0 | } |
1876 | | |
1877 | 0 | OGRGeometry *poGeom = poFeature->GetGeometryRef(); |
1878 | 0 | if (poGeom != nullptr && |
1879 | 0 | ((wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) || |
1880 | 0 | (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection))) |
1881 | 0 | { |
1882 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1883 | 0 | "SetFeature() failed: setting MultiPoint or " |
1884 | 0 | "GeometryCollection not supported"); |
1885 | 0 | return OGRERR_FAILURE; |
1886 | 0 | } |
1887 | | |
1888 | 0 | TABFeature *poTABFeature = CreateTABFeature(poFeature); |
1889 | 0 | if (poTABFeature == nullptr) |
1890 | 0 | return OGRERR_FAILURE; |
1891 | | |
1892 | 0 | if (m_bLastOpWasWrite) |
1893 | 0 | ResetReading(); |
1894 | |
|
1895 | 0 | if (m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr) |
1896 | 0 | { |
1897 | | /*CPLError(CE_Failure, CPLE_IllegalArg, |
1898 | | "SetFeature() failed: invalid feature id " CPL_FRMT_GIB, |
1899 | | nFeatureId);*/ |
1900 | 0 | delete poTABFeature; |
1901 | 0 | return OGRERR_NON_EXISTING_FEATURE; |
1902 | 0 | } |
1903 | | |
1904 | | /* If the object is not already deleted, delete it */ |
1905 | 0 | if (!(m_poDATFile->IsCurrentRecordDeleted())) |
1906 | 0 | { |
1907 | 0 | OGRFeature *poOldFeature = GetFeature(nFeatureId); |
1908 | 0 | if (poOldFeature != nullptr) |
1909 | 0 | { |
1910 | | /* Optimization: if old and new features are the same, do nothing */ |
1911 | 0 | if (poOldFeature->Equal(poFeature)) |
1912 | 0 | { |
1913 | 0 | CPLDebug("MITAB", "Un-modified object " CPL_FRMT_GIB, |
1914 | 0 | nFeatureId); |
1915 | 0 | delete poTABFeature; |
1916 | 0 | delete poOldFeature; |
1917 | 0 | return OGRERR_NONE; |
1918 | 0 | } |
1919 | | |
1920 | | /* Optimization: if old and new geometries are the same, just */ |
1921 | | /* rewrite the attributes */ |
1922 | 0 | OGRGeometry *poOldGeom = poOldFeature->GetGeometryRef(); |
1923 | 0 | OGRGeometry *poNewGeom = poFeature->GetGeometryRef(); |
1924 | 0 | if ((poOldGeom == nullptr && poNewGeom == nullptr) || |
1925 | 0 | (poOldGeom != nullptr && poNewGeom != nullptr && |
1926 | 0 | poOldGeom->Equals(poNewGeom))) |
1927 | 0 | { |
1928 | 0 | const char *pszOldStyle = poOldFeature->GetStyleString(); |
1929 | 0 | const char *pszNewStyle = poFeature->GetStyleString(); |
1930 | 0 | if ((pszOldStyle == nullptr && pszNewStyle == nullptr) || |
1931 | 0 | (pszOldStyle != nullptr && pszNewStyle != nullptr && |
1932 | 0 | EQUAL(pszOldStyle, pszNewStyle))) |
1933 | 0 | { |
1934 | 0 | CPLDebug("MITAB", |
1935 | 0 | "Rewrite only attributes for object " CPL_FRMT_GIB, |
1936 | 0 | nFeatureId); |
1937 | 0 | if (poTABFeature->WriteRecordToDATFile( |
1938 | 0 | m_poDATFile, m_poINDFile, m_panIndexNo) != 0) |
1939 | 0 | { |
1940 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1941 | 0 | "Failed writing attributes for feature " |
1942 | 0 | "id " CPL_FRMT_GIB " in %s", |
1943 | 0 | nFeatureId, m_pszFname); |
1944 | 0 | delete poTABFeature; |
1945 | 0 | delete poOldFeature; |
1946 | 0 | return OGRERR_FAILURE; |
1947 | 0 | } |
1948 | | |
1949 | 0 | delete poTABFeature; |
1950 | 0 | delete poOldFeature; |
1951 | 0 | return OGRERR_NONE; |
1952 | 0 | } |
1953 | 0 | } |
1954 | | |
1955 | 0 | delete poOldFeature; |
1956 | 0 | } |
1957 | | |
1958 | 0 | if (DeleteFeature(nFeatureId) != OGRERR_NONE) |
1959 | 0 | { |
1960 | 0 | delete poTABFeature; |
1961 | 0 | return OGRERR_FAILURE; |
1962 | 0 | } |
1963 | 0 | } |
1964 | | |
1965 | 0 | int nStatus = WriteFeature(poTABFeature); |
1966 | |
|
1967 | 0 | delete poTABFeature; |
1968 | |
|
1969 | 0 | if (nStatus < 0) |
1970 | 0 | return OGRERR_FAILURE; |
1971 | | |
1972 | 0 | return OGRERR_NONE; |
1973 | 0 | } |
1974 | | |
1975 | | /********************************************************************** |
1976 | | * TABFile::GetLayerDefn() const |
1977 | | * |
1978 | | * Returns a reference to the OGRFeatureDefn that will be used to create |
1979 | | * features in this dataset. |
1980 | | * |
1981 | | * Returns a reference to an object that is maintained by this TABFile |
1982 | | * object (and thus should not be modified or freed by the caller) or |
1983 | | * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file |
1984 | | * opened yet) |
1985 | | **********************************************************************/ |
1986 | | const OGRFeatureDefn *TABFile::GetLayerDefn() const |
1987 | 326k | { |
1988 | 326k | return m_poDefn; |
1989 | 326k | } |
1990 | | |
1991 | | /********************************************************************** |
1992 | | * TABFile::SetFeatureDefn() |
1993 | | * |
1994 | | * Pass a reference to the OGRFeatureDefn that will be used to create |
1995 | | * features in this dataset. This function should be called after |
1996 | | * creating a new dataset, but before writing the first feature. |
1997 | | * All features that will be written to this dataset must share this same |
1998 | | * OGRFeatureDefn. |
1999 | | * |
2000 | | * A reference to the OGRFeatureDefn will be kept and will be used to |
2001 | | * build the .DAT file, etc. |
2002 | | * |
2003 | | * Returns 0 on success, -1 on error. |
2004 | | **********************************************************************/ |
2005 | | int TABFile::SetFeatureDefn( |
2006 | | OGRFeatureDefn *poFeatureDefn, |
2007 | | TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */) |
2008 | 0 | { |
2009 | 0 | if (m_eAccessMode != TABWrite) |
2010 | 0 | { |
2011 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2012 | 0 | "SetFeatureDefn() can be used only with Write access."); |
2013 | 0 | return -1; |
2014 | 0 | } |
2015 | | |
2016 | | /*----------------------------------------------------------------- |
2017 | | * Keep a reference to the OGRFeatureDefn... we'll have to take the |
2018 | | * reference count into account when we are done with it. |
2019 | | *----------------------------------------------------------------*/ |
2020 | 0 | if (m_poDefn && m_poDefn->Dereference() == 0) |
2021 | 0 | delete m_poDefn; |
2022 | |
|
2023 | 0 | m_poDefn = poFeatureDefn; |
2024 | 0 | m_poDefn->Reference(); |
2025 | | |
2026 | | /*----------------------------------------------------------------- |
2027 | | * Pass field information to the .DAT file, after making sure that |
2028 | | * it has been created and that it does not contain any field |
2029 | | * definition yet. |
2030 | | *----------------------------------------------------------------*/ |
2031 | 0 | if (m_poDATFile == nullptr || m_poDATFile->GetNumFields() > 0) |
2032 | 0 | { |
2033 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
2034 | 0 | "SetFeatureDefn() can be called only once in a newly " |
2035 | 0 | "created dataset."); |
2036 | 0 | return -1; |
2037 | 0 | } |
2038 | | |
2039 | 0 | const int numFields = poFeatureDefn->GetFieldCount(); |
2040 | 0 | TABFieldType eMapInfoType = TABFUnknown; |
2041 | 0 | int nStatus = 0; |
2042 | 0 | for (int iField = 0; nStatus == 0 && iField < numFields; iField++) |
2043 | 0 | { |
2044 | 0 | OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField); |
2045 | |
|
2046 | 0 | if (paeMapInfoNativeFieldTypes) |
2047 | 0 | { |
2048 | 0 | eMapInfoType = paeMapInfoNativeFieldTypes[iField]; |
2049 | 0 | } |
2050 | 0 | else |
2051 | 0 | { |
2052 | | /*--------------------------------------------------------- |
2053 | | * Map OGRFieldTypes to MapInfo native types |
2054 | | *--------------------------------------------------------*/ |
2055 | 0 | switch (poFieldDefn->GetType()) |
2056 | 0 | { |
2057 | 0 | case OFTInteger: |
2058 | 0 | eMapInfoType = poFieldDefn->GetSubType() == OFSTBoolean |
2059 | 0 | ? TABFLogical |
2060 | 0 | : TABFInteger; |
2061 | 0 | break; |
2062 | 0 | case OFTReal: |
2063 | 0 | if (poFieldDefn->GetWidth() > 0 || |
2064 | 0 | poFieldDefn->GetPrecision() > 0) |
2065 | 0 | eMapInfoType = TABFDecimal; |
2066 | 0 | else |
2067 | 0 | eMapInfoType = TABFFloat; |
2068 | 0 | break; |
2069 | 0 | case OFTDateTime: |
2070 | 0 | eMapInfoType = TABFDateTime; |
2071 | 0 | break; |
2072 | 0 | case OFTDate: |
2073 | 0 | eMapInfoType = TABFDate; |
2074 | 0 | break; |
2075 | 0 | case OFTTime: |
2076 | 0 | eMapInfoType = TABFTime; |
2077 | 0 | break; |
2078 | 0 | case OFTString: |
2079 | 0 | default: |
2080 | 0 | eMapInfoType = TABFChar; |
2081 | 0 | } |
2082 | 0 | } |
2083 | | |
2084 | 0 | nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(), eMapInfoType, |
2085 | 0 | poFieldDefn->GetWidth(), |
2086 | 0 | poFieldDefn->GetPrecision()); |
2087 | 0 | } |
2088 | | |
2089 | | /*----------------------------------------------------------------- |
2090 | | * Alloc the array to keep track of indexed fields (default=NOT indexed) |
2091 | | *----------------------------------------------------------------*/ |
2092 | 0 | m_panIndexNo = static_cast<int *>(CPLCalloc(numFields, sizeof(int))); |
2093 | |
|
2094 | 0 | return nStatus; |
2095 | 0 | } |
2096 | | |
2097 | | /********************************************************************** |
2098 | | * TABFile::AddFieldNative() |
2099 | | * |
2100 | | * Create a new field using a native mapinfo data type... this is an |
2101 | | * alternative to defining fields through the OGR interface. |
2102 | | * This function should be called after creating a new dataset. |
2103 | | * |
2104 | | * This function will build/update the OGRFeatureDefn that will have to be |
2105 | | * used when writing features to this dataset. |
2106 | | * |
2107 | | * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn(). |
2108 | | * |
2109 | | * Note: The bUnique flag has no effect on TABFiles. See the TABView class. |
2110 | | * |
2111 | | * Returns 0 on success, -1 on error. |
2112 | | **********************************************************************/ |
2113 | | int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType, |
2114 | | int nWidth /*=0*/, int nPrecision /*=0*/, |
2115 | | GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/, |
2116 | | int /*bApproxOK*/) |
2117 | 12.4k | { |
2118 | 12.4k | if (m_eAccessMode == TABRead || m_poDATFile == nullptr) |
2119 | 0 | { |
2120 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2121 | 0 | "AddFieldNative() cannot be used only with Read access."); |
2122 | 0 | return -1; |
2123 | 0 | } |
2124 | | |
2125 | 12.4k | m_bNeedTABRewrite = TRUE; |
2126 | | |
2127 | | /*----------------------------------------------------------------- |
2128 | | * Validate field width... must be <= 254 |
2129 | | *----------------------------------------------------------------*/ |
2130 | 12.4k | if (nWidth > 254) |
2131 | 0 | { |
2132 | 0 | CPLError(CE_Warning, CPLE_IllegalArg, |
2133 | 0 | "Invalid size (%d) for field '%s'. " |
2134 | 0 | "Size must be 254 or less.", |
2135 | 0 | nWidth, pszName); |
2136 | 0 | nWidth = 254; |
2137 | 0 | } |
2138 | | |
2139 | | /*----------------------------------------------------------------- |
2140 | | * Map fields with width=0 (variable length in OGR) to a valid default |
2141 | | *----------------------------------------------------------------*/ |
2142 | 12.4k | if (eMapInfoType == TABFDecimal && nWidth == 0) |
2143 | 0 | nWidth = 20; |
2144 | 12.4k | else if (nWidth == 0) |
2145 | 0 | nWidth = 254; /* char fields */ |
2146 | | |
2147 | 12.4k | CPLString osName(NormalizeFieldName(pszName)); |
2148 | | |
2149 | | /*----------------------------------------------------------------- |
2150 | | * Map MapInfo native types to OGR types |
2151 | | *----------------------------------------------------------------*/ |
2152 | 12.4k | OGRFieldDefn *poFieldDefn = nullptr; |
2153 | | |
2154 | 12.4k | switch (eMapInfoType) |
2155 | 12.4k | { |
2156 | 12.4k | case TABFChar: |
2157 | | /*------------------------------------------------- |
2158 | | * CHAR type |
2159 | | *------------------------------------------------*/ |
2160 | 12.4k | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString); |
2161 | 12.4k | poFieldDefn->SetWidth(nWidth); |
2162 | 12.4k | break; |
2163 | 5 | case TABFInteger: |
2164 | | /*------------------------------------------------- |
2165 | | * INTEGER type |
2166 | | *------------------------------------------------*/ |
2167 | 5 | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger); |
2168 | 5 | if (nWidth <= 10) |
2169 | 0 | poFieldDefn->SetWidth(nWidth); |
2170 | 5 | break; |
2171 | 0 | case TABFSmallInt: |
2172 | | /*------------------------------------------------- |
2173 | | * SMALLINT type |
2174 | | *------------------------------------------------*/ |
2175 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger); |
2176 | 0 | if (nWidth <= 5) |
2177 | 0 | poFieldDefn->SetWidth(nWidth); |
2178 | 0 | break; |
2179 | 0 | case TABFLargeInt: |
2180 | | /*------------------------------------------------- |
2181 | | * SMALLINT type |
2182 | | *------------------------------------------------*/ |
2183 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger64); |
2184 | 0 | break; |
2185 | 0 | case TABFDecimal: |
2186 | | /*------------------------------------------------- |
2187 | | * DECIMAL type |
2188 | | *------------------------------------------------*/ |
2189 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal); |
2190 | 0 | poFieldDefn->SetWidth(nWidth); |
2191 | 0 | poFieldDefn->SetPrecision(nPrecision); |
2192 | 0 | break; |
2193 | 5 | case TABFFloat: |
2194 | | /*------------------------------------------------- |
2195 | | * FLOAT type |
2196 | | *------------------------------------------------*/ |
2197 | 5 | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal); |
2198 | 5 | break; |
2199 | 0 | case TABFDate: |
2200 | | /*------------------------------------------------- |
2201 | | * DATE type (V450, returned as a string: "DD/MM/YYYY") |
2202 | | *------------------------------------------------*/ |
2203 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), |
2204 | 0 | #ifdef MITAB_USE_OFTDATETIME |
2205 | 0 | OFTDate); |
2206 | | #else |
2207 | | OFTString); |
2208 | | #endif |
2209 | 0 | poFieldDefn->SetWidth(10); |
2210 | 0 | m_nVersion = std::max(m_nVersion, 450); |
2211 | 0 | break; |
2212 | 0 | case TABFTime: |
2213 | | /*------------------------------------------------- |
2214 | | * TIME type (V900, returned as a string: "HH:MM:SS") |
2215 | | *------------------------------------------------*/ |
2216 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), |
2217 | 0 | #ifdef MITAB_USE_OFTDATETIME |
2218 | 0 | OFTTime); |
2219 | | #else |
2220 | | OFTString); |
2221 | | #endif |
2222 | 0 | poFieldDefn->SetWidth(8); |
2223 | 0 | m_nVersion = std::max(m_nVersion, 900); |
2224 | 0 | break; |
2225 | 0 | case TABFDateTime: |
2226 | | /*------------------------------------------------- |
2227 | | * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS") |
2228 | | *------------------------------------------------*/ |
2229 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), |
2230 | 0 | #ifdef MITAB_USE_OFTDATETIME |
2231 | 0 | OFTDateTime); |
2232 | | #else |
2233 | | OFTString); |
2234 | | #endif |
2235 | 0 | poFieldDefn->SetWidth(19); |
2236 | 0 | m_nVersion = std::max(m_nVersion, 900); |
2237 | 0 | break; |
2238 | 0 | case TABFLogical: |
2239 | | /*------------------------------------------------- |
2240 | | * LOGICAL type (value "T" or "F") |
2241 | | *------------------------------------------------*/ |
2242 | 0 | poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger); |
2243 | 0 | poFieldDefn->SetSubType(OFSTBoolean); |
2244 | 0 | poFieldDefn->SetWidth(1); |
2245 | 0 | break; |
2246 | 0 | default: |
2247 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2248 | 0 | "Unsupported type for field %s", osName.c_str()); |
2249 | 0 | return -1; |
2250 | 12.4k | } |
2251 | | |
2252 | | /*----------------------------------------------------- |
2253 | | * Add the FieldDefn to the FeatureDefn |
2254 | | *----------------------------------------------------*/ |
2255 | 12.4k | whileUnsealing(m_poDefn)->AddFieldDefn(poFieldDefn); |
2256 | 12.4k | m_oSetFields.insert(CPLString(poFieldDefn->GetNameRef()).toupper()); |
2257 | 12.4k | delete poFieldDefn; |
2258 | | |
2259 | | /*----------------------------------------------------- |
2260 | | * ... and pass field info to the .DAT file. |
2261 | | *----------------------------------------------------*/ |
2262 | 12.4k | int nStatus = |
2263 | 12.4k | m_poDATFile->AddField(osName.c_str(), eMapInfoType, nWidth, nPrecision); |
2264 | | |
2265 | | /*----------------------------------------------------------------- |
2266 | | * Extend the array to keep track of indexed fields (default=NOT indexed) |
2267 | | *----------------------------------------------------------------*/ |
2268 | 12.4k | m_panIndexNo = static_cast<int *>( |
2269 | 12.4k | CPLRealloc(m_panIndexNo, m_poDefn->GetFieldCount() * sizeof(int))); |
2270 | 12.4k | m_panIndexNo[m_poDefn->GetFieldCount() - 1] = 0; |
2271 | | |
2272 | | /*----------------------------------------------------------------- |
2273 | | * Index the field if requested |
2274 | | *----------------------------------------------------------------*/ |
2275 | 12.4k | if (nStatus == 0 && bIndexed) |
2276 | 0 | nStatus = SetFieldIndexed(m_poDefn->GetFieldCount() - 1); |
2277 | | |
2278 | 12.4k | if (nStatus == 0 && m_eAccessMode == TABReadWrite) |
2279 | 0 | nStatus = WriteTABFile(); |
2280 | | |
2281 | 12.4k | return nStatus; |
2282 | 12.4k | } |
2283 | | |
2284 | | /********************************************************************** |
2285 | | * TABFile::GetNativeFieldType() |
2286 | | * |
2287 | | * Returns the native MapInfo field type for the specified field. |
2288 | | * |
2289 | | * Returns TABFUnknown if file is not opened, or if specified field index is |
2290 | | * invalid. |
2291 | | * |
2292 | | * Note that field ids are positive and start at 0. |
2293 | | **********************************************************************/ |
2294 | | TABFieldType TABFile::GetNativeFieldType(int nFieldId) |
2295 | 21.2k | { |
2296 | 21.2k | if (m_poDATFile) |
2297 | 21.2k | { |
2298 | 21.2k | return m_poDATFile->GetFieldType(nFieldId); |
2299 | 21.2k | } |
2300 | 0 | return TABFUnknown; |
2301 | 21.2k | } |
2302 | | |
2303 | | /********************************************************************** |
2304 | | * TABFile::GetFieldIndexNumber() |
2305 | | * |
2306 | | * Returns the field's index number that was specified in the .TAB header |
2307 | | * or 0 if the specified field is not indexed. |
2308 | | * |
2309 | | * Note that field ids are positive and start at 0 |
2310 | | * and valid index ids are positive and start at 1. |
2311 | | **********************************************************************/ |
2312 | | int TABFile::GetFieldIndexNumber(int nFieldId) |
2313 | 19.4k | { |
2314 | 19.4k | if (m_panIndexNo == nullptr || nFieldId < 0 || m_poDATFile == nullptr || |
2315 | 19.4k | nFieldId >= m_poDefn->GetFieldCount()) |
2316 | 28 | return 0; // no index |
2317 | | |
2318 | 19.4k | return m_panIndexNo[nFieldId]; |
2319 | 19.4k | } |
2320 | | |
2321 | | /************************************************************************ |
2322 | | * TABFile::SetFieldIndexed() |
2323 | | * |
2324 | | * Request that a field be indexed. This will create the .IND file if |
2325 | | * necessary, etc. |
2326 | | * |
2327 | | * Note that field ids are positive and start at 0. |
2328 | | * |
2329 | | * Returns 0 on success, -1 on error. |
2330 | | ************************************************************************/ |
2331 | | int TABFile::SetFieldIndexed(int nFieldId) |
2332 | 0 | { |
2333 | | /*----------------------------------------------------------------- |
2334 | | * Make sure things are OK |
2335 | | *----------------------------------------------------------------*/ |
2336 | 0 | if (m_pszFname == nullptr || m_eAccessMode != TABWrite || |
2337 | 0 | m_poDefn == nullptr) |
2338 | 0 | { |
2339 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
2340 | 0 | "SetFieldIndexed() must be called after opening a new " |
2341 | 0 | "dataset, but before writing the first feature to it."); |
2342 | 0 | return -1; |
2343 | 0 | } |
2344 | | |
2345 | 0 | if (m_panIndexNo == nullptr || nFieldId < 0 || m_poDATFile == nullptr || |
2346 | 0 | nFieldId >= m_poDefn->GetFieldCount()) |
2347 | 0 | { |
2348 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
2349 | 0 | "Invalid field number in SetFieldIndexed()."); |
2350 | 0 | return -1; |
2351 | 0 | } |
2352 | | |
2353 | | /*----------------------------------------------------------------- |
2354 | | * If field is already indexed then just return |
2355 | | *----------------------------------------------------------------*/ |
2356 | 0 | if (m_panIndexNo[nFieldId] != 0) |
2357 | 0 | return 0; // Nothing to do |
2358 | | |
2359 | | /*----------------------------------------------------------------- |
2360 | | * Create .IND file if it is not done yet. |
2361 | | * |
2362 | | * Note: We can pass the .TAB's filename directly and the |
2363 | | * TABINDFile class will automagically adjust the extension. |
2364 | | *----------------------------------------------------------------*/ |
2365 | 0 | if (m_poINDFile == nullptr) |
2366 | 0 | { |
2367 | 0 | m_poINDFile = new TABINDFile; |
2368 | |
|
2369 | 0 | if (m_poINDFile->Open(m_pszFname, "w", TRUE) != 0) |
2370 | 0 | { |
2371 | | // File could not be opened... |
2372 | 0 | delete m_poINDFile; |
2373 | 0 | m_poINDFile = nullptr; |
2374 | 0 | return -1; |
2375 | 0 | } |
2376 | 0 | } |
2377 | | |
2378 | | /*----------------------------------------------------------------- |
2379 | | * Init new index. |
2380 | | *----------------------------------------------------------------*/ |
2381 | 0 | OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId); |
2382 | |
|
2383 | 0 | if (poFieldDefn == nullptr) |
2384 | 0 | return -1; |
2385 | 0 | const int nNewIndexNo = m_poINDFile->CreateIndex( |
2386 | 0 | GetNativeFieldType(nFieldId), poFieldDefn->GetWidth()); |
2387 | 0 | if (nNewIndexNo < 1) |
2388 | 0 | { |
2389 | | // Failed... an error has already been reported. |
2390 | 0 | return -1; |
2391 | 0 | } |
2392 | | |
2393 | 0 | m_panIndexNo[nFieldId] = nNewIndexNo; |
2394 | |
|
2395 | 0 | return 0; |
2396 | 0 | } |
2397 | | |
2398 | | /************************************************************************ |
2399 | | * TABFile::IsFieldIndexed() |
2400 | | * |
2401 | | * Returns TRUE if field is indexed, or FALSE otherwise. |
2402 | | ************************************************************************/ |
2403 | | GBool TABFile::IsFieldIndexed(int nFieldId) |
2404 | 0 | { |
2405 | 0 | return GetFieldIndexNumber(nFieldId) > 0 ? TRUE : FALSE; |
2406 | 0 | } |
2407 | | |
2408 | | /********************************************************************** |
2409 | | * TABFile::GetINDFileRef() |
2410 | | * |
2411 | | * Opens the .IND file for this dataset and returns a reference to |
2412 | | * the handle. |
2413 | | * If the .IND file has already been opened then the same handle is |
2414 | | * returned directly. |
2415 | | * If the .IND file does not exist then the function silently returns NULL. |
2416 | | * |
2417 | | * Note that the returned TABINDFile handle is only a reference to an |
2418 | | * object that is owned by this class. Callers can use it but cannot |
2419 | | * destroy the object. The object will remain valid for as long as |
2420 | | * the TABFile will remain open. |
2421 | | **********************************************************************/ |
2422 | | TABINDFile *TABFile::GetINDFileRef() |
2423 | 397 | { |
2424 | 397 | if (m_pszFname == nullptr) |
2425 | 0 | return nullptr; |
2426 | | |
2427 | 397 | if (m_eAccessMode == TABRead && m_poINDFile == nullptr) |
2428 | 397 | { |
2429 | | /*------------------------------------------------------------- |
2430 | | * File is not opened yet... do it now. |
2431 | | * |
2432 | | * Note: We can pass the .TAB's filename directly and the |
2433 | | * TABINDFile class will automagically adjust the extension. |
2434 | | *------------------------------------------------------------*/ |
2435 | 397 | m_poINDFile = new TABINDFile; |
2436 | | |
2437 | 397 | if (m_poINDFile->Open(m_pszFname, "r", TRUE) != 0) |
2438 | 82 | { |
2439 | | // File could not be opened... probably does not exist |
2440 | 82 | delete m_poINDFile; |
2441 | 82 | m_poINDFile = nullptr; |
2442 | 82 | } |
2443 | 315 | else if (m_panIndexNo && m_poDATFile) |
2444 | 315 | { |
2445 | | /*--------------------------------------------------------- |
2446 | | * Pass type information for each indexed field. |
2447 | | *--------------------------------------------------------*/ |
2448 | 811 | for (int i = 0; i < m_poDefn->GetFieldCount(); i++) |
2449 | 496 | { |
2450 | 496 | if (m_panIndexNo[i] > 0) |
2451 | 310 | { |
2452 | 310 | m_poINDFile->SetIndexFieldType(m_panIndexNo[i], |
2453 | 310 | GetNativeFieldType(i)); |
2454 | 310 | } |
2455 | 496 | } |
2456 | 315 | } |
2457 | 397 | } |
2458 | | |
2459 | 397 | return m_poINDFile; |
2460 | 397 | } |
2461 | | |
2462 | | /********************************************************************** |
2463 | | * TABFile::SetBounds() |
2464 | | * |
2465 | | * Set projection coordinates bounds of the newly created dataset. |
2466 | | * |
2467 | | * This function must be called after creating a new dataset and before any |
2468 | | * feature can be written to it. |
2469 | | * |
2470 | | * Returns 0 on success, -1 on error. |
2471 | | **********************************************************************/ |
2472 | | int TABFile::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax) |
2473 | 226 | { |
2474 | 226 | if (m_eAccessMode != TABWrite) |
2475 | 0 | { |
2476 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2477 | 0 | "SetBounds() can be used only with Write access."); |
2478 | 0 | return -1; |
2479 | 0 | } |
2480 | | |
2481 | | /*----------------------------------------------------------------- |
2482 | | * Check that dataset has been created but no feature set yet. |
2483 | | *----------------------------------------------------------------*/ |
2484 | 226 | if (m_poMAPFile && m_nLastFeatureId < 1) |
2485 | 226 | { |
2486 | 226 | m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax); |
2487 | | |
2488 | | /*----------------------------------------------------------------- |
2489 | | * Set BOUNDS metadata |
2490 | | *----------------------------------------------------------------*/ |
2491 | 226 | { |
2492 | 226 | OGREnvelope e; |
2493 | 226 | GetBounds(e.MinX, e.MinY, e.MaxX, e.MaxY, 1); |
2494 | 226 | const char *pszBounds = CPLSPrintf("%.17g,%.17g,%.17g,%.17g", |
2495 | 226 | e.MinX, e.MinY, e.MaxX, e.MaxY); |
2496 | 226 | SetMetadataItem("BOUNDS", pszBounds); |
2497 | 226 | } |
2498 | | |
2499 | 226 | m_bBoundsSet = TRUE; |
2500 | 226 | } |
2501 | 0 | else |
2502 | 0 | { |
2503 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
2504 | 0 | "SetBounds() can be called only after dataset has been " |
2505 | 0 | "created and before any feature is set."); |
2506 | 0 | return -1; |
2507 | 0 | } |
2508 | | |
2509 | 226 | return 0; |
2510 | 226 | } |
2511 | | |
2512 | | /********************************************************************** |
2513 | | * TABFile::GetBounds() |
2514 | | * |
2515 | | * Fetch projection coordinates bounds of a dataset. |
2516 | | * |
2517 | | * The bForce flag has no effect on TAB files since the bounds are |
2518 | | * always in the header. |
2519 | | * |
2520 | | * Returns 0 on success, -1 on error. |
2521 | | **********************************************************************/ |
2522 | | int TABFile::GetBounds(double &dXMin, double &dYMin, double &dXMax, |
2523 | | double &dYMax, GBool /*bForce = TRUE*/) |
2524 | 2.33k | { |
2525 | 2.33k | if (m_poMAPFile) |
2526 | 2.33k | { |
2527 | 2.33k | TABMAPHeaderBlock *poHeader = m_poMAPFile->GetHeaderBlock(); |
2528 | 2.33k | if (poHeader != nullptr) |
2529 | 2.33k | { |
2530 | | /*------------------------------------------------------------- |
2531 | | * Projection bounds correspond to the +/- 1e9 integer coord. limits |
2532 | | *------------------------------------------------------------*/ |
2533 | 2.33k | double dX0 = 0.0; |
2534 | 2.33k | double dX1 = 0.0; |
2535 | 2.33k | double dY0 = 0.0; |
2536 | 2.33k | double dY1 = 0.0; |
2537 | 2.33k | m_poMAPFile->Int2Coordsys(-1000000000, -1000000000, dX0, dY0); |
2538 | 2.33k | m_poMAPFile->Int2Coordsys(1000000000, 1000000000, dX1, dY1); |
2539 | | /*------------------------------------------------------------- |
2540 | | * ... and make sure that Min < Max |
2541 | | *------------------------------------------------------------*/ |
2542 | 2.33k | dXMin = std::min(dX0, dX1); |
2543 | 2.33k | dXMax = std::max(dX0, dX1); |
2544 | 2.33k | dYMin = std::min(dY0, dY1); |
2545 | 2.33k | dYMax = std::max(dY0, dY1); |
2546 | 2.33k | return 0; |
2547 | 2.33k | } |
2548 | 2.33k | } |
2549 | | |
2550 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2551 | 0 | "GetBounds() can be called only after dataset has been opened."); |
2552 | 0 | return -1; |
2553 | 2.33k | } |
2554 | | |
2555 | | /********************************************************************** |
2556 | | * TABFile::IGetExtent() |
2557 | | * |
2558 | | * Fetch extent of the data currently stored in the dataset. |
2559 | | * |
2560 | | * The bForce flag has no effect on TAB files since that value is |
2561 | | * always in the header. |
2562 | | * |
2563 | | * Returns OGRERR_NONE/OGRRERR_FAILURE. |
2564 | | **********************************************************************/ |
2565 | | OGRErr TABFile::IGetExtent(int /*iGeomField*/, OGREnvelope *psExtent, |
2566 | | bool /* bForce */) |
2567 | 0 | { |
2568 | 0 | TABMAPHeaderBlock *poHeader = nullptr; |
2569 | |
|
2570 | 0 | if (m_poMAPFile && (poHeader = m_poMAPFile->GetHeaderBlock()) != nullptr && |
2571 | 0 | GetGeomType() != wkbNone) |
2572 | 0 | { |
2573 | 0 | double dX0 = 0.0; |
2574 | 0 | double dX1 = 0.0; |
2575 | 0 | double dY0 = 0.0; |
2576 | 0 | double dY1 = 0.0; |
2577 | | /*------------------------------------------------------------- |
2578 | | * Fetch extent of the data from the .map header block |
2579 | | * this value is different from the projection bounds. |
2580 | | *------------------------------------------------------------*/ |
2581 | 0 | m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin, dX0, |
2582 | 0 | dY0); |
2583 | 0 | m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax, dX1, |
2584 | 0 | dY1); |
2585 | | |
2586 | | /*------------------------------------------------------------- |
2587 | | * ... and make sure that Min < Max |
2588 | | *------------------------------------------------------------*/ |
2589 | 0 | psExtent->MinX = std::min(dX0, dX1); |
2590 | 0 | psExtent->MaxX = std::max(dX0, dX1); |
2591 | 0 | psExtent->MinY = std::min(dY0, dY1); |
2592 | 0 | psExtent->MaxY = std::max(dY0, dY1); |
2593 | |
|
2594 | 0 | return OGRERR_NONE; |
2595 | 0 | } |
2596 | | |
2597 | 0 | return OGRERR_FAILURE; |
2598 | 0 | } |
2599 | | |
2600 | | /********************************************************************** |
2601 | | * TABFile::GetFeatureCountByType() |
2602 | | * |
2603 | | * Return number of features of each type. |
2604 | | * |
2605 | | * Note that the sum of the 4 returned values may be different from |
2606 | | * the total number of features since features with NONE geometry |
2607 | | * are not taken into account here. |
2608 | | * |
2609 | | * Note: the bForce flag has nmo effect on .TAB files since the info |
2610 | | * is always in the header. |
2611 | | * |
2612 | | * Returns 0 on success, or silently returns -1 (with no error) if this |
2613 | | * information is not available. |
2614 | | **********************************************************************/ |
2615 | | int TABFile::GetFeatureCountByType(int &numPoints, int &numLines, |
2616 | | int &numRegions, int &numTexts, |
2617 | | GBool /* bForce = TRUE*/) |
2618 | 1.87k | { |
2619 | 1.87k | TABMAPHeaderBlock *poHeader = nullptr; |
2620 | | |
2621 | 1.87k | if (m_poMAPFile && (poHeader = m_poMAPFile->GetHeaderBlock()) != nullptr) |
2622 | 1.87k | { |
2623 | 1.87k | numPoints = poHeader->m_numPointObjects; |
2624 | 1.87k | numLines = poHeader->m_numLineObjects; |
2625 | 1.87k | numRegions = poHeader->m_numRegionObjects; |
2626 | 1.87k | numTexts = poHeader->m_numTextObjects; |
2627 | 1.87k | } |
2628 | 0 | else |
2629 | 0 | { |
2630 | 0 | numPoints = numLines = numRegions = numTexts = 0; |
2631 | 0 | return -1; |
2632 | 0 | } |
2633 | | |
2634 | 1.87k | return 0; |
2635 | 1.87k | } |
2636 | | |
2637 | | /********************************************************************** |
2638 | | * TABFile::SetMIFCoordSys() |
2639 | | * |
2640 | | * Set projection for a new file using a MIF coordsys string. |
2641 | | * |
2642 | | * This function must be called after creating a new dataset and before any |
2643 | | * feature can be written to it. |
2644 | | * |
2645 | | * Returns 0 on success, -1 on error. |
2646 | | **********************************************************************/ |
2647 | | int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys) |
2648 | 0 | { |
2649 | 0 | if (m_eAccessMode != TABWrite) |
2650 | 0 | { |
2651 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2652 | 0 | "SetMIFCoordSys() can be used only with Write access."); |
2653 | 0 | return -1; |
2654 | 0 | } |
2655 | | |
2656 | | /*----------------------------------------------------------------- |
2657 | | * Check that dataset has been created but no feature set yet. |
2658 | | *----------------------------------------------------------------*/ |
2659 | 0 | if (m_poMAPFile && m_nLastFeatureId < 1) |
2660 | 0 | { |
2661 | 0 | OGRSpatialReference *poSpatialRef = |
2662 | 0 | MITABCoordSys2SpatialRef(pszMIFCoordSys); |
2663 | |
|
2664 | 0 | if (poSpatialRef) |
2665 | 0 | { |
2666 | 0 | double dXMin = 0.0; |
2667 | 0 | double dYMin = 0.0; |
2668 | 0 | double dXMax = 0.0; |
2669 | 0 | double dYMax = 0.0; |
2670 | 0 | if (SetSpatialRef(poSpatialRef) == 0) |
2671 | 0 | { |
2672 | 0 | if (MITABExtractCoordSysBounds(pszMIFCoordSys, dXMin, dYMin, |
2673 | 0 | dXMax, dYMax)) |
2674 | 0 | { |
2675 | | // If the coordsys string contains bounds, then use them |
2676 | 0 | if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0) |
2677 | 0 | { |
2678 | | // Failed Setting Bounds... an error should have |
2679 | | // been already reported. |
2680 | 0 | return -1; |
2681 | 0 | } |
2682 | 0 | } |
2683 | 0 | } |
2684 | 0 | else |
2685 | 0 | { |
2686 | | // Failed setting poSpatialRef... and error should have |
2687 | | // been reported. |
2688 | 0 | return -1; |
2689 | 0 | } |
2690 | | |
2691 | | // Release our handle on poSpatialRef |
2692 | 0 | if (poSpatialRef->Dereference() == 0) |
2693 | 0 | delete poSpatialRef; |
2694 | 0 | } |
2695 | 0 | } |
2696 | 0 | else |
2697 | 0 | { |
2698 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
2699 | 0 | "SetMIFCoordSys() can be called only after dataset has been " |
2700 | 0 | "created and before any feature is set."); |
2701 | 0 | return -1; |
2702 | 0 | } |
2703 | | |
2704 | 0 | return 0; |
2705 | 0 | } |
2706 | | |
2707 | | /********************************************************************** |
2708 | | * TABFile::SetProjInfo() |
2709 | | * |
2710 | | * Set projection for a new file using a TABProjInfo structure. |
2711 | | * |
2712 | | * This function must be called after creating a new dataset and before any |
2713 | | * feature can be written to it. |
2714 | | * |
2715 | | * This call will also trigger a lookup of default bounds for the specified |
2716 | | * projection (except nonearth), and reset the m_bBoundsValid flag. |
2717 | | * |
2718 | | * Returns 0 on success, -1 on error. |
2719 | | **********************************************************************/ |
2720 | | int TABFile::SetProjInfo(TABProjInfo *poPI) |
2721 | 16 | { |
2722 | 16 | if (m_eAccessMode != TABWrite) |
2723 | 0 | { |
2724 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
2725 | 0 | "SetProjInfo() can be used only with Write access."); |
2726 | 0 | return -1; |
2727 | 0 | } |
2728 | | |
2729 | | /*----------------------------------------------------------------- |
2730 | | * Lookup default bounds and reset m_bBoundsSet flag |
2731 | | *----------------------------------------------------------------*/ |
2732 | 16 | double dXMin; |
2733 | 16 | double dYMin; |
2734 | 16 | double dXMax; |
2735 | 16 | double dYMax; |
2736 | | |
2737 | 16 | m_bBoundsSet = FALSE; |
2738 | 16 | if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax)) |
2739 | 1 | { |
2740 | 1 | SetBounds(dXMin, dYMin, dXMax, dYMax); |
2741 | 1 | } |
2742 | | |
2743 | | /*----------------------------------------------------------------- |
2744 | | * Check that dataset has been created but no feature set yet. |
2745 | | *----------------------------------------------------------------*/ |
2746 | 16 | if (m_poMAPFile && m_nLastFeatureId < 1) |
2747 | 16 | { |
2748 | 16 | if (m_poMAPFile->GetHeaderBlock()->SetProjInfo(poPI) != 0) |
2749 | 0 | return -1; |
2750 | 16 | } |
2751 | 0 | else |
2752 | 0 | { |
2753 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
2754 | 0 | "SetProjInfo() can be called only after dataset has been " |
2755 | 0 | "created and before any feature is set."); |
2756 | 0 | return -1; |
2757 | 0 | } |
2758 | | |
2759 | 16 | return 0; |
2760 | 16 | } |
2761 | | |
2762 | | /************************************************************************/ |
2763 | | /* DeleteField() */ |
2764 | | /************************************************************************/ |
2765 | | |
2766 | | OGRErr TABFile::DeleteField(int iField) |
2767 | 0 | { |
2768 | 0 | if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField)) |
2769 | 0 | { |
2770 | 0 | CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY, |
2771 | 0 | "DeleteField"); |
2772 | 0 | return OGRERR_FAILURE; |
2773 | 0 | } |
2774 | | |
2775 | 0 | if (iField < 0 || iField >= m_poDefn->GetFieldCount()) |
2776 | 0 | { |
2777 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index"); |
2778 | 0 | return OGRERR_FAILURE; |
2779 | 0 | } |
2780 | | |
2781 | 0 | if (m_poDATFile->DeleteField(iField) == 0) |
2782 | 0 | { |
2783 | 0 | m_bNeedTABRewrite = TRUE; |
2784 | 0 | m_oSetFields.erase( |
2785 | 0 | CPLString(m_poDefn->GetFieldDefn(iField)->GetNameRef()).toupper()); |
2786 | | |
2787 | | /* Delete from the array of indexed fields */ |
2788 | 0 | if (iField < m_poDefn->GetFieldCount() - 1) |
2789 | 0 | { |
2790 | 0 | memmove(m_panIndexNo + iField, m_panIndexNo + iField + 1, |
2791 | 0 | (m_poDefn->GetFieldCount() - 1 - iField) * sizeof(int)); |
2792 | 0 | } |
2793 | |
|
2794 | 0 | whileUnsealing(m_poDefn)->DeleteFieldDefn(iField); |
2795 | |
|
2796 | 0 | if (m_eAccessMode == TABReadWrite) |
2797 | 0 | WriteTABFile(); |
2798 | |
|
2799 | 0 | return OGRERR_NONE; |
2800 | 0 | } |
2801 | 0 | else |
2802 | 0 | return OGRERR_FAILURE; |
2803 | 0 | } |
2804 | | |
2805 | | /************************************************************************/ |
2806 | | /* ReorderFields() */ |
2807 | | /************************************************************************/ |
2808 | | |
2809 | | OGRErr TABFile::ReorderFields(int *panMap) |
2810 | 0 | { |
2811 | 0 | if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField)) |
2812 | 0 | { |
2813 | 0 | CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY, |
2814 | 0 | "ReorderFields"); |
2815 | 0 | return OGRERR_FAILURE; |
2816 | 0 | } |
2817 | 0 | if (m_poDefn->GetFieldCount() == 0) |
2818 | 0 | return OGRERR_NONE; |
2819 | | |
2820 | 0 | OGRErr eErr = OGRCheckPermutation(panMap, m_poDefn->GetFieldCount()); |
2821 | 0 | if (eErr != OGRERR_NONE) |
2822 | 0 | return eErr; |
2823 | | |
2824 | 0 | if (m_poDATFile->ReorderFields(panMap) == 0) |
2825 | 0 | { |
2826 | 0 | m_bNeedTABRewrite = TRUE; |
2827 | |
|
2828 | 0 | int *panNewIndexedField = static_cast<int *>( |
2829 | 0 | CPLMalloc(sizeof(int) * m_poDefn->GetFieldCount())); |
2830 | 0 | for (int i = 0; i < m_poDefn->GetFieldCount(); i++) |
2831 | 0 | { |
2832 | 0 | panNewIndexedField[i] = m_panIndexNo[panMap[i]]; |
2833 | 0 | } |
2834 | 0 | CPLFree(m_panIndexNo); |
2835 | 0 | m_panIndexNo = panNewIndexedField; |
2836 | |
|
2837 | 0 | whileUnsealing(m_poDefn)->ReorderFieldDefns(panMap); |
2838 | |
|
2839 | 0 | if (m_eAccessMode == TABReadWrite) |
2840 | 0 | WriteTABFile(); |
2841 | |
|
2842 | 0 | return OGRERR_NONE; |
2843 | 0 | } |
2844 | 0 | else |
2845 | 0 | return OGRERR_FAILURE; |
2846 | 0 | } |
2847 | | |
2848 | | /************************************************************************/ |
2849 | | /* AlterFieldDefn() */ |
2850 | | /************************************************************************/ |
2851 | | |
2852 | | OGRErr TABFile::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn, |
2853 | | int nFlagsIn) |
2854 | 0 | { |
2855 | 0 | if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField)) |
2856 | 0 | { |
2857 | 0 | CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY, |
2858 | 0 | "AlterFieldDefn"); |
2859 | 0 | return OGRERR_FAILURE; |
2860 | 0 | } |
2861 | | |
2862 | 0 | if (iField < 0 || iField >= m_poDefn->GetFieldCount()) |
2863 | 0 | { |
2864 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index"); |
2865 | 0 | return OGRERR_FAILURE; |
2866 | 0 | } |
2867 | | |
2868 | 0 | OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField); |
2869 | 0 | if (m_poDATFile->AlterFieldDefn(iField, poFieldDefn, poNewFieldDefn, |
2870 | 0 | nFlagsIn) == 0) |
2871 | 0 | { |
2872 | 0 | m_bNeedTABRewrite = TRUE; |
2873 | |
|
2874 | 0 | auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer()); |
2875 | 0 | if ((nFlagsIn & ALTER_TYPE_FLAG) && |
2876 | 0 | poNewFieldDefn->GetType() != poFieldDefn->GetType()) |
2877 | 0 | { |
2878 | 0 | poFieldDefn->SetType(poNewFieldDefn->GetType()); |
2879 | 0 | } |
2880 | 0 | if (nFlagsIn & ALTER_NAME_FLAG) |
2881 | 0 | { |
2882 | 0 | m_oSetFields.erase(CPLString(poFieldDefn->GetNameRef()).toupper()); |
2883 | 0 | poFieldDefn->SetName(poNewFieldDefn->GetNameRef()); |
2884 | 0 | m_oSetFields.insert( |
2885 | 0 | CPLString(poNewFieldDefn->GetNameRef()).toupper()); |
2886 | 0 | } |
2887 | 0 | if (poFieldDefn->GetType() == OFTString) |
2888 | 0 | { |
2889 | 0 | poFieldDefn->SetWidth(m_poDATFile->GetFieldWidth(iField)); |
2890 | 0 | } |
2891 | 0 | else if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) |
2892 | 0 | { |
2893 | 0 | poFieldDefn->SetWidth(poNewFieldDefn->GetWidth()); |
2894 | 0 | poFieldDefn->SetPrecision(poNewFieldDefn->GetPrecision()); |
2895 | 0 | } |
2896 | | |
2897 | | // Take into account .dat limitations on width & precision to clamp |
2898 | | // what user might have specify |
2899 | 0 | int nWidth = 0; |
2900 | 0 | int nPrecision = 0; |
2901 | 0 | GetTABType(poFieldDefn, nullptr, &nWidth, &nPrecision); |
2902 | 0 | poFieldDefn->SetWidth(nWidth); |
2903 | 0 | poFieldDefn->SetPrecision(nPrecision); |
2904 | |
|
2905 | 0 | if (m_eAccessMode == TABReadWrite) |
2906 | 0 | WriteTABFile(); |
2907 | |
|
2908 | 0 | return OGRERR_NONE; |
2909 | 0 | } |
2910 | 0 | else |
2911 | 0 | return OGRERR_FAILURE; |
2912 | 0 | } |
2913 | | |
2914 | | /************************************************************************/ |
2915 | | /* SyncToDisk() */ |
2916 | | /************************************************************************/ |
2917 | | |
2918 | | OGRErr TABFile::SyncToDisk() |
2919 | 226 | { |
2920 | | /* Silently return */ |
2921 | 226 | if (m_eAccessMode == TABRead) |
2922 | 0 | return OGRERR_NONE; |
2923 | | |
2924 | 226 | OGRErr eErr = OGRERR_NONE; |
2925 | | |
2926 | | // This is a hack for Windows and VSIFFlushL() issue. See |
2927 | | // http://trac.osgeo.org/gdal/ticket/5556 |
2928 | 226 | CPLSetConfigOption("VSI_FLUSH", "TRUE"); |
2929 | | |
2930 | 226 | if (WriteTABFile() != 0) |
2931 | 0 | eErr = OGRERR_FAILURE; |
2932 | | |
2933 | 226 | if (m_poMAPFile->SyncToDisk() != 0) |
2934 | 0 | eErr = OGRERR_FAILURE; |
2935 | | |
2936 | 226 | if (m_poDATFile->SyncToDisk() != 0) |
2937 | 0 | eErr = OGRERR_FAILURE; |
2938 | | |
2939 | 226 | CPLSetConfigOption("VSI_FLUSH", nullptr); |
2940 | | |
2941 | 226 | return eErr; |
2942 | 226 | } |
2943 | | |
2944 | | /************************************************************************/ |
2945 | | /* TestCapability() */ |
2946 | | /************************************************************************/ |
2947 | | |
2948 | | int TABFile::TestCapability(const char *pszCap) const |
2949 | | |
2950 | 648 | { |
2951 | 648 | if (EQUAL(pszCap, OLCRandomRead)) |
2952 | 0 | return TRUE; |
2953 | | |
2954 | 648 | else if (EQUAL(pszCap, OLCSequentialWrite)) |
2955 | 0 | return m_eAccessMode != TABRead; |
2956 | | |
2957 | 648 | else if (EQUAL(pszCap, OLCRandomWrite)) |
2958 | 0 | return m_eAccessMode != TABRead; |
2959 | | |
2960 | 648 | else if (EQUAL(pszCap, OLCDeleteFeature)) |
2961 | 0 | return m_eAccessMode != TABRead; |
2962 | | |
2963 | 648 | else if (EQUAL(pszCap, OLCFastFeatureCount)) |
2964 | 0 | return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr; |
2965 | | |
2966 | 648 | else if (EQUAL(pszCap, OLCFastSpatialFilter)) |
2967 | 0 | return TRUE; |
2968 | | |
2969 | 648 | else if (EQUAL(pszCap, OLCFastGetExtent)) |
2970 | 0 | return TRUE; |
2971 | | |
2972 | 648 | else if (EQUAL(pszCap, OLCCreateField)) |
2973 | 0 | return m_eAccessMode != TABRead; |
2974 | | |
2975 | 648 | else if (EQUAL(pszCap, OLCDeleteField)) |
2976 | 0 | return m_eAccessMode != TABRead; |
2977 | | |
2978 | 648 | else if (EQUAL(pszCap, OLCReorderFields)) |
2979 | 0 | return m_eAccessMode != TABRead; |
2980 | | |
2981 | 648 | else if (EQUAL(pszCap, OLCAlterFieldDefn)) |
2982 | 0 | return m_eAccessMode != TABRead; |
2983 | | |
2984 | 648 | else if (EQUAL(pszCap, OLCStringsAsUTF8)) |
2985 | 0 | return TestUtf8Capability(); |
2986 | | |
2987 | 648 | else |
2988 | 648 | return FALSE; |
2989 | 648 | } |
2990 | | |
2991 | | /********************************************************************** |
2992 | | * TABFile::Dump() |
2993 | | * |
2994 | | * Dump block contents... available only in DEBUG mode. |
2995 | | **********************************************************************/ |
2996 | | #ifdef DEBUG |
2997 | | |
2998 | | void TABFile::Dump(FILE *fpOut /*=NULL*/) |
2999 | | { |
3000 | | if (fpOut == nullptr) |
3001 | | fpOut = stdout; |
3002 | | |
3003 | | fprintf(fpOut, "----- TABFile::Dump() -----\n"); |
3004 | | |
3005 | | if (m_poMAPFile == nullptr) |
3006 | | { |
3007 | | fprintf(fpOut, "File is not opened.\n"); |
3008 | | } |
3009 | | else |
3010 | | { |
3011 | | fprintf(fpOut, "File is opened: %s\n", m_pszFname); |
3012 | | fprintf(fpOut, "Associated TABLE file ...\n\n"); |
3013 | | m_poDATFile->Dump(fpOut); |
3014 | | fprintf(fpOut, "... end of TABLE file dump.\n\n"); |
3015 | | if (GetSpatialRef() != nullptr) |
3016 | | { |
3017 | | char *pszWKT = nullptr; |
3018 | | |
3019 | | GetSpatialRef()->exportToWkt(&pszWKT); |
3020 | | fprintf(fpOut, "SRS = %s\n", pszWKT); |
3021 | | CPLFree(pszWKT); |
3022 | | } |
3023 | | fprintf(fpOut, "Associated .MAP file ...\n\n"); |
3024 | | m_poMAPFile->Dump(fpOut); |
3025 | | fprintf(fpOut, "... end of .MAP file dump.\n\n"); |
3026 | | } |
3027 | | |
3028 | | fflush(fpOut); |
3029 | | } |
3030 | | |
3031 | | #endif // DEBUG |
3032 | | |
3033 | | /* |
3034 | | * SetMetadataItem() |
3035 | | */ |
3036 | | CPLErr TABFile::SetMetadataItem(const char *pszName, const char *pszValue, |
3037 | | const char *pszDomain) |
3038 | 2.55k | { |
3039 | 2.55k | if (EQUAL(DESCRIPTION_KEY, pszName) && EQUAL(pszDomain, "")) |
3040 | 226 | { |
3041 | 226 | if (m_eAccessMode == TABRead) |
3042 | 0 | { |
3043 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3044 | 0 | "Description will not save in TAB file in readonly mode."); |
3045 | 0 | } |
3046 | | |
3047 | 226 | m_bNeedTABRewrite = TRUE; |
3048 | 226 | std::shared_ptr<char> oEscapedString(EscapeString(pszValue), CPLFree); |
3049 | 226 | auto result = IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY, |
3050 | 226 | oEscapedString.get()); |
3051 | 226 | if (oEscapedString) |
3052 | 0 | { |
3053 | 0 | CPLDebug("MITAB", "Set description to '%s'", oEscapedString.get()); |
3054 | 0 | } |
3055 | 226 | return result; |
3056 | 226 | } |
3057 | 2.33k | return IMapInfoFile::SetMetadataItem(pszName, pszValue, pszDomain); |
3058 | 2.55k | } |
3059 | | |
3060 | | /********************************************************************** |
3061 | | * TABFile::GetSpatialRef() |
3062 | | * |
3063 | | * Returns a reference to an OGRSpatialReference for this dataset. |
3064 | | * If the projection parameters have not been parsed yet, then we will |
3065 | | * parse them before returning. |
3066 | | * |
3067 | | * The returned object is owned and maintained by this TABFile and |
3068 | | * should not be modified or freed by the caller. |
3069 | | * |
3070 | | * Returns NULL if the SpatialRef cannot be accessed. |
3071 | | **********************************************************************/ |
3072 | | const OGRSpatialReference *TABFile::GetSpatialRef() const |
3073 | 1.97k | { |
3074 | 1.97k | if (m_poMAPFile == nullptr) |
3075 | 0 | { |
3076 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
3077 | 0 | "GetSpatialRef() failed: file has not been opened yet."); |
3078 | 0 | return nullptr; |
3079 | 0 | } |
3080 | | |
3081 | 1.97k | if (GetGeomType() == wkbNone) |
3082 | 215 | return nullptr; |
3083 | | |
3084 | | /*----------------------------------------------------------------- |
3085 | | * If projection params have already been processed, just use them. |
3086 | | *----------------------------------------------------------------*/ |
3087 | 1.75k | if (m_poSpatialRef != nullptr) |
3088 | 622 | return m_poSpatialRef; |
3089 | | |
3090 | | /*----------------------------------------------------------------- |
3091 | | * Fetch the parameters from the header. |
3092 | | *----------------------------------------------------------------*/ |
3093 | 1.13k | TABProjInfo sTABProj; |
3094 | | |
3095 | 1.13k | TABMAPHeaderBlock *poHeader = nullptr; |
3096 | 1.13k | if ((poHeader = m_poMAPFile->GetHeaderBlock()) == nullptr || |
3097 | 1.13k | poHeader->GetProjInfo(&sTABProj) != 0) |
3098 | 0 | { |
3099 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
3100 | 0 | "GetSpatialRef() failed reading projection parameters."); |
3101 | 0 | return nullptr; |
3102 | 0 | } |
3103 | | |
3104 | 1.13k | m_poSpatialRef = TABFileGetSpatialRefFromTABProj(sTABProj); |
3105 | 1.13k | return m_poSpatialRef; |
3106 | 1.13k | } |
3107 | | |
3108 | | /********************************************************************** |
3109 | | * TABFile::SetSpatialRef() |
3110 | | * |
3111 | | * Set the OGRSpatialReference for this dataset. |
3112 | | * A reference to the OGRSpatialReference will be kept, and it will also |
3113 | | * be converted into a TABProjInfo to be stored in the .MAP header. |
3114 | | * |
3115 | | * Returns 0 on success, and -1 on error. |
3116 | | **********************************************************************/ |
3117 | | int TABFile::SetSpatialRef(OGRSpatialReference *poSpatialRef) |
3118 | 16 | { |
3119 | 16 | if (m_eAccessMode != TABWrite) |
3120 | 0 | { |
3121 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
3122 | 0 | "SetSpatialRef() can be used only with Write access."); |
3123 | 0 | return -1; |
3124 | 0 | } |
3125 | | |
3126 | 16 | if (m_poMAPFile == nullptr) |
3127 | 0 | { |
3128 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
3129 | 0 | "SetSpatialRef() failed: file has not been opened yet."); |
3130 | 0 | return -1; |
3131 | 0 | } |
3132 | | |
3133 | 16 | if (poSpatialRef == nullptr) |
3134 | 0 | { |
3135 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, |
3136 | 0 | "SetSpatialRef() failed: Called with NULL poSpatialRef."); |
3137 | 0 | return -1; |
3138 | 0 | } |
3139 | | |
3140 | | /*----------------------------------------------------------------- |
3141 | | * Keep a copy of the OGRSpatialReference... |
3142 | | * Note: we have to take the reference count into account... |
3143 | | *----------------------------------------------------------------*/ |
3144 | 16 | if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0) |
3145 | 0 | delete m_poSpatialRef; |
3146 | | |
3147 | 16 | m_poSpatialRef = poSpatialRef->Clone(); |
3148 | | |
3149 | 16 | TABProjInfo sTABProj; |
3150 | 16 | int nParamCount = 0; |
3151 | 16 | TABFileGetTABProjFromSpatialRef(poSpatialRef, sTABProj, nParamCount); |
3152 | | |
3153 | | /*----------------------------------------------------------------- |
3154 | | * Set the new parameters in the .MAP header. |
3155 | | * This will also trigger lookup of default bounds for the projection. |
3156 | | *----------------------------------------------------------------*/ |
3157 | 16 | if (SetProjInfo(&sTABProj) != 0) |
3158 | 0 | { |
3159 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
3160 | 0 | "SetSpatialRef() failed setting projection parameters."); |
3161 | 0 | return -1; |
3162 | 0 | } |
3163 | | |
3164 | 16 | return 0; |
3165 | 16 | } |