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

Generated by: LCOV version 1.14