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