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: 728 1052 69.2 %
Date: 2026-01-16 04:37:55 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         377 : int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp)
      50             : {
      51             : #ifdef MITAB_USE_OFTDATETIME
      52         377 :     int nYear = 0;
      53         377 :     int nMonth = 0;
      54         377 :     int nDay = 0;
      55         377 :     int nHour = 0;
      56         377 :     int nMin = 0;
      57         377 :     int nSec = 0;
      58         377 :     int nMS = 0;
      59             :     // int nTZFlag = 0;
      60             : #endif
      61             : 
      62         377 :     const int nFields = GetFieldCount();
      63             : 
      64         377 :     char **papszToken = fp->GetTokenizedNextLine();
      65         377 :     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         377 :     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         377 :     if (CSLCount(papszToken) < nFields)
      83             :     {
      84           0 :         CSLDestroy(papszToken);
      85           0 :         return -1;
      86             :     }
      87             : 
      88        1548 :     for (int i = 0; i < nFields; i++)
      89             :     {
      90        1171 :         const auto poFDefn = GetFieldDefnRef(i);
      91        1171 :         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         380 :             case OFTInteger:
     128             :             {
     129         380 :                 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         372 :                     SetField(i, papszToken[i]);
     140             :                 }
     141         380 :                 break;
     142             :             }
     143         417 :             case OFTString:
     144             :             {
     145         834 :                 CPLString osValue(papszToken[i]);
     146         417 :                 if (!fp->GetEncoding().empty())
     147             :                 {
     148          92 :                     osValue.Recode(fp->GetEncoding(), CPL_ENC_UTF8);
     149             :                 }
     150         417 :                 SetField(i, osValue);
     151         417 :                 break;
     152             :             }
     153         356 :             default:
     154         356 :                 SetField(i, papszToken[i]);
     155             :         }
     156             :     }
     157             : 
     158         377 :     CSLDestroy(papszToken);
     159             : 
     160         377 :     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         560 : int TABRegion::ReadGeometryFromMIFFile(MIDDATAFile *fp)
     954             : {
     955         560 :     m_bSmooth = FALSE;
     956             : 
     957             :     /*=============================================================
     958             :      * REGION (Similar to PLINE MULTIPLE)
     959             :      *============================================================*/
     960             :     CPLStringList aosTokens(
     961        1120 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
     962             : 
     963         560 :     int numLineSections = (aosTokens.size() == 2) ? atoi(aosTokens[1]) : 0;
     964         560 :     aosTokens.Clear();
     965             : 
     966         560 :     if (numLineSections < 0 ||
     967         560 :         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        1120 :     std::vector<std::unique_ptr<OGRGeometry>> apoPolys;
     975         560 :     constexpr int MAX_INITIAL_SECTIONS = 100000;
     976             :     const int numInitialLineSections =
     977         560 :         std::min(numLineSections, MAX_INITIAL_SECTIONS);
     978         560 :     apoPolys.reserve(numInitialLineSections);
     979             : 
     980         560 :     const char *pszLine = nullptr;
     981             : 
     982        1049 :     for (int iSection = 0; iSection < numLineSections; iSection++)
     983             :     {
     984         556 :         int numSectionVertices = 0;
     985             : 
     986         556 :         if ((pszLine = fp->GetLine()) != nullptr)
     987             :         {
     988         552 :             numSectionVertices = atoi(pszLine);
     989             :         }
     990         556 :         if (numSectionVertices < 2)
     991             :         {
     992          10 :             CPLError(CE_Failure, CPLE_FileIO,
     993             :                      "Invalid number of points (%d) in REGION "
     994             :                      "segment.",
     995             :                      numSectionVertices);
     996          67 :             return -1;
     997             :         }
     998             : 
     999         546 :         auto poRing = std::make_unique<OGRLinearRing>();
    1000             : 
    1001         546 :         constexpr int MAX_INITIAL_POINTS = 100000;
    1002             :         const int nInitialNumPoints =
    1003         546 :             std::min(numSectionVertices, MAX_INITIAL_POINTS);
    1004             :         /* Do not allocate too much memory to begin with */
    1005         546 :         poRing->setNumPoints(nInitialNumPoints);
    1006         546 :         if (poRing->getNumPoints() != nInitialNumPoints)
    1007             :         {
    1008           0 :             return -1;
    1009             :         }
    1010        8912 :         for (int i = 0; i < numSectionVertices; i++)
    1011             :         {
    1012        8423 :             if (i == MAX_INITIAL_POINTS)
    1013             :             {
    1014           0 :                 poRing->setNumPoints(numSectionVertices);
    1015           0 :                 if (poRing->getNumPoints() != numSectionVertices)
    1016             :                 {
    1017           0 :                     return -1;
    1018             :                 }
    1019             :             }
    1020             : 
    1021             :             aosTokens =
    1022        8423 :                 CSLTokenizeStringComplex(fp->GetLine(), " ,\t", TRUE, FALSE);
    1023        8423 :             if (aosTokens.size() < 2)
    1024             :             {
    1025          57 :                 return -1;
    1026             :             }
    1027             : 
    1028        8366 :             const double dX = fp->GetXTrans(CPLAtof(aosTokens[0]));
    1029        8366 :             const double dY = fp->GetYTrans(CPLAtof(aosTokens[1]));
    1030        8366 :             poRing->setPoint(i, dX, dY);
    1031        8366 :             aosTokens.Clear();
    1032             :         }
    1033             : 
    1034         489 :         poRing->closeRings();
    1035             : 
    1036         489 :         auto poPoly = std::make_unique<OGRPolygon>();
    1037         489 :         poPoly->addRing(std::move(poRing));
    1038         489 :         apoPolys.push_back(std::move(poPoly));
    1039             :     }
    1040             : 
    1041           0 :     std::unique_ptr<OGRGeometry> poGeometry;
    1042         493 :     if (apoPolys.size() == 1)
    1043             :     {
    1044         489 :         poGeometry = std::move(apoPolys[0]);
    1045             :     }
    1046           4 :     else if (apoPolys.size() > 1)
    1047             :     {
    1048           0 :         bool isValidGeometry = false;
    1049           0 :         const char *const apszOptions[] = {"METHOD=DEFAULT", nullptr};
    1050           0 :         poGeometry = OGRGeometryFactory::organizePolygons(
    1051           0 :             apoPolys, &isValidGeometry, apszOptions);
    1052             : 
    1053           0 :         if (!isValidGeometry)
    1054             :         {
    1055           0 :             CPLError(
    1056             :                 CE_Warning, CPLE_AppDefined,
    1057             :                 "Geometry of polygon cannot be translated to Simple Geometry. "
    1058             :                 "All polygons will be contained in a multipolygon.\n");
    1059             :         }
    1060             :     }
    1061             : 
    1062         493 :     if (poGeometry)
    1063             :     {
    1064         489 :         OGREnvelope sEnvelope;
    1065         489 :         poGeometry->getEnvelope(&sEnvelope);
    1066         489 :         SetGeometry(std::move(poGeometry));
    1067             : 
    1068         489 :         SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    1069             :     }
    1070             : 
    1071        2497 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1072        1219 :            fp->IsValidFeature(pszLine) == FALSE)
    1073             :     {
    1074         785 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1075             : 
    1076         785 :         if (aosTokens.size() > 1)
    1077             :         {
    1078         662 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1079             :             {
    1080             : 
    1081         330 :                 if (aosTokens.size() == 4)
    1082             :                 {
    1083         330 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1084         330 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1085         330 :                     SetPenColor(atoi(aosTokens[3]));
    1086             :                 }
    1087             :             }
    1088         332 :             else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
    1089             :             {
    1090         330 :                 if (aosTokens.size() >= 3)
    1091             :                 {
    1092         330 :                     SetBrushFGColor(atoi(aosTokens[2]));
    1093         330 :                     SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
    1094             : 
    1095         330 :                     if (aosTokens.size() == 4)
    1096         330 :                         SetBrushBGColor(atoi(aosTokens[3]));
    1097             :                     else
    1098           0 :                         SetBrushTransparent(TRUE);
    1099             :                 }
    1100             :             }
    1101           2 :             else if (STARTS_WITH_CI(aosTokens[0], "CENTER"))
    1102             :             {
    1103           2 :                 if (aosTokens.size() == 3)
    1104             :                 {
    1105           2 :                     SetCenter(fp->GetXTrans(CPLAtof(aosTokens[1])),
    1106           2 :                               fp->GetYTrans(CPLAtof(aosTokens[2])));
    1107             :                 }
    1108             :             }
    1109             :         }
    1110         785 :         aosTokens.Clear();
    1111             :     }
    1112             : 
    1113         493 :     return 0;
    1114             : }
    1115             : 
    1116             : /**********************************************************************
    1117             :  *                   TABRegion::WriteGeometryToMIFFile()
    1118             :  *
    1119             :  * Write the geometry and representation (color, etc...) part of the
    1120             :  * feature to the .MIF file
    1121             :  *
    1122             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1123             :  * been called.
    1124             :  **********************************************************************/
    1125          21 : int TABRegion::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1126             : {
    1127          21 :     OGRGeometry *poGeom = GetGeometryRef();
    1128             : 
    1129          21 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    1130           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    1131             :     {
    1132             :         /*=============================================================
    1133             :          * REGIONs are similar to PLINE MULTIPLE
    1134             :          *
    1135             :          * We accept both OGRPolygons (with one or multiple rings) and
    1136             :          * OGRMultiPolygons as input.
    1137             :          *============================================================*/
    1138             :         int i, iRing, numRingsTotal, numPoints;
    1139             : 
    1140          21 :         numRingsTotal = GetNumRings();
    1141             : 
    1142          21 :         fp->WriteLine("Region %d\n", numRingsTotal);
    1143             : 
    1144          42 :         for (iRing = 0; iRing < numRingsTotal; iRing++)
    1145             :         {
    1146          21 :             OGRLinearRing *poRing = GetRingRef(iRing);
    1147          21 :             if (poRing == nullptr)
    1148             :             {
    1149           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    1150             :                          "TABRegion: Object Geometry contains NULL rings!");
    1151           0 :                 return -1;
    1152             :             }
    1153             : 
    1154          21 :             numPoints = poRing->getNumPoints();
    1155             : 
    1156          21 :             fp->WriteLine("  %d\n", numPoints);
    1157         516 :             for (i = 0; i < numPoints; i++)
    1158             :             {
    1159         495 :                 fp->WriteLine("%.15g %.15g\n", poRing->getX(i),
    1160             :                               poRing->getY(i));
    1161             :             }
    1162             :         }
    1163             : 
    1164          21 :         if (GetPenPattern())
    1165          42 :             fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(),
    1166          21 :                           GetPenPattern(), GetPenColor());
    1167             : 
    1168          21 :         if (GetBrushPattern())
    1169             :         {
    1170          21 :             if (GetBrushTransparent() == 0)
    1171          21 :                 fp->WriteLine("    Brush (%d,%d,%d)\n", GetBrushPattern(),
    1172             :                               GetBrushFGColor(), GetBrushBGColor());
    1173             :             else
    1174           0 :                 fp->WriteLine("    Brush (%d,%d)\n", GetBrushPattern(),
    1175             :                               GetBrushFGColor());
    1176             :         }
    1177             : 
    1178          21 :         if (m_bCenterIsSet)
    1179             :         {
    1180           0 :             fp->WriteLine("    Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    1181             :         }
    1182             :     }
    1183             :     else
    1184             :     {
    1185           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1186             :                  "TABRegion: Object contains an invalid Geometry!");
    1187           0 :         return -1;
    1188             :     }
    1189             : 
    1190          21 :     return 0;
    1191             : }
    1192             : 
    1193             : /**********************************************************************
    1194             :  *
    1195             :  **********************************************************************/
    1196         941 : int TABRectangle::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1197             : {
    1198             :     CPLStringList aosTokens(
    1199        1882 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1200             : 
    1201         941 :     if (aosTokens.size() < 5)
    1202             :     {
    1203          20 :         return -1;
    1204             :     }
    1205             : 
    1206         921 :     double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1207         921 :     double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1208         921 :     double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1209         921 :     double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1210             : 
    1211             :     /*-----------------------------------------------------------------
    1212             :      * Call SetMBR() and GetMBR() now to make sure that min values are
    1213             :      * really smaller than max values.
    1214             :      *----------------------------------------------------------------*/
    1215         921 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1216         921 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    1217             : 
    1218         921 :     m_bRoundCorners = FALSE;
    1219         921 :     m_dRoundXRadius = 0.0;
    1220         921 :     m_dRoundYRadius = 0.0;
    1221             : 
    1222         921 :     if (STARTS_WITH_CI(aosTokens[0], "ROUNDRECT"))
    1223             :     {
    1224         436 :         m_bRoundCorners = TRUE;
    1225         436 :         if (aosTokens.size() == 6)
    1226             :         {
    1227         434 :             m_dRoundXRadius = CPLAtof(aosTokens[5]) / 2.0;
    1228         434 :             m_dRoundYRadius = m_dRoundXRadius;
    1229             :         }
    1230             :         else
    1231             :         {
    1232             :             aosTokens =
    1233           2 :                 CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1234           2 :             if (aosTokens.size() == 1)
    1235           0 :                 m_dRoundXRadius = m_dRoundYRadius = CPLAtof(aosTokens[0]) / 2.0;
    1236             :         }
    1237             :     }
    1238         921 :     aosTokens.Clear();
    1239             : 
    1240             :     /*-----------------------------------------------------------------
    1241             :      * Create and fill geometry object
    1242             :      *----------------------------------------------------------------*/
    1243             : 
    1244         921 :     OGRPolygon *poPolygon = new OGRPolygon;
    1245         921 :     OGRLinearRing *poRing = new OGRLinearRing();
    1246         921 :     if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    1247             :     {
    1248             :         /*-------------------------------------------------------------
    1249             :          * For rounded rectangles, we generate arcs with 45 line
    1250             :          * segments for each corner.  We start with lower-left corner
    1251             :          * and proceed counterclockwise
    1252             :          * We also have to make sure that rounding radius is not too
    1253             :          * large for the MBR however, we
    1254             :          * always return the true X/Y radius (not adjusted) since this
    1255             :          * is the way MapInfo seems to do it when a radius bigger than
    1256             :          * the MBR is passed from TBA to MIF.
    1257             :          *------------------------------------------------------------*/
    1258             :         const double dXRadius =
    1259         434 :             std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
    1260             :         const double dYRadius =
    1261         434 :             std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
    1262         434 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
    1263             :                        dYRadius, M_PI, 3.0 * M_PI / 2.0);
    1264         434 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
    1265             :                        dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
    1266         434 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
    1267             :                        dYRadius, 0.0, M_PI / 2.0);
    1268         434 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
    1269             :                        dYRadius, M_PI / 2.0, M_PI);
    1270             : 
    1271         434 :         TABCloseRing(poRing);
    1272             :     }
    1273             :     else
    1274             :     {
    1275         487 :         poRing->addPoint(dXMin, dYMin);
    1276         487 :         poRing->addPoint(dXMax, dYMin);
    1277         487 :         poRing->addPoint(dXMax, dYMax);
    1278         487 :         poRing->addPoint(dXMin, dYMax);
    1279         487 :         poRing->addPoint(dXMin, dYMin);
    1280             :     }
    1281             : 
    1282         921 :     poPolygon->addRingDirectly(poRing);
    1283         921 :     SetGeometryDirectly(poPolygon);
    1284             : 
    1285         921 :     const char *pszLine = nullptr;
    1286        7118 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1287        3520 :            fp->IsValidFeature(pszLine) == FALSE)
    1288             :     {
    1289        2677 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1290             : 
    1291        2677 :         if (aosTokens.size() > 1)
    1292             :         {
    1293        1776 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1294             :             {
    1295         903 :                 if (aosTokens.size() == 4)
    1296             :                 {
    1297         895 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1298         895 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1299         895 :                     SetPenColor(atoi(aosTokens[3]));
    1300             :                 }
    1301             :             }
    1302         873 :             else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
    1303             :             {
    1304         873 :                 if (aosTokens.size() >= 3)
    1305             :                 {
    1306         869 :                     SetBrushFGColor(atoi(aosTokens[2]));
    1307         869 :                     SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
    1308             : 
    1309         869 :                     if (aosTokens.size() == 4)
    1310         865 :                         SetBrushBGColor(atoi(aosTokens[3]));
    1311             :                     else
    1312           4 :                         SetBrushTransparent(TRUE);
    1313             :                 }
    1314             :             }
    1315             :         }
    1316        2677 :         aosTokens.Clear();
    1317             :     }
    1318             : 
    1319         921 :     return 0;
    1320             : }
    1321             : 
    1322             : /**********************************************************************
    1323             :  *
    1324             :  **********************************************************************/
    1325           0 : int TABRectangle::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1326             : {
    1327             :     /*-----------------------------------------------------------------
    1328             :      * Fetch and validate geometry
    1329             :      *----------------------------------------------------------------*/
    1330           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1331           0 :     OGRPolygon *poPolygon = nullptr;
    1332           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    1333           0 :         poPolygon = poGeom->toPolygon();
    1334             :     else
    1335             :     {
    1336           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1337             :                  "TABRectangle: Missing or Invalid Geometry!");
    1338           0 :         return -1;
    1339             :     }
    1340             :     /*-----------------------------------------------------------------
    1341             :      * Note that we will simply use the rectangle's MBR and don't really
    1342             :      * read the polygon geometry... this should be OK unless the
    1343             :      * polygon geometry was not really a rectangle.
    1344             :      *----------------------------------------------------------------*/
    1345           0 :     OGREnvelope sEnvelope;
    1346           0 :     poPolygon->getEnvelope(&sEnvelope);
    1347             : 
    1348           0 :     if (m_bRoundCorners == TRUE)
    1349             :     {
    1350           0 :         fp->WriteLine("Roundrect %.15g %.15g %.15g %.15g %.15g\n",
    1351             :                       sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
    1352           0 :                       sEnvelope.MaxY, m_dRoundXRadius * 2.0);
    1353             :     }
    1354             :     else
    1355             :     {
    1356           0 :         fp->WriteLine("Rect %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
    1357             :                       sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    1358             :     }
    1359             : 
    1360           0 :     if (GetPenPattern())
    1361           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
    1362             :                       GetPenColor());
    1363             : 
    1364           0 :     if (GetBrushPattern())
    1365             :     {
    1366           0 :         if (GetBrushTransparent() == 0)
    1367           0 :             fp->WriteLine("    Brush (%d,%d,%d)\n", GetBrushPattern(),
    1368             :                           GetBrushFGColor(), GetBrushBGColor());
    1369             :         else
    1370           0 :             fp->WriteLine("    Brush (%d,%d)\n", GetBrushPattern(),
    1371             :                           GetBrushFGColor());
    1372             :     }
    1373           0 :     return 0;
    1374             : }
    1375             : 
    1376             : /**********************************************************************
    1377             :  *
    1378             :  **********************************************************************/
    1379         397 : int TABEllipse::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1380             : {
    1381             :     CPLStringList aosTokens(
    1382         794 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1383             : 
    1384         397 :     if (aosTokens.size() != 5)
    1385             :     {
    1386          10 :         return -1;
    1387             :     }
    1388             : 
    1389         387 :     double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1390         387 :     double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1391         387 :     double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1392         387 :     double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1393         387 :     aosTokens.Clear();
    1394             : 
    1395             :     /*-----------------------------------------------------------------
    1396             :      * Save info about the ellipse def. inside class members
    1397             :      *----------------------------------------------------------------*/
    1398         387 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    1399         387 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    1400         387 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    1401         387 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    1402             : 
    1403         387 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1404             : 
    1405             :     /*-----------------------------------------------------------------
    1406             :      * Create and fill geometry object
    1407             :      *----------------------------------------------------------------*/
    1408         387 :     OGRPolygon *poPolygon = new OGRPolygon;
    1409         387 :     OGRLinearRing *poRing = new OGRLinearRing();
    1410             : 
    1411             :     /*-----------------------------------------------------------------
    1412             :      * For the OGR geometry, we generate an ellipse with 2 degrees line
    1413             :      * segments.
    1414             :      *----------------------------------------------------------------*/
    1415         387 :     TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
    1416             :                    0.0, 2.0 * M_PI);
    1417         387 :     TABCloseRing(poRing);
    1418             : 
    1419         387 :     poPolygon->addRingDirectly(poRing);
    1420         387 :     SetGeometryDirectly(poPolygon);
    1421             : 
    1422         387 :     const char *pszLine = nullptr;
    1423        2973 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1424        1470 :            fp->IsValidFeature(pszLine) == FALSE)
    1425             :     {
    1426        1116 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1427             : 
    1428        1116 :         if (aosTokens.size() > 1)
    1429             :         {
    1430         743 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1431             :             {
    1432         379 :                 if (aosTokens.size() == 4)
    1433             :                 {
    1434         375 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1435         375 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1436         375 :                     SetPenColor(atoi(aosTokens[3]));
    1437             :                 }
    1438             :             }
    1439         364 :             else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
    1440             :             {
    1441         364 :                 if (aosTokens.size() >= 3)
    1442             :                 {
    1443         362 :                     SetBrushFGColor(atoi(aosTokens[2]));
    1444         362 :                     SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
    1445             : 
    1446         362 :                     if (aosTokens.size() == 4)
    1447         360 :                         SetBrushBGColor(atoi(aosTokens[3]));
    1448             :                     else
    1449           2 :                         SetBrushTransparent(TRUE);
    1450             :                 }
    1451             :             }
    1452             :         }
    1453        1116 :         aosTokens.Clear();
    1454             :     }
    1455         387 :     return 0;
    1456             : }
    1457             : 
    1458             : /**********************************************************************
    1459             :  *
    1460             :  **********************************************************************/
    1461           0 : int TABEllipse::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1462             : {
    1463           0 :     OGREnvelope sEnvelope;
    1464           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1465           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    1466           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    1467           0 :         poGeom->getEnvelope(&sEnvelope);
    1468             :     else
    1469             :     {
    1470           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1471             :                  "TABEllipse: Missing or Invalid Geometry!");
    1472           0 :         return -1;
    1473             :     }
    1474             : 
    1475           0 :     fp->WriteLine("Ellipse %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
    1476             :                   sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    1477             : 
    1478           0 :     if (GetPenPattern())
    1479           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
    1480             :                       GetPenColor());
    1481             : 
    1482           0 :     if (GetBrushPattern())
    1483             :     {
    1484           0 :         if (GetBrushTransparent() == 0)
    1485           0 :             fp->WriteLine("    Brush (%d,%d,%d)\n", GetBrushPattern(),
    1486             :                           GetBrushFGColor(), GetBrushBGColor());
    1487             :         else
    1488           0 :             fp->WriteLine("    Brush (%d,%d)\n", GetBrushPattern(),
    1489             :                           GetBrushFGColor());
    1490             :     }
    1491           0 :     return 0;
    1492             : }
    1493             : 
    1494             : /**********************************************************************
    1495             :  *
    1496             :  **********************************************************************/
    1497         672 : int TABArc::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1498             : {
    1499         672 :     double dXMin = 0.0;
    1500         672 :     double dXMax = 0.0;
    1501         672 :     double dYMin = 0.0;
    1502         672 :     double dYMax = 0.0;
    1503             : 
    1504             :     CPLStringList aosTokens(
    1505        1344 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1506             : 
    1507         672 :     if (aosTokens.size() == 5)
    1508             :     {
    1509         346 :         dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1510         346 :         dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1511         346 :         dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1512         346 :         dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1513             : 
    1514             :         aosTokens =
    1515         346 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1516         346 :         if (aosTokens.size() != 2)
    1517             :         {
    1518           8 :             return -1;
    1519             :         }
    1520             : 
    1521         338 :         m_dStartAngle = CPLAtof(aosTokens[0]);
    1522         338 :         m_dEndAngle = CPLAtof(aosTokens[1]);
    1523             :     }
    1524         326 :     else if (aosTokens.size() == 7)
    1525             :     {
    1526         304 :         dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
    1527         304 :         dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
    1528         304 :         dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
    1529         304 :         dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
    1530         304 :         m_dStartAngle = CPLAtof(aosTokens[5]);
    1531         304 :         m_dEndAngle = CPLAtof(aosTokens[6]);
    1532             :     }
    1533             :     else
    1534             :     {
    1535          22 :         return -1;
    1536             :     }
    1537             : 
    1538         642 :     aosTokens.Clear();
    1539             : 
    1540         642 :     if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
    1541             :     {
    1542           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1543             :                  "Wrong start and end angles: %f %f", m_dStartAngle,
    1544             :                  m_dEndAngle);
    1545           0 :         return -1;
    1546             :     }
    1547             : 
    1548             :     /*-------------------------------------------------------------
    1549             :      * Start/End angles
    1550             :      * Since the angles are specified for integer coordinates, and
    1551             :      * that these coordinates can have the X axis reversed, we have to
    1552             :      * adjust the angle values for the change in the X axis
    1553             :      * direction.
    1554             :      *
    1555             :      * This should be necessary only when X axis is flipped.
    1556             :      * __TODO__ Why is order of start/end values reversed as well???
    1557             :      *------------------------------------------------------------*/
    1558             : 
    1559         642 :     if (fp->GetXMultiplier() <= 0.0)
    1560             :     {
    1561           0 :         m_dStartAngle = 360.0 - m_dStartAngle;
    1562           0 :         m_dEndAngle = 360.0 - m_dEndAngle;
    1563             :     }
    1564             : 
    1565         642 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    1566         642 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    1567         642 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    1568         642 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    1569             : 
    1570             :     /*-----------------------------------------------------------------
    1571             :      * Create and fill geometry object
    1572             :      * For the OGR geometry, we generate an arc with 2 degrees line
    1573             :      * segments.
    1574             :      *----------------------------------------------------------------*/
    1575         642 :     OGRLineString *poLine = new OGRLineString;
    1576             : 
    1577             :     int numPts = std::max(
    1578        1284 :         2,
    1579         642 :         (m_dEndAngle < m_dStartAngle
    1580         642 :              ? static_cast<int>(
    1581           0 :                    std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
    1582         642 :              : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
    1583         642 :                                 1)));
    1584             : 
    1585         642 :     TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    1586         642 :                    m_dYRadius, m_dStartAngle * M_PI / 180.0,
    1587         642 :                    m_dEndAngle * M_PI / 180.0);
    1588             : 
    1589         642 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1590         642 :     SetGeometryDirectly(poLine);
    1591             : 
    1592         642 :     const char *pszLine = nullptr;
    1593        3176 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1594        1574 :            fp->IsValidFeature(pszLine) == FALSE)
    1595             :     {
    1596         960 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1597             : 
    1598         960 :         if (aosTokens.size() > 1)
    1599             :         {
    1600         328 :             if (STARTS_WITH_CI(aosTokens[0], "PEN"))
    1601             :             {
    1602             : 
    1603         328 :                 if (aosTokens.size() == 4)
    1604             :                 {
    1605         324 :                     SetPenWidthMIF(atoi(aosTokens[1]));
    1606         324 :                     SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
    1607         324 :                     SetPenColor(atoi(aosTokens[3]));
    1608             :                 }
    1609             :             }
    1610             :         }
    1611         960 :         aosTokens.Clear();
    1612             :     }
    1613         642 :     return 0;
    1614             : }
    1615             : 
    1616             : /**********************************************************************
    1617             :  *
    1618             :  **********************************************************************/
    1619           0 : int TABArc::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1620             : {
    1621             :     /*-------------------------------------------------------------
    1622             :      * Start/End angles
    1623             :      * Since we ALWAYS produce files in quadrant 1 then we can
    1624             :      * ignore the special angle conversion required by flipped axis.
    1625             :      *------------------------------------------------------------*/
    1626             : 
    1627             :     // Write the Arc's actual MBR
    1628           0 :     fp->WriteLine("Arc %.15g %.15g %.15g %.15g\n", m_dCenterX - m_dXRadius,
    1629           0 :                   m_dCenterY - m_dYRadius, m_dCenterX + m_dXRadius,
    1630           0 :                   m_dCenterY + m_dYRadius);
    1631             : 
    1632           0 :     fp->WriteLine("  %.15g %.15g\n", m_dStartAngle, m_dEndAngle);
    1633             : 
    1634           0 :     if (GetPenPattern())
    1635           0 :         fp->WriteLine("    Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
    1636             :                       GetPenColor());
    1637             : 
    1638           0 :     return 0;
    1639             : }
    1640             : 
    1641             : /**********************************************************************
    1642             :  *
    1643             :  **********************************************************************/
    1644         297 : int TABText::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    1645             : {
    1646         297 :     const char *pszString = nullptr;
    1647         297 :     int bXYBoxRead = 0;
    1648             : 
    1649             :     CPLStringList aosTokens(
    1650         594 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
    1651         297 :     if (aosTokens.size() == 1)
    1652             :     {
    1653             :         aosTokens =
    1654           3 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1655           3 :         const int tokenLen = aosTokens.size();
    1656           3 :         if (tokenLen == 4)
    1657             :         {
    1658           0 :             pszString = nullptr;
    1659           0 :             bXYBoxRead = 1;
    1660             :         }
    1661           3 :         else if (tokenLen == 0)
    1662             :         {
    1663           3 :             pszString = nullptr;
    1664             :         }
    1665           0 :         else if (tokenLen != 1)
    1666             :         {
    1667           0 :             return -1;
    1668             :         }
    1669             :         else
    1670             :         {
    1671           0 :             pszString = aosTokens[0];
    1672             :         }
    1673             :     }
    1674         294 :     else if (aosTokens.size() == 2)
    1675             :     {
    1676         294 :         pszString = aosTokens[1];
    1677             :     }
    1678             :     else
    1679             :     {
    1680           0 :         return -1;
    1681             :     }
    1682             : 
    1683             :     /*-------------------------------------------------------------
    1684             :      * Note: The text string may contain escaped "\n" chars, and we
    1685             :      * sstore them in memory in the UnEscaped form to be OGR
    1686             :      * compliant. See Maptools bug 1107 for more details.
    1687             :      *------------------------------------------------------------*/
    1688         297 :     char *pszTmpString = CPLStrdup(pszString);
    1689         297 :     m_pszString = TABUnEscapeString(pszTmpString, TRUE);
    1690         297 :     if (pszTmpString != m_pszString)
    1691           0 :         CPLFree(pszTmpString);
    1692         297 :     if (!fp->GetEncoding().empty())
    1693             :     {
    1694             :         char *pszUtf8String =
    1695           1 :             CPLRecode(m_pszString, fp->GetEncoding(), CPL_ENC_UTF8);
    1696           1 :         CPLFree(m_pszString);
    1697           1 :         m_pszString = pszUtf8String;
    1698             :     }
    1699         297 :     if (!bXYBoxRead)
    1700             :     {
    1701             :         aosTokens =
    1702         297 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    1703             :     }
    1704             : 
    1705         297 :     if (aosTokens.size() != 4)
    1706             :     {
    1707          17 :         return -1;
    1708             :     }
    1709             : 
    1710         280 :     double dXMin = fp->GetXTrans(CPLAtof(aosTokens[0]));
    1711         280 :     double dXMax = fp->GetXTrans(CPLAtof(aosTokens[2]));
    1712         280 :     double dYMin = fp->GetYTrans(CPLAtof(aosTokens[1]));
    1713         280 :     double dYMax = fp->GetYTrans(CPLAtof(aosTokens[3]));
    1714             : 
    1715         280 :     m_dHeight = dYMax - dYMin;  // SetTextBoxHeight(dYMax - dYMin);
    1716         280 :     m_dWidth = dXMax - dXMin;   // SetTextBoxWidth(dXMax - dXMin);
    1717             : 
    1718         280 :     if (m_dHeight < 0.0)
    1719           0 :         m_dHeight *= -1.0;
    1720         280 :     if (m_dWidth < 0.0)
    1721           0 :         m_dWidth *= -1.0;
    1722             : 
    1723             :     /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
    1724             :      */
    1725             : 
    1726         280 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    1727         280 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    1728             : 
    1729         280 :     const char *pszLine = nullptr;
    1730        3169 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    1731        1526 :            fp->IsValidFeature(pszLine) == FALSE)
    1732             :     {
    1733        1363 :         aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
    1734             : 
    1735        1363 :         if (aosTokens.size() > 1)
    1736             :         {
    1737        1134 :             if (STARTS_WITH_CI(aosTokens[0], "FONT"))
    1738             :             {
    1739         270 :                 if (aosTokens.size() >= 5)
    1740             :                 {
    1741         261 :                     SetFontName(aosTokens[1]);
    1742         261 :                     SetFontFGColor(atoi(aosTokens[4]));
    1743         261 :                     if (aosTokens.size() == 6)
    1744             :                     {
    1745         254 :                         SetFontBGColor(atoi(aosTokens[5]));
    1746         254 :                         SetFontStyleMIFValue(atoi(aosTokens[2]), TRUE);
    1747             :                     }
    1748             :                     else
    1749           7 :                         SetFontStyleMIFValue(atoi(aosTokens[2]));
    1750             : 
    1751             :                     // papsztoken[3] = Size ???
    1752             :                 }
    1753             :             }
    1754         864 :             else if (STARTS_WITH_CI(aosTokens[0], "SPACING"))
    1755             :             {
    1756         237 :                 if (aosTokens.size() >= 2)
    1757             :                 {
    1758         237 :                     if (STARTS_WITH_CI(aosTokens[1], "2"))
    1759             :                     {
    1760         237 :                         SetTextSpacing(TABTSDouble);
    1761             :                     }
    1762           0 :                     else if (STARTS_WITH_CI(aosTokens[1], "1.5"))
    1763             :                     {
    1764           0 :                         SetTextSpacing(TABTS1_5);
    1765             :                     }
    1766             :                 }
    1767             : 
    1768         237 :                 if (aosTokens.size() == 7)
    1769             :                 {
    1770           0 :                     if (STARTS_WITH_CI(aosTokens[2], "LAbel"))
    1771             :                     {
    1772           0 :                         if (STARTS_WITH_CI(aosTokens[4], "simple"))
    1773             :                         {
    1774           0 :                             SetTextLineType(TABTLSimple);
    1775           0 :                             SetTextLineEndPoint(
    1776           0 :                                 fp->GetXTrans(CPLAtof(aosTokens[5])),
    1777           0 :                                 fp->GetYTrans(CPLAtof(aosTokens[6])));
    1778             :                         }
    1779           0 :                         else if (STARTS_WITH_CI(aosTokens[4], "arrow"))
    1780             :                         {
    1781           0 :                             SetTextLineType(TABTLArrow);
    1782           0 :                             SetTextLineEndPoint(
    1783           0 :                                 fp->GetXTrans(CPLAtof(aosTokens[5])),
    1784           0 :                                 fp->GetYTrans(CPLAtof(aosTokens[6])));
    1785             :                         }
    1786             :                     }
    1787             :                 }
    1788             :             }
    1789         627 :             else if (STARTS_WITH_CI(aosTokens[0], "Justify"))
    1790             :             {
    1791         224 :                 if (aosTokens.size() == 2)
    1792             :                 {
    1793         224 :                     if (STARTS_WITH_CI(aosTokens[1], "Center"))
    1794             :                     {
    1795         219 :                         SetTextJustification(TABTJCenter);
    1796             :                     }
    1797           5 :                     else if (STARTS_WITH_CI(aosTokens[1], "Right"))
    1798             :                     {
    1799           0 :                         SetTextJustification(TABTJRight);
    1800             :                     }
    1801             :                 }
    1802             :             }
    1803         403 :             else if (STARTS_WITH_CI(aosTokens[0], "Angle"))
    1804             :             {
    1805         208 :                 if (aosTokens.size() == 2)
    1806             :                 {
    1807         208 :                     SetTextAngle(CPLAtof(aosTokens[1]));
    1808             :                 }
    1809             :             }
    1810         195 :             else if (STARTS_WITH_CI(aosTokens[0], "LAbel"))
    1811             :             {
    1812         195 :                 if (aosTokens.size() == 5)
    1813             :                 {
    1814         178 :                     if (STARTS_WITH_CI(aosTokens[2], "simple"))
    1815             :                     {
    1816         178 :                         SetTextLineType(TABTLSimple);
    1817         356 :                         SetTextLineEndPoint(
    1818         178 :                             fp->GetXTrans(CPLAtof(aosTokens[3])),
    1819         178 :                             fp->GetYTrans(CPLAtof(aosTokens[4])));
    1820             :                     }
    1821           0 :                     else if (STARTS_WITH_CI(aosTokens[2], "arrow"))
    1822             :                     {
    1823           0 :                         SetTextLineType(TABTLArrow);
    1824           0 :                         SetTextLineEndPoint(
    1825           0 :                             fp->GetXTrans(CPLAtof(aosTokens[3])),
    1826           0 :                             fp->GetYTrans(CPLAtof(aosTokens[4])));
    1827             :                     }
    1828             :                 }
    1829             :                 // What I do with the XY coordinate
    1830             :             }
    1831             :         }
    1832             :     }
    1833             :     /*-----------------------------------------------------------------
    1834             :      * Create an OGRPoint Geometry...
    1835             :      * The point X,Y values will be the coords of the lower-left corner before
    1836             :      * rotation is applied.  (Note that the rotation in MapInfo is done around
    1837             :      * the upper-left corner)
    1838             :      * We need to calculate the true lower left corner of the text based
    1839             :      * on the MBR after rotation, the text height and the rotation angle.
    1840             :      *---------------------------------------------------------------- */
    1841         280 :     double dSin = sin(m_dAngle * M_PI / 180.0);
    1842         280 :     double dCos = cos(m_dAngle * M_PI / 180.0);
    1843         280 :     double dX = 0.0;
    1844         280 :     double dY = 0.0;
    1845         280 :     if (dSin > 0.0 && dCos > 0.0)
    1846             :     {
    1847         208 :         dX = dXMin + m_dHeight * dSin;
    1848         208 :         dY = dYMin;
    1849             :     }
    1850          72 :     else if (dSin > 0.0 && dCos < 0.0)
    1851             :     {
    1852           0 :         dX = dXMax;
    1853           0 :         dY = dYMin - m_dHeight * dCos;
    1854             :     }
    1855          72 :     else if (dSin < 0.0 && dCos < 0.0)
    1856             :     {
    1857           0 :         dX = dXMax + m_dHeight * dSin;
    1858           0 :         dY = dYMax;
    1859             :     }
    1860             :     else  // dSin < 0 && dCos > 0
    1861             :     {
    1862          72 :         dX = dXMin;
    1863          72 :         dY = dYMax - m_dHeight * dCos;
    1864             :     }
    1865             : 
    1866         280 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1867             : 
    1868         280 :     SetGeometryDirectly(poGeometry);
    1869             : 
    1870             :     /*-----------------------------------------------------------------
    1871             :      * Compute Text Width: the width of the Text MBR before rotation
    1872             :      * in ground units... unfortunately this value is not stored in the
    1873             :      * file, so we have to compute it with the MBR after rotation and
    1874             :      * the height of the MBR before rotation:
    1875             :      * With  W = Width of MBR before rotation
    1876             :      *       H = Height of MBR before rotation
    1877             :      *       dX = Width of MBR after rotation
    1878             :      *       dY = Height of MBR after rotation
    1879             :      *       teta = rotation angle
    1880             :      *
    1881             :      *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
    1882             :      *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
    1883             :      *
    1884             :      * and for other teta values, use:
    1885             :      *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
    1886             :      *---------------------------------------------------------------- */
    1887         280 :     dSin = std::abs(dSin);
    1888         280 :     dCos = std::abs(dCos);
    1889         280 :     if (m_dHeight == 0.0)
    1890           0 :         m_dWidth = 0.0;
    1891         280 :     else if (dCos > dSin)
    1892         280 :         m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
    1893         280 :                    (m_dHeight * dCos);
    1894             :     else
    1895           0 :         m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
    1896           0 :                    (m_dHeight * dSin);
    1897         280 :     m_dWidth = std::abs(m_dWidth);
    1898             : 
    1899         280 :     return 0;
    1900             : }
    1901             : 
    1902             : /**********************************************************************
    1903             :  *
    1904             :  **********************************************************************/
    1905           0 : int TABText::WriteGeometryToMIFFile(MIDDATAFile *fp)
    1906             : {
    1907             :     /*-------------------------------------------------------------
    1908             :      * Note: The text string may contain unescaped "\n" chars or
    1909             :      * "\\" chars and we expect to receive them in an unescaped
    1910             :      * form. Those characters are unescaped in memory to be like
    1911             :      * other OGR drivers. See MapTools bug 1107 for more details.
    1912             :      *------------------------------------------------------------*/
    1913             :     char *pszTmpString;
    1914           0 :     if (fp->GetEncoding().empty())
    1915             :     {
    1916           0 :         pszTmpString = TABEscapeString(m_pszString);
    1917             :     }
    1918             :     else
    1919             :     {
    1920             :         char *pszEncString =
    1921           0 :             CPLRecode(m_pszString, CPL_ENC_UTF8, fp->GetEncoding());
    1922           0 :         pszTmpString = TABEscapeString(pszEncString);
    1923           0 :         if (pszTmpString != pszEncString)
    1924           0 :             CPLFree(pszEncString);
    1925             :     }
    1926             : 
    1927           0 :     if (pszTmpString == nullptr)
    1928           0 :         fp->WriteLine("Text \"\"\n");
    1929             :     else
    1930           0 :         fp->WriteLine("Text \"%s\"\n", pszTmpString);
    1931           0 :     if (pszTmpString != m_pszString)
    1932           0 :         CPLFree(pszTmpString);
    1933             : 
    1934           0 :     double dXMin = 0.0;
    1935           0 :     double dYMin = 0.0;
    1936           0 :     double dXMax = 0.0;
    1937           0 :     double dYMax = 0.0;
    1938           0 :     UpdateMBR();
    1939           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    1940           0 :     fp->WriteLine("    %.15g %.15g %.15g %.15g\n", dXMin, dYMin, dXMax, dYMax);
    1941             : 
    1942           0 :     if (IsFontBGColorUsed())
    1943           0 :         fp->WriteLine("    Font (\"%s\",%d,%d,%d,%d)\n", GetFontNameRef(),
    1944             :                       GetFontStyleMIFValue(), 0, GetFontFGColor(),
    1945             :                       GetFontBGColor());
    1946             :     else
    1947           0 :         fp->WriteLine("    Font (\"%s\",%d,%d,%d)\n", GetFontNameRef(),
    1948             :                       GetFontStyleMIFValue(), 0, GetFontFGColor());
    1949             : 
    1950           0 :     switch (GetTextSpacing())
    1951             :     {
    1952           0 :         case TABTS1_5:
    1953           0 :             fp->WriteLine("    Spacing 1.5\n");
    1954           0 :             break;
    1955           0 :         case TABTSDouble:
    1956           0 :             fp->WriteLine("    Spacing 2.0\n");
    1957           0 :             break;
    1958           0 :         case TABTSSingle:
    1959             :         default:
    1960           0 :             break;
    1961             :     }
    1962             : 
    1963           0 :     switch (GetTextJustification())
    1964             :     {
    1965           0 :         case TABTJCenter:
    1966           0 :             fp->WriteLine("    Justify Center\n");
    1967           0 :             break;
    1968           0 :         case TABTJRight:
    1969           0 :             fp->WriteLine("    Justify Right\n");
    1970           0 :             break;
    1971           0 :         case TABTJLeft:
    1972             :         default:
    1973           0 :             break;
    1974             :     }
    1975             : 
    1976           0 :     if (std::abs(GetTextAngle()) > 0.000001)
    1977           0 :         fp->WriteLine("    Angle %.15g\n", GetTextAngle());
    1978             : 
    1979           0 :     switch (GetTextLineType())
    1980             :     {
    1981           0 :         case TABTLSimple:
    1982           0 :             if (m_bLineEndSet)
    1983           0 :                 fp->WriteLine("    Label Line Simple %.15g %.15g \n",
    1984             :                               m_dfLineEndX, m_dfLineEndY);
    1985           0 :             break;
    1986           0 :         case TABTLArrow:
    1987           0 :             if (m_bLineEndSet)
    1988           0 :                 fp->WriteLine("    Label Line Arrow %.15g %.15g \n",
    1989             :                               m_dfLineEndX, m_dfLineEndY);
    1990           0 :             break;
    1991           0 :         case TABTLNoLine:
    1992             :         default:
    1993           0 :             break;
    1994             :     }
    1995           0 :     return 0;
    1996             : }
    1997             : 
    1998             : /**********************************************************************
    1999             :  *
    2000             :  **********************************************************************/
    2001         183 : int TABMultiPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    2002             : {
    2003             :     char **papszToken =
    2004         183 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
    2005             : 
    2006         183 :     if (CSLCount(papszToken) != 2)
    2007             :     {
    2008           4 :         CSLDestroy(papszToken);
    2009           4 :         return -1;
    2010             :     }
    2011             : 
    2012         179 :     int nNumPoint = atoi(papszToken[1]);
    2013         179 :     OGRMultiPoint *poMultiPoint = new OGRMultiPoint;
    2014             : 
    2015         179 :     CSLDestroy(papszToken);
    2016         179 :     papszToken = nullptr;
    2017             : 
    2018             :     // Get each point and add them to the multipoint feature
    2019         495 :     for (int i = 0; i < nNumPoint; i++)
    2020             :     {
    2021             :         papszToken =
    2022         344 :             CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
    2023         344 :         if (CSLCount(papszToken) != 2)
    2024             :         {
    2025          28 :             CSLDestroy(papszToken);
    2026          28 :             delete poMultiPoint;
    2027          28 :             return -1;
    2028             :         }
    2029             : 
    2030         316 :         const double dfX = fp->GetXTrans(CPLAtof(papszToken[0]));
    2031         316 :         const double dfY = fp->GetXTrans(CPLAtof(papszToken[1]));
    2032         316 :         OGRPoint *poPoint = new OGRPoint(dfX, dfY);
    2033         316 :         if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
    2034             :         {
    2035           0 :             CPLAssert(false);  // Just in case OGR is modified
    2036             :         }
    2037             : 
    2038             :         // Set center
    2039         316 :         if (i == 0)
    2040             :         {
    2041         165 :             SetCenter(dfX, dfY);
    2042             :         }
    2043         316 :         CSLDestroy(papszToken);
    2044             :     }
    2045             : 
    2046         151 :     OGREnvelope sEnvelope;
    2047         151 :     poMultiPoint->getEnvelope(&sEnvelope);
    2048         151 :     if (SetGeometryDirectly(poMultiPoint) != OGRERR_NONE)
    2049             :     {
    2050           0 :         CPLAssert(false);  // Just in case OGR is modified
    2051             :     }
    2052             : 
    2053         151 :     SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    2054             : 
    2055             :     // Read optional SYMBOL line...
    2056             : 
    2057         151 :     const char *pszLine = nullptr;
    2058         596 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    2059         293 :            fp->IsValidFeature(pszLine) == FALSE)
    2060             :     {
    2061         152 :         papszToken = CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
    2062         152 :         if (CSLCount(papszToken) == 4 && EQUAL(papszToken[0], "SYMBOL"))
    2063             :         {
    2064           0 :             SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
    2065           0 :             SetSymbolColor(atoi(papszToken[2]));
    2066           0 :             SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
    2067             :         }
    2068         152 :         CSLDestroy(papszToken);
    2069             :     }
    2070             : 
    2071         151 :     return 0;
    2072             : }
    2073             : 
    2074             : /**********************************************************************
    2075             :  *
    2076             :  **********************************************************************/
    2077           0 : int TABMultiPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
    2078             : {
    2079             :     /*-----------------------------------------------------------------
    2080             :      * Fetch and validate geometry
    2081             :      *----------------------------------------------------------------*/
    2082           0 :     OGRGeometry *poGeom = GetGeometryRef();
    2083           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    2084             :     {
    2085           0 :         OGRMultiPoint *poMultiPoint = poGeom->toMultiPoint();
    2086           0 :         const int nNumPoints = poMultiPoint->getNumGeometries();
    2087             : 
    2088           0 :         fp->WriteLine("MultiPoint %d\n", nNumPoints);
    2089             : 
    2090           0 :         for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
    2091             :         {
    2092             :             /*------------------------------------------------------------
    2093             :              * Validate each point
    2094             :              *-----------------------------------------------------------*/
    2095           0 :             poGeom = poMultiPoint->getGeometryRef(iPoint);
    2096           0 :             if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    2097             :             {
    2098           0 :                 OGRPoint *poPoint = poGeom->toPoint();
    2099           0 :                 fp->WriteLine("%.15g %.15g\n", poPoint->getX(),
    2100             :                               poPoint->getY());
    2101             :             }
    2102             :             else
    2103             :             {
    2104           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2105             :                          "TABMultiPoint: Missing or Invalid Geometry!");
    2106           0 :                 return -1;
    2107             :             }
    2108             :         }
    2109             :         // Write symbol
    2110           0 :         fp->WriteLine("    Symbol (%d,%d,%d)\n", GetSymbolNo(),
    2111           0 :                       GetSymbolColor(), GetSymbolSize());
    2112             :     }
    2113             : 
    2114           0 :     return 0;
    2115             : }
    2116             : 
    2117             : /**********************************************************************
    2118             :  *
    2119             :  **********************************************************************/
    2120          99 : int TABCollection::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    2121             : {
    2122             :     /*-----------------------------------------------------------------
    2123             :      * Fetch number of parts in "COLLECTION %d" line
    2124             :      *----------------------------------------------------------------*/
    2125             :     char **papszToken =
    2126          99 :         CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
    2127             : 
    2128          99 :     if (CSLCount(papszToken) != 2)
    2129             :     {
    2130           2 :         CSLDestroy(papszToken);
    2131           2 :         return -1;
    2132             :     }
    2133             : 
    2134          97 :     int numParts = atoi(papszToken[1]);
    2135          97 :     CSLDestroy(papszToken);
    2136          97 :     papszToken = nullptr;
    2137             : 
    2138             :     // Make sure collection is empty
    2139          97 :     EmptyCollection();
    2140             : 
    2141          97 :     const char *pszLine = fp->GetLine();
    2142             : 
    2143             :     /*-----------------------------------------------------------------
    2144             :      * Read each part and add them to the feature
    2145             :      *----------------------------------------------------------------*/
    2146         184 :     for (int i = 0; i < numParts; i++)
    2147             :     {
    2148         182 :         if (pszLine == nullptr)
    2149             :         {
    2150          26 :             CPLError(
    2151             :                 CE_Failure, CPLE_FileIO,
    2152             :                 "Unexpected EOF while reading TABCollection from MIF file.");
    2153          26 :             return -1;
    2154             :         }
    2155             : 
    2156         156 :         while (*pszLine == ' ' || *pszLine == '\t')
    2157           0 :             pszLine++;  // skip leading spaces
    2158             : 
    2159         156 :         if (*pszLine == '\0')
    2160             :         {
    2161           2 :             pszLine = fp->GetLine();
    2162           2 :             continue;  // Skip blank lines
    2163             :         }
    2164             : 
    2165         154 :         if (STARTS_WITH_CI(pszLine, "REGION"))
    2166             :         {
    2167          88 :             delete m_poRegion;
    2168          88 :             m_poRegion = new TABRegion(GetDefnRef());
    2169          88 :             if (m_poRegion->ReadGeometryFromMIFFile(fp) != 0)
    2170             :             {
    2171          38 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2172             :                          "TABCollection: Error reading REGION part.");
    2173          38 :                 delete m_poRegion;
    2174          38 :                 m_poRegion = nullptr;
    2175          38 :                 return -1;
    2176             :             }
    2177             :         }
    2178          66 :         else if (STARTS_WITH_CI(pszLine, "LINE") ||
    2179          25 :                  STARTS_WITH_CI(pszLine, "PLINE"))
    2180             :         {
    2181          41 :             delete m_poPline;
    2182          41 :             m_poPline = new TABPolyline(GetDefnRef());
    2183          41 :             if (m_poPline->ReadGeometryFromMIFFile(fp) != 0)
    2184             :             {
    2185           8 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2186             :                          "TABCollection: Error reading PLINE part.");
    2187           8 :                 delete m_poPline;
    2188           8 :                 m_poPline = nullptr;
    2189           8 :                 return -1;
    2190             :             }
    2191             :         }
    2192          25 :         else if (STARTS_WITH_CI(pszLine, "MULTIPOINT"))
    2193             :         {
    2194          20 :             delete m_poMpoint;
    2195          20 :             m_poMpoint = new TABMultiPoint(GetDefnRef());
    2196          20 :             if (m_poMpoint->ReadGeometryFromMIFFile(fp) != 0)
    2197             :             {
    2198          18 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2199             :                          "TABCollection: Error reading MULTIPOINT part.");
    2200          18 :                 delete m_poMpoint;
    2201          18 :                 m_poMpoint = nullptr;
    2202          18 :                 return -1;
    2203             :             }
    2204             :         }
    2205             :         else
    2206             :         {
    2207           5 :             CPLError(CE_Failure, CPLE_FileIO,
    2208             :                      "Reading TABCollection from MIF failed, expecting one "
    2209             :                      "of REGION, PLINE or MULTIPOINT, got: '%s'",
    2210             :                      pszLine);
    2211           5 :             return -1;
    2212             :         }
    2213             : 
    2214          85 :         pszLine = fp->GetLastLine();
    2215             :     }
    2216             : 
    2217             :     /*-----------------------------------------------------------------
    2218             :      * Set the main OGRFeature Geometry
    2219             :      * (this is actually duplicating geometries from each member)
    2220             :      *----------------------------------------------------------------*/
    2221             :     // use addGeometry() rather than addGeometryDirectly() as this clones
    2222             :     // the added geometry so won't leave dangling ptrs when the above features
    2223             :     // are deleted
    2224             : 
    2225           2 :     OGRGeometryCollection *poGeomColl = new OGRGeometryCollection();
    2226           2 :     if (m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
    2227           2 :         poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
    2228             : 
    2229           2 :     if (m_poPline && m_poPline->GetGeometryRef() != nullptr)
    2230           2 :         poGeomColl->addGeometry(m_poPline->GetGeometryRef());
    2231             : 
    2232           2 :     if (m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
    2233           2 :         poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
    2234             : 
    2235           2 :     OGREnvelope sEnvelope;
    2236           2 :     poGeomColl->getEnvelope(&sEnvelope);
    2237           2 :     this->SetGeometryDirectly(poGeomColl);
    2238             : 
    2239           2 :     SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
    2240             : 
    2241           2 :     return 0;
    2242             : }
    2243             : 
    2244             : /**********************************************************************
    2245             :  *
    2246             :  **********************************************************************/
    2247           0 : int TABCollection::WriteGeometryToMIFFile(MIDDATAFile *fp)
    2248             : {
    2249           0 :     int numParts = 0;
    2250           0 :     if (m_poRegion)
    2251           0 :         numParts++;
    2252           0 :     if (m_poPline)
    2253           0 :         numParts++;
    2254           0 :     if (m_poMpoint)
    2255           0 :         numParts++;
    2256             : 
    2257           0 :     fp->WriteLine("COLLECTION %d\n", numParts);
    2258             : 
    2259           0 :     if (m_poRegion)
    2260             :     {
    2261           0 :         if (m_poRegion->WriteGeometryToMIFFile(fp) != 0)
    2262           0 :             return -1;
    2263             :     }
    2264             : 
    2265           0 :     if (m_poPline)
    2266             :     {
    2267           0 :         if (m_poPline->WriteGeometryToMIFFile(fp) != 0)
    2268           0 :             return -1;
    2269             :     }
    2270             : 
    2271           0 :     if (m_poMpoint)
    2272             :     {
    2273           0 :         if (m_poMpoint->WriteGeometryToMIFFile(fp) != 0)
    2274           0 :             return -1;
    2275             :     }
    2276             : 
    2277           0 :     return 0;
    2278             : }
    2279             : 
    2280             : /**********************************************************************
    2281             :  *
    2282             :  **********************************************************************/
    2283           0 : int TABDebugFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp)
    2284             : {
    2285             :     // Go to the first line of the next feature.
    2286           0 :     printf("%s\n", fp->GetLastLine()); /*ok*/
    2287             : 
    2288           0 :     const char *pszLine = nullptr;
    2289           0 :     while (((pszLine = fp->GetLine()) != nullptr) &&
    2290           0 :            fp->IsValidFeature(pszLine) == FALSE)
    2291             :     {
    2292             :     }
    2293             : 
    2294           0 :     return 0;
    2295             : }
    2296             : 
    2297             : /**********************************************************************
    2298             :  *
    2299             :  **********************************************************************/
    2300           0 : int TABDebugFeature::WriteGeometryToMIFFile(MIDDATAFile * /* fp */)
    2301             : {
    2302           0 :     return -1;
    2303             : }

Generated by: LCOV version 1.14