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