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