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

Generated by: LCOV version 1.14