Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_miffile.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the MIDFile class.
7 : * To be used by external programs to handle reading/writing of
8 : * features from/to MID/MIF datasets.
9 : * Author: Stephane Villeneuve, stephane.v@videotron.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2003, Stephane Villeneuve
13 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
14 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
15 : *
16 : * SPDX-License-Identifier: MIT
17 : **********************************************************************/
18 :
19 : #include "cpl_port.h"
20 : #include "mitab.h"
21 :
22 : #include <cctype>
23 : #include <cstdio>
24 : #include <cstdlib>
25 : #include <cstring>
26 : #include <algorithm>
27 :
28 : #include "cpl_conv.h"
29 : #include "cpl_error.h"
30 : #include "cpl_string.h"
31 : #include "mitab_priv.h"
32 : #include "mitab_utils.h"
33 : #include "ogr_core.h"
34 : #include "ogr_feature.h"
35 : #include "ogr_spatialref.h"
36 : #include "ogrsf_frmts.h"
37 :
38 : /*=====================================================================
39 : * class MIFFile
40 : *====================================================================*/
41 :
42 : /**********************************************************************
43 : * MIFFile::MIFFile()
44 : *
45 : * Constructor.
46 : **********************************************************************/
47 962 : MIFFile::MIFFile(GDALDataset *poDS)
48 : : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead),
49 : m_nVersion(300),
50 : // Tab is default delimiter in MIF spec if not explicitly specified. Use
51 : // that by default for read mode. In write mode, we will use "," as
52 : // delimiter since it is more common than tab (we do this in Open())
53 1924 : m_pszDelimiter(CPLStrdup("\t")), m_pszUnique(nullptr),
54 : m_pszIndex(nullptr), m_pszCoordSys(nullptr), m_paeFieldType(nullptr),
55 : m_pabFieldIndexed(nullptr), m_pabFieldUnique(nullptr),
56 : m_dfXMultiplier(1.0), m_dfYMultiplier(1.0), m_dfXDisplacement(0.0),
57 : m_dfYDisplacement(0.0), m_dXMin(0), m_dYMin(0), m_dXMax(0), m_dYMax(0),
58 : m_bExtentsSet(FALSE), m_nPoints(0), m_nLines(0), m_nRegions(0),
59 : m_nTexts(0), m_nPreloadedId(0), m_poMIDFile(nullptr),
60 : m_poMIFFile(nullptr), m_poDefn(nullptr), m_poSpatialRef(nullptr),
61 : m_nFeatureCount(0), m_nWriteFeatureId(-1), m_nAttribute(0),
62 962 : m_bPreParsed(FALSE), m_bHeaderWrote(FALSE)
63 : {
64 962 : m_nCurFeatureId = 0;
65 962 : m_poCurFeature = nullptr;
66 962 : }
67 :
68 : /**********************************************************************
69 : * MIFFile::~MIFFile()
70 : *
71 : * Destructor.
72 : **********************************************************************/
73 1924 : MIFFile::~MIFFile()
74 : {
75 962 : MIFFile::Close();
76 1924 : }
77 :
78 : /**********************************************************************
79 : * MIFFile::Open()
80 : *
81 : * Returns 0 on success, -1 on error.
82 : **********************************************************************/
83 962 : int MIFFile::Open(const char *pszFname, TABAccess eAccess,
84 : GBool bTestOpenNoError /*=FALSE*/,
85 : const char *pszCharset /* = NULL */)
86 : {
87 962 : char *pszTmpFname = nullptr;
88 962 : int nFnameLen = 0;
89 :
90 962 : CPLErrorReset();
91 :
92 962 : if (m_poMIFFile)
93 : {
94 0 : CPLError(CE_Failure, CPLE_FileIO,
95 : "Open() failed: object already contains an open file");
96 :
97 0 : return -1;
98 : }
99 :
100 : /*-----------------------------------------------------------------
101 : * Validate access mode
102 : *----------------------------------------------------------------*/
103 962 : const char *pszAccess = nullptr;
104 962 : if (eAccess == TABRead)
105 : {
106 874 : m_eAccessMode = TABRead;
107 874 : pszAccess = "rt";
108 : }
109 88 : else if (eAccess == TABWrite)
110 : {
111 87 : m_eAccessMode = TABWrite;
112 87 : pszAccess = "wt";
113 :
114 : // In write mode, use "," as delimiter since it is more common than tab
115 87 : CPLFree(m_pszDelimiter);
116 87 : m_pszDelimiter = CPLStrdup(",");
117 : }
118 : else
119 : {
120 1 : if (!bTestOpenNoError)
121 0 : CPLError(CE_Failure, CPLE_FileIO,
122 : "Open() failed: access mode \"%d\" not supported",
123 : eAccess);
124 : else
125 1 : CPLErrorReset();
126 :
127 1 : return -1;
128 : }
129 :
130 : /*-----------------------------------------------------------------
131 : * Make sure filename has a .MIF or .MID extension...
132 : *----------------------------------------------------------------*/
133 961 : m_pszFname = CPLStrdup(pszFname);
134 961 : nFnameLen = static_cast<int>(strlen(m_pszFname));
135 961 : if (nFnameLen > 4 && (strcmp(m_pszFname + nFnameLen - 4, ".MID") == 0 ||
136 961 : strcmp(m_pszFname + nFnameLen - 4, ".MIF") == 0))
137 0 : strcpy(m_pszFname + nFnameLen - 4, ".MIF");
138 961 : else if (nFnameLen > 4 && (EQUAL(m_pszFname + nFnameLen - 4, ".mid") ||
139 961 : EQUAL(m_pszFname + nFnameLen - 4, ".mif")))
140 961 : strcpy(m_pszFname + nFnameLen - 4, ".mif");
141 : else
142 : {
143 0 : if (!bTestOpenNoError)
144 0 : CPLError(CE_Failure, CPLE_FileIO,
145 : "Open() failed for %s: invalid filename extension",
146 : m_pszFname);
147 : else
148 0 : CPLErrorReset();
149 :
150 0 : return -1;
151 : }
152 :
153 961 : pszTmpFname = CPLStrdup(m_pszFname);
154 :
155 : /*-----------------------------------------------------------------
156 : * Open .MIF file
157 : *----------------------------------------------------------------*/
158 :
159 : #ifndef _WIN32
160 : /*-----------------------------------------------------------------
161 : * On Unix, make sure extension uses the right cases
162 : * We do it even for write access because if a file with the same
163 : * extension already exists we want to overwrite it.
164 : *----------------------------------------------------------------*/
165 961 : TABAdjustFilenameExtension(pszTmpFname);
166 : #endif
167 :
168 961 : m_poMIFFile = new MIDDATAFile(CharsetToEncoding(pszCharset));
169 :
170 961 : if (m_poMIFFile->Open(pszTmpFname, pszAccess) != 0)
171 : {
172 0 : if (!bTestOpenNoError)
173 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unable to open %s.",
174 : pszTmpFname);
175 : else
176 0 : CPLErrorReset();
177 :
178 0 : CPLFree(pszTmpFname);
179 0 : Close();
180 :
181 0 : return -1;
182 : }
183 :
184 : /*-----------------------------------------------------------------
185 : * Read MIF File Header
186 : *----------------------------------------------------------------*/
187 961 : int bIsEmpty = FALSE;
188 961 : if (m_eAccessMode == TABRead && ParseMIFHeader(&bIsEmpty) != 0)
189 : {
190 58 : Close();
191 :
192 58 : if (!bTestOpenNoError)
193 0 : CPLError(CE_Failure, CPLE_NotSupported,
194 : "Failed parsing header in %s.", m_pszFname);
195 : else
196 58 : CPLErrorReset();
197 :
198 58 : CPLFree(pszTmpFname);
199 :
200 58 : return -1;
201 : }
202 :
203 903 : if (m_nAttribute > 0 || m_eAccessMode == TABWrite)
204 : {
205 : /*-----------------------------------------------------------------
206 : * Open .MID file
207 : *----------------------------------------------------------------*/
208 185 : if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".MIF") == 0)
209 0 : strcpy(pszTmpFname + nFnameLen - 4, ".MID");
210 : else
211 185 : strcpy(pszTmpFname + nFnameLen - 4, ".mid");
212 :
213 : #ifndef _WIN32
214 185 : TABAdjustFilenameExtension(pszTmpFname);
215 : #endif
216 :
217 185 : m_poMIDFile = new MIDDATAFile("");
218 185 : if (eAccess == TABRead || eAccess == TABReadWrite)
219 : {
220 98 : m_poMIDFile->SetEncoding(CharsetToEncoding(GetCharset()));
221 : }
222 87 : else if (eAccess == TABWrite)
223 : {
224 87 : m_poMIDFile->SetEncoding(CharsetToEncoding(pszCharset));
225 : }
226 :
227 185 : if (m_poMIDFile->Open(pszTmpFname, pszAccess) != 0)
228 : {
229 4 : if (m_eAccessMode == TABWrite)
230 : {
231 0 : if (!bTestOpenNoError)
232 0 : CPLError(CE_Failure, CPLE_NotSupported,
233 : "Unable to open %s.", pszTmpFname);
234 : else
235 0 : CPLErrorReset();
236 :
237 0 : CPLFree(pszTmpFname);
238 0 : Close();
239 :
240 0 : return -1;
241 : }
242 : else
243 : {
244 4 : CPLDebug("MITAB",
245 : "%s is not found, although %d attributes are declared",
246 : pszTmpFname, m_nAttribute);
247 4 : delete m_poMIDFile;
248 4 : m_poMIDFile = nullptr;
249 : }
250 : }
251 : }
252 :
253 903 : CPLFree(pszTmpFname);
254 903 : pszTmpFname = nullptr;
255 :
256 : /*-----------------------------------------------------------------
257 : * In write access, set some defaults
258 : *----------------------------------------------------------------*/
259 903 : if (m_eAccessMode == TABWrite)
260 : {
261 87 : m_nVersion = 300;
262 87 : if (pszCharset != nullptr)
263 87 : SetCharset(pszCharset);
264 : else
265 0 : SetCharset("Neutral");
266 : }
267 :
268 903 : m_poMIFFile->SetTranslation(m_dfXMultiplier, m_dfYMultiplier,
269 : m_dfXDisplacement, m_dfYDisplacement);
270 903 : if (m_poMIDFile != nullptr)
271 181 : m_poMIDFile->SetTranslation(m_dfXMultiplier, m_dfYMultiplier,
272 : m_dfXDisplacement, m_dfYDisplacement);
273 903 : m_poMIFFile->SetDelimiter(m_pszDelimiter);
274 903 : if (m_poMIDFile != nullptr)
275 181 : m_poMIDFile->SetDelimiter(m_pszDelimiter);
276 :
277 : /*-------------------------------------------------------------
278 : * Set geometry type if the geometry objects are uniform.
279 : *------------------------------------------------------------*/
280 903 : int numPoints = 0, numRegions = 0, numTexts = 0, numLines = 0;
281 :
282 903 : if (GetFeatureCountByType(numPoints, numLines, numRegions, numTexts,
283 903 : FALSE) == 0)
284 : {
285 0 : numPoints += numTexts;
286 0 : if (numPoints > 0 && numLines == 0 && numRegions == 0)
287 0 : m_poDefn->SetGeomType(wkbPoint);
288 0 : else if (numPoints == 0 && numLines > 0 && numRegions == 0)
289 0 : m_poDefn->SetGeomType(wkbLineString);
290 : else
291 : {
292 : /* we leave it unknown indicating a mixture */
293 : }
294 : }
295 :
296 : /* A newly created layer should have OGRFeatureDefn */
297 903 : if (m_poDefn == nullptr)
298 : {
299 87 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
300 87 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
301 87 : CPLFree(pszFeatureClassName);
302 : // Ref count defaults to 0... set it to 1
303 87 : m_poDefn->Reference();
304 87 : m_poDefn->Seal(/* bSealFields = */ true);
305 : }
306 :
307 903 : return 0;
308 : }
309 :
310 : /**********************************************************************
311 : * MIFFile::ParseMIFHeader()
312 : *
313 : * Scan the header of a MIF file, and store any useful information into
314 : * class members. The main piece of information being the fields
315 : * definition that we use to build the OGRFeatureDefn for this file.
316 : *
317 : * This private method should be used only during the Open() call.
318 : *
319 : * Returns 0 on success, -1 on error.
320 : **********************************************************************/
321 874 : int MIFFile::ParseMIFHeader(int *pbIsEmpty)
322 : {
323 874 : *pbIsEmpty = FALSE;
324 :
325 874 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
326 874 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
327 874 : CPLFree(pszFeatureClassName);
328 : // Ref count defaults to 0... set it to 1
329 874 : m_poDefn->Reference();
330 874 : m_poDefn->Seal(/* bSealFields = */ true);
331 :
332 874 : if (m_eAccessMode != TABRead)
333 : {
334 0 : CPLError(CE_Failure, CPLE_NotSupported,
335 : "ParseMIDFile() can be used only with Read access.");
336 0 : return -1;
337 : }
338 :
339 : /*-----------------------------------------------------------------
340 : * Parse header until we find the "Data" line
341 : *----------------------------------------------------------------*/
342 874 : char **papszToken = nullptr;
343 874 : GBool bColumns = FALSE;
344 874 : GBool bAllColumnsRead = FALSE;
345 874 : int nColumns = 0;
346 874 : GBool bCoordSys = FALSE;
347 1748 : CPLString osCoordSys;
348 874 : int nLineCount = 0;
349 :
350 874 : const char *pszLine = nullptr;
351 5360 : while (((pszLine = m_poMIFFile->GetLine()) != nullptr) &&
352 824 : ((bAllColumnsRead == FALSE) || !STARTS_WITH_CI(pszLine, "Data")))
353 : {
354 3662 : nLineCount++;
355 3662 : if (nLineCount == 100000)
356 : {
357 : // Arbitrary threshold. The number of lines must be at least as big
358 : // as the number of fields we want to support.
359 0 : CPLError(CE_Failure, CPLE_NotSupported,
360 : "Too many lines in MIF header");
361 0 : return -1;
362 : }
363 :
364 3662 : if (bColumns == TRUE && nColumns > 0)
365 : {
366 171 : if (AddFields(pszLine) == 0)
367 : {
368 171 : nColumns--;
369 171 : if (nColumns == 0)
370 : {
371 98 : bAllColumnsRead = TRUE;
372 98 : bColumns = FALSE;
373 : }
374 : }
375 : else
376 : {
377 0 : bColumns = FALSE;
378 : }
379 : }
380 3491 : else if (STARTS_WITH_CI(pszLine, "VERSION"))
381 : {
382 : papszToken =
383 867 : CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
384 867 : bColumns = FALSE;
385 867 : bCoordSys = FALSE;
386 867 : if (CSLCount(papszToken) == 2)
387 865 : m_nVersion = atoi(papszToken[1]);
388 :
389 867 : CSLDestroy(papszToken);
390 : }
391 2624 : else if (STARTS_WITH_CI(pszLine, "CHARSET"))
392 : {
393 : papszToken =
394 855 : CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
395 855 : bColumns = FALSE;
396 855 : bCoordSys = FALSE;
397 :
398 855 : if (CSLCount(papszToken) == 2)
399 : {
400 852 : SetCharset(papszToken[1]);
401 : }
402 855 : CSLDestroy(papszToken);
403 : }
404 1769 : else if (STARTS_WITH_CI(pszLine, "DELIMITER"))
405 : {
406 : papszToken =
407 833 : CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
408 833 : bColumns = FALSE;
409 833 : bCoordSys = FALSE;
410 :
411 833 : if (CSLCount(papszToken) == 2)
412 : {
413 830 : CPLFree(m_pszDelimiter);
414 830 : m_pszDelimiter = CPLStrdup(papszToken[1]);
415 : }
416 833 : CSLDestroy(papszToken);
417 : }
418 936 : else if (m_pszUnique == nullptr && STARTS_WITH_CI(pszLine, "UNIQUE"))
419 : {
420 0 : bColumns = FALSE;
421 0 : bCoordSys = FALSE;
422 :
423 0 : m_pszUnique = CPLStrdup(pszLine + 6);
424 : }
425 936 : else if (m_pszIndex == nullptr && STARTS_WITH_CI(pszLine, "INDEX"))
426 : {
427 1 : bColumns = FALSE;
428 1 : bCoordSys = FALSE;
429 :
430 1 : m_pszIndex = CPLStrdup(pszLine + 5);
431 : }
432 1013 : else if (osCoordSys.empty() && STARTS_WITH_CI(pszLine, "COORDSYS") &&
433 78 : CPLStrnlen(pszLine, 9) >= 9)
434 : {
435 78 : bCoordSys = TRUE;
436 78 : osCoordSys = pszLine + 9;
437 : }
438 857 : else if (STARTS_WITH_CI(pszLine, "TRANSFORM"))
439 : {
440 0 : papszToken = CSLTokenizeStringComplex(pszLine, " ,\t", TRUE, FALSE);
441 0 : bColumns = FALSE;
442 0 : bCoordSys = FALSE;
443 :
444 0 : if (CSLCount(papszToken) == 5)
445 : {
446 0 : m_dfXMultiplier = CPLAtof(papszToken[1]);
447 0 : m_dfYMultiplier = CPLAtof(papszToken[2]);
448 0 : m_dfXDisplacement = CPLAtof(papszToken[3]);
449 0 : m_dfYDisplacement = CPLAtof(papszToken[4]);
450 :
451 0 : if (m_dfXMultiplier == 0.0)
452 0 : m_dfXMultiplier = 1.0;
453 0 : if (m_dfYMultiplier == 0.0)
454 0 : m_dfYMultiplier = 1.0;
455 : }
456 0 : CSLDestroy(papszToken);
457 : }
458 857 : else if (STARTS_WITH_CI(pszLine, "COLUMNS"))
459 : {
460 : papszToken =
461 823 : CSLTokenizeStringComplex(pszLine, " ()\t", TRUE, FALSE);
462 823 : bCoordSys = FALSE;
463 823 : bColumns = TRUE;
464 823 : if (CSLCount(papszToken) == 2)
465 : {
466 821 : nColumns = atoi(papszToken[1]);
467 821 : m_nAttribute = nColumns;
468 821 : if (nColumns == 0)
469 : {
470 : // Permit to 0 columns
471 723 : bAllColumnsRead = TRUE;
472 723 : bColumns = FALSE;
473 : }
474 : }
475 : else
476 : {
477 2 : bColumns = FALSE;
478 2 : m_nAttribute = 0;
479 : }
480 823 : CSLDestroy(papszToken);
481 : }
482 34 : else if (bCoordSys == TRUE)
483 : {
484 0 : if (osCoordSys.size() > 10000) // Arbitrary threshold
485 : {
486 0 : CPLError(CE_Failure, CPLE_NotSupported,
487 : "COORDSYS value too long");
488 0 : return -1;
489 : }
490 0 : osCoordSys += ' ';
491 0 : osCoordSys += pszLine;
492 : }
493 : }
494 :
495 874 : if (!osCoordSys.empty())
496 : {
497 78 : m_pszCoordSys = CPLStrdup(osCoordSys);
498 :
499 : // Extract bounds if present
500 : char **papszFields =
501 78 : CSLTokenizeStringComplex(osCoordSys, " ,()\t", TRUE, FALSE);
502 78 : int iBounds = CSLFindString(papszFields, "Bounds");
503 78 : if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
504 : {
505 16 : m_dXMin = CPLAtof(papszFields[++iBounds]);
506 16 : m_dYMin = CPLAtof(papszFields[++iBounds]);
507 16 : m_dXMax = CPLAtof(papszFields[++iBounds]);
508 16 : m_dYMax = CPLAtof(papszFields[++iBounds]);
509 16 : m_bBoundsSet = TRUE;
510 : }
511 78 : CSLDestroy(papszFields);
512 : }
513 :
514 874 : if (!bAllColumnsRead)
515 : {
516 53 : CPLError(CE_Failure, CPLE_NotSupported,
517 : "COLUMNS keyword not found or invalid number of columns read "
518 : "in %s. File may be corrupt.",
519 : m_pszFname);
520 53 : return -1;
521 : }
522 :
523 1637 : if (m_poMIFFile->GetLastLine() == nullptr ||
524 816 : STARTS_WITH_CI(m_poMIFFile->GetLastLine(), "DATA") == FALSE)
525 : {
526 5 : CPLError(CE_Failure, CPLE_NotSupported,
527 : "DATA keyword not found in %s. File may be corrupt.",
528 : m_pszFname);
529 5 : return -1;
530 : }
531 :
532 : /*-----------------------------------------------------------------
533 : * Move pointer to first line of first object
534 : *----------------------------------------------------------------*/
535 2443 : while (((pszLine = m_poMIFFile->GetLine()) != nullptr) &&
536 1617 : m_poMIFFile->IsValidFeature(pszLine) == FALSE)
537 : ;
538 :
539 816 : *pbIsEmpty = (pszLine == nullptr);
540 :
541 : /*-----------------------------------------------------------------
542 : * Check for Unique and Indexed flags
543 : *----------------------------------------------------------------*/
544 816 : if (m_pszIndex)
545 : {
546 1 : papszToken = CSLTokenizeStringComplex(m_pszIndex, " ,\t", TRUE, FALSE);
547 3 : for (int i = 0; papszToken && papszToken[i]; i++)
548 : {
549 2 : int nVal = atoi(papszToken[i]);
550 2 : if (nVal > 0 && nVal <= m_poDefn->GetFieldCount())
551 2 : m_pabFieldIndexed[nVal - 1] = TRUE;
552 : }
553 1 : CSLDestroy(papszToken);
554 : }
555 :
556 816 : if (m_pszUnique)
557 : {
558 0 : papszToken = CSLTokenizeStringComplex(m_pszUnique, " ,\t", TRUE, FALSE);
559 0 : for (int i = 0; papszToken && papszToken[i]; i++)
560 : {
561 0 : int nVal = atoi(papszToken[i]);
562 0 : if (nVal > 0 && nVal <= m_poDefn->GetFieldCount())
563 0 : m_pabFieldUnique[nVal - 1] = TRUE;
564 : }
565 0 : CSLDestroy(papszToken);
566 : }
567 :
568 816 : return 0;
569 : }
570 :
571 : /************************************************************************/
572 : /* AddFields() */
573 : /************************************************************************/
574 :
575 171 : int MIFFile::AddFields(const char *pszLine)
576 : {
577 171 : int nStatus = 0;
578 :
579 171 : CPLAssert(m_bHeaderWrote == FALSE);
580 : char **papszToken =
581 171 : CSLTokenizeStringComplex(pszLine, " (,)\t", TRUE, FALSE);
582 171 : int numTok = CSLCount(papszToken);
583 :
584 342 : CPLString osFieldName;
585 171 : if (numTok > 0)
586 : {
587 171 : osFieldName = papszToken[0];
588 171 : if (strlen(GetEncoding()) > 0)
589 : {
590 68 : osFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
591 : }
592 : }
593 :
594 171 : if (numTok >= 3 && EQUAL(papszToken[1], "char"))
595 : {
596 : /*-------------------------------------------------
597 : * CHAR type
598 : *------------------------------------------------*/
599 104 : nStatus = AddFieldNative(osFieldName, TABFChar, atoi(papszToken[2]));
600 : }
601 67 : else if (numTok >= 2 && EQUAL(papszToken[1], "integer"))
602 : {
603 35 : if (numTok == 2)
604 : {
605 : /*-------------------------------------------------
606 : * INTEGER type without a specified width
607 : *------------------------------------------------*/
608 35 : nStatus = AddFieldNative(osFieldName, TABFInteger);
609 : }
610 : else /* if (numTok > 2) */
611 : {
612 : /*-------------------------------------------------
613 : * INTEGER type with a specified width
614 : *------------------------------------------------*/
615 : nStatus =
616 0 : AddFieldNative(osFieldName, TABFInteger, atoi(papszToken[2]));
617 : }
618 : }
619 32 : else if (numTok >= 2 && EQUAL(papszToken[1], "smallint"))
620 : {
621 1 : if (numTok == 2)
622 : {
623 : /*-------------------------------------------------
624 : * SMALLINT type without a specified width
625 : *------------------------------------------------*/
626 1 : nStatus = AddFieldNative(osFieldName, TABFSmallInt);
627 : }
628 : else /* if (numTok > 2) */
629 : {
630 : /*-------------------------------------------------
631 : * SMALLINT type with a specified width
632 : *------------------------------------------------*/
633 : nStatus =
634 0 : AddFieldNative(osFieldName, TABFSmallInt, atoi(papszToken[2]));
635 : }
636 : }
637 31 : else if (numTok >= 2 && EQUAL(papszToken[1], "largeint"))
638 : {
639 2 : if (numTok == 2)
640 : {
641 : /*-------------------------------------------------
642 : * LargeInt type without a specified width
643 : *------------------------------------------------*/
644 2 : nStatus = AddFieldNative(osFieldName, TABFLargeInt);
645 : }
646 : else /* if (numTok > 2) */
647 : {
648 : /*-------------------------------------------------
649 : * LargeInt type with a specified width
650 : *------------------------------------------------*/
651 : nStatus =
652 0 : AddFieldNative(osFieldName, TABFLargeInt, atoi(papszToken[2]));
653 : }
654 : }
655 29 : else if (numTok >= 4 && EQUAL(papszToken[1], "decimal"))
656 : {
657 : /*-------------------------------------------------
658 : * DECIMAL type
659 : *------------------------------------------------*/
660 4 : nStatus = AddFieldNative(osFieldName, TABFDecimal, atoi(papszToken[2]),
661 4 : atoi(papszToken[3]));
662 : }
663 25 : else if (numTok >= 2 && EQUAL(papszToken[1], "float"))
664 : {
665 : /*-------------------------------------------------
666 : * FLOAT type
667 : *------------------------------------------------*/
668 16 : nStatus = AddFieldNative(osFieldName, TABFFloat);
669 : }
670 9 : else if (numTok >= 2 && EQUAL(papszToken[1], "date"))
671 : {
672 : /*-------------------------------------------------
673 : * DATE type (returned as a string: "DD/MM/YYYY" or "YYYYMMDD")
674 : *------------------------------------------------*/
675 2 : nStatus = AddFieldNative(osFieldName, TABFDate);
676 : }
677 7 : else if (numTok >= 2 && EQUAL(papszToken[1], "time"))
678 : {
679 : /*-------------------------------------------------
680 : * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm")
681 : *------------------------------------------------*/
682 2 : nStatus = AddFieldNative(osFieldName, TABFTime);
683 : }
684 5 : else if (numTok >= 2 && EQUAL(papszToken[1], "datetime"))
685 : {
686 : /*-------------------------------------------------
687 : * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS",
688 : * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm")
689 : *------------------------------------------------*/
690 2 : nStatus = AddFieldNative(osFieldName, TABFDateTime);
691 : }
692 3 : else if (numTok >= 2 && EQUAL(papszToken[1], "logical"))
693 : {
694 : /*-------------------------------------------------
695 : * LOGICAL type (value "T" or "F")
696 : *------------------------------------------------*/
697 3 : nStatus = AddFieldNative(osFieldName, TABFLogical);
698 : }
699 : else
700 0 : nStatus = -1; // Unrecognized field type or line corrupt
701 :
702 171 : CSLDestroy(papszToken);
703 171 : papszToken = nullptr;
704 :
705 171 : if (nStatus != 0)
706 : {
707 0 : CPLError(CE_Failure, CPLE_FileIO,
708 : "Failed to parse field definition in file %s", m_pszFname);
709 0 : return -1;
710 : }
711 :
712 171 : return 0;
713 : }
714 :
715 : /************************************************************************/
716 : /* GetFeatureCount() */
717 : /************************************************************************/
718 :
719 15 : GIntBig MIFFile::GetFeatureCount(int bForce)
720 : {
721 :
722 15 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
723 6 : return OGRLayer::GetFeatureCount(bForce);
724 : else
725 : {
726 9 : if (bForce == TRUE)
727 9 : PreParseFile();
728 :
729 9 : if (m_bPreParsed)
730 9 : return m_nFeatureCount;
731 : else
732 0 : return -1;
733 : }
734 : }
735 :
736 : /************************************************************************/
737 : /* ResetReading() */
738 : /************************************************************************/
739 :
740 848 : void MIFFile::ResetReading()
741 :
742 : {
743 848 : m_poMIFFile->Rewind();
744 :
745 848 : const char *pszLine = nullptr;
746 4706 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
747 4706 : if (STARTS_WITH_CI(pszLine, "DATA"))
748 848 : break;
749 :
750 1762 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
751 : {
752 1755 : if (m_poMIFFile->IsValidFeature(pszLine))
753 841 : break;
754 : }
755 :
756 848 : if (m_poMIDFile != nullptr)
757 : {
758 129 : m_poMIDFile->Rewind();
759 : }
760 :
761 : // We're positioned on first feature. Feature Ids start at 1.
762 848 : if (m_poCurFeature)
763 : {
764 10 : delete m_poCurFeature;
765 10 : m_poCurFeature = nullptr;
766 : }
767 :
768 848 : m_nCurFeatureId = 0;
769 848 : m_nPreloadedId = 1;
770 848 : }
771 :
772 : /************************************************************************/
773 : /* PreParseFile() */
774 : /************************************************************************/
775 :
776 13 : void MIFFile::PreParseFile()
777 : {
778 13 : char **papszToken = nullptr;
779 :
780 13 : GBool bPLine = FALSE;
781 13 : GBool bText = FALSE;
782 :
783 13 : if (m_bPreParsed == TRUE)
784 11 : return;
785 :
786 2 : m_poMIFFile->Rewind();
787 :
788 2 : const char *pszLine = nullptr;
789 16 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
790 16 : if (STARTS_WITH_CI(pszLine, "DATA"))
791 2 : break;
792 :
793 2 : m_nPoints = m_nLines = m_nRegions = m_nTexts = 0;
794 :
795 298 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
796 : {
797 296 : if (m_poMIFFile->IsValidFeature(pszLine))
798 : {
799 11 : bPLine = FALSE;
800 11 : bText = FALSE;
801 11 : m_nFeatureCount++;
802 : }
803 :
804 296 : CSLDestroy(papszToken);
805 296 : papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS);
806 :
807 296 : if (STARTS_WITH_CI(pszLine, "POINT"))
808 : {
809 0 : m_nPoints++;
810 0 : if (CSLCount(papszToken) == 3)
811 : {
812 0 : UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[1])),
813 0 : m_poMIFFile->GetYTrans(CPLAtof(papszToken[2])));
814 : }
815 : }
816 296 : else if (STARTS_WITH_CI(pszLine, "LINE") ||
817 296 : STARTS_WITH_CI(pszLine, "RECT") ||
818 296 : STARTS_WITH_CI(pszLine, "ROUNDRECT") ||
819 296 : STARTS_WITH_CI(pszLine, "ARC") ||
820 296 : STARTS_WITH_CI(pszLine, "ELLIPSE"))
821 : {
822 0 : if (CSLCount(papszToken) == 5)
823 : {
824 0 : m_nLines++;
825 0 : UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[1])),
826 0 : m_poMIFFile->GetYTrans(CPLAtof(papszToken[2])));
827 0 : UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[3])),
828 0 : m_poMIFFile->GetYTrans(CPLAtof(papszToken[4])));
829 : }
830 : }
831 296 : else if (STARTS_WITH_CI(pszLine, "REGION"))
832 : {
833 11 : m_nRegions++;
834 11 : bPLine = TRUE;
835 : }
836 285 : else if (STARTS_WITH_CI(pszLine, "PLINE"))
837 : {
838 0 : m_nLines++;
839 0 : bPLine = TRUE;
840 : }
841 285 : else if (STARTS_WITH_CI(pszLine, "TEXT"))
842 : {
843 0 : m_nTexts++;
844 0 : bText = TRUE;
845 : }
846 285 : else if (bPLine == TRUE)
847 : {
848 555 : if (CSLCount(papszToken) == 2 &&
849 272 : strchr("-.0123456789", papszToken[0][0]) != nullptr)
850 : {
851 250 : UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[0])),
852 250 : m_poMIFFile->GetYTrans(CPLAtof(papszToken[1])));
853 : }
854 : }
855 2 : else if (bText == TRUE)
856 : {
857 0 : if (CSLCount(papszToken) == 4 &&
858 0 : strchr("-.0123456789", papszToken[0][0]) != nullptr)
859 : {
860 0 : UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[0])),
861 0 : m_poMIFFile->GetYTrans(CPLAtof(papszToken[1])));
862 0 : UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[2])),
863 0 : m_poMIFFile->GetYTrans(CPLAtof(papszToken[3])));
864 : }
865 : }
866 : }
867 :
868 2 : CSLDestroy(papszToken);
869 :
870 2 : m_poMIFFile->Rewind();
871 :
872 16 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
873 16 : if (STARTS_WITH_CI(pszLine, "DATA"))
874 2 : break;
875 :
876 4 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
877 : {
878 4 : if (m_poMIFFile->IsValidFeature(pszLine))
879 2 : break;
880 : }
881 :
882 2 : if (m_poMIDFile != nullptr)
883 : {
884 2 : m_poMIDFile->Rewind();
885 : }
886 :
887 2 : m_bPreParsed = TRUE;
888 : }
889 :
890 : /**********************************************************************
891 : * MIFFile::WriteMIFHeader()
892 : *
893 : * Generate the .MIF header.
894 : *
895 : * Returns 0 on success, -1 on error.
896 : **********************************************************************/
897 87 : int MIFFile::WriteMIFHeader()
898 : {
899 : GBool bFound;
900 :
901 87 : if (m_eAccessMode != TABWrite)
902 : {
903 0 : CPLError(CE_Failure, CPLE_NotSupported,
904 : "WriteMIFHeader() can be used only with Write access.");
905 0 : return -1;
906 : }
907 :
908 87 : if (m_poDefn == nullptr || m_poDefn->GetFieldCount() == 0)
909 : {
910 0 : CPLError(CE_Failure, CPLE_NotSupported,
911 : "File %s must contain at least 1 attribute field.",
912 : m_pszFname);
913 0 : return -1;
914 : }
915 :
916 : /*-----------------------------------------------------------------
917 : * Start writing header.
918 : *----------------------------------------------------------------*/
919 87 : m_bHeaderWrote = TRUE;
920 87 : m_poMIFFile->WriteLine("Version %d\n", m_nVersion);
921 87 : m_poMIFFile->WriteLine("Charset \"%s\"\n", m_pszCharset);
922 :
923 : // Delimiter is not required if you use \t as delimiter
924 87 : if (!EQUAL(m_pszDelimiter, "\t"))
925 87 : m_poMIFFile->WriteLine("Delimiter \"%s\"\n", m_pszDelimiter);
926 :
927 87 : bFound = FALSE;
928 :
929 195 : for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
930 : {
931 108 : if (m_pabFieldUnique[iField])
932 : {
933 0 : if (!bFound)
934 0 : m_poMIFFile->WriteLine("Unique %d", iField + 1);
935 : else
936 0 : m_poMIFFile->WriteLine(",%d", iField + 1);
937 0 : bFound = TRUE;
938 : }
939 : }
940 87 : if (bFound)
941 0 : m_poMIFFile->WriteLine("\n");
942 :
943 87 : bFound = FALSE;
944 195 : for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
945 : {
946 108 : if (m_pabFieldIndexed[iField])
947 : {
948 0 : if (!bFound)
949 0 : m_poMIFFile->WriteLine("Index %d", iField + 1);
950 : else
951 0 : m_poMIFFile->WriteLine(",%d", iField + 1);
952 0 : bFound = TRUE;
953 : }
954 : }
955 87 : if (bFound)
956 0 : m_poMIFFile->WriteLine("\n");
957 :
958 87 : if (m_pszCoordSys && m_bBoundsSet)
959 : {
960 6 : m_poMIFFile->WriteLine("CoordSys %s "
961 : "Bounds (%.15g, %.15g) (%.15g, %.15g)\n",
962 : m_pszCoordSys, m_dXMin, m_dYMin, m_dXMax,
963 : m_dYMax);
964 : }
965 81 : else if (m_pszCoordSys)
966 : {
967 65 : m_poMIFFile->WriteLine("CoordSys %s\n", m_pszCoordSys);
968 : }
969 :
970 : /*-----------------------------------------------------------------
971 : * Column definitions
972 : *----------------------------------------------------------------*/
973 87 : CPLAssert(m_paeFieldType);
974 :
975 87 : m_poMIFFile->WriteLine("Columns %d\n", m_poDefn->GetFieldCount());
976 :
977 195 : for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
978 : {
979 108 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
980 216 : CPLString osFieldName(poFieldDefn->GetNameRef());
981 :
982 108 : if (strlen(GetEncoding()) > 0)
983 7 : osFieldName.Recode(CPL_ENC_UTF8, GetEncoding());
984 :
985 : char *pszCleanName =
986 108 : TABCleanFieldName(osFieldName, GetEncoding(), m_bStrictLaundering);
987 108 : osFieldName = pszCleanName;
988 108 : CPLFree(pszCleanName);
989 :
990 108 : switch (m_paeFieldType[iField])
991 : {
992 20 : case TABFInteger:
993 20 : m_poMIFFile->WriteLine(" %s Integer\n", osFieldName.c_str());
994 20 : break;
995 0 : case TABFSmallInt:
996 0 : m_poMIFFile->WriteLine(" %s SmallInt\n", osFieldName.c_str());
997 0 : break;
998 1 : case TABFLargeInt:
999 1 : m_poMIFFile->WriteLine(" %s LargeInt\n", osFieldName.c_str());
1000 1 : break;
1001 5 : case TABFFloat:
1002 5 : m_poMIFFile->WriteLine(" %s Float\n", osFieldName.c_str());
1003 5 : break;
1004 3 : case TABFDecimal:
1005 3 : m_poMIFFile->WriteLine(
1006 : " %s Decimal(%d,%d)\n", osFieldName.c_str(),
1007 : poFieldDefn->GetWidth(), poFieldDefn->GetPrecision());
1008 3 : break;
1009 1 : case TABFLogical:
1010 1 : m_poMIFFile->WriteLine(" %s Logical\n", osFieldName.c_str());
1011 1 : break;
1012 1 : case TABFDate:
1013 1 : m_poMIFFile->WriteLine(" %s Date\n", osFieldName.c_str());
1014 1 : break;
1015 1 : case TABFTime:
1016 1 : m_poMIFFile->WriteLine(" %s Time\n", osFieldName.c_str());
1017 1 : break;
1018 1 : case TABFDateTime:
1019 1 : m_poMIFFile->WriteLine(" %s DateTime\n", osFieldName.c_str());
1020 1 : break;
1021 75 : case TABFChar:
1022 : default:
1023 75 : m_poMIFFile->WriteLine(" %s Char(%d)\n", osFieldName.c_str(),
1024 : poFieldDefn->GetWidth());
1025 : }
1026 : }
1027 :
1028 : /*-----------------------------------------------------------------
1029 : * Ready to write objects
1030 : *----------------------------------------------------------------*/
1031 87 : m_poMIFFile->WriteLine("Data\n\n");
1032 :
1033 87 : return 0;
1034 : }
1035 :
1036 : /**********************************************************************
1037 : * MIFFile::Close()
1038 : *
1039 : * Close current file, and release all memory used.
1040 : *
1041 : * Returns 0 on success, -1 on error.
1042 : **********************************************************************/
1043 1020 : int MIFFile::Close()
1044 : {
1045 : /* flush .mif header if not already written */
1046 1020 : if (m_poDefn != nullptr && m_bHeaderWrote == FALSE &&
1047 878 : m_eAccessMode != TABRead)
1048 : {
1049 4 : WriteMIFHeader();
1050 : }
1051 :
1052 1020 : if (m_poMIDFile)
1053 : {
1054 181 : m_poMIDFile->Close();
1055 181 : delete m_poMIDFile;
1056 181 : m_poMIDFile = nullptr;
1057 : }
1058 :
1059 1020 : if (m_poMIFFile)
1060 : {
1061 961 : m_poMIFFile->Close();
1062 961 : delete m_poMIFFile;
1063 961 : m_poMIFFile = nullptr;
1064 : }
1065 :
1066 1020 : if (m_poCurFeature)
1067 : {
1068 0 : delete m_poCurFeature;
1069 0 : m_poCurFeature = nullptr;
1070 : }
1071 :
1072 : /*-----------------------------------------------------------------
1073 : * Note: we have to check the reference count before deleting
1074 : * m_poSpatialRef and m_poDefn
1075 : *----------------------------------------------------------------*/
1076 1020 : if (m_poDefn && m_poDefn->Dereference() == 0)
1077 158 : delete m_poDefn;
1078 1020 : m_poDefn = nullptr;
1079 :
1080 1020 : if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
1081 59 : delete m_poSpatialRef;
1082 1020 : m_poSpatialRef = nullptr;
1083 :
1084 1020 : CPLFree(m_pszCoordSys);
1085 1020 : m_pszCoordSys = nullptr;
1086 :
1087 1020 : CPLFree(m_pszDelimiter);
1088 1020 : m_pszDelimiter = nullptr;
1089 :
1090 1020 : CPLFree(m_pszUnique);
1091 1020 : m_pszUnique = nullptr;
1092 :
1093 1020 : CPLFree(m_pszFname);
1094 1020 : m_pszFname = nullptr;
1095 :
1096 1020 : m_nVersion = 0;
1097 :
1098 1020 : CPLFree(m_pszCharset);
1099 1020 : m_pszCharset = nullptr;
1100 :
1101 1020 : CPLFree(m_pabFieldIndexed);
1102 1020 : m_pabFieldIndexed = nullptr;
1103 1020 : CPLFree(m_pabFieldUnique);
1104 1020 : m_pabFieldUnique = nullptr;
1105 :
1106 1020 : CPLFree(m_pszIndex);
1107 1020 : m_pszIndex = nullptr;
1108 :
1109 1020 : CPLFree(m_paeFieldType);
1110 1020 : m_paeFieldType = nullptr;
1111 :
1112 1020 : m_nCurFeatureId = 0;
1113 1020 : m_nPreloadedId = 0;
1114 1020 : m_nFeatureCount = 0;
1115 :
1116 1020 : m_bBoundsSet = FALSE;
1117 :
1118 1020 : return 0;
1119 : }
1120 :
1121 : /**********************************************************************
1122 : * MIFFile::GetNextFeatureId()
1123 : *
1124 : * Returns feature id that follows nPrevId, or -1 if it is the
1125 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
1126 : **********************************************************************/
1127 7950 : GIntBig MIFFile::GetNextFeatureId(GIntBig nPrevId)
1128 : {
1129 7950 : if (m_eAccessMode != TABRead)
1130 : {
1131 0 : CPLError(CE_Failure, CPLE_NotSupported,
1132 : "GetNextFeatureId() can be used only with Read access.");
1133 0 : return -1;
1134 : }
1135 :
1136 7950 : if (nPrevId <= 0 && m_poMIFFile->GetLastLine() != nullptr)
1137 782 : return 1; // Feature Ids start at 1
1138 7168 : else if (nPrevId > 0 && m_poMIFFile->GetLastLine() != nullptr)
1139 6747 : return nPrevId + 1;
1140 : else
1141 421 : return -1;
1142 : }
1143 :
1144 : /**********************************************************************
1145 : * MIFFile::GotoFeature()
1146 : *
1147 : * Private method to move MIF and MID pointers ready to read specified
1148 : * feature. Note that Feature Ids start at 1.
1149 : *
1150 : * Returns 0 on success, -1 on error (likely request for invalid feature id)
1151 : **********************************************************************/
1152 7536 : int MIFFile::GotoFeature(int nFeatureId)
1153 : {
1154 :
1155 7536 : if (nFeatureId < 1)
1156 1 : return -1;
1157 :
1158 7535 : if (nFeatureId == m_nPreloadedId) // CorrectPosition
1159 : {
1160 7504 : return 0;
1161 : }
1162 : else
1163 : {
1164 31 : if (nFeatureId < m_nPreloadedId || m_nCurFeatureId == 0)
1165 29 : ResetReading();
1166 :
1167 36 : while (m_nPreloadedId < nFeatureId)
1168 : {
1169 : const char *pszLine;
1170 136 : while ((pszLine = m_poMIFFile->GetLine()) != nullptr)
1171 : {
1172 135 : if (m_poMIFFile->IsValidFeature(pszLine))
1173 : {
1174 5 : m_nPreloadedId++;
1175 5 : if (m_poMIDFile != nullptr)
1176 5 : CSLDestroy(m_poMIDFile->GetTokenizedNextLine());
1177 5 : break;
1178 : }
1179 : }
1180 6 : if (pszLine == nullptr)
1181 1 : return -1;
1182 : }
1183 :
1184 30 : CPLAssert(m_nPreloadedId == nFeatureId);
1185 :
1186 30 : return 0;
1187 : }
1188 : }
1189 :
1190 : /**********************************************************************
1191 : * MIFFile::GetFeatureRef()
1192 : *
1193 : * Fill and return a TABFeature object for the specified feature id.
1194 : *
1195 : * The returned pointer is a reference to an object owned and maintained
1196 : * by this MIFFile object. It should not be altered or freed by the
1197 : * caller and its contents is guaranteed to be valid only until the next
1198 : * call to GetFeatureRef() or Close().
1199 : *
1200 : * Returns NULL if the specified feature id does not exist of if an
1201 : * error happened. In any case, CPLError() will have been called to
1202 : * report the reason of the failure.
1203 : **********************************************************************/
1204 7537 : TABFeature *MIFFile::GetFeatureRef(GIntBig nFeatureId)
1205 : {
1206 7537 : if (m_eAccessMode != TABRead)
1207 : {
1208 0 : CPLError(CE_Failure, CPLE_NotSupported,
1209 : "GetFeatureRef() can be used only with Read access.");
1210 0 : return nullptr;
1211 : }
1212 :
1213 : /*-----------------------------------------------------------------
1214 : * Make sure file is opened and Validate feature id by positioning
1215 : * the read pointers for the .MAP and .DAT files to this feature id.
1216 : *----------------------------------------------------------------*/
1217 7537 : if (m_poMIFFile == nullptr)
1218 : {
1219 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1220 : "GetFeatureRef() failed: file is not opened!");
1221 0 : return nullptr;
1222 : }
1223 :
1224 15073 : if (!CPL_INT64_FITS_ON_INT32(nFeatureId) ||
1225 7536 : GotoFeature(static_cast<int>(nFeatureId)) != 0)
1226 : {
1227 3 : CPLError(CE_Failure, CPLE_IllegalArg,
1228 : "GetFeatureRef() failed: invalid feature id " CPL_FRMT_GIB,
1229 : nFeatureId);
1230 3 : return nullptr;
1231 : }
1232 :
1233 : /*-----------------------------------------------------------------
1234 : * Create new feature object of the right type
1235 : *----------------------------------------------------------------*/
1236 7534 : const char *pszLine = nullptr;
1237 7534 : if ((pszLine = m_poMIFFile->GetLastLine()) != nullptr)
1238 : {
1239 : // Delete previous feature... we'll start we a clean one.
1240 7534 : if (m_poCurFeature)
1241 79 : delete m_poCurFeature;
1242 7534 : m_poCurFeature = nullptr;
1243 :
1244 7534 : m_nCurFeatureId = m_nPreloadedId;
1245 :
1246 7534 : if (STARTS_WITH_CI(pszLine, "NONE"))
1247 : {
1248 22 : m_poCurFeature = new TABFeature(m_poDefn);
1249 : }
1250 7512 : else if (STARTS_WITH_CI(pszLine, "POINT"))
1251 : {
1252 : // Special case, we need to know two lines to decide the type
1253 : char **papszToken =
1254 2096 : CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS);
1255 :
1256 2096 : if (CSLCount(papszToken) != 3)
1257 : {
1258 12 : CSLDestroy(papszToken);
1259 12 : CPLError(CE_Failure, CPLE_NotSupported,
1260 : "GetFeatureRef() failed: invalid point line: '%s'",
1261 : pszLine);
1262 12 : return nullptr;
1263 : }
1264 :
1265 2084 : m_poMIFFile->SaveLine(pszLine);
1266 :
1267 2084 : if ((pszLine = m_poMIFFile->GetLine()) != nullptr)
1268 : {
1269 2078 : CSLDestroy(papszToken);
1270 : papszToken =
1271 2078 : CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
1272 3449 : if (CSLCount(papszToken) > 0 &&
1273 1371 : STARTS_WITH_CI(papszToken[0], "SYMBOL"))
1274 : {
1275 1361 : switch (CSLCount(papszToken))
1276 : {
1277 29 : case 4:
1278 29 : m_poCurFeature = new TABPoint(m_poDefn);
1279 29 : break;
1280 635 : case 7:
1281 635 : m_poCurFeature = new TABFontPoint(m_poDefn);
1282 635 : break;
1283 678 : case 5:
1284 678 : m_poCurFeature = new TABCustomPoint(m_poDefn);
1285 678 : break;
1286 19 : default:
1287 19 : CSLDestroy(papszToken);
1288 19 : CPLError(CE_Failure, CPLE_NotSupported,
1289 : "GetFeatureRef() failed: invalid symbol "
1290 : "line: '%s'",
1291 : pszLine);
1292 19 : return nullptr;
1293 : break;
1294 : }
1295 : }
1296 : }
1297 2065 : CSLDestroy(papszToken);
1298 :
1299 2065 : if (m_poCurFeature == nullptr)
1300 : {
1301 : // No symbol clause... default to TABPoint
1302 723 : m_poCurFeature = new TABPoint(m_poDefn);
1303 : }
1304 : }
1305 5416 : else if (STARTS_WITH_CI(pszLine, "LINE") ||
1306 4791 : STARTS_WITH_CI(pszLine, "PLINE"))
1307 : {
1308 2392 : m_poCurFeature = new TABPolyline(m_poDefn);
1309 : }
1310 3024 : else if (STARTS_WITH_CI(pszLine, "REGION"))
1311 : {
1312 455 : m_poCurFeature = new TABRegion(m_poDefn);
1313 : }
1314 2569 : else if (STARTS_WITH_CI(pszLine, "ARC"))
1315 : {
1316 672 : m_poCurFeature = new TABArc(m_poDefn);
1317 : }
1318 1897 : else if (STARTS_WITH_CI(pszLine, "TEXT"))
1319 : {
1320 297 : m_poCurFeature = new TABText(m_poDefn);
1321 : }
1322 1600 : else if (STARTS_WITH_CI(pszLine, "RECT") ||
1323 1105 : STARTS_WITH_CI(pszLine, "ROUNDRECT"))
1324 : {
1325 941 : m_poCurFeature = new TABRectangle(m_poDefn);
1326 : }
1327 659 : else if (STARTS_WITH_CI(pszLine, "ELLIPSE"))
1328 : {
1329 397 : m_poCurFeature = new TABEllipse(m_poDefn);
1330 : }
1331 262 : else if (STARTS_WITH_CI(pszLine, "MULTIPOINT"))
1332 : {
1333 163 : m_poCurFeature = new TABMultiPoint(m_poDefn);
1334 : }
1335 99 : else if (STARTS_WITH_CI(pszLine, "COLLECTION"))
1336 : {
1337 99 : m_poCurFeature = new TABCollection(m_poDefn);
1338 : }
1339 : else
1340 : {
1341 0 : if (!EQUAL(pszLine, ""))
1342 0 : CPLError(CE_Failure, CPLE_NotSupported,
1343 : "Error during reading, unknown type %s.", pszLine);
1344 :
1345 : // m_poCurFeature = new TABDebugFeature(m_poDefn);
1346 0 : return nullptr;
1347 : }
1348 : }
1349 :
1350 7503 : CPLAssert(m_poCurFeature);
1351 7503 : if (m_poCurFeature == nullptr)
1352 0 : return nullptr;
1353 :
1354 : /*-----------------------------------------------------------------
1355 : * Read fields from the .DAT file
1356 : * GetRecordBlock() has already been called above...
1357 : *----------------------------------------------------------------*/
1358 7865 : if (m_poMIDFile != nullptr &&
1359 362 : m_poCurFeature->ReadRecordFromMIDFile(m_poMIDFile) != 0)
1360 : {
1361 0 : CPLError(CE_Failure, CPLE_NotSupported, "Error during reading Record.");
1362 :
1363 0 : delete m_poCurFeature;
1364 0 : m_poCurFeature = nullptr;
1365 0 : return nullptr;
1366 : }
1367 :
1368 : /*-----------------------------------------------------------------
1369 : * Read geometry from the .MAP file
1370 : * MoveToObjId() has already been called above...
1371 : *----------------------------------------------------------------*/
1372 7503 : if (m_poCurFeature->ReadGeometryFromMIFFile(m_poMIFFile) != 0)
1373 : {
1374 297 : CPLError(CE_Failure, CPLE_NotSupported,
1375 : "Error during reading Geometry.");
1376 :
1377 297 : delete m_poCurFeature;
1378 297 : m_poCurFeature = nullptr;
1379 297 : return nullptr;
1380 : }
1381 :
1382 : /* If the feature geometry is Text, and the value is empty(""), transform
1383 : it to a geometry none */
1384 7206 : if (m_poCurFeature->GetFeatureClass() == TABFCText)
1385 : {
1386 280 : TABText *poTextFeature = cpl::down_cast<TABText *>(m_poCurFeature);
1387 280 : if (strlen(poTextFeature->GetTextString()) == 0)
1388 : {
1389 0 : TABFeature *poTmpFeature = new TABFeature(m_poDefn);
1390 0 : for (int i = 0; i < m_poDefn->GetFieldCount(); i++)
1391 : {
1392 0 : poTmpFeature->SetField(i, m_poCurFeature->GetRawFieldRef(i));
1393 : }
1394 0 : delete m_poCurFeature;
1395 0 : m_poCurFeature = poTmpFeature;
1396 : }
1397 : }
1398 :
1399 : /*---------------------------------------------------------------------
1400 : * The act of reading the geometry causes the first line of the
1401 : * next object to be preloaded. Set the preloaded id appropriately.
1402 : *--------------------------------------------------------------------- */
1403 7206 : if (m_poMIFFile->GetLastLine() != nullptr)
1404 6767 : m_nPreloadedId++;
1405 : else
1406 439 : m_nPreloadedId = 0;
1407 :
1408 : /* Update the Current Feature ID */
1409 7206 : m_poCurFeature->SetFID(m_nCurFeatureId);
1410 :
1411 7206 : return m_poCurFeature;
1412 : }
1413 :
1414 : /**********************************************************************
1415 : * MIFFile::CreateFeature()
1416 : *
1417 : * Write a new feature to this dataset. The passed in feature is updated
1418 : * with the new feature id.
1419 : *
1420 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
1421 : * error happened in which case, CPLError() will have been called to
1422 : * report the reason of the failure.
1423 : **********************************************************************/
1424 118 : OGRErr MIFFile::CreateFeature(TABFeature *poFeature)
1425 : {
1426 118 : int nFeatureId = -1;
1427 :
1428 118 : if (m_eAccessMode != TABWrite)
1429 : {
1430 0 : CPLError(CE_Failure, CPLE_NotSupported,
1431 : "CreateFeature() can be used only with Write access.");
1432 0 : return OGRERR_UNSUPPORTED_OPERATION;
1433 : }
1434 :
1435 : /*-----------------------------------------------------------------
1436 : * Make sure file is opened and establish new feature id.
1437 : *----------------------------------------------------------------*/
1438 118 : if (m_poMIDFile == nullptr)
1439 : {
1440 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1441 : "CreateFeature() failed: file is not opened!");
1442 0 : return OGRERR_FAILURE;
1443 : }
1444 :
1445 118 : if (m_bHeaderWrote == FALSE)
1446 : {
1447 : /*-------------------------------------------------------------
1448 : * OK, this is the first feature in the dataset... make sure the
1449 : * .MID schema has been initialized.
1450 : *------------------------------------------------------------*/
1451 83 : if (m_poDefn == nullptr)
1452 0 : SetFeatureDefn(poFeature->GetDefnRef(), nullptr);
1453 :
1454 83 : WriteMIFHeader();
1455 83 : nFeatureId = 1;
1456 : }
1457 : else
1458 : {
1459 35 : nFeatureId = ++m_nWriteFeatureId;
1460 : }
1461 :
1462 : /*-----------------------------------------------------------------
1463 : * Write geometry to the .Mif file
1464 : *----------------------------------------------------------------*/
1465 236 : if (m_poMIFFile == nullptr ||
1466 118 : poFeature->WriteGeometryToMIFFile(m_poMIFFile) != 0)
1467 : {
1468 0 : CPLError(CE_Failure, CPLE_FileIO,
1469 : "Failed writing geometry for feature id %d in %s", nFeatureId,
1470 : m_pszFname);
1471 0 : return OGRERR_FAILURE;
1472 : }
1473 :
1474 236 : if (m_poMIDFile == nullptr ||
1475 118 : poFeature->WriteRecordToMIDFile(m_poMIDFile) != 0)
1476 : {
1477 0 : CPLError(CE_Failure, CPLE_FileIO,
1478 : "Failed writing attributes for feature id %d in %s",
1479 : nFeatureId, m_pszFname);
1480 0 : return OGRERR_FAILURE;
1481 : }
1482 :
1483 118 : poFeature->SetFID(nFeatureId);
1484 :
1485 118 : return OGRERR_NONE;
1486 : }
1487 :
1488 : /**********************************************************************
1489 : * MIFFile::GetLayerDefn()
1490 : *
1491 : * Returns a reference to the OGRFeatureDefn that will be used to create
1492 : * features in this dataset.
1493 : *
1494 : * Returns a reference to an object that is maintained by this MIFFile
1495 : * object (and thus should not be modified or freed by the caller) or
1496 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
1497 : * opened yet)
1498 : **********************************************************************/
1499 1684 : OGRFeatureDefn *MIFFile::GetLayerDefn()
1500 : {
1501 1684 : return m_poDefn;
1502 : }
1503 :
1504 : /**********************************************************************
1505 : * MIFFile::SetFeatureDefn()
1506 : *
1507 : * Pass a reference to the OGRFeatureDefn that will be used to create
1508 : * features in this dataset. This function should be called after
1509 : * creating a new dataset, but before writing the first feature.
1510 : * All features that will be written to this dataset must share this same
1511 : * OGRFeatureDefn.
1512 : *
1513 : * This function will use poFeatureDefn to create a local copy that
1514 : * will be used to build the .MID file, etc.
1515 : *
1516 : * Returns 0 on success, -1 on error.
1517 : **********************************************************************/
1518 0 : int MIFFile::SetFeatureDefn(
1519 : OGRFeatureDefn *poFeatureDefn,
1520 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1521 : {
1522 : /*-----------------------------------------------------------------
1523 : * Check that call happens at the right time in dataset's life.
1524 : *----------------------------------------------------------------*/
1525 0 : if (m_eAccessMode == TABWrite && m_bHeaderWrote)
1526 : {
1527 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1528 : "SetFeatureDefn() must be called after opening a new "
1529 : "dataset, but before writing the first feature to it.");
1530 0 : return -1;
1531 : }
1532 :
1533 : /*-----------------------------------------------------------------
1534 : * Delete current feature defn if there is already one.
1535 : * AddFieldNative() will take care of creating a new one for us.
1536 : *----------------------------------------------------------------*/
1537 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1538 0 : delete m_poDefn;
1539 0 : m_poDefn = nullptr;
1540 :
1541 : /*-----------------------------------------------------------------
1542 : * Copy field information
1543 : *----------------------------------------------------------------*/
1544 0 : const int numFields = poFeatureDefn->GetFieldCount();
1545 0 : int nStatus = 0;
1546 :
1547 0 : for (int iField = 0; iField < numFields; iField++)
1548 : {
1549 : TABFieldType eMapInfoType;
1550 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1551 :
1552 0 : if (paeMapInfoNativeFieldTypes)
1553 : {
1554 0 : eMapInfoType = paeMapInfoNativeFieldTypes[iField];
1555 : }
1556 : else
1557 : {
1558 : /*---------------------------------------------------------
1559 : * Map OGRFieldTypes to MapInfo native types
1560 : *--------------------------------------------------------*/
1561 0 : switch (poFieldDefn->GetType())
1562 : {
1563 0 : case OFTInteger:
1564 0 : eMapInfoType = poFieldDefn->GetSubType() == OFSTBoolean
1565 0 : ? TABFLogical
1566 : : TABFInteger;
1567 0 : break;
1568 0 : case OFTReal:
1569 0 : eMapInfoType = TABFFloat;
1570 0 : break;
1571 0 : case OFTDateTime:
1572 0 : eMapInfoType = TABFDateTime;
1573 0 : break;
1574 0 : case OFTDate:
1575 0 : eMapInfoType = TABFDate;
1576 0 : break;
1577 0 : case OFTTime:
1578 0 : eMapInfoType = TABFTime;
1579 0 : break;
1580 0 : case OFTString:
1581 : default:
1582 0 : eMapInfoType = TABFChar;
1583 : }
1584 : }
1585 :
1586 0 : nStatus = AddFieldNative(poFieldDefn->GetNameRef(), eMapInfoType,
1587 : poFieldDefn->GetWidth(),
1588 : poFieldDefn->GetPrecision(), FALSE, FALSE);
1589 : }
1590 :
1591 0 : return nStatus;
1592 : }
1593 :
1594 : /**********************************************************************
1595 : * MIFFile::AddFieldNative()
1596 : *
1597 : * Create a new field using a native mapinfo data type... this is an
1598 : * alternative to defining fields through the OGR interface.
1599 : * This function should be called after creating a new dataset, but before
1600 : * writing the first feature.
1601 : *
1602 : * This function will build/update the OGRFeatureDefn that will have to be
1603 : * used when writing features to this dataset.
1604 : *
1605 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1606 : *
1607 : * Returns 0 on success, -1 on error.
1608 : **********************************************************************/
1609 279 : int MIFFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1610 : int nWidth /*=0*/, int nPrecision /*=0*/,
1611 : GBool bIndexed /*=FALSE*/, GBool bUnique /*=FALSE*/,
1612 : int /*bApproxOK*/)
1613 : {
1614 : /*-----------------------------------------------------------------
1615 : * Check that call happens at the right time in dataset's life.
1616 : *----------------------------------------------------------------*/
1617 279 : if (m_eAccessMode == TABWrite && m_bHeaderWrote)
1618 : {
1619 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1620 : "AddFieldNative() must be called after opening a new "
1621 : "dataset, but before writing the first feature to it.");
1622 0 : return -1;
1623 : }
1624 :
1625 : /*-----------------------------------------------------------------
1626 : * Validate field width... must be <= 254
1627 : *----------------------------------------------------------------*/
1628 279 : if (nWidth > 254)
1629 : {
1630 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1631 : "Invalid size (%d) for field '%s'. "
1632 : "Size must be 254 or less.",
1633 : nWidth, pszName);
1634 0 : nWidth = 254;
1635 : }
1636 :
1637 : /*-----------------------------------------------------------------
1638 : * Map fields with width=0 (variable length in OGR) to a valid default
1639 : *----------------------------------------------------------------*/
1640 279 : if (eMapInfoType == TABFDecimal && nWidth == 0)
1641 0 : nWidth = 20;
1642 279 : else if (eMapInfoType == TABFChar && nWidth == 0)
1643 0 : nWidth = 254; /* char fields */
1644 :
1645 : /*-----------------------------------------------------------------
1646 : * Create new OGRFeatureDefn if not done yet...
1647 : *----------------------------------------------------------------*/
1648 279 : if (m_poDefn == nullptr)
1649 : {
1650 0 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
1651 0 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
1652 0 : CPLFree(pszFeatureClassName);
1653 : // Ref count defaults to 0... set it to 1
1654 0 : m_poDefn->Reference();
1655 0 : m_poDefn->Seal(/* bSealFields = */ true);
1656 : }
1657 :
1658 558 : CPLString osName(NormalizeFieldName(pszName));
1659 :
1660 : /*-----------------------------------------------------------------
1661 : * Map MapInfo native types to OGR types
1662 : *----------------------------------------------------------------*/
1663 279 : OGRFieldDefn *poFieldDefn = nullptr;
1664 :
1665 279 : switch (eMapInfoType)
1666 : {
1667 179 : case TABFChar:
1668 : /*-------------------------------------------------
1669 : * CHAR type
1670 : *------------------------------------------------*/
1671 179 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
1672 179 : poFieldDefn->SetWidth(nWidth);
1673 179 : break;
1674 55 : case TABFInteger:
1675 : /*-------------------------------------------------
1676 : * INTEGER type
1677 : *------------------------------------------------*/
1678 55 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
1679 55 : poFieldDefn->SetWidth(nWidth);
1680 55 : break;
1681 1 : case TABFSmallInt:
1682 : /*-------------------------------------------------
1683 : * SMALLINT type
1684 : *------------------------------------------------*/
1685 1 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
1686 1 : poFieldDefn->SetWidth(nWidth);
1687 1 : break;
1688 3 : case TABFLargeInt:
1689 : /*-------------------------------------------------
1690 : * LargeInt type
1691 : *------------------------------------------------*/
1692 3 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger64);
1693 3 : poFieldDefn->SetWidth(nWidth);
1694 3 : break;
1695 7 : case TABFDecimal:
1696 : /*-------------------------------------------------
1697 : * DECIMAL type
1698 : *------------------------------------------------*/
1699 7 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
1700 7 : poFieldDefn->SetWidth(nWidth);
1701 7 : poFieldDefn->SetPrecision(nPrecision);
1702 7 : break;
1703 21 : case TABFFloat:
1704 : /*-------------------------------------------------
1705 : * FLOAT type
1706 : *------------------------------------------------*/
1707 21 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
1708 21 : break;
1709 3 : case TABFDate:
1710 : /*-------------------------------------------------
1711 : * DATE type (V450, returned as a string: "DD/MM/YYYY" or
1712 : *"YYYYMMDD")
1713 : *------------------------------------------------*/
1714 3 : poFieldDefn = new OGRFieldDefn(osName.c_str(),
1715 : #ifdef MITAB_USE_OFTDATETIME
1716 3 : OFTDate);
1717 : #else
1718 : OFTString);
1719 : #endif
1720 3 : poFieldDefn->SetWidth(10);
1721 3 : m_nVersion = std::max(m_nVersion, 450);
1722 3 : break;
1723 3 : case TABFTime:
1724 : /*-------------------------------------------------
1725 : * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm")
1726 : *------------------------------------------------*/
1727 3 : poFieldDefn = new OGRFieldDefn(osName.c_str(),
1728 : #ifdef MITAB_USE_OFTDATETIME
1729 3 : OFTTime);
1730 : #else
1731 : OFTString);
1732 : #endif
1733 3 : poFieldDefn->SetWidth(9);
1734 3 : m_nVersion = std::max(m_nVersion, 900);
1735 3 : break;
1736 3 : case TABFDateTime:
1737 : /*-------------------------------------------------
1738 : * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS",
1739 : * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm")
1740 : *------------------------------------------------*/
1741 3 : poFieldDefn = new OGRFieldDefn(osName.c_str(),
1742 : #ifdef MITAB_USE_OFTDATETIME
1743 3 : OFTDateTime);
1744 : #else
1745 : OFTString);
1746 : #endif
1747 3 : poFieldDefn->SetWidth(19);
1748 3 : m_nVersion = std::max(m_nVersion, 900);
1749 3 : break;
1750 4 : case TABFLogical:
1751 : /*-------------------------------------------------
1752 : * LOGICAL type (value "T" or "F")
1753 : *------------------------------------------------*/
1754 4 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
1755 4 : poFieldDefn->SetSubType(OFSTBoolean);
1756 4 : poFieldDefn->SetWidth(1);
1757 4 : break;
1758 0 : default:
1759 0 : CPLError(CE_Failure, CPLE_NotSupported,
1760 : "Unsupported type for field %s", pszName);
1761 0 : return -1;
1762 : }
1763 :
1764 : /*-----------------------------------------------------
1765 : * Add the FieldDefn to the FeatureDefn
1766 : *----------------------------------------------------*/
1767 279 : whileUnsealing(m_poDefn)->AddFieldDefn(poFieldDefn);
1768 279 : m_oSetFields.insert(CPLString(poFieldDefn->GetNameRef()).toupper());
1769 279 : delete poFieldDefn;
1770 :
1771 : /*-----------------------------------------------------------------
1772 : * Keep track of native field type
1773 : *----------------------------------------------------------------*/
1774 558 : m_paeFieldType = static_cast<TABFieldType *>(CPLRealloc(
1775 279 : m_paeFieldType, m_poDefn->GetFieldCount() * sizeof(TABFieldType)));
1776 279 : m_paeFieldType[m_poDefn->GetFieldCount() - 1] = eMapInfoType;
1777 :
1778 : /*-----------------------------------------------------------------
1779 : * Extend array of Indexed/Unique flags
1780 : *----------------------------------------------------------------*/
1781 558 : m_pabFieldIndexed = static_cast<GBool *>(CPLRealloc(
1782 279 : m_pabFieldIndexed, m_poDefn->GetFieldCount() * sizeof(GBool)));
1783 558 : m_pabFieldUnique = static_cast<GBool *>(CPLRealloc(
1784 279 : m_pabFieldUnique, m_poDefn->GetFieldCount() * sizeof(GBool)));
1785 279 : m_pabFieldIndexed[m_poDefn->GetFieldCount() - 1] = bIndexed;
1786 279 : m_pabFieldUnique[m_poDefn->GetFieldCount() - 1] = bUnique;
1787 :
1788 279 : return 0;
1789 : }
1790 :
1791 : /**********************************************************************
1792 : * MIFFile::GetNativeFieldType()
1793 : *
1794 : * Returns the native MapInfo field type for the specified field.
1795 : *
1796 : * Returns TABFUnknown if file is not opened, or if specified field index is
1797 : * invalid.
1798 : **********************************************************************/
1799 0 : TABFieldType MIFFile::GetNativeFieldType(int nFieldId)
1800 : {
1801 0 : if (m_poDefn == nullptr || m_paeFieldType == nullptr || nFieldId < 0 ||
1802 0 : nFieldId >= m_poDefn->GetFieldCount())
1803 0 : return TABFUnknown;
1804 :
1805 0 : return m_paeFieldType[nFieldId];
1806 : }
1807 :
1808 : /************************************************************************
1809 : * MIFFile::SetFieldIndexed()
1810 : ************************************************************************/
1811 :
1812 0 : int MIFFile::SetFieldIndexed(int nFieldId)
1813 :
1814 : {
1815 0 : if (m_poDefn == nullptr || m_pabFieldIndexed == nullptr || nFieldId < 0 ||
1816 0 : nFieldId >= m_poDefn->GetFieldCount())
1817 0 : return -1;
1818 :
1819 0 : m_pabFieldIndexed[nFieldId] = TRUE;
1820 :
1821 0 : return 0;
1822 : }
1823 :
1824 : /************************************************************************
1825 : * MIFFile::IsFieldIndexed()
1826 : ************************************************************************/
1827 :
1828 0 : GBool MIFFile::IsFieldIndexed(int nFieldId)
1829 :
1830 : {
1831 0 : if (m_poDefn == nullptr || m_pabFieldIndexed == nullptr || nFieldId < 0 ||
1832 0 : nFieldId >= m_poDefn->GetFieldCount())
1833 0 : return FALSE;
1834 :
1835 0 : return m_pabFieldIndexed[nFieldId];
1836 : }
1837 :
1838 : /************************************************************************
1839 : * MIFFile::IsFieldUnique()
1840 : ************************************************************************/
1841 :
1842 0 : GBool MIFFile::IsFieldUnique(int nFieldId)
1843 :
1844 : {
1845 0 : if (m_poDefn == nullptr || m_pabFieldUnique == nullptr || nFieldId < 0 ||
1846 0 : nFieldId >= m_poDefn->GetFieldCount())
1847 0 : return FALSE;
1848 :
1849 0 : return m_pabFieldUnique[nFieldId];
1850 : }
1851 :
1852 : /************************************************************************/
1853 : /* MIFFile::SetSpatialRef() */
1854 : /************************************************************************/
1855 :
1856 71 : int MIFFile::SetSpatialRef(OGRSpatialReference *poSpatialRef)
1857 :
1858 : {
1859 71 : CPLFree(m_pszCoordSys);
1860 71 : m_pszCoordSys = nullptr;
1861 :
1862 71 : char *pszCoordSys = MITABSpatialRef2CoordSys(poSpatialRef);
1863 71 : if (pszCoordSys)
1864 : {
1865 71 : SetMIFCoordSys(pszCoordSys);
1866 71 : CPLFree(pszCoordSys);
1867 : }
1868 :
1869 71 : return m_pszCoordSys != nullptr;
1870 : }
1871 :
1872 : /************************************************************************/
1873 : /* MIFFile::SetMIFCoordSys() */
1874 : /************************************************************************/
1875 :
1876 71 : int MIFFile::SetMIFCoordSys(const char *pszMIFCoordSys)
1877 :
1878 : {
1879 71 : char *pszCoordSys = nullptr;
1880 :
1881 : // Extract the word 'COORDSYS' if present
1882 71 : if (STARTS_WITH_CI(pszMIFCoordSys, "COORDSYS"))
1883 : {
1884 0 : pszCoordSys = CPLStrdup(pszMIFCoordSys + 9);
1885 : }
1886 : else
1887 : {
1888 71 : pszCoordSys = CPLStrdup(pszMIFCoordSys);
1889 : }
1890 :
1891 : // Extract bounds if present
1892 : char **papszFields =
1893 71 : CSLTokenizeStringComplex(pszCoordSys, " ,()\t", TRUE, FALSE);
1894 71 : int iBounds = CSLFindString(papszFields, "Bounds");
1895 71 : if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
1896 : {
1897 5 : m_dXMin = CPLAtof(papszFields[++iBounds]);
1898 5 : m_dYMin = CPLAtof(papszFields[++iBounds]);
1899 5 : m_dXMax = CPLAtof(papszFields[++iBounds]);
1900 5 : m_dYMax = CPLAtof(papszFields[++iBounds]);
1901 5 : m_bBoundsSet = TRUE;
1902 :
1903 5 : char *pszBounds = strstr(pszCoordSys, " Bounds");
1904 5 : if (pszBounds == nullptr)
1905 0 : pszBounds = strstr(pszCoordSys, "Bounds");
1906 5 : pszCoordSys[pszBounds - pszCoordSys] = '\0';
1907 : }
1908 71 : CSLDestroy(papszFields);
1909 :
1910 : // Assign the CoordSys
1911 71 : CPLFree(m_pszCoordSys);
1912 :
1913 71 : m_pszCoordSys = CPLStrdup(pszCoordSys);
1914 71 : CPLFree(pszCoordSys);
1915 :
1916 71 : return m_pszCoordSys != nullptr;
1917 : }
1918 :
1919 940 : int MIFFile::SetCharset(const char *pszCharset)
1920 : {
1921 940 : if (0 != IMapInfoFile::SetCharset(pszCharset))
1922 : {
1923 0 : return -1;
1924 : }
1925 :
1926 940 : if (m_poMIDFile != nullptr)
1927 : {
1928 88 : m_poMIDFile->SetEncoding(CharsetToEncoding(pszCharset));
1929 : }
1930 940 : if (m_poMIFFile != nullptr)
1931 : {
1932 940 : m_poMIFFile->SetEncoding(CharsetToEncoding(pszCharset));
1933 : }
1934 940 : if (EQUAL(pszCharset, "UTF-8"))
1935 : {
1936 3 : m_nVersion = std::max(m_nVersion, 1520);
1937 : }
1938 940 : return 0;
1939 : }
1940 :
1941 171 : void MIFFile::SetStrictLaundering(bool bStrictLaundering)
1942 : {
1943 171 : IMapInfoFile::SetStrictLaundering(bStrictLaundering);
1944 171 : if (!bStrictLaundering)
1945 : {
1946 3 : m_nVersion = std::max(m_nVersion, 1520);
1947 : }
1948 171 : }
1949 :
1950 : /************************************************************************/
1951 : /* MIFFile::GetSpatialRef() */
1952 : /************************************************************************/
1953 :
1954 7239 : OGRSpatialReference *MIFFile::GetSpatialRef()
1955 :
1956 : {
1957 7239 : if (m_poSpatialRef == nullptr)
1958 7003 : m_poSpatialRef = MITABCoordSys2SpatialRef(m_pszCoordSys);
1959 :
1960 7239 : return m_poSpatialRef;
1961 : }
1962 :
1963 : /**********************************************************************
1964 : * MIFFile::UpdateExtents()
1965 : *
1966 : * Private method used to update the dataset extents.
1967 : **********************************************************************/
1968 250 : void MIFFile::UpdateExtents(double dfX, double dfY)
1969 : {
1970 250 : if (m_bExtentsSet == FALSE)
1971 : {
1972 2 : m_bExtentsSet = TRUE;
1973 2 : m_sExtents.MinX = m_sExtents.MaxX = dfX;
1974 2 : m_sExtents.MinY = m_sExtents.MaxY = dfY;
1975 : }
1976 : else
1977 : {
1978 248 : if (dfX < m_sExtents.MinX)
1979 18 : m_sExtents.MinX = dfX;
1980 248 : if (dfX > m_sExtents.MaxX)
1981 28 : m_sExtents.MaxX = dfX;
1982 248 : if (dfY < m_sExtents.MinY)
1983 31 : m_sExtents.MinY = dfY;
1984 248 : if (dfY > m_sExtents.MaxY)
1985 6 : m_sExtents.MaxY = dfY;
1986 : }
1987 250 : }
1988 :
1989 : /**********************************************************************
1990 : * MIFFile::SetBounds()
1991 : *
1992 : * Set projection coordinates bounds of the newly created dataset.
1993 : *
1994 : * This function must be called after creating a new dataset and before any
1995 : * feature can be written to it.
1996 : *
1997 : * Returns 0 on success, -1 on error.
1998 : **********************************************************************/
1999 1 : int MIFFile::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
2000 : {
2001 1 : if (m_eAccessMode != TABWrite)
2002 : {
2003 0 : CPLError(CE_Failure, CPLE_NotSupported,
2004 : "SetBounds() can be used only with Write access.");
2005 0 : return -1;
2006 : }
2007 :
2008 1 : m_dXMin = dXMin;
2009 1 : m_dXMax = dXMax;
2010 1 : m_dYMin = dYMin;
2011 1 : m_dYMax = dYMax;
2012 1 : m_bBoundsSet = TRUE;
2013 :
2014 1 : return 0;
2015 : }
2016 :
2017 : /**********************************************************************
2018 : * MIFFile::GetFeatureCountByType()
2019 : *
2020 : * Return number of features of each type.
2021 : *
2022 : * NOTE: The current implementation always returns -1 for MIF files
2023 : * since this would require scanning the whole file.
2024 : *
2025 : * When properly implemented, the bForce flag will force scanning the
2026 : * whole file by default.
2027 : *
2028 : * Returns 0 on success, or silently returns -1 (with no error) if this
2029 : * information is not available.
2030 : **********************************************************************/
2031 903 : int MIFFile::GetFeatureCountByType(int &numPoints, int &numLines,
2032 : int &numRegions, int &numTexts, GBool bForce)
2033 : {
2034 903 : if (m_bPreParsed || bForce)
2035 : {
2036 0 : PreParseFile();
2037 :
2038 0 : numPoints = m_nPoints;
2039 0 : numLines = m_nLines;
2040 0 : numRegions = m_nRegions;
2041 0 : numTexts = m_nTexts;
2042 0 : return 0;
2043 : }
2044 : else
2045 : {
2046 903 : numPoints = numLines = numRegions = numTexts = 0;
2047 903 : return -1;
2048 : }
2049 : }
2050 :
2051 : /**********************************************************************
2052 : * MIFFile::GetBounds()
2053 : *
2054 : * Fetch projection coordinates bounds of a dataset.
2055 : *
2056 : * Pass bForce=FALSE to avoid a scan of the whole file if the bounds
2057 : * are not already available.
2058 : *
2059 : * Returns 0 on success, -1 on error or if bounds are not available and
2060 : * bForce=FALSE.
2061 : **********************************************************************/
2062 0 : int MIFFile::GetBounds(double &dXMin, double &dYMin, double &dXMax,
2063 : double &dYMax, GBool bForce /*= TRUE*/)
2064 : {
2065 0 : if (m_bBoundsSet == FALSE && bForce == FALSE)
2066 : {
2067 0 : return -1;
2068 : }
2069 0 : else if (m_bBoundsSet == FALSE)
2070 : {
2071 0 : PreParseFile();
2072 : }
2073 :
2074 0 : if (m_bBoundsSet == FALSE)
2075 : {
2076 0 : return -1;
2077 : }
2078 :
2079 0 : dXMin = m_dXMin;
2080 0 : dXMax = m_dXMax;
2081 0 : dYMin = m_dYMin;
2082 0 : dYMax = m_dYMax;
2083 :
2084 0 : return 0;
2085 : }
2086 :
2087 : /**********************************************************************
2088 : * MIFFile::GetExtent()
2089 : *
2090 : * Fetch extent of the data currently stored in the dataset. We collect
2091 : * this information while preparsing the file ... often already done for
2092 : * other reasons, and if not it is still faster than fully reading all
2093 : * the features just to count them.
2094 : *
2095 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
2096 : **********************************************************************/
2097 4 : OGRErr MIFFile::GetExtent(OGREnvelope *psExtent, int bForce)
2098 : {
2099 4 : if (bForce == TRUE)
2100 4 : PreParseFile();
2101 :
2102 4 : if (m_bPreParsed && m_bExtentsSet)
2103 : {
2104 4 : *psExtent = m_sExtents;
2105 4 : return OGRERR_NONE;
2106 : }
2107 : else
2108 0 : return OGRERR_FAILURE;
2109 : }
2110 :
2111 : /************************************************************************/
2112 : /* TestCapability() */
2113 : /************************************************************************/
2114 :
2115 215 : int MIFFile::TestCapability(const char *pszCap)
2116 :
2117 : {
2118 215 : if (EQUAL(pszCap, OLCRandomRead))
2119 0 : return TRUE;
2120 :
2121 215 : else if (EQUAL(pszCap, OLCSequentialWrite))
2122 1 : return TRUE;
2123 :
2124 214 : else if (EQUAL(pszCap, OLCFastFeatureCount))
2125 0 : return m_bPreParsed;
2126 :
2127 214 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2128 0 : return FALSE;
2129 :
2130 214 : else if (EQUAL(pszCap, OLCFastGetExtent))
2131 2 : return m_bPreParsed;
2132 :
2133 212 : else if (EQUAL(pszCap, OLCCreateField))
2134 2 : return TRUE;
2135 :
2136 210 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2137 21 : return TestUtf8Capability();
2138 :
2139 : else
2140 189 : return FALSE;
2141 : }
|