LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_feature_mif.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 742 1079 68.8 %
Date: 2024-11-21 22:18:42 Functions: 17 28 60.7 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_feature.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of R/W Fcts for (Mid/Mif) in feature classes
       7             :  *           specific to MapInfo files.
       8             :  * Author:   Stephane Villeneuve, stephane.v@videotron.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999-2002, Stephane Villeneuve
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  **********************************************************************/
      15             : 
      16             : #include "cpl_port.h"
      17             : #include "mitab.h"
      18             : 
      19             : #include <cctype>
      20             : #include <cmath>
      21             : #include <cstdio>
      22             : #include <cstdlib>
      23             : #include <cstring>
      24             : #include <algorithm>
      25             : 
      26             : #include "cpl_conv.h"
      27             : #include "cpl_error.h"
      28             : #include "cpl_string.h"
      29             : #include "cpl_vsi.h"
      30             : #include "mitab_priv.h"
      31             : #include "mitab_utils.h"
      32             : #include "ogr_core.h"
      33             : #include "ogr_feature.h"
      34             : #include "ogr_geometry.h"
      35             : 
      36             : /*=====================================================================
      37             :  *                      class TABFeature
      38             :  *====================================================================*/
      39             : 
      40             : /**********************************************************************
      41             :  *                   TABFeature::ReadRecordFromMIDFile()
      42             :  *
      43             :  *  This method is used to read the Record (Attributes) for all type of
      44             :  *  features included in a mid/mif file.
      45             :  *
      46             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
      47             :  * been called.
      48             :  **********************************************************************/
      49         362 : int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp)
      50             : {
      51             : #ifdef MITAB_USE_OFTDATETIME
      52         362 :     int nYear = 0;
      53         362 :     int nMonth = 0;
      54         362 :     int nDay = 0;
      55         362 :     int nHour = 0;
      56         362 :     int nMin = 0;
      57         362 :     int nSec = 0;
      58         362 :     int nMS = 0;
      59             :     // int nTZFlag = 0;
      60             : #endif
      61             : 
      62         362 :     const int nFields = GetFieldCount();
      63             : 
      64         362 :     char **papszToken = fp->GetTokenizedNextLine();
      65         362 :     if (papszToken == nullptr)
      66             :     {
      67           0 :         CPLError(
      68             :             CE_Failure, CPLE_FileIO,
      69             :             "Unexpected EOF while reading attribute record from MID file.");
      70           0 :         return -1;
      71             :     }
      72             : 
      73             :     // Ensure that a blank line in a mid file is treated as one field
      74             :     // containing an empty string.
      75         362 :     if (nFields == 1 && CSLCount(papszToken) == 0)
      76           8 :         papszToken = CSLAddString(papszToken, "");
      77             : 
      78             :     // Make sure we found at least the expected number of field values.
      79             :     // Note that it is possible to have a stray delimiter at the end of
      80             :     // the line (mif/mid files from Geomedia), so don't produce an error
      81             :     // if we find more tokens than expected.
      82         362 :     if (CSLCount(papszToken) < nFields)
      83             :     {
      84           0 :         CSLDestroy(papszToken);
      85           0 :         return -1;
      86             :     }
      87             : 
      88        1472 :     for (int i = 0; i < nFields; i++)
      89             :     {
      90        1110 :         const auto poFDefn = GetFieldDefnRef(i);
      91        1110 :         switch (poFDefn->GetType())
      92             :         {
      93             : #ifdef MITAB_USE_OFTDATETIME
      94           6 :             case OFTTime:
      95             :             {
      96           6 :                 if (strlen(papszToken[i]) == 9)
      97             :                 {
      98           6 :                     sscanf(papszToken[i], "%2d%2d%2d%3d", &nHour, &nMin, &nSec,
      99             :                            &nMS);
     100           6 :                     SetField(i, nYear, nMonth, nDay, nHour, nMin,
     101           6 :                              static_cast<float>(nSec + nMS / 1000.0f), 0);
     102             :                 }
     103           6 :                 break;
     104             :             }
     105           6 :             case OFTDate:
     106             :             {
     107           6 :                 if (strlen(papszToken[i]) == 8)
     108             :                 {
     109           6 :                     sscanf(papszToken[i], "%4d%2d%2d", &nYear, &nMonth, &nDay);
     110           6 :                     SetField(i, nYear, nMonth, nDay, nHour, nMin,
     111             :                              static_cast<float>(nSec), 0);
     112             :                 }
     113           6 :                 break;
     114             :             }
     115           6 :             case OFTDateTime:
     116             :             {
     117           6 :                 if (strlen(papszToken[i]) == 17)
     118             :                 {
     119           6 :                     sscanf(papszToken[i], "%4d%2d%2d%2d%2d%2d%3d", &nYear,
     120             :                            &nMonth, &nDay, &nHour, &nMin, &nSec, &nMS);
     121           6 :                     SetField(i, nYear, nMonth, nDay, nHour, nMin,
     122           6 :                              static_cast<float>(nSec + nMS / 1000.0f), 0);
     123             :                 }
     124           6 :                 break;
     125             :             }
     126             : #endif
     127         361 :             case OFTInteger:
     128             :             {
     129         361 :                 if (poFDefn->GetSubType() == OFSTBoolean)
     130             :                 {
     131           8 :                     char ch = papszToken[i][0];
     132          13 :                     SetField(i, (ch == 'T' || ch == 't' || ch == 'Y' ||
     133           5 :                                  ch == 'y' || ch == '1')
     134             :                                     ? 1
     135             :                                     : 0);
     136             :                 }
     137             :                 else
     138             :                 {
     139         353 :                     SetField(i, papszToken[i]);
     140             :                 }
     141         361 :                 break;
     142             :             }
     143         394 :             case OFTString:
     144             :             {
     145         788 :                 CPLString osValue(papszToken[i]);
     146         394 :                 if (!fp->GetEncoding().empty())
     147             :                 {
     148          80 :                     osValue.Recode(fp->GetEncoding(), CPL_ENC_UTF8);
     149             :                 }
     150         394 :                 SetField(i, osValue);
     151         394 :                 break;
     152             :             }
     153         337 :             default:
     154         337 :                 SetField(i, papszToken[i]);
     155             :         }
     156             :     }
     157             : 
     158         362 :     CSLDestroy(papszToken);
     159             : 
     160         362 :     return 0;
     161             : }
     162             : 
     163             : /**********************************************************************
     164             :  *                   TABFeature::WriteRecordToMIDFile()
     165             :  *
     166             :  *  This method is used to write the Record (Attributes) for all type
     167             :  *  of feature included in a mid file.
     168             :  *
     169             :  *  Return 0 on success, -1 on error
     170             :  **********************************************************************/
     171         118 : int TABFeature::WriteRecordToMIDFile(MIDDATAFile *fp)
     172             : {
     173         118 :     CPLAssert(fp);
     174             : 
     175             : #ifdef MITAB_USE_OFTDATETIME
     176             :     char szBuffer[20];
     177         118 :     int nYear = 0;
     178         118 :     int nMonth = 0;
     179         118 :     int nDay = 0;
     180         118 :     int nHour = 0;
     181         118 :     int nMin = 0;
     182             :     // int nMS = 0;
     183         118 :     int nTZFlag = 0;
     184         118 :     float fSec = 0.0f;
     185             : #endif
     186             : 
     187         118 :     const char *delimiter = fp->GetDelimiter();
     188             : 
     189         118 :     OGRFieldDefn *poFDefn = nullptr;
     190         118 :     const int numFields = GetFieldCount();
     191             : 
     192         308 :     for (int iField = 0; iField < numFields; iField++)
     193             :     {
     194         190 :         if (iField != 0)
     195          72 :             fp->WriteLine("%s", delimiter);
     196         190 :         poFDefn = GetFieldDefnRef(iField);
     197             : 
     198         190 :         switch (poFDefn->GetType())
     199             :         {
     200         105 :             case OFTString:
     201             :             {
     202         210 :                 CPLString osString(GetFieldAsString(iField));
     203             : 
     204         105 :                 if (!fp->GetEncoding().empty())
     205             :                 {
     206          18 :                     osString.Recode(CPL_ENC_UTF8, fp->GetEncoding());
     207             :                 }
     208             : 
     209         105 :                 int nStringLen = static_cast<int>(osString.length());
     210         105 :                 const char *pszString = osString.c_str();
     211             :                 char *pszWorkString = static_cast<char *>(
     212         105 :                     CPLMalloc((2 * (nStringLen) + 1) * sizeof(char)));
     213         105 :                 int j = 0;
     214         770 :                 for (int i = 0; i < nStringLen; ++i)
     215             :                 {
     216         665 :                     if (pszString[i] == '"')
     217             :                     {
     218           0 :                         pszWorkString[j] = pszString[i];
     219           0 :                         ++j;
     220           0 :                         pszWorkString[j] = pszString[i];
     221             :                     }
     222         665 :                     else if (pszString[i] == '\n')
     223             :                     {
     224           0 :                         pszWorkString[j] = '\\';
     225           0 :                         ++j;
     226           0 :                         pszWorkString[j] = 'n';
     227             :                     }
     228             :                     else
     229         665 :                         pszWorkString[j] = pszString[i];
     230         665 :                     ++j;
     231             :                 }
     232             : 
     233         105 :                 pszWorkString[j] = '\0';
     234         105 :                 fp->WriteLine("\"%s\"", pszWorkString);
     235         105 :                 CPLFree(pszWorkString);
     236         105 :                 break;
     237             :             }
     238             : #ifdef MITAB_USE_OFTDATETIME
     239           2 :             case OFTTime:
     240             :             {
     241           2 :                 if (!IsFieldSetAndNotNull(iField))
     242             :                 {
     243           0 :                     szBuffer[0] = '\0';
     244             :                 }
     245             :                 else
     246             :                 {
     247           2 :                     GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, &nHour,
     248             :                                        &nMin, &fSec, &nTZFlag);
     249           2 :                     snprintf(szBuffer, sizeof(szBuffer), "%2.2d%2.2d%2.2d%3.3d",
     250             :                              nHour, nMin, static_cast<int>(fSec),
     251             :                              OGR_GET_MS(fSec));
     252             :                 }
     253           2 :                 fp->WriteLine("%s", szBuffer);
     254           2 :                 break;
     255             :             }
     256           2 :             case OFTDate:
     257             :             {
     258           2 :                 if (!IsFieldSetAndNotNull(iField))
     259             :                 {
     260           0 :                     szBuffer[0] = '\0';
     261             :                 }
     262             :                 else
     263             :                 {
     264           2 :                     GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, &nHour,
     265             :                                        &nMin, &fSec, &nTZFlag);
     266           2 :                     snprintf(szBuffer, sizeof(szBuffer), "%4.4d%2.2d%2.2d",
     267             :                              nYear, nMonth, nDay);
     268             :                 }
     269           2 :                 fp->WriteLine("%s", szBuffer);
     270           2 :                 break;
     271             :             }
     272           2 :             case OFTDateTime:
     273             :             {
     274           2 :                 if (!IsFieldSetAndNotNull(iField))
     275             :                 {
     276           0 :                     szBuffer[0] = '\0';
     277             :                 }
     278             :                 else
     279             :                 {
     280           2 :                     GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, &nHour,
     281             :                                        &nMin, &fSec, &nTZFlag);
     282           2 :                     snprintf(szBuffer, sizeof(szBuffer),
     283             :                              "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d%3.3d", nYear,
     284             :                              nMonth, nDay, nHour, nMin, static_cast<int>(fSec),
     285             :                              OGR_GET_MS(fSec));
     286             :                 }
     287           2 :                 fp->WriteLine("%s", szBuffer);
     288           2 :                 break;
     289             :             }
     290             : #endif
     291          51 :             case OFTInteger:
     292             :             {
     293          51 :                 if (poFDefn->GetSubType() == OFSTBoolean)
     294             :                 {
     295           2 :                     fp->WriteLine("%c", GetFieldAsInteger(iField) ? 'T' : 'F');
     296             :                 }
     297             :                 else
     298             :                 {
     299          49 :                     fp->WriteLine("%s", GetFieldAsString(iField));
     300             :                 }
     301          51 :                 break;
     302             :             }
     303          28 :             default:
     304          28 :                 fp->WriteLine("%s", GetFieldAsString(iField));
     305             :         }
     306             :     }
     307             : 
     308         118 :     fp->WriteLine("\n");
     309             : 
     310         118 :     return 0;
     311             : }
     312             : 
     313             : /**********************************************************************
     314             :  *                   TABFeature::ReadGeometryFromMIFFile()
     315             :  *
     316             :  * In derived classes, this method should be reimplemented to
     317             :  * fill the geometry and representation (color, etc...) part of the
     318             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     319             :  *
     320             :  * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
     321             :  * currently points to the beginning of a map object.
     322             :  *
     323             :  * The current implementation does nothing since instances of TABFeature
     324             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     325             :  *
     326             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     327             :  * been called.
     328             :  **********************************************************************/
     329          22 : int TABFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     330             : {
     331          22 :     const char *pszLine = nullptr;
     332             : 
     333             :     /* Go to the first line of the next feature */
     334             : 
     335          37 :     while (((pszLine = fp->GetLine()) != nullptr) &&
     336          15 :            fp->IsValidFeature(pszLine) == FALSE)
     337             :         ;
     338             : 
     339          22 :     return 0;
     340             : }
     341             : 
     342             : /**********************************************************************
     343             :  *                   TABFeature::WriteGeometryToMIFFile()
     344             :  *
     345             :  *
     346             :  * In derived classes, this method should be reimplemented to
     347             :  * write the geometry and representation (color, etc...) part of the
     348             :  * feature to the .MAP object pointed to by poMAPFile.
     349             :  *
     350             :  * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
     351             :  * currently points to a valid map object.
     352             :  *
     353             :  * The current implementation does nothing since instances of TABFeature
     354             :  * objects contain no geometry.
     355             :  *
     356             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     357             :  * been called.
     358             :  **********************************************************************/
     359          15 : int TABFeature::WriteGeometryToMIFFile(MIDDATAFile *fp)
     360             : {
     361          15 :     fp->WriteLine("NONE\n");
     362          15 :     return 0;
     363             : }
     364             : 
     365             : /**********************************************************************
     366             :  *
     367             :  **********************************************************************/
     368         752 : int TABPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     369             : {
     370             :     char **papszToken =
     371         752 :         CSLTokenizeString2(fp->GetSavedLine(), " \t", CSLT_HONOURSTRINGS);
     372             : 
     373         752 :     if (CSLCount(papszToken) != 3)
     374             :     {
     375           0 :         CSLDestroy(papszToken);
     376           0 :         return -1;
     377             :     }
     378             : 
     379         752 :     const double dfX = fp->GetXTrans(CPLAtof(papszToken[1]));
     380         752 :     const double dfY = fp->GetYTrans(CPLAtof(papszToken[2]));
     381             : 
     382         752 :     CSLDestroy(papszToken);
     383         752 :     papszToken = nullptr;
     384             : 
     385             :     // Read optional SYMBOL line...
     386         752 :     const char *pszLine = fp->GetLastLine();
     387         752 :     if (pszLine != nullptr)
     388         746 :         papszToken = CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
     389         781 :     if (papszToken != nullptr && CSLCount(papszToken) == 4 &&
     390          29 :         EQUAL(papszToken[0], "SYMBOL"))
     391             :     {
     392          29 :         SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
     393          29 :         SetSymbolColor(atoi(papszToken[2]));
     394          29 :         SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
     395             :     }
     396             : 
     397         752 :     CSLDestroy(papszToken);
     398         752 :     papszToken = nullptr;
     399             : 
     400             :     // scan until we reach 1st line of next feature
     401             :     // Since SYMBOL is optional, we have to test IsValidFeature() on that
     402             :     // line as well.
     403        1502 :     while (pszLine && fp->IsValidFeature(pszLine) == FALSE)
     404             :     {
     405         750 :         pszLine = fp->GetLine();
     406             :     }
     407             : 
     408         752 :     OGRGeometry *poGeometry = new OGRPoint(dfX, dfY);
     409             : 
     410         752 :     SetGeometryDirectly(poGeometry);
     411             : 
     412         752 :     SetMBR(dfX, dfY, dfX, dfY);
     413             : 
     414         752 :     return 0;
     415             : }
     416             : 
     417             : /**********************************************************************
     418             :  *
     419             :  **********************************************************************/
     420          82 : int TABPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
     421             : {
     422             :     /*-----------------------------------------------------------------
     423             :      * Fetch and validate geometry
     424             :      *----------------------------------------------------------------*/
     425          82 :     OGRGeometry *poGeom = GetGeometryRef();
     426          82 :     OGRPoint *poPoint = nullptr;
     427          82 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     428          82 :         poPoint = poGeom->toPoint();
     429             :     else
     430             :     {
     431           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     432             :                  "TABPoint: Missing or Invalid Geometry!");
     433           0 :         return -1;
     434             :     }
     435             : 
     436          82 :     fp->WriteLine("Point %.15g %.15g\n", poPoint->getX(), poPoint->getY());
     437          82 :     fp->WriteLine("    Symbol (%d,%d,%d)\n", GetSymbolNo(), GetSymbolColor(),
     438          82 :                   GetSymbolSize());
     439             : 
     440          82 :     return 0;
     441             : }
     442             : 
     443             : /**********************************************************************
     444             :  *
     445             :  **********************************************************************/
     446         635 : int TABFontPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     447             : {
     448             :     char **papszToken =
     449         635 :         CSLTokenizeString2(fp->GetSavedLine(), " \t", CSLT_HONOURSTRINGS);
     450             : 
     451         635 :     if (CSLCount(papszToken) != 3)
     452             :     {
     453           0 :         CSLDestroy(papszToken);
     454           0 :         return -1;
     455             :     }
     456             : 
     457         635 :     const double dfX = fp->GetXTrans(CPLAtof(papszToken[1]));
     458         635 :     const double dfY = fp->GetYTrans(CPLAtof(papszToken[2]));
     459             : 
     460         635 :     CSLDestroy(papszToken);
     461             : 
     462             :     papszToken =
     463         635 :         CSLTokenizeStringComplex(fp->GetLastLine(), " ,()\t", TRUE, FALSE);
     464             : 
     465         635 :     if (CSLCount(papszToken) != 7)
     466             :     {
     467           0 :         CSLDestroy(papszToken);
     468           0 :         return -1;
     469             :     }
     470             : 
     471         635 :     SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
     472         635 :     SetSymbolColor(atoi(papszToken[2]));
     473         635 :     SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
     474         635 :     SetFontName(papszToken[4]);
     475         635 :     SetFontStyleMIFValue(atoi(papszToken[5]));
     476         635 :     SetSymbolAngle(CPLAtof(papszToken[6]));
     477             : 
     478         635 :     CSLDestroy(papszToken);
     479             : 
     480         635 :     OGRGeometry *poGeometry = new OGRPoint(dfX, dfY);
     481             : 
     482         635 :     SetGeometryDirectly(poGeometry);
     483             : 
     484         635 :     SetMBR(dfX, dfY, dfX, dfY);
     485             : 
     486             :     /* Go to the first line of the next feature */
     487             : 
     488         635 :     const char *pszLine = nullptr;
     489        1902 :     while (((pszLine = fp->GetLine()) != nullptr) &&
     490        1257 :            fp->IsValidFeature(pszLine) == FALSE)
     491             :         ;
     492         635 :     return 0;
     493             : }
     494             : 
     495             : /**********************************************************************
     496             :  *
     497             :  **********************************************************************/
     498           0 : int TABFontPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
     499             : {
     500             :     /*-----------------------------------------------------------------
     501             :      * Fetch and validate geometry
     502             :      *----------------------------------------------------------------*/
     503           0 :     OGRGeometry *poGeom = GetGeometryRef();
     504           0 :     OGRPoint *poPoint = nullptr;
     505           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     506           0 :         poPoint = poGeom->toPoint();
     507             :     else
     508             :     {
     509           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     510             :                  "TABFontPoint: Missing or Invalid Geometry!");
     511           0 :         return -1;
     512             :     }
     513             : 
     514           0 :     fp->WriteLine("Point %.15g %.15g\n", poPoint->getX(), poPoint->getY());
     515           0 :     fp->WriteLine("    Symbol (%d,%d,%d,\"%s\",%d,%.15g)\n", GetSymbolNo(),
     516           0 :                   GetSymbolColor(), GetSymbolSize(), GetFontNameRef(),
     517             :                   GetFontStyleMIFValue(), GetSymbolAngle());
     518             : 
     519           0 :     return 0;
     520             : }
     521             : 
     522             : /**********************************************************************
     523             :  *
     524             :  **********************************************************************/
     525         678 : int TABCustomPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     526             : {
     527             :     char **papszToken =
     528         678 :         CSLTokenizeString2(fp->GetSavedLine(), " \t", CSLT_HONOURSTRINGS);
     529             : 
     530         678 :     if (CSLCount(papszToken) != 3)
     531             :     {
     532           0 :         CSLDestroy(papszToken);
     533           0 :         return -1;
     534             :     }
     535             : 
     536         678 :     double dfX = fp->GetXTrans(CPLAtof(papszToken[1]));
     537         678 :     double dfY = fp->GetYTrans(CPLAtof(papszToken[2]));
     538             : 
     539         678 :     CSLDestroy(papszToken);
     540             : 
     541             :     papszToken =
     542         678 :         CSLTokenizeStringComplex(fp->GetLastLine(), " ,()\t", TRUE, FALSE);
     543         678 :     if (CSLCount(papszToken) != 5)
     544             :     {
     545             : 
     546           0 :         CSLDestroy(papszToken);
     547           0 :         return -1;
     548             :     }
     549             : 
     550         678 :     SetFontName(papszToken[1]);
     551         678 :     SetSymbolColor(atoi(papszToken[2]));
     552         678 :     SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
     553         678 :     m_nCustomStyle = static_cast<GByte>(atoi(papszToken[4]));
     554             : 
     555         678 :     CSLDestroy(papszToken);
     556             : 
     557         678 :     OGRGeometry *poGeometry = new OGRPoint(dfX, dfY);
     558             : 
     559         678 :     SetGeometryDirectly(poGeometry);
     560             : 
     561         678 :     SetMBR(dfX, dfY, dfX, dfY);
     562             : 
     563             :     /* Go to the first line of the next feature */
     564             : 
     565         678 :     const char *pszLine = nullptr;
     566        2030 :     while (((pszLine = fp->GetLine()) != nullptr) &&
     567        1339 :            fp->IsValidFeature(pszLine) == FALSE)
     568             :         ;
     569             : 
     570         678 :     return 0;
     571             : }
     572             : 
     573             : /**********************************************************************
     574             :  *
     575             :  **********************************************************************/
     576           0 : int TABCustomPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
     577             : {
     578             :     /*-----------------------------------------------------------------
     579             :      * Fetch and validate geometry
     580             :      *----------------------------------------------------------------*/
     581           0 :     OGRGeometry *poGeom = GetGeometryRef();
     582           0 :     OGRPoint *poPoint = nullptr;
     583           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     584           0 :         poPoint = poGeom->toPoint();
     585             :     else
     586             :     {
     587           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     588             :                  "TABCustomPoint: Missing or Invalid Geometry!");
     589           0 :         return -1;
     590             :     }
     591             : 
     592           0 :     fp->WriteLine("Point %.15g %.15g\n", poPoint->getX(), poPoint->getY());
     593           0 :     fp->WriteLine("    Symbol (\"%s\",%d,%d,%d)\n", GetFontNameRef(),
     594           0 :                   GetSymbolColor(), GetSymbolSize(), m_nCustomStyle);
     595             : 
     596           0 :     return 0;
     597             : }
     598             : 
     599             : /**********************************************************************
     600             :  *
     601             :  **********************************************************************/
     602        2433 : int TABPolyline::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     603             : {
     604             :     char **papszToken =
     605        2433 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
     606             : 
     607        2433 :     if (CSLCount(papszToken) < 1)
     608             :     {
     609           0 :         CSLDestroy(papszToken);
     610           0 :         return -1;
     611             :     }
     612             : 
     613        2433 :     const char *pszLine = nullptr;
     614        2433 :     OGRLineString *poLine = nullptr;
     615        2433 :     OGRMultiLineString *poMultiLine = nullptr;
     616        2433 :     GBool bMultiple = FALSE;
     617        2433 :     int nNumPoints = 0;
     618        2433 :     int nNumSec = 0;
     619        2433 :     OGREnvelope sEnvelope;
     620             : 
     621        2433 :     if (STARTS_WITH_CI(papszToken[0], "LINE"))
     622             :     {
     623         666 :         if (CSLCount(papszToken) != 5)
     624             :         {
     625          16 :             CSLDestroy(papszToken);
     626          16 :             return -1;
     627             :         }
     628             : 
     629         650 :         poLine = new OGRLineString();
     630         650 :         poLine->setNumPoints(2);
     631         650 :         poLine->setPoint(0, fp->GetXTrans(CPLAtof(papszToken[1])),
     632         650 :                          fp->GetYTrans(CPLAtof(papszToken[2])));
     633         650 :         poLine->setPoint(1, fp->GetXTrans(CPLAtof(papszToken[3])),
     634         650 :                          fp->GetYTrans(CPLAtof(papszToken[4])));
     635         650 :         poLine->getEnvelope(&sEnvelope);
     636         650 :         SetGeometryDirectly(poLine);
     637         650 :         SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
     638             :     }
     639        1767 :     else if (STARTS_WITH_CI(papszToken[0], "PLINE"))
     640             :     {
     641        1767 :         switch (CSLCount(papszToken))
     642             :         {
     643         614 :             case 1:
     644         614 :                 bMultiple = FALSE;
     645         614 :                 pszLine = fp->GetLine();
     646         614 :                 if (pszLine == nullptr)
     647             :                 {
     648           6 :                     CSLDestroy(papszToken);
     649           6 :                     return -1;
     650             :                 }
     651         608 :                 nNumPoints = atoi(pszLine);
     652         608 :                 break;
     653         598 :             case 2:
     654         598 :                 bMultiple = FALSE;
     655         598 :                 nNumPoints = atoi(papszToken[1]);
     656         598 :                 break;
     657         555 :             case 3:
     658         555 :                 if (STARTS_WITH_CI(papszToken[1], "MULTIPLE"))
     659             :                 {
     660         555 :                     bMultiple = TRUE;
     661         555 :                     nNumSec = atoi(papszToken[2]);
     662         555 :                     pszLine = fp->GetLine();
     663         555 :                     if (pszLine == nullptr)
     664             :                     {
     665           2 :                         CSLDestroy(papszToken);
     666           2 :                         return -1;
     667             :                     }
     668         553 :                     nNumPoints = atoi(pszLine);
     669         553 :                     break;
     670             :                 }
     671             :                 else
     672             :                 {
     673           0 :                     CSLDestroy(papszToken);
     674           0 :                     return -1;
     675             :                 }
     676             :                 break;
     677           0 :             case 4:
     678           0 :                 if (STARTS_WITH_CI(papszToken[1], "MULTIPLE"))
     679             :                 {
     680           0 :                     bMultiple = TRUE;
     681           0 :                     nNumSec = atoi(papszToken[2]);
     682           0 :                     nNumPoints = atoi(papszToken[3]);
     683           0 :                     break;
     684             :                 }
     685             :                 else
     686             :                 {
     687           0 :                     CSLDestroy(papszToken);
     688           0 :                     return -1;
     689             :                 }
     690             :                 break;
     691           0 :             default:
     692           0 :                 CSLDestroy(papszToken);
     693           0 :                 return -1;
     694             :                 break;
     695             :         }
     696             : 
     697        1759 :         if (bMultiple)
     698             :         {
     699         553 :             poMultiLine = new OGRMultiLineString();
     700        1615 :             for (int j = 0; j < nNumSec; j++)
     701             :             {
     702        1092 :                 if (j != 0)
     703             :                 {
     704         539 :                     pszLine = fp->GetLine();
     705         539 :                     if (pszLine == nullptr)
     706             :                     {
     707           2 :                         delete poMultiLine;
     708           2 :                         CSLDestroy(papszToken);
     709           2 :                         return -1;
     710             :                     }
     711         537 :                     nNumPoints = atoi(pszLine);
     712             :                 }
     713        1090 :                 if (nNumPoints < 2)
     714             :                 {
     715           4 :                     CPLError(CE_Failure, CPLE_FileIO,
     716             :                              "Invalid number of vertices (%d) in PLINE "
     717             :                              "MULTIPLE segment.",
     718             :                              nNumPoints);
     719           4 :                     delete poMultiLine;
     720           4 :                     CSLDestroy(papszToken);
     721           4 :                     return -1;
     722             :                 }
     723        1086 :                 poLine = new OGRLineString();
     724        1086 :                 const int MAX_INITIAL_POINTS = 100000;
     725        2172 :                 const int nInitialNumPoints = (nNumPoints < MAX_INITIAL_POINTS)
     726        1086 :                                                   ? nNumPoints
     727             :                                                   : MAX_INITIAL_POINTS;
     728             :                 /* Do not allocate too much memory to begin with */
     729        1086 :                 poLine->setNumPoints(nInitialNumPoints);
     730        1086 :                 if (poLine->getNumPoints() != nInitialNumPoints)
     731             :                 {
     732           0 :                     delete poLine;
     733           0 :                     delete poMultiLine;
     734           0 :                     CSLDestroy(papszToken);
     735           0 :                     return -1;
     736             :                 }
     737        3222 :                 for (int i = 0; i < nNumPoints; i++)
     738             :                 {
     739        2160 :                     if (i == MAX_INITIAL_POINTS)
     740             :                     {
     741           0 :                         poLine->setNumPoints(nNumPoints);
     742           0 :                         if (poLine->getNumPoints() != nNumPoints)
     743             :                         {
     744           0 :                             delete poLine;
     745           0 :                             delete poMultiLine;
     746           0 :                             CSLDestroy(papszToken);
     747           0 :                             return -1;
     748             :                         }
     749             :                     }
     750        2160 :                     CSLDestroy(papszToken);
     751        2160 :                     papszToken = CSLTokenizeString2(fp->GetLine(), " \t",
     752             :                                                     CSLT_HONOURSTRINGS);
     753        2160 :                     if (CSLCount(papszToken) != 2)
     754             :                     {
     755          24 :                         CSLDestroy(papszToken);
     756          24 :                         delete poLine;
     757          24 :                         delete poMultiLine;
     758          24 :                         return -1;
     759             :                     }
     760        2136 :                     poLine->setPoint(i, fp->GetXTrans(CPLAtof(papszToken[0])),
     761        2136 :                                      fp->GetYTrans(CPLAtof(papszToken[1])));
     762             :                 }
     763        1062 :                 if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
     764             :                 {
     765           0 :                     CPLAssert(false);  // Just in case OGR is modified
     766             :                 }
     767             :             }
     768         523 :             poMultiLine->getEnvelope(&sEnvelope);
     769         523 :             if (SetGeometryDirectly(poMultiLine) != OGRERR_NONE)
     770             :             {
     771           0 :                 CPLAssert(false);  // Just in case OGR is modified
     772             :             }
     773         523 :             SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
     774             :                    sEnvelope.MaxY);
     775             :         }
     776             :         else
     777             :         {
     778        1206 :             if (nNumPoints < 2)
     779             :             {
     780          11 :                 CPLError(CE_Failure, CPLE_FileIO,
     781             :                          "Invalid number of vertices (%d) in PLINE "
     782             :                          "segment.",
     783             :                          nNumPoints);
     784          11 :                 CSLDestroy(papszToken);
     785          11 :                 return -1;
     786             :             }
     787        1195 :             poLine = new OGRLineString();
     788        1195 :             const int MAX_INITIAL_POINTS = 100000;
     789        2390 :             const int nInitialNumPoints = (nNumPoints < MAX_INITIAL_POINTS)
     790        1195 :                                               ? nNumPoints
     791             :                                               : MAX_INITIAL_POINTS;
     792             :             /* Do not allocate too much memory to begin with */
     793        1195 :             poLine->setNumPoints(nInitialNumPoints);
     794        1195 :             if (poLine->getNumPoints() != nInitialNumPoints)
     795             :             {
     796           0 :                 delete poLine;
     797           0 :                 CSLDestroy(papszToken);
     798           0 :                 return -1;
     799             :             }
     800        3555 :             for (int i = 0; i < nNumPoints; i++)
     801             :             {
     802        2384 :                 if (i == MAX_INITIAL_POINTS)
     803             :                 {
     804           0 :                     poLine->setNumPoints(nNumPoints);
     805           0 :                     if (poLine->getNumPoints() != nNumPoints)
     806             :                     {
     807           0 :                         delete poLine;
     808           0 :                         CSLDestroy(papszToken);
     809           0 :                         return -1;
     810             :                     }
     811             :                 }
     812        2384 :                 CSLDestroy(papszToken);
     813        2384 :                 papszToken = CSLTokenizeString2(fp->GetLine(), " \t",
     814             :                                                 CSLT_HONOURSTRINGS);
     815             : 
     816        2384 :                 if (CSLCount(papszToken) != 2)
     817             :                 {
     818          24 :                     CSLDestroy(papszToken);
     819          24 :                     delete poLine;
     820          24 :                     return -1;
     821             :                 }
     822        2360 :                 poLine->setPoint(i, fp->GetXTrans(CPLAtof(papszToken[0])),
     823        2360 :                                  fp->GetYTrans(CPLAtof(papszToken[1])));
     824             :             }
     825        1171 :             poLine->getEnvelope(&sEnvelope);
     826        1171 :             SetGeometryDirectly(poLine);
     827        1171 :             SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
     828             :                    sEnvelope.MaxY);
     829             :         }
     830             :     }
     831             : 
     832        2344 :     CSLDestroy(papszToken);
     833        2344 :     papszToken = nullptr;
     834             : 
     835       11299 :     while (((pszLine = fp->GetLine()) != nullptr) &&
     836        5618 :            fp->IsValidFeature(pszLine) == FALSE)
     837             :     {
     838        3337 :         papszToken = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
     839             : 
     840        3337 :         if (CSLCount(papszToken) >= 1)
     841             :         {
     842        1053 :             if (STARTS_WITH_CI(papszToken[0], "PEN"))
     843             :             {
     844             : 
     845         521 :                 if (CSLCount(papszToken) == 4)
     846             :                 {
     847         515 :                     SetPenWidthMIF(atoi(papszToken[1]));
     848         515 :                     SetPenPattern(static_cast<GByte>(atoi(papszToken[2])));
     849         515 :                     SetPenColor(atoi(papszToken[3]));
     850             :                 }
     851             :             }
     852         532 :             else if (STARTS_WITH_CI(papszToken[0], "SMOOTH"))
     853             :             {
     854         501 :                 m_bSmooth = TRUE;
     855             :             }
     856             :         }
     857        3337 :         CSLDestroy(papszToken);
     858             :     }
     859        2344 :     return 0;
     860             : }
     861             : 
     862             : /**********************************************************************
     863             :  *
     864             :  **********************************************************************/
     865           0 : int TABPolyline::WriteGeometryToMIFFile(MIDDATAFile *fp)
     866             : {
     867           0 :     OGRMultiLineString *poMultiLine = nullptr;
     868           0 :     OGRLineString *poLine = nullptr;
     869             :     int nNumPoints, i;
     870             : 
     871             :     /*-----------------------------------------------------------------
     872             :      * Fetch and validate geometry
     873             :      *----------------------------------------------------------------*/
     874           0 :     OGRGeometry *poGeom = GetGeometryRef();
     875           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
     876             :     {
     877             :         /*-------------------------------------------------------------
     878             :          * Simple polyline
     879             :          *------------------------------------------------------------*/
     880           0 :         poLine = poGeom->toLineString();
     881           0 :         nNumPoints = poLine->getNumPoints();
     882           0 :         if (nNumPoints == 2)
     883             :         {
     884           0 :             fp->WriteLine("Line %.15g %.15g %.15g %.15g\n", poLine->getX(0),
     885             :                           poLine->getY(0), poLine->getX(1), poLine->getY(1));
     886             :         }
     887             :         else
     888             :         {
     889             : 
     890           0 :             fp->WriteLine("Pline %d\n", nNumPoints);
     891           0 :             for (i = 0; i < nNumPoints; i++)
     892             :             {
     893           0 :                 fp->WriteLine("%.15g %.15g\n", poLine->getX(i),
     894             :                               poLine->getY(i));
     895             :             }
     896             :         }
     897             :     }
     898           0 :     else if (poGeom &&
     899           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
     900             :     {
     901             :         /*-------------------------------------------------------------
     902             :          * Multiple polyline... validate all components
     903             :          *------------------------------------------------------------*/
     904             :         int iLine, numLines;
     905           0 :         poMultiLine = poGeom->toMultiLineString();
     906           0 :         numLines = poMultiLine->getNumGeometries();
     907             : 
     908           0 :         fp->WriteLine("PLINE MULTIPLE %d\n", numLines);
     909             : 
     910           0 :         for (iLine = 0; iLine < numLines; iLine++)
     911             :         {
     912           0 :             poGeom = poMultiLine->getGeometryRef(iLine);
     913           0 :             if (poGeom &&
     914           0 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
     915             :             {
     916           0 :                 poLine = poGeom->toLineString();
     917           0 :                 nNumPoints = poLine->getNumPoints();
     918             : 
     919           0 :                 fp->WriteLine("  %d\n", nNumPoints);
     920           0 :                 for (i = 0; i < nNumPoints; i++)
     921             :                 {
     922           0 :                     fp->WriteLine("%.15g %.15g\n", poLine->getX(i),
     923             :                                   poLine->getY(i));
     924             :                 }
     925             :             }
     926             :             else
     927             :             {
     928           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     929             :                          "TABPolyline: Object contains an invalid Geometry!");
     930             :             }
     931             :         }
     932             :     }
     933             :     else
     934             :     {
     935           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     936             :                  "TABPolyline: Missing or Invalid Geometry!");
     937             :     }
     938             : 
     939           0 :     if (GetPenPattern())
     940           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
     941             :                       GetPenColor());
     942           0 :     if (m_bSmooth)
     943           0 :         fp->WriteLine("    Smooth\n");
     944             : 
     945           0 :     return 0;
     946             : }
     947             : 
     948             : /**********************************************************************
     949             :  *                   TABRegion::ReadGeometryFromMIFFile()
     950             :  *
     951             :  * Fill the geometry and representation (color, etc...) part of the
     952             :  * feature from the contents of the .MIF file
     953             :  *
     954             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     955             :  * been called.
     956             :  **********************************************************************/
     957         543 : int TABRegion::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     958             : {
     959         543 :     m_bSmooth = FALSE;
     960             : 
     961             :     /*=============================================================
     962             :      * REGION (Similar to PLINE MULTIPLE)
     963             :      *============================================================*/
     964             :     CPLStringList aosTokens(
     965        1086 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
     966             : 
     967         543 :     int numLineSections = (aosTokens.size() == 2) ? atoi(aosTokens[1]) : 0;
     968         543 :     aosTokens.Clear();
     969             : 
     970         543 :     if (numLineSections < 0 ||
     971         543 :         numLineSections > INT_MAX / static_cast<int>(sizeof(OGRPolygon *)))
     972             :     {
     973           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of sections: %d",
     974             :                  numLineSections);
     975           0 :         return -1;
     976             :     }
     977             : 
     978         543 :     OGRPolygon **tabPolygons = nullptr;
     979         543 :     const int MAX_INITIAL_SECTIONS = 100000;
     980        1086 :     const int numInitialLineSections = (numLineSections < MAX_INITIAL_SECTIONS)
     981         543 :                                            ? numLineSections
     982             :                                            : MAX_INITIAL_SECTIONS;
     983         543 :     if (numLineSections > 0)
     984             :     {
     985             :         tabPolygons = static_cast<OGRPolygon **>(
     986         539 :             VSI_MALLOC2_VERBOSE(numInitialLineSections, sizeof(OGRPolygon *)));
     987         539 :         if (tabPolygons == nullptr)
     988           0 :             return -1;
     989             :     }
     990             : 
     991         543 :     const char *pszLine = nullptr;
     992             : 
     993        1016 :     for (int iSection = 0; iSection < numLineSections; iSection++)
     994             :     {
     995         539 :         if (iSection == MAX_INITIAL_SECTIONS)
     996             :         {
     997             :             OGRPolygon **newTabPolygons =
     998           0 :                 static_cast<OGRPolygon **>(VSI_REALLOC_VERBOSE(
     999             :                     tabPolygons, numLineSections * sizeof(OGRPolygon *)));
    1000           0 :             if (newTabPolygons == nullptr)
    1001             :             {
    1002           0 :                 iSection--;
    1003           0 :                 for (; iSection >= 0; --iSection)
    1004           0 :                     delete tabPolygons[iSection];
    1005           0 :                 VSIFree(tabPolygons);
    1006          66 :                 return -1;
    1007             :             }
    1008           0 :             tabPolygons = newTabPolygons;
    1009             :         }
    1010             : 
    1011         539 :         int numSectionVertices = 0;
    1012             : 
    1013         539 :         tabPolygons[iSection] = new OGRPolygon();
    1014             : 
    1015         539 :         if ((pszLine = fp->GetLine()) != nullptr)
    1016             :         {
    1017         535 :             numSectionVertices = atoi(pszLine);
    1018             :         }
    1019         539 :         if (numSectionVertices < 2)
    1020             :         {
    1021          10 :             CPLError(CE_Failure, CPLE_FileIO,
    1022             :                      "Invalid number of points (%d) in REGION "
    1023             :                      "segment.",
    1024             :                      numSectionVertices);
    1025          20 :             for (; iSection >= 0; --iSection)
    1026          10 :                 delete tabPolygons[iSection];
    1027          10 :             VSIFree(tabPolygons);
    1028          10 :             return -1;
    1029             :         }
    1030             : 
    1031         529 :         auto poRing = std::make_unique<OGRLinearRing>();
    1032             : 
    1033         529 :         const int MAX_INITIAL_POINTS = 100000;
    1034        1058 :         const int nInitialNumPoints = (numSectionVertices < MAX_INITIAL_POINTS)
    1035         529 :                                           ? numSectionVertices
    1036             :                                           : MAX_INITIAL_POINTS;
    1037             :         /* Do not allocate too much memory to begin with */
    1038         529 :         poRing->setNumPoints(nInitialNumPoints);
    1039         529 :         if (poRing->getNumPoints() != nInitialNumPoints)
    1040             :         {
    1041           0 :             for (; iSection >= 0; --iSection)
    1042           0 :                 delete tabPolygons[iSection];
    1043           0 :             VSIFree(tabPolygons);
    1044           0 :             return -1;
    1045             :         }
    1046        8597 :         for (int i = 0; i < numSectionVertices; i++)
    1047             :         {
    1048        8124 :             if (i == MAX_INITIAL_POINTS)
    1049             :             {
    1050           0 :                 poRing->setNumPoints(numSectionVertices);
    1051           0 :                 if (poRing->getNumPoints() != numSectionVertices)
    1052             :                 {
    1053           0 :                     for (; iSection >= 0; --iSection)
    1054           0 :                         delete tabPolygons[iSection];
    1055           0 :                     VSIFree(tabPolygons);
    1056           0 :                     return -1;
    1057             :                 }
    1058             :             }
    1059             : 
    1060             :             aosTokens =
    1061        8124 :                 CSLTokenizeStringComplex(fp->GetLine(), " ,\t", TRUE, FALSE);
    1062        8124 :             if (aosTokens.size() < 2)
    1063             :             {
    1064         112 :                 for (; iSection >= 0; --iSection)
    1065          56 :                     delete tabPolygons[iSection];
    1066          56 :                 VSIFree(tabPolygons);
    1067          56 :                 return -1;
    1068             :             }
    1069             : 
    1070        8068 :             const double dX = fp->GetXTrans(CPLAtof(aosTokens[0]));
    1071        8068 :             const double dY = fp->GetYTrans(CPLAtof(aosTokens[1]));
    1072        8068 :             poRing->setPoint(i, dX, dY);
    1073        8068 :             aosTokens.Clear();
    1074             :         }
    1075             : 
    1076         473 :         poRing->closeRings();
    1077             : 
    1078         473 :         tabPolygons[iSection]->addRingDirectly(poRing.release());
    1079             :     }
    1080             : 
    1081           0 :     std::unique_ptr<OGRGeometry> poGeometry;
    1082         477 :     if (numLineSections == 1)
    1083             :     {
    1084         473 :         poGeometry.reset(tabPolygons[0]);
    1085         473 :         tabPolygons[0] = nullptr;
    1086             :     }
    1087           4 :     else if (numLineSections > 1)
    1088             :     {
    1089           0 :         int isValidGeometry = FALSE;
    1090           0 :         const char *papszOptions[] = {"METHOD=DEFAULT", nullptr};
    1091           0 :         poGeometry.reset(OGRGeometryFactory::organizePolygons(
    1092             :             reinterpret_cast<OGRGeometry **>(tabPolygons), numLineSections,
    1093             :             &isValidGeometry, papszOptions));
    1094             : 
    1095           0 :         if (!isValidGeometry)
    1096             :         {
    1097           0 :             CPLError(
    1098             :                 CE_Warning, CPLE_AppDefined,
    1099             :                 "Geometry of polygon cannot be translated to Simple Geometry. "
    1100             :                 "All polygons will be contained in a multipolygon.\n");
    1101             :         }
    1102             :     }
    1103             : 
    1104         477 :     VSIFree(tabPolygons);
    1105             : 
    1106         477 :     if (poGeometry)
    1107             :     {
    1108         473 :         OGREnvelope sEnvelope;
    1109         473 :         poGeometry->getEnvelope(&sEnvelope);
    1110         473 :         SetGeometryDirectly(poGeometry.release());
    1111             : 
    1112         473 :         SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    1113             :     }
    1114             : 
    1115        2404 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1116        1174 :            fp->IsValidFeature(pszLine) == FALSE)
    1117             :     {
    1118         753 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1119             : 
    1120         753 :         if (aosTokens.size() > 1)
    1121             :         {
    1122         630 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1123             :             {
    1124             : 
    1125         314 :                 if (aosTokens.size() == 4)
    1126             :                 {
    1127         314 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1128         314 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1129         314 :                     SetPenColor(atoi(aosTokens[3]));
    1130             :                 }
    1131             :             }
    1132         316 :             else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
    1133             :             {
    1134         314 :                 if (aosTokens.size() >= 3)
    1135             :                 {
    1136         314 :                     SetBrushFGColor(atoi(aosTokens[2]));
    1137         314 :                     SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
    1138             : 
    1139         314 :                     if (aosTokens.size() == 4)
    1140         314 :                         SetBrushBGColor(atoi(aosTokens[3]));
    1141             :                     else
    1142           0 :                         SetBrushTransparent(TRUE);
    1143             :                 }
    1144             :             }
    1145           2 :             else if (STARTS_WITH_CI(aosTokens[0], "CENTER"))
    1146             :             {
    1147           2 :                 if (aosTokens.size() == 3)
    1148             :                 {
    1149           2 :                     SetCenter(fp->GetXTrans(CPLAtof(aosTokens[1])),
    1150           2 :                               fp->GetYTrans(CPLAtof(aosTokens[2])));
    1151             :                 }
    1152             :             }
    1153             :         }
    1154         753 :         aosTokens.Clear();
    1155             :     }
    1156             : 
    1157         477 :     return 0;
    1158             : }
    1159             : 
    1160             : /**********************************************************************
    1161             :  *                   TABRegion::WriteGeometryToMIFFile()
    1162             :  *
    1163             :  * Write the geometry and representation (color, etc...) part of the
    1164             :  * feature to the .MIF file
    1165             :  *
    1166             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1167             :  * been called.
    1168             :  **********************************************************************/
    1169          21 : int TABRegion::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1170             : {
    1171          21 :     OGRGeometry *poGeom = GetGeometryRef();
    1172             : 
    1173          21 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    1174           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    1175             :     {
    1176             :         /*=============================================================
    1177             :          * REGIONs are similar to PLINE MULTIPLE
    1178             :          *
    1179             :          * We accept both OGRPolygons (with one or multiple rings) and
    1180             :          * OGRMultiPolygons as input.
    1181             :          *============================================================*/
    1182             :         int i, iRing, numRingsTotal, numPoints;
    1183             : 
    1184          21 :         numRingsTotal = GetNumRings();
    1185             : 
    1186          21 :         fp->WriteLine("Region %d\n", numRingsTotal);
    1187             : 
    1188          42 :         for (iRing = 0; iRing < numRingsTotal; iRing++)
    1189             :         {
    1190          21 :             OGRLinearRing *poRing = GetRingRef(iRing);
    1191          21 :             if (poRing == nullptr)
    1192             :             {
    1193           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    1194             :                          "TABRegion: Object Geometry contains NULL rings!");
    1195           0 :                 return -1;
    1196             :             }
    1197             : 
    1198          21 :             numPoints = poRing->getNumPoints();
    1199             : 
    1200          21 :             fp->WriteLine("  %d\n", numPoints);
    1201         516 :             for (i = 0; i < numPoints; i++)
    1202             :             {
    1203         495 :                 fp->WriteLine("%.15g %.15g\n", poRing->getX(i),
    1204             :                               poRing->getY(i));
    1205             :             }
    1206             :         }
    1207             : 
    1208          21 :         if (GetPenPattern())
    1209          42 :             fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(),
    1210          21 :                           GetPenPattern(), GetPenColor());
    1211             : 
    1212          21 :         if (GetBrushPattern())
    1213             :         {
    1214          21 :             if (GetBrushTransparent() == 0)
    1215          21 :                 fp->WriteLine("    Brush (%d,%d,%d)\n", GetBrushPattern(),
    1216             :                               GetBrushFGColor(), GetBrushBGColor());
    1217             :             else
    1218           0 :                 fp->WriteLine("    Brush (%d,%d)\n", GetBrushPattern(),
    1219             :                               GetBrushFGColor());
    1220             :         }
    1221             : 
    1222          21 :         if (m_bCenterIsSet)
    1223             :         {
    1224           0 :             fp->WriteLine("    Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    1225             :         }
    1226             :     }
    1227             :     else
    1228             :     {
    1229           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1230             :                  "TABRegion: Object contains an invalid Geometry!");
    1231           0 :         return -1;
    1232             :     }
    1233             : 
    1234          21 :     return 0;
    1235             : }
    1236             : 
    1237             : /**********************************************************************
    1238             :  *
    1239             :  **********************************************************************/
    1240         941 : int TABRectangle::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1241             : {
    1242             :     CPLStringList aosTokens(
    1243        1882 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1244             : 
    1245         941 :     if (aosTokens.size() < 5)
    1246             :     {
    1247          20 :         return -1;
    1248             :     }
    1249             : 
    1250         921 :     double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1251         921 :     double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1252         921 :     double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1253         921 :     double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1254             : 
    1255             :     /*-----------------------------------------------------------------
    1256             :      * Call SetMBR() and GetMBR() now to make sure that min values are
    1257             :      * really smaller than max values.
    1258             :      *----------------------------------------------------------------*/
    1259         921 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1260         921 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    1261             : 
    1262         921 :     m_bRoundCorners = FALSE;
    1263         921 :     m_dRoundXRadius = 0.0;
    1264         921 :     m_dRoundYRadius = 0.0;
    1265             : 
    1266         921 :     if (STARTS_WITH_CI(aosTokens[0], "ROUNDRECT"))
    1267             :     {
    1268         436 :         m_bRoundCorners = TRUE;
    1269         436 :         if (aosTokens.size() == 6)
    1270             :         {
    1271         434 :             m_dRoundXRadius = CPLAtof(aosTokens[5]) / 2.0;
    1272         434 :             m_dRoundYRadius = m_dRoundXRadius;
    1273             :         }
    1274             :         else
    1275             :         {
    1276             :             aosTokens =
    1277           2 :                 CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1278           2 :             if (aosTokens.size() == 1)
    1279           0 :                 m_dRoundXRadius = m_dRoundYRadius = CPLAtof(aosTokens[0]) / 2.0;
    1280             :         }
    1281             :     }
    1282         921 :     aosTokens.Clear();
    1283             : 
    1284             :     /*-----------------------------------------------------------------
    1285             :      * Create and fill geometry object
    1286             :      *----------------------------------------------------------------*/
    1287             : 
    1288         921 :     OGRPolygon *poPolygon = new OGRPolygon;
    1289         921 :     OGRLinearRing *poRing = new OGRLinearRing();
    1290         921 :     if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    1291             :     {
    1292             :         /*-------------------------------------------------------------
    1293             :          * For rounded rectangles, we generate arcs with 45 line
    1294             :          * segments for each corner.  We start with lower-left corner
    1295             :          * and proceed counterclockwise
    1296             :          * We also have to make sure that rounding radius is not too
    1297             :          * large for the MBR however, we
    1298             :          * always return the true X/Y radius (not adjusted) since this
    1299             :          * is the way MapInfo seems to do it when a radius bigger than
    1300             :          * the MBR is passed from TBA to MIF.
    1301             :          *------------------------------------------------------------*/
    1302             :         const double dXRadius =
    1303         434 :             std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
    1304             :         const double dYRadius =
    1305         434 :             std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
    1306         434 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
    1307             :                        dYRadius, M_PI, 3.0 * M_PI / 2.0);
    1308         434 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
    1309             :                        dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
    1310         434 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
    1311             :                        dYRadius, 0.0, M_PI / 2.0);
    1312         434 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
    1313             :                        dYRadius, M_PI / 2.0, M_PI);
    1314             : 
    1315         434 :         TABCloseRing(poRing);
    1316             :     }
    1317             :     else
    1318             :     {
    1319         487 :         poRing->addPoint(dXMin, dYMin);
    1320         487 :         poRing->addPoint(dXMax, dYMin);
    1321         487 :         poRing->addPoint(dXMax, dYMax);
    1322         487 :         poRing->addPoint(dXMin, dYMax);
    1323         487 :         poRing->addPoint(dXMin, dYMin);
    1324             :     }
    1325             : 
    1326         921 :     poPolygon->addRingDirectly(poRing);
    1327         921 :     SetGeometryDirectly(poPolygon);
    1328             : 
    1329         921 :     const char *pszLine = nullptr;
    1330        7118 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1331        3520 :            fp->IsValidFeature(pszLine) == FALSE)
    1332             :     {
    1333        2677 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1334             : 
    1335        2677 :         if (aosTokens.size() > 1)
    1336             :         {
    1337        1776 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1338             :             {
    1339         903 :                 if (aosTokens.size() == 4)
    1340             :                 {
    1341         895 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1342         895 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1343         895 :                     SetPenColor(atoi(aosTokens[3]));
    1344             :                 }
    1345             :             }
    1346         873 :             else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
    1347             :             {
    1348         873 :                 if (aosTokens.size() >= 3)
    1349             :                 {
    1350         869 :                     SetBrushFGColor(atoi(aosTokens[2]));
    1351         869 :                     SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
    1352             : 
    1353         869 :                     if (aosTokens.size() == 4)
    1354         865 :                         SetBrushBGColor(atoi(aosTokens[3]));
    1355             :                     else
    1356           4 :                         SetBrushTransparent(TRUE);
    1357             :                 }
    1358             :             }
    1359             :         }
    1360        2677 :         aosTokens.Clear();
    1361             :     }
    1362             : 
    1363         921 :     return 0;
    1364             : }
    1365             : 
    1366             : /**********************************************************************
    1367             :  *
    1368             :  **********************************************************************/
    1369           0 : int TABRectangle::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1370             : {
    1371             :     /*-----------------------------------------------------------------
    1372             :      * Fetch and validate geometry
    1373             :      *----------------------------------------------------------------*/
    1374           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1375           0 :     OGRPolygon *poPolygon = nullptr;
    1376           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    1377           0 :         poPolygon = poGeom->toPolygon();
    1378             :     else
    1379             :     {
    1380           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1381             :                  "TABRectangle: Missing or Invalid Geometry!");
    1382           0 :         return -1;
    1383             :     }
    1384             :     /*-----------------------------------------------------------------
    1385             :      * Note that we will simply use the rectangle's MBR and don't really
    1386             :      * read the polygon geometry... this should be OK unless the
    1387             :      * polygon geometry was not really a rectangle.
    1388             :      *----------------------------------------------------------------*/
    1389           0 :     OGREnvelope sEnvelope;
    1390           0 :     poPolygon->getEnvelope(&sEnvelope);
    1391             : 
    1392           0 :     if (m_bRoundCorners == TRUE)
    1393             :     {
    1394           0 :         fp->WriteLine("Roundrect %.15g %.15g %.15g %.15g %.15g\n",
    1395             :                       sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
    1396           0 :                       sEnvelope.MaxY, m_dRoundXRadius * 2.0);
    1397             :     }
    1398             :     else
    1399             :     {
    1400           0 :         fp->WriteLine("Rect %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
    1401             :                       sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    1402             :     }
    1403             : 
    1404           0 :     if (GetPenPattern())
    1405           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
    1406             :                       GetPenColor());
    1407             : 
    1408           0 :     if (GetBrushPattern())
    1409             :     {
    1410           0 :         if (GetBrushTransparent() == 0)
    1411           0 :             fp->WriteLine("    Brush (%d,%d,%d)\n", GetBrushPattern(),
    1412             :                           GetBrushFGColor(), GetBrushBGColor());
    1413             :         else
    1414           0 :             fp->WriteLine("    Brush (%d,%d)\n", GetBrushPattern(),
    1415             :                           GetBrushFGColor());
    1416             :     }
    1417           0 :     return 0;
    1418             : }
    1419             : 
    1420             : /**********************************************************************
    1421             :  *
    1422             :  **********************************************************************/
    1423         397 : int TABEllipse::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1424             : {
    1425             :     CPLStringList aosTokens(
    1426         794 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1427             : 
    1428         397 :     if (aosTokens.size() != 5)
    1429             :     {
    1430          10 :         return -1;
    1431             :     }
    1432             : 
    1433         387 :     double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1434         387 :     double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1435         387 :     double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1436         387 :     double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1437         387 :     aosTokens.Clear();
    1438             : 
    1439             :     /*-----------------------------------------------------------------
    1440             :      * Save info about the ellipse def. inside class members
    1441             :      *----------------------------------------------------------------*/
    1442         387 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    1443         387 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    1444         387 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    1445         387 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    1446             : 
    1447         387 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1448             : 
    1449             :     /*-----------------------------------------------------------------
    1450             :      * Create and fill geometry object
    1451             :      *----------------------------------------------------------------*/
    1452         387 :     OGRPolygon *poPolygon = new OGRPolygon;
    1453         387 :     OGRLinearRing *poRing = new OGRLinearRing();
    1454             : 
    1455             :     /*-----------------------------------------------------------------
    1456             :      * For the OGR geometry, we generate an ellipse with 2 degrees line
    1457             :      * segments.
    1458             :      *----------------------------------------------------------------*/
    1459         387 :     TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
    1460             :                    0.0, 2.0 * M_PI);
    1461         387 :     TABCloseRing(poRing);
    1462             : 
    1463         387 :     poPolygon->addRingDirectly(poRing);
    1464         387 :     SetGeometryDirectly(poPolygon);
    1465             : 
    1466         387 :     const char *pszLine = nullptr;
    1467        2973 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1468        1470 :            fp->IsValidFeature(pszLine) == FALSE)
    1469             :     {
    1470        1116 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1471             : 
    1472        1116 :         if (aosTokens.size() > 1)
    1473             :         {
    1474         743 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1475             :             {
    1476         379 :                 if (aosTokens.size() == 4)
    1477             :                 {
    1478         375 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1479         375 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1480         375 :                     SetPenColor(atoi(aosTokens[3]));
    1481             :                 }
    1482             :             }
    1483         364 :             else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
    1484             :             {
    1485         364 :                 if (aosTokens.size() >= 3)
    1486             :                 {
    1487         362 :                     SetBrushFGColor(atoi(aosTokens[2]));
    1488         362 :                     SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
    1489             : 
    1490         362 :                     if (aosTokens.size() == 4)
    1491         360 :                         SetBrushBGColor(atoi(aosTokens[3]));
    1492             :                     else
    1493           2 :                         SetBrushTransparent(TRUE);
    1494             :                 }
    1495             :             }
    1496             :         }
    1497        1116 :         aosTokens.Clear();
    1498             :     }
    1499         387 :     return 0;
    1500             : }
    1501             : 
    1502             : /**********************************************************************
    1503             :  *
    1504             :  **********************************************************************/
    1505           0 : int TABEllipse::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1506             : {
    1507           0 :     OGREnvelope sEnvelope;
    1508           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1509           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    1510           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    1511           0 :         poGeom->getEnvelope(&sEnvelope);
    1512             :     else
    1513             :     {
    1514           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1515             :                  "TABEllipse: Missing or Invalid Geometry!");
    1516           0 :         return -1;
    1517             :     }
    1518             : 
    1519           0 :     fp->WriteLine("Ellipse %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
    1520             :                   sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    1521             : 
    1522           0 :     if (GetPenPattern())
    1523           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
    1524             :                       GetPenColor());
    1525             : 
    1526           0 :     if (GetBrushPattern())
    1527             :     {
    1528           0 :         if (GetBrushTransparent() == 0)
    1529           0 :             fp->WriteLine("    Brush (%d,%d,%d)\n", GetBrushPattern(),
    1530             :                           GetBrushFGColor(), GetBrushBGColor());
    1531             :         else
    1532           0 :             fp->WriteLine("    Brush (%d,%d)\n", GetBrushPattern(),
    1533             :                           GetBrushFGColor());
    1534             :     }
    1535           0 :     return 0;
    1536             : }
    1537             : 
    1538             : /**********************************************************************
    1539             :  *
    1540             :  **********************************************************************/
    1541         672 : int TABArc::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1542             : {
    1543         672 :     double dXMin = 0.0;
    1544         672 :     double dXMax = 0.0;
    1545         672 :     double dYMin = 0.0;
    1546         672 :     double dYMax = 0.0;
    1547             : 
    1548             :     CPLStringList aosTokens(
    1549        1344 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1550             : 
    1551         672 :     if (aosTokens.size() == 5)
    1552             :     {
    1553         346 :         dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1554         346 :         dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1555         346 :         dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1556         346 :         dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1557             : 
    1558             :         aosTokens =
    1559         346 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1560         346 :         if (aosTokens.size() != 2)
    1561             :         {
    1562           8 :             return -1;
    1563             :         }
    1564             : 
    1565         338 :         m_dStartAngle = CPLAtof(aosTokens[0]);
    1566         338 :         m_dEndAngle = CPLAtof(aosTokens[1]);
    1567             :     }
    1568         326 :     else if (aosTokens.size() == 7)
    1569             :     {
    1570         304 :         dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1571         304 :         dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1572         304 :         dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1573         304 :         dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1574         304 :         m_dStartAngle = CPLAtof(aosTokens[5]);
    1575         304 :         m_dEndAngle = CPLAtof(aosTokens[6]);
    1576             :     }
    1577             :     else
    1578             :     {
    1579          22 :         return -1;
    1580             :     }
    1581             : 
    1582         642 :     aosTokens.Clear();
    1583             : 
    1584         642 :     if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
    1585             :     {
    1586           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1587             :                  "Wrong start and end angles: %f %f", m_dStartAngle,
    1588             :                  m_dEndAngle);
    1589           0 :         return -1;
    1590             :     }
    1591             : 
    1592             :     /*-------------------------------------------------------------
    1593             :      * Start/End angles
    1594             :      * Since the angles are specified for integer coordinates, and
    1595             :      * that these coordinates can have the X axis reversed, we have to
    1596             :      * adjust the angle values for the change in the X axis
    1597             :      * direction.
    1598             :      *
    1599             :      * This should be necessary only when X axis is flipped.
    1600             :      * __TODO__ Why is order of start/end values reversed as well???
    1601             :      *------------------------------------------------------------*/
    1602             : 
    1603         642 :     if (fp->GetXMultiplier() <= 0.0)
    1604             :     {
    1605           0 :         m_dStartAngle = 360.0 - m_dStartAngle;
    1606           0 :         m_dEndAngle = 360.0 - m_dEndAngle;
    1607             :     }
    1608             : 
    1609         642 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    1610         642 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    1611         642 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    1612         642 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    1613             : 
    1614             :     /*-----------------------------------------------------------------
    1615             :      * Create and fill geometry object
    1616             :      * For the OGR geometry, we generate an arc with 2 degrees line
    1617             :      * segments.
    1618             :      *----------------------------------------------------------------*/
    1619         642 :     OGRLineString *poLine = new OGRLineString;
    1620             : 
    1621             :     int numPts = std::max(
    1622        1284 :         2,
    1623         642 :         (m_dEndAngle < m_dStartAngle
    1624         642 :              ? static_cast<int>(
    1625           0 :                    std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
    1626         642 :              : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
    1627         642 :                                 1)));
    1628             : 
    1629         642 :     TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    1630         642 :                    m_dYRadius, m_dStartAngle * M_PI / 180.0,
    1631         642 :                    m_dEndAngle * M_PI / 180.0);
    1632             : 
    1633         642 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1634         642 :     SetGeometryDirectly(poLine);
    1635             : 
    1636         642 :     const char *pszLine = nullptr;
    1637        3176 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1638        1574 :            fp->IsValidFeature(pszLine) == FALSE)
    1639             :     {
    1640         960 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1641             : 
    1642         960 :         if (aosTokens.size() > 1)
    1643             :         {
    1644         328 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1645             :             {
    1646             : 
    1647         328 :                 if (aosTokens.size() == 4)
    1648             :                 {
    1649         324 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1650         324 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1651         324 :                     SetPenColor(atoi(aosTokens[3]));
    1652             :                 }
    1653             :             }
    1654             :         }
    1655         960 :         aosTokens.Clear();
    1656             :     }
    1657         642 :     return 0;
    1658             : }
    1659             : 
    1660             : /**********************************************************************
    1661             :  *
    1662             :  **********************************************************************/
    1663           0 : int TABArc::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1664             : {
    1665             :     /*-------------------------------------------------------------
    1666             :      * Start/End angles
    1667             :      * Since we ALWAYS produce files in quadrant 1 then we can
    1668             :      * ignore the special angle conversion required by flipped axis.
    1669             :      *------------------------------------------------------------*/
    1670             : 
    1671             :     // Write the Arc's actual MBR
    1672           0 :     fp->WriteLine("Arc %.15g %.15g %.15g %.15g\n", m_dCenterX - m_dXRadius,
    1673           0 :                   m_dCenterY - m_dYRadius, m_dCenterX + m_dXRadius,
    1674           0 :                   m_dCenterY + m_dYRadius);
    1675             : 
    1676           0 :     fp->WriteLine("  %.15g %.15g\n", m_dStartAngle, m_dEndAngle);
    1677             : 
    1678           0 :     if (GetPenPattern())
    1679           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
    1680             :                       GetPenColor());
    1681             : 
    1682           0 :     return 0;
    1683             : }
    1684             : 
    1685             : /**********************************************************************
    1686             :  *
    1687             :  **********************************************************************/
    1688         297 : int TABText::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1689             : {
    1690         297 :     const char *pszString = nullptr;
    1691         297 :     int bXYBoxRead = 0;
    1692             : 
    1693             :     CPLStringList aosTokens(
    1694         594 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1695         297 :     if (aosTokens.size() == 1)
    1696             :     {
    1697             :         aosTokens =
    1698           3 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1699           3 :         const int tokenLen = aosTokens.size();
    1700           3 :         if (tokenLen == 4)
    1701             :         {
    1702           0 :             pszString = nullptr;
    1703           0 :             bXYBoxRead = 1;
    1704             :         }
    1705           3 :         else if (tokenLen == 0)
    1706             :         {
    1707           3 :             pszString = nullptr;
    1708             :         }
    1709           0 :         else if (tokenLen != 1)
    1710             :         {
    1711           0 :             return -1;
    1712             :         }
    1713             :         else
    1714             :         {
    1715           0 :             pszString = aosTokens[0];
    1716             :         }
    1717             :     }
    1718         294 :     else if (aosTokens.size() == 2)
    1719             :     {
    1720         294 :         pszString = aosTokens[1];
    1721             :     }
    1722             :     else
    1723             :     {
    1724           0 :         return -1;
    1725             :     }
    1726             : 
    1727             :     /*-------------------------------------------------------------
    1728             :      * Note: The text string may contain escaped "\n" chars, and we
    1729             :      * sstore them in memory in the UnEscaped form to be OGR
    1730             :      * compliant. See Maptools bug 1107 for more details.
    1731             :      *------------------------------------------------------------*/
    1732         297 :     char *pszTmpString = CPLStrdup(pszString);
    1733         297 :     m_pszString = TABUnEscapeString(pszTmpString, TRUE);
    1734         297 :     if (pszTmpString != m_pszString)
    1735           0 :         CPLFree(pszTmpString);
    1736         297 :     if (!fp->GetEncoding().empty())
    1737             :     {
    1738             :         char *pszUtf8String =
    1739           1 :             CPLRecode(m_pszString, fp->GetEncoding(), CPL_ENC_UTF8);
    1740           1 :         CPLFree(m_pszString);
    1741           1 :         m_pszString = pszUtf8String;
    1742             :     }
    1743         297 :     if (!bXYBoxRead)
    1744             :     {
    1745             :         aosTokens =
    1746         297 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1747             :     }
    1748             : 
    1749         297 :     if (aosTokens.size() != 4)
    1750             :     {
    1751          17 :         return -1;
    1752             :     }
    1753             : 
    1754         280 :     double dXMin = fp->GetXTrans(CPLAtof(aosTokens[0]));
    1755         280 :     double dXMax = fp->GetXTrans(CPLAtof(aosTokens[2]));
    1756         280 :     double dYMin = fp->GetYTrans(CPLAtof(aosTokens[1]));
    1757         280 :     double dYMax = fp->GetYTrans(CPLAtof(aosTokens[3]));
    1758             : 
    1759         280 :     m_dHeight = dYMax - dYMin;  // SetTextBoxHeight(dYMax - dYMin);
    1760         280 :     m_dWidth = dXMax - dXMin;   // SetTextBoxWidth(dXMax - dXMin);
    1761             : 
    1762         280 :     if (m_dHeight < 0.0)
    1763           0 :         m_dHeight *= -1.0;
    1764         280 :     if (m_dWidth < 0.0)
    1765           0 :         m_dWidth *= -1.0;
    1766             : 
    1767             :     /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
    1768             :      */
    1769             : 
    1770         280 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1771         280 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    1772             : 
    1773         280 :     const char *pszLine = nullptr;
    1774        3169 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1775        1526 :            fp->IsValidFeature(pszLine) == FALSE)
    1776             :     {
    1777        1363 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1778             : 
    1779        1363 :         if (aosTokens.size() > 1)
    1780             :         {
    1781        1134 :             if (STARTS_WITH_CI(aosTokens[0], "FONT"))
    1782             :             {
    1783         270 :                 if (aosTokens.size() >= 5)
    1784             :                 {
    1785         261 :                     SetFontName(aosTokens[1]);
    1786         261 :                     SetFontFGColor(atoi(aosTokens[4]));
    1787         261 :                     if (aosTokens.size() == 6)
    1788             :                     {
    1789         254 :                         SetFontBGColor(atoi(aosTokens[5]));
    1790         254 :                         SetFontStyleMIFValue(atoi(aosTokens[2]), TRUE);
    1791             :                     }
    1792             :                     else
    1793           7 :                         SetFontStyleMIFValue(atoi(aosTokens[2]));
    1794             : 
    1795             :                     // papsztoken[3] = Size ???
    1796             :                 }
    1797             :             }
    1798         864 :             else if (STARTS_WITH_CI(aosTokens[0], "SPACING"))
    1799             :             {
    1800         237 :                 if (aosTokens.size() >= 2)
    1801             :                 {
    1802         237 :                     if (STARTS_WITH_CI(aosTokens[1], "2"))
    1803             :                     {
    1804         237 :                         SetTextSpacing(TABTSDouble);
    1805             :                     }
    1806           0 :                     else if (STARTS_WITH_CI(aosTokens[1], "1.5"))
    1807             :                     {
    1808           0 :                         SetTextSpacing(TABTS1_5);
    1809             :                     }
    1810             :                 }
    1811             : 
    1812         237 :                 if (aosTokens.size() == 7)
    1813             :                 {
    1814           0 :                     if (STARTS_WITH_CI(aosTokens[2], "LAbel"))
    1815             :                     {
    1816           0 :                         if (STARTS_WITH_CI(aosTokens[4], "simple"))
    1817             :                         {
    1818           0 :                             SetTextLineType(TABTLSimple);
    1819           0 :                             SetTextLineEndPoint(
    1820           0 :                                 fp->GetXTrans(CPLAtof(aosTokens[5])),
    1821           0 :                                 fp->GetYTrans(CPLAtof(aosTokens[6])));
    1822             :                         }
    1823           0 :                         else if (STARTS_WITH_CI(aosTokens[4], "arrow"))
    1824             :                         {
    1825           0 :                             SetTextLineType(TABTLArrow);
    1826           0 :                             SetTextLineEndPoint(
    1827           0 :                                 fp->GetXTrans(CPLAtof(aosTokens[5])),
    1828           0 :                                 fp->GetYTrans(CPLAtof(aosTokens[6])));
    1829             :                         }
    1830             :                     }
    1831             :                 }
    1832             :             }
    1833         627 :             else if (STARTS_WITH_CI(aosTokens[0], "Justify"))
    1834             :             {
    1835         224 :                 if (aosTokens.size() == 2)
    1836             :                 {
    1837         224 :                     if (STARTS_WITH_CI(aosTokens[1], "Center"))
    1838             :                     {
    1839         219 :                         SetTextJustification(TABTJCenter);
    1840             :                     }
    1841           5 :                     else if (STARTS_WITH_CI(aosTokens[1], "Right"))
    1842             :                     {
    1843           0 :                         SetTextJustification(TABTJRight);
    1844             :                     }
    1845             :                 }
    1846             :             }
    1847         403 :             else if (STARTS_WITH_CI(aosTokens[0], "Angle"))
    1848             :             {
    1849         208 :                 if (aosTokens.size() == 2)
    1850             :                 {
    1851         208 :                     SetTextAngle(CPLAtof(aosTokens[1]));
    1852             :                 }
    1853             :             }
    1854         195 :             else if (STARTS_WITH_CI(aosTokens[0], "LAbel"))
    1855             :             {
    1856         195 :                 if (aosTokens.size() == 5)
    1857             :                 {
    1858         178 :                     if (STARTS_WITH_CI(aosTokens[2], "simple"))
    1859             :                     {
    1860         178 :                         SetTextLineType(TABTLSimple);
    1861         356 :                         SetTextLineEndPoint(
    1862         178 :                             fp->GetXTrans(CPLAtof(aosTokens[3])),
    1863         178 :                             fp->GetYTrans(CPLAtof(aosTokens[4])));
    1864             :                     }
    1865           0 :                     else if (STARTS_WITH_CI(aosTokens[2], "arrow"))
    1866             :                     {
    1867           0 :                         SetTextLineType(TABTLArrow);
    1868           0 :                         SetTextLineEndPoint(
    1869           0 :                             fp->GetXTrans(CPLAtof(aosTokens[3])),
    1870           0 :                             fp->GetYTrans(CPLAtof(aosTokens[4])));
    1871             :                     }
    1872             :                 }
    1873             :                 // What I do with the XY coordinate
    1874             :             }
    1875             :         }
    1876             :     }
    1877             :     /*-----------------------------------------------------------------
    1878             :      * Create an OGRPoint Geometry...
    1879             :      * The point X,Y values will be the coords of the lower-left corner before
    1880             :      * rotation is applied.  (Note that the rotation in MapInfo is done around
    1881             :      * the upper-left corner)
    1882             :      * We need to calculate the true lower left corner of the text based
    1883             :      * on the MBR after rotation, the text height and the rotation angle.
    1884             :      *---------------------------------------------------------------- */
    1885         280 :     double dSin = sin(m_dAngle * M_PI / 180.0);
    1886         280 :     double dCos = cos(m_dAngle * M_PI / 180.0);
    1887         280 :     double dX = 0.0;
    1888         280 :     double dY = 0.0;
    1889         280 :     if (dSin > 0.0 && dCos > 0.0)
    1890             :     {
    1891         208 :         dX = dXMin + m_dHeight * dSin;
    1892         208 :         dY = dYMin;
    1893             :     }
    1894          72 :     else if (dSin > 0.0 && dCos < 0.0)
    1895             :     {
    1896           0 :         dX = dXMax;
    1897           0 :         dY = dYMin - m_dHeight * dCos;
    1898             :     }
    1899          72 :     else if (dSin < 0.0 && dCos < 0.0)
    1900             :     {
    1901           0 :         dX = dXMax + m_dHeight * dSin;
    1902           0 :         dY = dYMax;
    1903             :     }
    1904             :     else  // dSin < 0 && dCos > 0
    1905             :     {
    1906          72 :         dX = dXMin;
    1907          72 :         dY = dYMax - m_dHeight * dCos;
    1908             :     }
    1909             : 
    1910         280 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1911             : 
    1912         280 :     SetGeometryDirectly(poGeometry);
    1913             : 
    1914             :     /*-----------------------------------------------------------------
    1915             :      * Compute Text Width: the width of the Text MBR before rotation
    1916             :      * in ground units... unfortunately this value is not stored in the
    1917             :      * file, so we have to compute it with the MBR after rotation and
    1918             :      * the height of the MBR before rotation:
    1919             :      * With  W = Width of MBR before rotation
    1920             :      *       H = Height of MBR before rotation
    1921             :      *       dX = Width of MBR after rotation
    1922             :      *       dY = Height of MBR after rotation
    1923             :      *       teta = rotation angle
    1924             :      *
    1925             :      *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
    1926             :      *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
    1927             :      *
    1928             :      * and for other teta values, use:
    1929             :      *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
    1930             :      *---------------------------------------------------------------- */
    1931         280 :     dSin = std::abs(dSin);
    1932         280 :     dCos = std::abs(dCos);
    1933         280 :     if (m_dHeight == 0.0)
    1934           0 :         m_dWidth = 0.0;
    1935         280 :     else if (dCos > dSin)
    1936         280 :         m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
    1937         280 :                    (m_dHeight * dCos);
    1938             :     else
    1939           0 :         m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
    1940           0 :                    (m_dHeight * dSin);
    1941         280 :     m_dWidth = std::abs(m_dWidth);
    1942             : 
    1943         280 :     return 0;
    1944             : }
    1945             : 
    1946             : /**********************************************************************
    1947             :  *
    1948             :  **********************************************************************/
    1949           0 : int TABText::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1950             : {
    1951             :     /*-------------------------------------------------------------
    1952             :      * Note: The text string may contain unescaped "\n" chars or
    1953             :      * "\\" chars and we expect to receive them in an unescaped
    1954             :      * form. Those characters are unescaped in memory to be like
    1955             :      * other OGR drivers. See MapTools bug 1107 for more details.
    1956             :      *------------------------------------------------------------*/
    1957             :     char *pszTmpString;
    1958           0 :     if (fp->GetEncoding().empty())
    1959             :     {
    1960           0 :         pszTmpString = TABEscapeString(m_pszString);
    1961             :     }
    1962             :     else
    1963             :     {
    1964             :         char *pszEncString =
    1965           0 :             CPLRecode(m_pszString, CPL_ENC_UTF8, fp->GetEncoding());
    1966           0 :         pszTmpString = TABEscapeString(pszEncString);
    1967           0 :         if (pszTmpString != pszEncString)
    1968           0 :             CPLFree(pszEncString);
    1969             :     }
    1970             : 
    1971           0 :     if (pszTmpString == nullptr)
    1972           0 :         fp->WriteLine("Text \"\"\n");
    1973             :     else
    1974           0 :         fp->WriteLine("Text \"%s\"\n", pszTmpString);
    1975           0 :     if (pszTmpString != m_pszString)
    1976           0 :         CPLFree(pszTmpString);
    1977             : 
    1978           0 :     double dXMin = 0.0;
    1979           0 :     double dYMin = 0.0;
    1980           0 :     double dXMax = 0.0;
    1981           0 :     double dYMax = 0.0;
    1982           0 :     UpdateMBR();
    1983           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    1984           0 :     fp->WriteLine("    %.15g %.15g %.15g %.15g\n", dXMin, dYMin, dXMax, dYMax);
    1985             : 
    1986           0 :     if (IsFontBGColorUsed())
    1987           0 :         fp->WriteLine("    Font (\"%s\",%d,%d,%d,%d)\n", GetFontNameRef(),
    1988             :                       GetFontStyleMIFValue(), 0, GetFontFGColor(),
    1989             :                       GetFontBGColor());
    1990             :     else
    1991           0 :         fp->WriteLine("    Font (\"%s\",%d,%d,%d)\n", GetFontNameRef(),
    1992             :                       GetFontStyleMIFValue(), 0, GetFontFGColor());
    1993             : 
    1994           0 :     switch (GetTextSpacing())
    1995             :     {
    1996           0 :         case TABTS1_5:
    1997           0 :             fp->WriteLine("    Spacing 1.5\n");
    1998           0 :             break;
    1999           0 :         case TABTSDouble:
    2000           0 :             fp->WriteLine("    Spacing 2.0\n");
    2001           0 :             break;
    2002           0 :         case TABTSSingle:
    2003             :         default:
    2004           0 :             break;
    2005             :     }
    2006             : 
    2007           0 :     switch (GetTextJustification())
    2008             :     {
    2009           0 :         case TABTJCenter:
    2010           0 :             fp->WriteLine("    Justify Center\n");
    2011           0 :             break;
    2012           0 :         case TABTJRight:
    2013           0 :             fp->WriteLine("    Justify Right\n");
    2014           0 :             break;
    2015           0 :         case TABTJLeft:
    2016             :         default:
    2017           0 :             break;
    2018             :     }
    2019             : 
    2020           0 :     if (std::abs(GetTextAngle()) > 0.000001)
    2021           0 :         fp->WriteLine("    Angle %.15g\n", GetTextAngle());
    2022             : 
    2023           0 :     switch (GetTextLineType())
    2024             :     {
    2025           0 :         case TABTLSimple:
    2026           0 :             if (m_bLineEndSet)
    2027           0 :                 fp->WriteLine("    Label Line Simple %.15g %.15g \n",
    2028             :                               m_dfLineEndX, m_dfLineEndY);
    2029           0 :             break;
    2030           0 :         case TABTLArrow:
    2031           0 :             if (m_bLineEndSet)
    2032           0 :                 fp->WriteLine("    Label Line Arrow %.15g %.15g \n",
    2033             :                               m_dfLineEndX, m_dfLineEndY);
    2034           0 :             break;
    2035           0 :         case TABTLNoLine:
    2036             :         default:
    2037           0 :             break;
    2038             :     }
    2039           0 :     return 0;
    2040             : }
    2041             : 
    2042             : /**********************************************************************
    2043             :  *
    2044             :  **********************************************************************/
    2045         183 : int TABMultiPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    2046             : {
    2047             :     char **papszToken =
    2048         183 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
    2049             : 
    2050         183 :     if (CSLCount(papszToken) != 2)
    2051             :     {
    2052           4 :         CSLDestroy(papszToken);
    2053           4 :         return -1;
    2054             :     }
    2055             : 
    2056         179 :     int nNumPoint = atoi(papszToken[1]);
    2057         179 :     OGRMultiPoint *poMultiPoint = new OGRMultiPoint;
    2058             : 
    2059         179 :     CSLDestroy(papszToken);
    2060         179 :     papszToken = nullptr;
    2061             : 
    2062             :     // Get each point and add them to the multipoint feature
    2063         495 :     for (int i = 0; i < nNumPoint; i++)
    2064             :     {
    2065             :         papszToken =
    2066         344 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    2067         344 :         if (CSLCount(papszToken) != 2)
    2068             :         {
    2069          28 :             CSLDestroy(papszToken);
    2070          28 :             delete poMultiPoint;
    2071          28 :             return -1;
    2072             :         }
    2073             : 
    2074         316 :         const double dfX = fp->GetXTrans(CPLAtof(papszToken[0]));
    2075         316 :         const double dfY = fp->GetXTrans(CPLAtof(papszToken[1]));
    2076         316 :         OGRPoint *poPoint = new OGRPoint(dfX, dfY);
    2077         316 :         if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
    2078             :         {
    2079           0 :             CPLAssert(false);  // Just in case OGR is modified
    2080             :         }
    2081             : 
    2082             :         // Set center
    2083         316 :         if (i == 0)
    2084             :         {
    2085         165 :             SetCenter(dfX, dfY);
    2086             :         }
    2087         316 :         CSLDestroy(papszToken);
    2088             :     }
    2089             : 
    2090         151 :     OGREnvelope sEnvelope;
    2091         151 :     poMultiPoint->getEnvelope(&sEnvelope);
    2092         151 :     if (SetGeometryDirectly(poMultiPoint) != OGRERR_NONE)
    2093             :     {
    2094           0 :         CPLAssert(false);  // Just in case OGR is modified
    2095             :     }
    2096             : 
    2097         151 :     SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    2098             : 
    2099             :     // Read optional SYMBOL line...
    2100             : 
    2101         151 :     const char *pszLine = nullptr;
    2102         596 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    2103         293 :            fp->IsValidFeature(pszLine) == FALSE)
    2104             :     {
    2105         152 :         papszToken = CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
    2106         152 :         if (CSLCount(papszToken) == 4 && EQUAL(papszToken[0], "SYMBOL"))
    2107             :         {
    2108           0 :             SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
    2109           0 :             SetSymbolColor(atoi(papszToken[2]));
    2110           0 :             SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
    2111             :         }
    2112         152 :         CSLDestroy(papszToken);
    2113             :     }
    2114             : 
    2115         151 :     return 0;
    2116             : }
    2117             : 
    2118             : /**********************************************************************
    2119             :  *
    2120             :  **********************************************************************/
    2121           0 : int TABMultiPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
    2122             : {
    2123             :     /*-----------------------------------------------------------------
    2124             :      * Fetch and validate geometry
    2125             :      *----------------------------------------------------------------*/
    2126           0 :     OGRGeometry *poGeom = GetGeometryRef();
    2127           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    2128             :     {
    2129           0 :         OGRMultiPoint *poMultiPoint = poGeom->toMultiPoint();
    2130           0 :         const int nNumPoints = poMultiPoint->getNumGeometries();
    2131             : 
    2132           0 :         fp->WriteLine("MultiPoint %d\n", nNumPoints);
    2133             : 
    2134           0 :         for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
    2135             :         {
    2136             :             /*------------------------------------------------------------
    2137             :              * Validate each point
    2138             :              *-----------------------------------------------------------*/
    2139           0 :             poGeom = poMultiPoint->getGeometryRef(iPoint);
    2140           0 :             if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    2141             :             {
    2142           0 :                 OGRPoint *poPoint = poGeom->toPoint();
    2143           0 :                 fp->WriteLine("%.15g %.15g\n", poPoint->getX(),
    2144             :                               poPoint->getY());
    2145             :             }
    2146             :             else
    2147             :             {
    2148           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2149             :                          "TABMultiPoint: Missing or Invalid Geometry!");
    2150           0 :                 return -1;
    2151             :             }
    2152             :         }
    2153             :         // Write symbol
    2154           0 :         fp->WriteLine("    Symbol (%d,%d,%d)\n", GetSymbolNo(),
    2155           0 :                       GetSymbolColor(), GetSymbolSize());
    2156             :     }
    2157             : 
    2158           0 :     return 0;
    2159             : }
    2160             : 
    2161             : /**********************************************************************
    2162             :  *
    2163             :  **********************************************************************/
    2164          99 : int TABCollection::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    2165             : {
    2166             :     /*-----------------------------------------------------------------
    2167             :      * Fetch number of parts in "COLLECTION %d" line
    2168             :      *----------------------------------------------------------------*/
    2169             :     char **papszToken =
    2170          99 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
    2171             : 
    2172          99 :     if (CSLCount(papszToken) != 2)
    2173             :     {
    2174           2 :         CSLDestroy(papszToken);
    2175           2 :         return -1;
    2176             :     }
    2177             : 
    2178          97 :     int numParts = atoi(papszToken[1]);
    2179          97 :     CSLDestroy(papszToken);
    2180          97 :     papszToken = nullptr;
    2181             : 
    2182             :     // Make sure collection is empty
    2183          97 :     EmptyCollection();
    2184             : 
    2185          97 :     const char *pszLine = fp->GetLine();
    2186             : 
    2187             :     /*-----------------------------------------------------------------
    2188             :      * Read each part and add them to the feature
    2189             :      *----------------------------------------------------------------*/
    2190         184 :     for (int i = 0; i < numParts; i++)
    2191             :     {
    2192         182 :         if (pszLine == nullptr)
    2193             :         {
    2194          26 :             CPLError(
    2195             :                 CE_Failure, CPLE_FileIO,
    2196             :                 "Unexpected EOF while reading TABCollection from MIF file.");
    2197          26 :             return -1;
    2198             :         }
    2199             : 
    2200         156 :         while (*pszLine == ' ' || *pszLine == '\t')
    2201           0 :             pszLine++;  // skip leading spaces
    2202             : 
    2203         156 :         if (*pszLine == '\0')
    2204             :         {
    2205           2 :             pszLine = fp->GetLine();
    2206           2 :             continue;  // Skip blank lines
    2207             :         }
    2208             : 
    2209         154 :         if (STARTS_WITH_CI(pszLine, "REGION"))
    2210             :         {
    2211          88 :             delete m_poRegion;
    2212          88 :             m_poRegion = new TABRegion(GetDefnRef());
    2213          88 :             if (m_poRegion->ReadGeometryFromMIFFile(fp) != 0)
    2214             :             {
    2215          38 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2216             :                          "TABCollection: Error reading REGION part.");
    2217          38 :                 delete m_poRegion;
    2218          38 :                 m_poRegion = nullptr;
    2219          38 :                 return -1;
    2220             :             }
    2221             :         }
    2222          66 :         else if (STARTS_WITH_CI(pszLine, "LINE") ||
    2223          25 :                  STARTS_WITH_CI(pszLine, "PLINE"))
    2224             :         {
    2225          41 :             delete m_poPline;
    2226          41 :             m_poPline = new TABPolyline(GetDefnRef());
    2227          41 :             if (m_poPline->ReadGeometryFromMIFFile(fp) != 0)
    2228             :             {
    2229           8 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2230             :                          "TABCollection: Error reading PLINE part.");
    2231           8 :                 delete m_poPline;
    2232           8 :                 m_poPline = nullptr;
    2233           8 :                 return -1;
    2234             :             }
    2235             :         }
    2236          25 :         else if (STARTS_WITH_CI(pszLine, "MULTIPOINT"))
    2237             :         {
    2238          20 :             delete m_poMpoint;
    2239          20 :             m_poMpoint = new TABMultiPoint(GetDefnRef());
    2240          20 :             if (m_poMpoint->ReadGeometryFromMIFFile(fp) != 0)
    2241             :             {
    2242          18 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2243             :                          "TABCollection: Error reading MULTIPOINT part.");
    2244          18 :                 delete m_poMpoint;
    2245          18 :                 m_poMpoint = nullptr;
    2246          18 :                 return -1;
    2247             :             }
    2248             :         }
    2249             :         else
    2250             :         {
    2251           5 :             CPLError(CE_Failure, CPLE_FileIO,
    2252             :                      "Reading TABCollection from MIF failed, expecting one "
    2253             :                      "of REGION, PLINE or MULTIPOINT, got: '%s'",
    2254             :                      pszLine);
    2255           5 :             return -1;
    2256             :         }
    2257             : 
    2258          85 :         pszLine = fp->GetLastLine();
    2259             :     }
    2260             : 
    2261             :     /*-----------------------------------------------------------------
    2262             :      * Set the main OGRFeature Geometry
    2263             :      * (this is actually duplicating geometries from each member)
    2264             :      *----------------------------------------------------------------*/
    2265             :     // use addGeometry() rather than addGeometryDirectly() as this clones
    2266             :     // the added geometry so won't leave dangling ptrs when the above features
    2267             :     // are deleted
    2268             : 
    2269           2 :     OGRGeometryCollection *poGeomColl = new OGRGeometryCollection();
    2270           2 :     if (m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
    2271           2 :         poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
    2272             : 
    2273           2 :     if (m_poPline && m_poPline->GetGeometryRef() != nullptr)
    2274           2 :         poGeomColl->addGeometry(m_poPline->GetGeometryRef());
    2275             : 
    2276           2 :     if (m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
    2277           2 :         poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
    2278             : 
    2279           2 :     OGREnvelope sEnvelope;
    2280           2 :     poGeomColl->getEnvelope(&sEnvelope);
    2281           2 :     this->SetGeometryDirectly(poGeomColl);
    2282             : 
    2283           2 :     SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    2284             : 
    2285           2 :     return 0;
    2286             : }
    2287             : 
    2288             : /**********************************************************************
    2289             :  *
    2290             :  **********************************************************************/
    2291           0 : int TABCollection::WriteGeometryToMIFFile(MIDDATAFile *fp)
    2292             : {
    2293           0 :     int numParts = 0;
    2294           0 :     if (m_poRegion)
    2295           0 :         numParts++;
    2296           0 :     if (m_poPline)
    2297           0 :         numParts++;
    2298           0 :     if (m_poMpoint)
    2299           0 :         numParts++;
    2300             : 
    2301           0 :     fp->WriteLine("COLLECTION %d\n", numParts);
    2302             : 
    2303           0 :     if (m_poRegion)
    2304             :     {
    2305           0 :         if (m_poRegion->WriteGeometryToMIFFile(fp) != 0)
    2306           0 :             return -1;
    2307             :     }
    2308             : 
    2309           0 :     if (m_poPline)
    2310             :     {
    2311           0 :         if (m_poPline->WriteGeometryToMIFFile(fp) != 0)
    2312           0 :             return -1;
    2313             :     }
    2314             : 
    2315           0 :     if (m_poMpoint)
    2316             :     {
    2317           0 :         if (m_poMpoint->WriteGeometryToMIFFile(fp) != 0)
    2318           0 :             return -1;
    2319             :     }
    2320             : 
    2321           0 :     return 0;
    2322             : }
    2323             : 
    2324             : /**********************************************************************
    2325             :  *
    2326             :  **********************************************************************/
    2327           0 : int TABDebugFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    2328             : {
    2329             :     // Go to the first line of the next feature.
    2330           0 :     printf("%s\n", fp->GetLastLine()); /*ok*/
    2331             : 
    2332           0 :     const char *pszLine = nullptr;
    2333           0 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    2334           0 :            fp->IsValidFeature(pszLine) == FALSE)
    2335             :     {
    2336             :     }
    2337             : 
    2338           0 :     return 0;
    2339             : }
    2340             : 
    2341             : /**********************************************************************
    2342             :  *
    2343             :  **********************************************************************/
    2344           0 : int TABDebugFeature::WriteGeometryToMIFFile(MIDDATAFile * /* fp */)
    2345             : {
    2346           0 :     return -1;
    2347             : }

Generated by: LCOV version 1.14