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