LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_feature.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2105 3738 56.3 %
Date: 2024-05-05 22:37:24 Functions: 147 220 66.8 %

          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 the feature classes specific to MapInfo files.
       7             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       8             :  *
       9             :  **********************************************************************
      10             :  * Copyright (c) 1999-2002, Daniel Morissette
      11             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      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             : #include "mitab_geometry.h"
      35             : #include "mitab_utils.h"
      36             : 
      37             : #include <cctype>
      38             : #include <cmath>
      39             : #include <cstdio>
      40             : #include <cstdlib>
      41             : #include <cstring>
      42             : #include <algorithm>
      43             : #include <utility>
      44             : 
      45             : #include "cpl_conv.h"
      46             : #include "cpl_error.h"
      47             : #include "cpl_string.h"
      48             : #include "cpl_vsi.h"
      49             : #include "mitab.h"
      50             : #include "mitab_geometry.h"
      51             : #include "mitab_priv.h"
      52             : #include "mitab_utils.h"
      53             : #include "ogr_core.h"
      54             : #include "ogr_feature.h"
      55             : #include "ogr_featurestyle.h"
      56             : #include "ogr_geometry.h"
      57             : 
      58             : /*=====================================================================
      59             :  *                      class TABFeature
      60             :  *====================================================================*/
      61             : 
      62             : /**********************************************************************
      63             :  *                   TABFeature::TABFeature()
      64             :  *
      65             :  * Constructor.
      66             :  **********************************************************************/
      67      539662 : TABFeature::TABFeature(OGRFeatureDefn *poDefnIn)
      68             :     : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
      69             :       m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
      70      539662 :       m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
      71             : {
      72      539662 : }
      73             : 
      74             : /**********************************************************************
      75             :  *                   TABFeature::~TABFeature()
      76             :  *
      77             :  * Destructor.
      78             :  **********************************************************************/
      79      539910 : TABFeature::~TABFeature()
      80             : {
      81      539910 : }
      82             : 
      83             : /**********************************************************************
      84             :  *                     TABFeature::CreateFromMapInfoType()
      85             :  *
      86             :  * Factory that creates a TABFeature of the right class for the specified
      87             :  * MapInfo Type
      88             :  *
      89             :  **********************************************************************/
      90      516804 : TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
      91             :                                               OGRFeatureDefn *poDefn)
      92             : {
      93      516804 :     TABFeature *poFeature = nullptr;
      94             : 
      95             :     /*-----------------------------------------------------------------
      96             :      * Create new feature object of the right type
      97             :      *----------------------------------------------------------------*/
      98      516804 :     switch (nMapInfoType)
      99             :     {
     100         147 :         case TAB_GEOM_NONE:
     101         147 :             poFeature = new TABFeature(poDefn);
     102         147 :             break;
     103      514727 :         case TAB_GEOM_SYMBOL_C:
     104             :         case TAB_GEOM_SYMBOL:
     105      514727 :             poFeature = new TABPoint(poDefn);
     106      514727 :             break;
     107           6 :         case TAB_GEOM_FONTSYMBOL_C:
     108             :         case TAB_GEOM_FONTSYMBOL:
     109           6 :             poFeature = new TABFontPoint(poDefn);
     110           6 :             break;
     111           6 :         case TAB_GEOM_CUSTOMSYMBOL_C:
     112             :         case TAB_GEOM_CUSTOMSYMBOL:
     113           6 :             poFeature = new TABCustomPoint(poDefn);
     114           6 :             break;
     115        1383 :         case TAB_GEOM_LINE_C:
     116             :         case TAB_GEOM_LINE:
     117             :         case TAB_GEOM_PLINE_C:
     118             :         case TAB_GEOM_PLINE:
     119             :         case TAB_GEOM_MULTIPLINE_C:
     120             :         case TAB_GEOM_MULTIPLINE:
     121             :         case TAB_GEOM_V450_MULTIPLINE_C:
     122             :         case TAB_GEOM_V450_MULTIPLINE:
     123             :         case TAB_GEOM_V800_MULTIPLINE_C:
     124             :         case TAB_GEOM_V800_MULTIPLINE:
     125        1383 :             poFeature = new TABPolyline(poDefn);
     126        1383 :             break;
     127           8 :         case TAB_GEOM_ARC_C:
     128             :         case TAB_GEOM_ARC:
     129           8 :             poFeature = new TABArc(poDefn);
     130           8 :             break;
     131             : 
     132         499 :         case TAB_GEOM_REGION_C:
     133             :         case TAB_GEOM_REGION:
     134             :         case TAB_GEOM_V450_REGION_C:
     135             :         case TAB_GEOM_V450_REGION:
     136             :         case TAB_GEOM_V800_REGION_C:
     137             :         case TAB_GEOM_V800_REGION:
     138         499 :             poFeature = new TABRegion(poDefn);
     139         499 :             break;
     140           8 :         case TAB_GEOM_RECT_C:
     141             :         case TAB_GEOM_RECT:
     142             :         case TAB_GEOM_ROUNDRECT_C:
     143             :         case TAB_GEOM_ROUNDRECT:
     144           8 :             poFeature = new TABRectangle(poDefn);
     145           8 :             break;
     146           4 :         case TAB_GEOM_ELLIPSE_C:
     147             :         case TAB_GEOM_ELLIPSE:
     148           4 :             poFeature = new TABEllipse(poDefn);
     149           4 :             break;
     150           8 :         case TAB_GEOM_TEXT_C:
     151             :         case TAB_GEOM_TEXT:
     152           8 :             poFeature = new TABText(poDefn);
     153           8 :             break;
     154           4 :         case TAB_GEOM_MULTIPOINT_C:
     155             :         case TAB_GEOM_MULTIPOINT:
     156             :         case TAB_GEOM_V800_MULTIPOINT_C:
     157             :         case TAB_GEOM_V800_MULTIPOINT:
     158           4 :             poFeature = new TABMultiPoint(poDefn);
     159           4 :             break;
     160           4 :         case TAB_GEOM_COLLECTION_C:
     161             :         case TAB_GEOM_COLLECTION:
     162             :         case TAB_GEOM_V800_COLLECTION_C:
     163             :         case TAB_GEOM_V800_COLLECTION:
     164           4 :             poFeature = new TABCollection(poDefn);
     165           4 :             break;
     166           0 :         default:
     167             :             /*-------------------------------------------------------------
     168             :              * Unsupported feature type... we still return a valid feature
     169             :              * with NONE geometry after producing a Warning.
     170             :              * Callers can trap that case by checking CPLGetLastErrorNo()
     171             :              * against TAB_WarningFeatureTypeNotSupported
     172             :              *------------------------------------------------------------*/
     173             :             // poFeature = new TABDebugFeature(poDefn);
     174           0 :             poFeature = new TABFeature(poDefn);
     175             : 
     176           0 :             CPLError(
     177             :                 CE_Warning,
     178             :                 static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
     179             :                 "Unsupported object type %d (0x%2.2x).  Feature will be "
     180             :                 "returned with NONE geometry.",
     181             :                 nMapInfoType, nMapInfoType);
     182             :     }
     183             : 
     184      516804 :     return poFeature;
     185             : }
     186             : 
     187             : /**********************************************************************
     188             :  *                     TABFeature::CopyTABFeatureBase()
     189             :  *
     190             :  * Used by CloneTABFeature() to copy the basic (fields, geometry, etc.)
     191             :  * TABFeature members.
     192             :  *
     193             :  * The newly created feature is owned by the caller, and will have its own
     194             :  * reference to the OGRFeatureDefn.
     195             :  *
     196             :  * It is possible to create the clone with a different OGRFeatureDefn,
     197             :  * in this case, the fields won't be copied of course.
     198             :  *
     199             :  **********************************************************************/
     200           2 : void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature)
     201             : {
     202             :     /*-----------------------------------------------------------------
     203             :      * Copy fields only if OGRFeatureDefn is the same
     204             :      *----------------------------------------------------------------*/
     205           2 :     OGRFeatureDefn *poThisDefnRef = GetDefnRef();
     206             : 
     207           2 :     if (poThisDefnRef == poDestFeature->GetDefnRef())
     208             :     {
     209           0 :         for (int i = 0; i < poThisDefnRef->GetFieldCount(); i++)
     210             :         {
     211           0 :             poDestFeature->SetField(i, GetRawFieldRef(i));
     212             :         }
     213             :     }
     214             : 
     215             :     /*-----------------------------------------------------------------
     216             :      * Copy the geometry
     217             :      *----------------------------------------------------------------*/
     218           2 :     poDestFeature->SetGeometry(GetGeometryRef());
     219             : 
     220           2 :     double dXMin = 0.0;
     221           2 :     double dYMin = 0.0;
     222           2 :     double dXMax = 0.0;
     223           2 :     double dYMax = 0.0;
     224           2 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
     225           2 :     poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
     226             : 
     227           2 :     GInt32 nXMin = 0;
     228           2 :     GInt32 nYMin = 0;
     229           2 :     GInt32 nXMax = 0;
     230           2 :     GInt32 nYMax = 0;
     231           2 :     GetIntMBR(nXMin, nYMin, nXMax, nYMax);
     232           2 :     poDestFeature->SetIntMBR(nXMin, nYMin, nXMax, nYMax);
     233             : 
     234             :     // m_nMapInfoType is not carried but it is not required anyways.
     235             :     // it will default to TAB_GEOM_NONE
     236           2 : }
     237             : 
     238             : /**********************************************************************
     239             :  *                     TABFeature::CloneTABFeature()
     240             :  *
     241             :  * Duplicate feature, including stuff specific to each TABFeature type.
     242             :  *
     243             :  * The newly created feature is owned by the caller, and will have its own
     244             :  * reference to the OGRFeatureDefn.
     245             :  *
     246             :  * It is possible to create the clone with a different OGRFeatureDefn,
     247             :  * in this case, the fields won't be copied of course.
     248             :  *
     249             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
     250             :  * then copies any members specific to its own type.
     251             :  **********************************************************************/
     252           0 : TABFeature *TABFeature::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
     253             : {
     254             :     /*-----------------------------------------------------------------
     255             :      * Alloc new feature and copy the base stuff
     256             :      *----------------------------------------------------------------*/
     257           0 :     TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
     258             : 
     259           0 :     CopyTABFeatureBase(poNew);
     260             : 
     261             :     /*-----------------------------------------------------------------
     262             :      * And members specific to this class
     263             :      *----------------------------------------------------------------*/
     264             :     // Nothing to do for this class
     265             : 
     266           0 :     return poNew;
     267             : }
     268             : 
     269             : /**********************************************************************
     270             :  *                   TABFeature::SetMBR()
     271             :  *
     272             :  * Set the values for the MBR corners for this feature.
     273             :  **********************************************************************/
     274      523942 : void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
     275             : {
     276      523942 :     m_dXMin = std::min(dXMin, dXMax);
     277      523942 :     m_dYMin = std::min(dYMin, dYMax);
     278      523942 :     m_dXMax = std::max(dXMin, dXMax);
     279      523942 :     m_dYMax = std::max(dYMin, dYMax);
     280      523942 : }
     281             : 
     282             : /**********************************************************************
     283             :  *                   TABFeature::GetMBR()
     284             :  *
     285             :  * Return the values for the MBR corners for this feature.
     286             :  **********************************************************************/
     287        1227 : void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
     288             :                         double &dYMax)
     289             : {
     290        1227 :     dXMin = m_dXMin;
     291        1227 :     dYMin = m_dYMin;
     292        1227 :     dXMax = m_dXMax;
     293        1227 :     dYMax = m_dYMax;
     294        1227 : }
     295             : 
     296             : /**********************************************************************
     297             :  *                   TABFeature::SetIntMBR()
     298             :  *
     299             :  * Set the integer coordinates values of the MBR of this feature.
     300             :  **********************************************************************/
     301      516663 : void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
     302             :                            GInt32 nYMax)
     303             : {
     304      516663 :     m_nXMin = nXMin;
     305      516663 :     m_nYMin = nYMin;
     306      516663 :     m_nXMax = nXMax;
     307      516663 :     m_nYMax = nYMax;
     308      516663 : }
     309             : 
     310             : /**********************************************************************
     311             :  *                   TABFeature::GetIntMBR()
     312             :  *
     313             :  * Return the integer coordinates values of the MBR of this feature.
     314             :  **********************************************************************/
     315       15019 : void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
     316             :                            GInt32 &nYMax)
     317             : {
     318       15019 :     nXMin = m_nXMin;
     319       15019 :     nYMin = m_nYMin;
     320       15019 :     nXMax = m_nXMax;
     321       15019 :     nYMax = m_nYMax;
     322       15019 : }
     323             : 
     324             : /**********************************************************************
     325             :  *                   TABFeature::ReadRecordFromDATFile()
     326             :  *
     327             :  * Fill the fields part of the feature from the contents of the
     328             :  * table record pointed to by poDATFile.
     329             :  *
     330             :  * It is assumed that poDATFile currently points to the beginning of
     331             :  * the table record and that this feature's OGRFeatureDefn has been
     332             :  * properly initialized for this table.
     333             :  **********************************************************************/
     334      516678 : int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
     335             : {
     336      516678 :     CPLAssert(poDATFile);
     337             : 
     338      516678 :     const int numFields = poDATFile->GetNumFields();
     339             : 
     340     1035080 :     for (int iField = 0; iField < numFields; iField++)
     341             :     {
     342      518400 :         switch (poDATFile->GetFieldType(iField))
     343             :         {
     344        1291 :             case TABFChar:
     345             :             {
     346        1291 :                 int iWidth(poDATFile->GetFieldWidth(iField));
     347        2582 :                 CPLString osValue(poDATFile->ReadCharField(iWidth));
     348             : 
     349        1291 :                 if (!poDATFile->GetEncoding().empty())
     350             :                 {
     351         107 :                     osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
     352             :                 }
     353        1291 :                 SetField(iField, osValue);
     354        1291 :                 break;
     355             :             }
     356          23 :             case TABFDecimal:
     357             :             {
     358          23 :                 const double dValue = poDATFile->ReadDecimalField(
     359             :                     poDATFile->GetFieldWidth(iField));
     360          23 :                 SetField(iField, dValue);
     361          23 :                 break;
     362             :             }
     363      516457 :             case TABFInteger:
     364             :             {
     365      516457 :                 const int nValue = poDATFile->ReadIntegerField(
     366             :                     poDATFile->GetFieldWidth(iField));
     367      516457 :                 SetField(iField, nValue);
     368      516457 :                 break;
     369             :             }
     370           1 :             case TABFSmallInt:
     371             :             {
     372           1 :                 const int nValue = poDATFile->ReadSmallIntField(
     373           1 :                     poDATFile->GetFieldWidth(iField));
     374           1 :                 SetField(iField, nValue);
     375           1 :                 break;
     376             :             }
     377           1 :             case TABFLargeInt:
     378             :             {
     379           1 :                 const GInt64 nValue = poDATFile->ReadLargeIntField(
     380             :                     poDATFile->GetFieldWidth(iField));
     381           1 :                 SetField(iField, nValue);
     382           1 :                 break;
     383             :             }
     384         582 :             case TABFFloat:
     385             :             {
     386             :                 const double dValue =
     387         582 :                     poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
     388         582 :                 SetField(iField, dValue);
     389         582 :                 break;
     390             :             }
     391           1 :             case TABFLogical:
     392             :             {
     393           1 :                 const char *pszValue = poDATFile->ReadLogicalField(
     394             :                     poDATFile->GetFieldWidth(iField));
     395           1 :                 SetField(iField, pszValue);
     396           1 :                 break;
     397             :             }
     398          24 :             case TABFDate:
     399             :             {
     400             : #ifdef MITAB_USE_OFTDATETIME
     401          24 :                 int nYear = 0;
     402          24 :                 int nMonth = 0;
     403          24 :                 int nDay = 0;
     404          24 :                 const int status = poDATFile->ReadDateField(
     405             :                     poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
     406          24 :                 if (status == 0)
     407             :                 {
     408           7 :                     SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
     409             :                 }
     410             : #else
     411             :                 const char *pszValue =
     412             :                     poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
     413             :                 SetField(iField, pszValue);
     414             : #endif
     415          24 :                 break;
     416             :             }
     417           2 :             case TABFTime:
     418             :             {
     419             : #ifdef MITAB_USE_OFTDATETIME
     420           2 :                 int nHour = 0;
     421           2 :                 int nMin = 0;
     422           2 :                 int nMS = 0;
     423           2 :                 int nSec = 0;
     424             :                 const int status =
     425           2 :                     poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
     426             :                                              &nHour, &nMin, &nSec, &nMS);
     427           2 :                 if (status == 0)
     428             :                 {
     429           1 :                     int nYear = 0;
     430           1 :                     int nMonth = 0;
     431           1 :                     int nDay = 0;
     432           1 :                     SetField(iField, nYear, nMonth, nDay, nHour, nMin,
     433           1 :                              nSec + nMS / 1000.0f, 0);
     434             :                 }
     435             : #else
     436             :                 const char *pszValue =
     437             :                     poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
     438             :                 SetField(iField, pszValue);
     439             : #endif
     440           2 :                 break;
     441             :             }
     442          18 :             case TABFDateTime:
     443             :             {
     444             : #ifdef MITAB_USE_OFTDATETIME
     445          18 :                 int nYear = 0;
     446          18 :                 int nMonth = 0;
     447          18 :                 int nDay = 0;
     448          18 :                 int nHour = 0;
     449          18 :                 int nMin = 0;
     450          18 :                 int nMS = 0;
     451          18 :                 int nSec = 0;
     452          18 :                 const int status = poDATFile->ReadDateTimeField(
     453             :                     poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
     454             :                     &nHour, &nMin, &nSec, &nMS);
     455          18 :                 if (status == 0)
     456             :                 {
     457           1 :                     SetField(iField, nYear, nMonth, nDay, nHour, nMin,
     458           1 :                              nSec + nMS / 1000.0f, 0);
     459             :                 }
     460             : #else
     461             :                 const char *pszValue = poDATFile->ReadDateTimeField(
     462             :                     poDATFile->GetFieldWidth(iField));
     463             :                 SetField(iField, pszValue);
     464             : #endif
     465          18 :                 break;
     466             :             }
     467           0 :             default:
     468             :                 // Other type???  Impossible!
     469           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     470             :                          "Unsupported field type!");
     471             :         }
     472             :     }
     473             : 
     474      516678 :     return 0;
     475             : }
     476             : 
     477             : /**********************************************************************
     478             :  *                   TABFeature::WriteRecordToDATFile()
     479             :  *
     480             :  * Write the attribute part of the feature to the .DAT file.
     481             :  *
     482             :  * It is assumed that poDATFile currently points to the beginning of
     483             :  * the table record and that this feature's OGRFeatureDefn has been
     484             :  * properly initialized for this table.
     485             :  *
     486             :  * Returns 0 on success, -1 on error.
     487             :  **********************************************************************/
     488       15078 : int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
     489             :                                      TABINDFile *poINDFile, int *panIndexNo)
     490             : {
     491             : #ifdef MITAB_USE_OFTDATETIME
     492       15078 :     int nYear = 0;
     493       15078 :     int nMon = 0;
     494       15078 :     int nDay = 0;
     495       15078 :     int nHour = 0;
     496       15078 :     int nMin = 0;
     497       15078 :     int nTZFlag = 0;
     498       15078 :     float fSec = 0.0f;
     499             : #endif
     500             : 
     501       15078 :     CPLAssert(poDATFile);
     502             : 
     503       15078 :     const int numFields = poDATFile->GetNumFields();
     504             : 
     505       15078 :     poDATFile->MarkRecordAsExisting();
     506             : 
     507       15078 :     int nStatus = 0;
     508       30827 :     for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
     509             :     {
     510             :         // Hack for "extra" introduced field.
     511       15749 :         if (iField >= GetDefnRef()->GetFieldCount())
     512             :         {
     513           1 :             CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
     514             :                       iField == 0);
     515           1 :             nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
     516             :                                                    poINDFile, 0);
     517           1 :             continue;
     518             :         }
     519       15748 :         CPLAssert(panIndexNo != nullptr);
     520             : 
     521       15748 :         switch (poDATFile->GetFieldType(iField))
     522             :         {
     523         374 :             case TABFChar:
     524             :             {
     525         374 :                 CPLString osValue(GetFieldAsString(iField));
     526         374 :                 if (!poDATFile->GetEncoding().empty())
     527             :                 {
     528          30 :                     osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
     529             :                 }
     530         374 :                 nStatus = poDATFile->WriteCharField(
     531             :                     osValue, poDATFile->GetFieldWidth(iField), poINDFile,
     532         374 :                     panIndexNo[iField]);
     533             :             }
     534         374 :             break;
     535           4 :             case TABFDecimal:
     536           4 :                 nStatus = poDATFile->WriteDecimalField(
     537             :                     GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
     538             :                     poDATFile->GetFieldPrecision(iField), poINDFile,
     539           4 :                     panIndexNo[iField]);
     540           4 :                 break;
     541       14984 :             case TABFInteger:
     542       14984 :                 nStatus = poDATFile->WriteIntegerField(
     543       14984 :                     GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
     544       14984 :                 break;
     545           0 :             case TABFSmallInt:
     546           0 :                 nStatus = poDATFile->WriteSmallIntField(
     547           0 :                     static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
     548           0 :                     panIndexNo[iField]);
     549           0 :                 break;
     550           1 :             case TABFLargeInt:
     551           3 :                 nStatus = poDATFile->WriteLargeIntField(
     552           1 :                     static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
     553           1 :                     panIndexNo[iField]);
     554           1 :                 break;
     555         234 :             case TABFFloat:
     556         234 :                 nStatus = poDATFile->WriteFloatField(
     557         234 :                     GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
     558         234 :                 break;
     559           0 :             case TABFLogical:
     560           0 :                 nStatus = poDATFile->WriteLogicalField(
     561           0 :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     562           0 :                 break;
     563          75 :             case TABFDate:
     564             : #ifdef MITAB_USE_OFTDATETIME
     565          75 :                 if (IsFieldSetAndNotNull(iField))
     566             :                 {
     567          58 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     568             :                                        &nMin, &fSec, &nTZFlag);
     569             :                 }
     570             :                 else
     571             :                 {
     572          17 :                     nYear = 0;
     573          17 :                     nMon = 0;
     574          17 :                     nDay = 0;
     575             :                 }
     576             : 
     577         150 :                 nStatus = poDATFile->WriteDateField(
     578          75 :                     nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
     579             : #else
     580             :                 nStatus = poDATFile->WriteDateField(
     581             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     582             : #endif
     583          75 :                 break;
     584           2 :             case TABFTime:
     585             : #ifdef MITAB_USE_OFTDATETIME
     586           2 :                 if (IsFieldSetAndNotNull(iField))
     587             :                 {
     588           1 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     589             :                                        &nMin, &fSec, &nTZFlag);
     590             :                 }
     591             :                 else
     592             :                 {
     593             :                     // Put negative values, so that WriteTimeField() forges
     594             :                     // a negative value, and ultimately write -1 in the binary
     595             :                     // field
     596           1 :                     nHour = -1;
     597           1 :                     nMin = -1;
     598           1 :                     fSec = -1;
     599             :                 }
     600           2 :                 nStatus = poDATFile->WriteTimeField(
     601             :                     nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
     602           2 :                     poINDFile, panIndexNo[iField]);
     603             : 
     604             : #else
     605             :                 nStatus = poDATFile->WriteTimeField(
     606             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     607             : #endif
     608           2 :                 break;
     609          74 :             case TABFDateTime:
     610             : #ifdef MITAB_USE_OFTDATETIME
     611          74 :                 if (IsFieldSetAndNotNull(iField))
     612             :                 {
     613          57 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     614             :                                        &nMin, &fSec, &nTZFlag);
     615             :                 }
     616             :                 else
     617             :                 {
     618          17 :                     nYear = 0;
     619          17 :                     nMon = 0;
     620          17 :                     nDay = 0;
     621          17 :                     nHour = 0;
     622          17 :                     nMin = 0;
     623          17 :                     fSec = 0;
     624             :                 }
     625             : 
     626          74 :                 nStatus = poDATFile->WriteDateTimeField(
     627             :                     nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
     628          74 :                     OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
     629             : #else
     630             :                 nStatus = poDATFile->WriteDateTimeField(
     631             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     632             : #endif
     633          74 :                 break;
     634           0 :             default:
     635             :                 // Other type???  Impossible!
     636           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     637             :                          "Unsupported field type!");
     638             :         }
     639             :     }
     640             : 
     641       15078 :     if (nStatus != 0)
     642           1 :         return nStatus;
     643             : 
     644       15077 :     if (poDATFile->CommitRecordToFile() != 0)
     645           0 :         return -1;
     646             : 
     647       15077 :     return 0;
     648             : }
     649             : 
     650             : /**********************************************************************
     651             :  *                   TABFeature::ReadGeometryFromMAPFile()
     652             :  *
     653             :  * In derived classes, this method should be reimplemented to
     654             :  * fill the geometry and representation (color, etc...) part of the
     655             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     656             :  *
     657             :  * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
     658             :  * currently points to the beginning of a map object.
     659             :  *
     660             :  * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
     661             :  * the CoordBlock data during splitting of object blocks. In this case we
     662             :  * need to process only the information related to the CoordBlock. One
     663             :  * important thing to avoid is reading/writing pen/brush/symbol definitions
     664             :  * as that would screw up their ref counters.
     665             :  *
     666             :  * ppoCoordBlock is used by TABCollection and by index splitting code
     667             :  * to provide a CoordBlock to use instead of the one from the poMAPFile and
     668             :  * return the current pointer at the end of the call.
     669             :  *
     670             :  * The current implementation does nothing since instances of TABFeature
     671             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     672             :  *
     673             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     674             :  * been called.
     675             :  **********************************************************************/
     676         147 : int TABFeature::ReadGeometryFromMAPFile(
     677             :     TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
     678             :     GBool /*bCoordBlockDataOnly=FALSE*/,
     679             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     680             : {
     681             :     // Nothing to do. Instances of TABFeature objects contain no geometry.
     682         147 :     return 0;
     683             : }
     684             : 
     685             : /**********************************************************************
     686             :  *                   TABFeature::UpdateMBR()
     687             :  *
     688             :  * Fetch envelope of poGeom and update MBR.
     689             :  * Integer coord MBR is updated only if poMapFile is not NULL.
     690             :  *
     691             :  * Returns 0 on success, or -1 if there is no geometry in object
     692             :  **********************************************************************/
     693       15013 : int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
     694             : {
     695       15013 :     OGRGeometry *poGeom = GetGeometryRef();
     696             : 
     697       15013 :     if (poGeom)
     698             :     {
     699       15013 :         OGREnvelope oEnv;
     700       15013 :         poGeom->getEnvelope(&oEnv);
     701             : 
     702       15013 :         m_dXMin = oEnv.MinX;
     703       15013 :         m_dYMin = oEnv.MinY;
     704       15013 :         m_dXMax = oEnv.MaxX;
     705       15013 :         m_dYMax = oEnv.MaxY;
     706             : 
     707       15013 :         if (poMapFile)
     708             :         {
     709       15013 :             poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
     710       15013 :             poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
     711             :             // Coordsy2Int can transform a min value to a max one and vice
     712             :             // versa.
     713       15013 :             if (m_nXMin > m_nXMax)
     714             :             {
     715           0 :                 std::swap(m_nXMin, m_nXMax);
     716             :             }
     717       15013 :             if (m_nYMin > m_nYMax)
     718             :             {
     719           0 :                 std::swap(m_nYMin, m_nYMax);
     720             :             }
     721             :         }
     722             : 
     723       15013 :         return 0;
     724             :     }
     725             : 
     726           0 :     return -1;
     727             : }
     728             : 
     729             : /**********************************************************************
     730             :  *                   TABFeature::ValidateCoordType()
     731             :  *
     732             :  * Checks the feature envelope to establish if the feature should be
     733             :  * written using Compressed coordinates or not and adjust m_nMapInfoType
     734             :  * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
     735             :  * m_nXMax, m_nYMax
     736             :  *
     737             :  * This function should be used only by the ValidateMapInfoType()
     738             :  * implementations.
     739             :  *
     740             :  * Returns TRUE if coord. should be compressed, FALSE otherwise
     741             :  **********************************************************************/
     742         303 : GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
     743             : {
     744         303 :     GBool bCompr = FALSE;
     745             : 
     746             :     /*-------------------------------------------------------------
     747             :      * Decide if coordinates should be compressed or not.
     748             :      *------------------------------------------------------------*/
     749         303 :     if (UpdateMBR(poMapFile) == 0)
     750             :     {
     751             :         /* Test for max range < 65535 here instead of < 65536 to avoid
     752             :          * compressed coordinate overflows in some boundary situations
     753             :          */
     754         303 :         if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
     755         294 :             (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
     756             :         {
     757         294 :             bCompr = TRUE;
     758             :         }
     759         303 :         m_nComprOrgX =
     760         303 :             static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
     761         303 :         m_nComprOrgY =
     762         303 :             static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
     763             :     }
     764             : 
     765             :     /*-------------------------------------------------------------
     766             :      * Adjust native type
     767             :      *------------------------------------------------------------*/
     768         303 :     if (bCompr && ((m_nMapInfoType % 3) == 2))
     769         294 :         m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
     770             :                                                   1);  // compr = 1, 4, 7, ...
     771           9 :     else if (!bCompr && ((m_nMapInfoType % 3) == 1))
     772           0 :         m_nMapInfoType = static_cast<TABGeomType>(
     773           0 :             m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
     774             : 
     775         303 :     return bCompr;
     776             : }
     777             : 
     778             : /**********************************************************************
     779             :  *                   TABFeature::ForceCoordTypeAndOrigin()
     780             :  *
     781             :  * This function is used by TABCollection::ValidateMapInfoType() to force
     782             :  * the coord type and compressed origin of all members of a collection
     783             :  * to be the same. (A replacement for ValidateCoordType() for this
     784             :  * specific case)
     785             :  **********************************************************************/
     786           0 : void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
     787             :                                          GInt32 nComprOrgX, GInt32 nComprOrgY,
     788             :                                          GInt32 nXMin, GInt32 nYMin,
     789             :                                          GInt32 nXMax, GInt32 nYMax)
     790             : {
     791             :     /*-------------------------------------------------------------
     792             :      * Set Compressed Origin and adjust native type
     793             :      *------------------------------------------------------------*/
     794           0 :     m_nComprOrgX = nComprOrgX;
     795           0 :     m_nComprOrgY = nComprOrgY;
     796             : 
     797           0 :     m_nMapInfoType = nMapInfoType;
     798             : 
     799           0 :     if (bCompr && ((m_nMapInfoType % 3) == 2))
     800           0 :         m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
     801             :                                                   1);  // compr = 1, 4, 7, ...
     802           0 :     else if (!bCompr && ((m_nMapInfoType % 3) == 1))
     803           0 :         m_nMapInfoType = static_cast<TABGeomType>(
     804           0 :             m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
     805             : 
     806           0 :     m_nXMin = nXMin;
     807           0 :     m_nYMin = nYMin;
     808           0 :     m_nXMax = nXMax;
     809           0 :     m_nYMax = nYMax;
     810           0 : }
     811             : 
     812             : /**********************************************************************
     813             :  *                   TABFeature::WriteGeometryToMAPFile()
     814             :  *
     815             :  *
     816             :  * In derived classes, this method should be reimplemented to
     817             :  * write the geometry and representation (color, etc...) part of the
     818             :  * feature to the .MAP object pointed to by poMAPFile.
     819             :  *
     820             :  * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
     821             :  * currently points to a valid map object.
     822             :  *
     823             :  * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
     824             :  * the CoordBlock data during splitting of object blocks. In this case we
     825             :  * need to process only the information related to the CoordBlock. One
     826             :  * important thing to avoid is reading/writing pen/brush/symbol definitions
     827             :  * as that would screw up their ref counters.
     828             :  *
     829             :  * ppoCoordBlock is used by TABCollection and by index splitting code
     830             :  * to provide a CoordBlock to use instead of the one from the poMAPFile and
     831             :  * return the current pointer at the end of the call.
     832             :  *
     833             :  * The current implementation does nothing since instances of TABFeature
     834             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     835             :  *
     836             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     837             :  * been called.
     838             :  **********************************************************************/
     839          57 : int TABFeature::WriteGeometryToMAPFile(
     840             :     TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
     841             :     GBool /*bCoordBlockDataOnly=FALSE*/,
     842             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     843             : {
     844             :     /*-----------------------------------------------------------------
     845             :      * Nothing to do... instances of TABFeature objects contain no geometry.
     846             :      *----------------------------------------------------------------*/
     847             : 
     848          57 :     return 0;
     849             : }
     850             : 
     851             : /**********************************************************************
     852             :  *                   TABFeature::DumpMID()
     853             :  *
     854             :  * Dump feature attributes in a format similar to .MID data records.
     855             :  **********************************************************************/
     856           0 : void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
     857             : {
     858           0 :     OGRFeatureDefn *l_poDefn = GetDefnRef();
     859             : 
     860           0 :     if (fpOut == nullptr)
     861           0 :         fpOut = stdout;
     862             : 
     863           0 :     for (int iField = 0; iField < GetFieldCount(); iField++)
     864             :     {
     865           0 :         OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
     866             : 
     867           0 :         fprintf(fpOut, "  %s (%s) = %s\n", poFDefn->GetNameRef(),
     868             :                 OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
     869             :                 GetFieldAsString(iField));
     870             :     }
     871             : 
     872           0 :     fflush(fpOut);
     873           0 : }
     874             : 
     875             : /**********************************************************************
     876             :  *                   TABFeature::DumpMIF()
     877             :  *
     878             :  * Dump feature geometry in a format similar to .MIF files.
     879             :  **********************************************************************/
     880           0 : void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
     881             : {
     882           0 :     if (fpOut == nullptr)
     883           0 :         fpOut = stdout;
     884             : 
     885             :     /*-----------------------------------------------------------------
     886             :      * Generate output... not much to do, feature contains no geometry.
     887             :      *----------------------------------------------------------------*/
     888           0 :     fprintf(fpOut, "NONE\n");
     889             : 
     890           0 :     fflush(fpOut);
     891           0 : }
     892             : 
     893             : /*=====================================================================
     894             :  *                      class TABPoint
     895             :  *====================================================================*/
     896             : 
     897             : /**********************************************************************
     898             :  *                   TABPoint::TABPoint()
     899             :  *
     900             :  * Constructor.
     901             :  **********************************************************************/
     902      531570 : TABPoint::TABPoint(OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
     903             : {
     904      531570 : }
     905             : 
     906             : /**********************************************************************
     907             :  *                   TABPoint::~TABPoint()
     908             :  *
     909             :  * Destructor.
     910             :  **********************************************************************/
     911     1061811 : TABPoint::~TABPoint()
     912             : {
     913     1061811 : }
     914             : 
     915             : /**********************************************************************
     916             :  *                     TABPoint::CloneTABFeature()
     917             :  *
     918             :  * Duplicate feature, including stuff specific to each TABFeature type.
     919             :  *
     920             :  * This method calls the generic TABFeature::CloneTABFeature() and
     921             :  * then copies any members specific to its own type.
     922             :  **********************************************************************/
     923           2 : TABFeature *TABPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
     924             : {
     925             :     /*-----------------------------------------------------------------
     926             :      * Alloc new feature and copy the base stuff
     927             :      *----------------------------------------------------------------*/
     928           2 :     TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
     929             : 
     930           2 :     CopyTABFeatureBase(poNew);
     931             : 
     932             :     /*-----------------------------------------------------------------
     933             :      * And members specific to this class
     934             :      *----------------------------------------------------------------*/
     935             :     // ITABFeatureSymbol
     936           2 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
     937             : 
     938           2 :     return poNew;
     939             : }
     940             : 
     941             : /**********************************************************************
     942             :  *                   TABPoint::ValidateMapInfoType()
     943             :  *
     944             :  * Check the feature's geometry part and return the corresponding
     945             :  * mapinfo object type code.  The m_nMapInfoType member will also
     946             :  * be updated for further calls to GetMapInfoType();
     947             :  *
     948             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
     949             :  * is expected for this object class.
     950             :  **********************************************************************/
     951       14687 : TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
     952             : {
     953             :     /*-----------------------------------------------------------------
     954             :      * Fetch and validate geometry
     955             :      * __TODO__ For now we always write in uncompressed format (until we
     956             :      * find that this is not correct... note that at this point the
     957             :      * decision to use compressed/uncompressed will likely be based on
     958             :      * the distance between the point and the object block center in
     959             :      * integer coordinates being > 32767 or not... remains to be verified)
     960             :      *----------------------------------------------------------------*/
     961       14687 :     OGRGeometry *poGeom = GetGeometryRef();
     962       14687 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     963             :     {
     964       14687 :         switch (GetFeatureClass())
     965             :         {
     966           2 :             case TABFCFontPoint:
     967           2 :                 m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
     968           2 :                 break;
     969           2 :             case TABFCCustomPoint:
     970           2 :                 m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
     971           2 :                 break;
     972       14683 :             case TABFCPoint:
     973             :             default:
     974       14683 :                 m_nMapInfoType = TAB_GEOM_SYMBOL;
     975       14683 :                 break;
     976             :         }
     977             :     }
     978             :     else
     979             :     {
     980           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     981             :                  "TABPoint: Missing or Invalid Geometry!");
     982           0 :         m_nMapInfoType = TAB_GEOM_NONE;
     983             :     }
     984             : 
     985       14687 :     UpdateMBR(poMapFile);
     986             : 
     987       14687 :     return m_nMapInfoType;
     988             : }
     989             : 
     990             : /**********************************************************************
     991             :  *                   TABPoint::ReadGeometryFromMAPFile()
     992             :  *
     993             :  * Fill the geometry and representation (color, etc...) part of the
     994             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     995             :  *
     996             :  * It is assumed that poMAPFile currently points to the beginning of
     997             :  * a map object.
     998             :  *
     999             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1000             :  * been called.
    1001             :  **********************************************************************/
    1002      514727 : int TABPoint::ReadGeometryFromMAPFile(
    1003             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1004             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1005             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1006             : {
    1007             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1008      514727 :     if (bCoordBlockDataOnly)
    1009           0 :         return 0;
    1010             : 
    1011             :     /*-----------------------------------------------------------------
    1012             :      * Fetch and validate geometry type
    1013             :      *----------------------------------------------------------------*/
    1014      514727 :     m_nMapInfoType = poObjHdr->m_nType;
    1015             : 
    1016      514727 :     if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
    1017       13206 :         m_nMapInfoType != TAB_GEOM_SYMBOL_C)
    1018             :     {
    1019           0 :         CPLError(
    1020             :             CE_Failure, CPLE_AssertionFailed,
    1021             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1022           0 :             m_nMapInfoType, m_nMapInfoType);
    1023           0 :         return -1;
    1024             :     }
    1025             : 
    1026             :     /*-----------------------------------------------------------------
    1027             :      * Read object information
    1028             :      *----------------------------------------------------------------*/
    1029      514727 :     TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
    1030             : 
    1031      514727 :     m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
    1032             : 
    1033      514727 :     poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    1034             : 
    1035             :     /*-----------------------------------------------------------------
    1036             :      * Create and fill geometry object
    1037             :      *----------------------------------------------------------------*/
    1038      514727 :     double dX = 0.0;
    1039      514727 :     double dY = 0.0;
    1040             : 
    1041      514727 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1042      514727 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1043             : 
    1044      514727 :     SetGeometryDirectly(poGeometry);
    1045             : 
    1046      514727 :     SetMBR(dX, dY, dX, dY);
    1047      514727 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1048             :               poObjHdr->m_nMaxY);
    1049             : 
    1050      514727 :     return 0;
    1051             : }
    1052             : 
    1053             : /**********************************************************************
    1054             :  *                   TABPoint::WriteGeometryToMAPFile()
    1055             :  *
    1056             :  * Write the geometry and representation (color, etc...) part of the
    1057             :  * feature to the .MAP object pointed to by poMAPFile.
    1058             :  *
    1059             :  * It is assumed that poMAPFile currently points to a valid map object.
    1060             :  *
    1061             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1062             :  * been called.
    1063             :  **********************************************************************/
    1064       14683 : int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    1065             :                                      TABMAPObjHdr *poObjHdr,
    1066             :                                      GBool bCoordBlockDataOnly /*=FALSE*/,
    1067             :                                      TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1068             : {
    1069             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1070       14683 :     if (bCoordBlockDataOnly)
    1071           0 :         return 0;
    1072             : 
    1073             :     /*-----------------------------------------------------------------
    1074             :      * We assume that ValidateMapInfoType() was called already and that
    1075             :      * the type in poObjHdr->m_nType is valid.
    1076             :      *----------------------------------------------------------------*/
    1077       14683 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1078             : 
    1079             :     /*-----------------------------------------------------------------
    1080             :      * Fetch and validate geometry
    1081             :      *----------------------------------------------------------------*/
    1082       14683 :     OGRGeometry *poGeom = GetGeometryRef();
    1083       14683 :     OGRPoint *poPoint = nullptr;
    1084       14683 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1085       14683 :         poPoint = poGeom->toPoint();
    1086             :     else
    1087             :     {
    1088           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1089             :                  "TABPoint: Missing or Invalid Geometry!");
    1090           0 :         return -1;
    1091             :     }
    1092             : 
    1093       14683 :     GInt32 nX = 0;
    1094       14683 :     GInt32 nY = 0;
    1095       14683 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1096             : 
    1097             :     /*-----------------------------------------------------------------
    1098             :      * Copy object information
    1099             :      *----------------------------------------------------------------*/
    1100       14683 :     TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
    1101             : 
    1102       14683 :     poPointHdr->m_nX = nX;
    1103       14683 :     poPointHdr->m_nY = nY;
    1104       14683 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1105             : 
    1106       14683 :     m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    1107       14683 :     poPointHdr->m_nSymbolId =
    1108       14683 :         static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    1109             : 
    1110       14683 :     if (CPLGetLastErrorType() == CE_Failure)
    1111           0 :         return -1;
    1112             : 
    1113       14683 :     return 0;
    1114             : }
    1115             : 
    1116             : /**********************************************************************
    1117             :  *                   TABPoint::GetX()
    1118             :  *
    1119             :  * Return this point's X coordinate.
    1120             :  **********************************************************************/
    1121           0 : double TABPoint::GetX()
    1122             : {
    1123             : 
    1124             :     /*-----------------------------------------------------------------
    1125             :      * Fetch and validate geometry
    1126             :      *----------------------------------------------------------------*/
    1127           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1128           0 :     OGRPoint *poPoint = nullptr;
    1129           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1130           0 :         poPoint = poGeom->toPoint();
    1131             :     else
    1132             :     {
    1133           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1134             :                  "TABPoint: Missing or Invalid Geometry!");
    1135           0 :         return 0.0;
    1136             :     }
    1137             : 
    1138           0 :     return poPoint->getX();
    1139             : }
    1140             : 
    1141             : /**********************************************************************
    1142             :  *                   TABPoint::GetY()
    1143             :  *
    1144             :  * Return this point's Y coordinate.
    1145             :  **********************************************************************/
    1146           0 : double TABPoint::GetY()
    1147             : {
    1148             :     /*-----------------------------------------------------------------
    1149             :      * Fetch and validate geometry
    1150             :      *----------------------------------------------------------------*/
    1151           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1152           0 :     OGRPoint *poPoint = nullptr;
    1153           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1154           0 :         poPoint = poGeom->toPoint();
    1155             :     else
    1156             :     {
    1157           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1158             :                  "TABPoint: Missing or Invalid Geometry!");
    1159           0 :         return 0.0;
    1160             :     }
    1161             : 
    1162           0 :     return poPoint->getY();
    1163             : }
    1164             : 
    1165             : /**********************************************************************
    1166             :  *                   TABPoint::GetStyleString() const
    1167             :  *
    1168             :  * Return style string for this feature.
    1169             :  *
    1170             :  * Style String is built only once during the first call to GetStyleString().
    1171             :  **********************************************************************/
    1172         239 : const char *TABPoint::GetStyleString() const
    1173             : {
    1174         239 :     if (m_pszStyleString == nullptr)
    1175             :     {
    1176          27 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    1177             :     }
    1178             : 
    1179         239 :     return m_pszStyleString;
    1180             : }
    1181             : 
    1182             : /**********************************************************************
    1183             :  *                   TABPoint::DumpMIF()
    1184             :  *
    1185             :  * Dump feature geometry in a format similar to .MIF POINTs.
    1186             :  **********************************************************************/
    1187           0 : void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
    1188             : {
    1189           0 :     if (fpOut == nullptr)
    1190           0 :         fpOut = stdout;
    1191             : 
    1192             :     /*-----------------------------------------------------------------
    1193             :      * Fetch and validate geometry
    1194             :      *----------------------------------------------------------------*/
    1195           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1196           0 :     OGRPoint *poPoint = nullptr;
    1197           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1198           0 :         poPoint = poGeom->toPoint();
    1199             :     else
    1200             :     {
    1201           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1202             :                  "TABPoint: Missing or Invalid Geometry!");
    1203           0 :         return;
    1204             :     }
    1205             : 
    1206             :     /*-----------------------------------------------------------------
    1207             :      * Generate output
    1208             :      *----------------------------------------------------------------*/
    1209           0 :     fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
    1210             : 
    1211           0 :     DumpSymbolDef(fpOut);
    1212             : 
    1213             :     /*-----------------------------------------------------------------
    1214             :      * Handle stuff specific to derived classes
    1215             :      *----------------------------------------------------------------*/
    1216             :     // cppcheck-suppress knownConditionTrueFalse
    1217           0 :     if (GetFeatureClass() == TABFCFontPoint)
    1218             :     {
    1219           0 :         TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
    1220           0 :         fprintf(fpOut, "  m_nFontStyle     = 0x%2.2x (%d)\n",
    1221             :                 poFeature->GetFontStyleTABValue(),
    1222             :                 poFeature->GetFontStyleTABValue());
    1223             : 
    1224           0 :         poFeature->DumpFontDef(fpOut);
    1225             :     }
    1226             :     // cppcheck-suppress knownConditionTrueFalse
    1227           0 :     if (GetFeatureClass() == TABFCCustomPoint)
    1228             :     {
    1229           0 :         TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
    1230             : 
    1231           0 :         fprintf(fpOut, "  m_nUnknown_      = 0x%2.2x (%d)\n",
    1232           0 :                 poFeature->m_nUnknown_, poFeature->m_nUnknown_);
    1233           0 :         fprintf(fpOut, "  m_nCustomStyle   = 0x%2.2x (%d)\n",
    1234           0 :                 poFeature->GetCustomSymbolStyle(),
    1235           0 :                 poFeature->GetCustomSymbolStyle());
    1236             : 
    1237           0 :         poFeature->DumpFontDef(fpOut);
    1238             :     }
    1239             : 
    1240           0 :     fflush(fpOut);
    1241             : }
    1242             : 
    1243             : /*=====================================================================
    1244             :  *                      class TABFontPoint
    1245             :  *====================================================================*/
    1246             : 
    1247             : /**********************************************************************
    1248             :  *                   TABFontPoint::TABFontPoint()
    1249             :  *
    1250             :  * Constructor.
    1251             :  **********************************************************************/
    1252         643 : TABFontPoint::TABFontPoint(OGRFeatureDefn *poDefnIn)
    1253         643 :     : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
    1254             : {
    1255         643 : }
    1256             : 
    1257             : /**********************************************************************
    1258             :  *                   TABFontPoint::~TABFontPoint()
    1259             :  *
    1260             :  * Destructor.
    1261             :  **********************************************************************/
    1262        1286 : TABFontPoint::~TABFontPoint()
    1263             : {
    1264        1286 : }
    1265             : 
    1266             : /**********************************************************************
    1267             :  *                     TABFontPoint::CloneTABFeature()
    1268             :  *
    1269             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1270             :  *
    1271             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1272             :  * then copies any members specific to its own type.
    1273             :  **********************************************************************/
    1274           0 : TABFeature *TABFontPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    1275             : {
    1276             :     /*-----------------------------------------------------------------
    1277             :      * Alloc new feature and copy the base stuff
    1278             :      *----------------------------------------------------------------*/
    1279             :     TABFontPoint *poNew =
    1280           0 :         new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
    1281             : 
    1282           0 :     CopyTABFeatureBase(poNew);
    1283             : 
    1284             :     /*-----------------------------------------------------------------
    1285             :      * And members specific to this class
    1286             :      *----------------------------------------------------------------*/
    1287             :     // ITABFeatureSymbol
    1288           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    1289             : 
    1290             :     // ITABFeatureFont
    1291           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    1292             : 
    1293           0 :     poNew->SetSymbolAngle(GetSymbolAngle());
    1294           0 :     poNew->SetFontStyleTABValue(GetFontStyleTABValue());
    1295             : 
    1296           0 :     return poNew;
    1297             : }
    1298             : 
    1299             : /**********************************************************************
    1300             :  *                   TABFontPoint::ReadGeometryFromMAPFile()
    1301             :  *
    1302             :  * Fill the geometry and representation (color, etc...) part of the
    1303             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    1304             :  *
    1305             :  * It is assumed that poMAPFile currently points to the beginning of
    1306             :  * a map object.
    1307             :  *
    1308             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1309             :  * been called.
    1310             :  **********************************************************************/
    1311           6 : int TABFontPoint::ReadGeometryFromMAPFile(
    1312             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1313             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1314             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1315             : {
    1316             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1317           6 :     if (bCoordBlockDataOnly)
    1318           0 :         return 0;
    1319             : 
    1320             :     /*-----------------------------------------------------------------
    1321             :      * Fetch and validate geometry type
    1322             :      *----------------------------------------------------------------*/
    1323           6 :     m_nMapInfoType = poObjHdr->m_nType;
    1324             : 
    1325           6 :     if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
    1326           0 :         m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
    1327             :     {
    1328           0 :         CPLError(
    1329             :             CE_Failure, CPLE_AssertionFailed,
    1330             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1331           0 :             m_nMapInfoType, m_nMapInfoType);
    1332           0 :         return -1;
    1333             :     }
    1334             : 
    1335             :     /*-----------------------------------------------------------------
    1336             :      * Read object information
    1337             :      * NOTE: This symbol type does not contain a reference to a
    1338             :      * SymbolDef block in the file, but we still use the m_sSymbolDef
    1339             :      * structure to store the information inside the class so that the
    1340             :      * ITABFeatureSymbol methods work properly for the class user.
    1341             :      *----------------------------------------------------------------*/
    1342             :     TABMAPObjFontPoint *poPointHdr =
    1343           6 :         cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
    1344             : 
    1345           6 :     m_nSymbolDefIndex = -1;
    1346           6 :     m_sSymbolDef.nRefCount = 0;
    1347             : 
    1348           6 :     m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId;    // shape
    1349           6 :     m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize;  // point size
    1350             : 
    1351           6 :     m_nFontStyle = poPointHdr->m_nFontStyle;  // font style
    1352             : 
    1353           6 :     m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
    1354           6 :                             poPointHdr->m_nG * 256 + poPointHdr->m_nB;
    1355             : 
    1356             :     /*-------------------------------------------------------------
    1357             :      * Symbol Angle, in tenths of degree.
    1358             :      * Contrary to arc start/end angles, no conversion based on
    1359             :      * origin quadrant is required here.
    1360             :      *------------------------------------------------------------*/
    1361           6 :     m_dAngle = poPointHdr->m_nAngle / 10.0;
    1362             : 
    1363           6 :     m_nFontDefIndex = poPointHdr->m_nFontId;  // Font name index
    1364             : 
    1365           6 :     poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    1366             : 
    1367             :     /*-----------------------------------------------------------------
    1368             :      * Create and fill geometry object
    1369             :      *----------------------------------------------------------------*/
    1370           6 :     double dX = 0.0;
    1371           6 :     double dY = 0.0;
    1372           6 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1373           6 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1374             : 
    1375           6 :     SetGeometryDirectly(poGeometry);
    1376             : 
    1377           6 :     SetMBR(dX, dY, dX, dY);
    1378           6 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1379             :               poObjHdr->m_nMaxY);
    1380             : 
    1381           6 :     return 0;
    1382             : }
    1383             : 
    1384             : /**********************************************************************
    1385             :  *                   TABFontPoint::WriteGeometryToMAPFile()
    1386             :  *
    1387             :  * Write the geometry and representation (color, etc...) part of the
    1388             :  * feature to the .MAP object pointed to by poMAPFile.
    1389             :  *
    1390             :  * It is assumed that poMAPFile currently points to a valid map object.
    1391             :  *
    1392             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1393             :  * been called.
    1394             :  **********************************************************************/
    1395           2 : int TABFontPoint::WriteGeometryToMAPFile(
    1396             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1397             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1398             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1399             : {
    1400             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1401           2 :     if (bCoordBlockDataOnly)
    1402           0 :         return 0;
    1403             : 
    1404             :     /*-----------------------------------------------------------------
    1405             :      * We assume that ValidateMapInfoType() was called already and that
    1406             :      * the type in poObjHdr->m_nType is valid.
    1407             :      *----------------------------------------------------------------*/
    1408           2 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1409             : 
    1410             :     /*-----------------------------------------------------------------
    1411             :      * Fetch and validate geometry
    1412             :      *----------------------------------------------------------------*/
    1413           2 :     OGRGeometry *poGeom = GetGeometryRef();
    1414           2 :     OGRPoint *poPoint = nullptr;
    1415           2 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1416           2 :         poPoint = poGeom->toPoint();
    1417             :     else
    1418             :     {
    1419           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1420             :                  "TABFontPoint: Missing or Invalid Geometry!");
    1421           0 :         return -1;
    1422             :     }
    1423             : 
    1424           2 :     GInt32 nX = 0;
    1425           2 :     GInt32 nY = 0;
    1426           2 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1427             : 
    1428             :     /*-----------------------------------------------------------------
    1429             :      * Copy object information
    1430             :      * NOTE: This symbol type does not contain a reference to a
    1431             :      * SymbolDef block in the file, but we still use the m_sSymbolDef
    1432             :      * structure to store the information inside the class so that the
    1433             :      * ITABFeatureSymbol methods work properly for the class user.
    1434             :      *----------------------------------------------------------------*/
    1435             :     TABMAPObjFontPoint *poPointHdr =
    1436           2 :         cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
    1437             : 
    1438           2 :     poPointHdr->m_nX = nX;
    1439           2 :     poPointHdr->m_nY = nY;
    1440           2 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1441             : 
    1442           2 :     poPointHdr->m_nSymbolId =
    1443           2 :         static_cast<GByte>(m_sSymbolDef.nSymbolNo);  // shape
    1444           2 :     poPointHdr->m_nPointSize =
    1445           2 :         static_cast<GByte>(m_sSymbolDef.nPointSize);  // point size
    1446           2 :     poPointHdr->m_nFontStyle = m_nFontStyle;          // font style
    1447             : 
    1448           2 :     poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
    1449           2 :     poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
    1450           2 :     poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
    1451             : 
    1452             :     /*-------------------------------------------------------------
    1453             :      * Symbol Angle, in tenths of degree.
    1454             :      * Contrary to arc start/end angles, no conversion based on
    1455             :      * origin quadrant is required here.
    1456             :      *------------------------------------------------------------*/
    1457           2 :     poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
    1458             : 
    1459             :     // Write Font Def
    1460           2 :     m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    1461           2 :     poPointHdr->m_nFontId =
    1462           2 :         static_cast<GByte>(m_nFontDefIndex);  // Font name index
    1463             : 
    1464           2 :     if (CPLGetLastErrorType() == CE_Failure)
    1465           0 :         return -1;
    1466             : 
    1467           2 :     return 0;
    1468             : }
    1469             : 
    1470             : /**********************************************************************
    1471             :  *                   TABFontPoint::QueryFontStyle()
    1472             :  *
    1473             :  * Return TRUE if the specified font style attribute is turned ON,
    1474             :  * or FALSE otherwise.  See enum TABFontStyle for the list of styles
    1475             :  * that can be queried on.
    1476             :  **********************************************************************/
    1477           0 : GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
    1478             : {
    1479           0 :     return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
    1480             : }
    1481             : 
    1482           0 : void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
    1483             : {
    1484           0 :     if (bStyleOn)
    1485           0 :         m_nFontStyle |= static_cast<int>(eStyleToToggle);
    1486             :     else
    1487           0 :         m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
    1488           0 : }
    1489             : 
    1490             : /**********************************************************************
    1491             :  *                   TABFontPoint::GetFontStyleMIFValue()
    1492             :  *
    1493             :  * Return the Font Style value for this object using the style values
    1494             :  * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
    1495             :  *
    1496             :  * The reason why we have to differentiate between the TAB and the MIF font
    1497             :  * style values is that in TAB, TABFSBox is included in the style value
    1498             :  * as code 0x100, but in MIF it is not included, instead it is implied by
    1499             :  * the presence of the BG color in the FONT() clause (the BG color is
    1500             :  * present only when TABFSBox or TABFSHalo is set).
    1501             :  * This also has the effect of shifting all the other style values > 0x100
    1502             :  * by 1 byte.
    1503             :  *
    1504             :  * NOTE: Even if there is no BG color for font symbols, we inherit this
    1505             :  * problem because Font Point styles use the same codes as Text Font styles.
    1506             :  **********************************************************************/
    1507           0 : int TABFontPoint::GetFontStyleMIFValue()
    1508             : {
    1509             :     // The conversion is simply to remove bit 0x100 from the value and shift
    1510             :     // down all values past this bit.
    1511           0 :     return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
    1512             : }
    1513             : 
    1514         635 : void TABFontPoint::SetFontStyleMIFValue(int nStyle)
    1515             : {
    1516         635 :     m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
    1517         635 : }
    1518             : 
    1519             : /**********************************************************************
    1520             :  *                   TABFontPoint::SetSymbolAngle()
    1521             :  *
    1522             :  * Set the symbol angle value in degrees, making sure the value is
    1523             :  * always in the range [0..360]
    1524             :  **********************************************************************/
    1525         635 : void TABFontPoint::SetSymbolAngle(double dAngle)
    1526             : {
    1527         635 :     dAngle = fmod(dAngle, 360.0);
    1528         635 :     if (dAngle < 0.0)
    1529           0 :         dAngle += 360.0;
    1530             : 
    1531         635 :     m_dAngle = dAngle;
    1532         635 : }
    1533             : 
    1534             : /**********************************************************************
    1535             :  *                   TABFontPoint::GetSymbolStyleString()
    1536             :  *
    1537             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    1538             :  **********************************************************************/
    1539           7 : const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
    1540             : {
    1541             :     /* Get the SymbolStyleString, and add the outline Color
    1542             :        (halo/border in MapInfo Symbol terminology) */
    1543           7 :     const char *outlineColor = nullptr;
    1544           7 :     if (m_nFontStyle & 16)
    1545           0 :         outlineColor = ",o:#000000";
    1546           7 :     else if (m_nFontStyle & 512)
    1547           0 :         outlineColor = ",o:#ffffff";
    1548             :     else
    1549           7 :         outlineColor = "";
    1550             : 
    1551           7 :     int nAngle = static_cast<int>(dfAngle);
    1552             :     const char *pszStyle;
    1553             : 
    1554           7 :     pszStyle = CPLSPrintf(
    1555             :         "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
    1556           7 :         nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
    1557           7 :         m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
    1558           7 :     return pszStyle;
    1559             : }
    1560             : 
    1561             : /**********************************************************************
    1562             :  *                   TABFontPoint::GetStyleString() const
    1563             :  *
    1564             :  * Return style string for this feature.
    1565             :  *
    1566             :  * Style String is built only once during the first call to GetStyleString().
    1567             :  **********************************************************************/
    1568           9 : const char *TABFontPoint::GetStyleString() const
    1569             : {
    1570           9 :     if (m_pszStyleString == nullptr)
    1571             :     {
    1572           7 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
    1573             :     }
    1574             : 
    1575           9 :     return m_pszStyleString;
    1576             : }
    1577             : 
    1578             : /**********************************************************************
    1579             :  *                   TABFontPoint::SetSymbolFromStyle()
    1580             :  *
    1581             :  *  Set all Symbol var from a OGRStyleSymbol.
    1582             :  **********************************************************************/
    1583           2 : void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    1584             : {
    1585           2 :     ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
    1586             : 
    1587           2 :     GBool bIsNull = 0;
    1588             : 
    1589             :     // Try to set font glyph number
    1590           2 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    1591           2 :     if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
    1592             :     {
    1593           2 :         const int nSymbolId = atoi(pszSymbolId + 9);
    1594           2 :         SetSymbolNo(static_cast<GInt16>(nSymbolId));
    1595             :     }
    1596             : 
    1597           2 :     const char *pszFontName = poSymbolStyle->FontName(bIsNull);
    1598           2 :     if ((!bIsNull) && pszFontName)
    1599             :     {
    1600           2 :         SetFontName(pszFontName);
    1601             :     }
    1602           2 : }
    1603             : 
    1604             : /*=====================================================================
    1605             :  *                      class TABCustomPoint
    1606             :  *====================================================================*/
    1607             : 
    1608             : /**********************************************************************
    1609             :  *                   TABCustomPoint::TABCustomPoint()
    1610             :  *
    1611             :  * Constructor.
    1612             :  **********************************************************************/
    1613         686 : TABCustomPoint::TABCustomPoint(OGRFeatureDefn *poDefnIn)
    1614         686 :     : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
    1615             : {
    1616         686 : }
    1617             : 
    1618             : /**********************************************************************
    1619             :  *                   TABCustomPoint::~TABCustomPoint()
    1620             :  *
    1621             :  * Destructor.
    1622             :  **********************************************************************/
    1623        1372 : TABCustomPoint::~TABCustomPoint()
    1624             : {
    1625        1372 : }
    1626             : 
    1627             : /**********************************************************************
    1628             :  *                     TABCustomPoint::CloneTABFeature()
    1629             :  *
    1630             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1631             :  *
    1632             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1633             :  * then copies any members specific to its own type.
    1634             :  **********************************************************************/
    1635           0 : TABFeature *TABCustomPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    1636             : {
    1637             :     /*-----------------------------------------------------------------
    1638             :      * Alloc new feature and copy the base stuff
    1639             :      *----------------------------------------------------------------*/
    1640             :     TABCustomPoint *poNew =
    1641           0 :         new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
    1642             : 
    1643           0 :     CopyTABFeatureBase(poNew);
    1644             : 
    1645             :     /*-----------------------------------------------------------------
    1646             :      * And members specific to this class
    1647             :      *----------------------------------------------------------------*/
    1648             :     // ITABFeatureSymbol
    1649           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    1650             : 
    1651             :     // ITABFeatureFont
    1652           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    1653             : 
    1654           0 :     poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
    1655             : 
    1656           0 :     return poNew;
    1657             : }
    1658             : 
    1659             : /**********************************************************************
    1660             :  *                   TABCustomPoint::ReadGeometryFromMAPFile()
    1661             :  *
    1662             :  * Fill the geometry and representation (color, etc...) part of the
    1663             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    1664             :  *
    1665             :  * It is assumed that poMAPFile currently points to the beginning of
    1666             :  * a map object.
    1667             :  *
    1668             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1669             :  * been called.
    1670             :  **********************************************************************/
    1671           6 : int TABCustomPoint::ReadGeometryFromMAPFile(
    1672             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1673             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1674             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1675             : {
    1676             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1677           6 :     if (bCoordBlockDataOnly)
    1678           0 :         return 0;
    1679             : 
    1680             :     /*-----------------------------------------------------------------
    1681             :      * Fetch and validate geometry type
    1682             :      *----------------------------------------------------------------*/
    1683           6 :     m_nMapInfoType = poObjHdr->m_nType;
    1684             : 
    1685           6 :     if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
    1686           0 :         m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
    1687             :     {
    1688           0 :         CPLError(
    1689             :             CE_Failure, CPLE_AssertionFailed,
    1690             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1691           0 :             m_nMapInfoType, m_nMapInfoType);
    1692           0 :         return -1;
    1693             :     }
    1694             : 
    1695             :     /*-----------------------------------------------------------------
    1696             :      * Read object information
    1697             :      *----------------------------------------------------------------*/
    1698             :     TABMAPObjCustomPoint *poPointHdr =
    1699           6 :         cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
    1700             : 
    1701           6 :     m_nUnknown_ = poPointHdr->m_nUnknown_;        // ???
    1702           6 :     m_nCustomStyle = poPointHdr->m_nCustomStyle;  // 0x01=Show BG,
    1703             :                                                   // 0x02=Apply Color
    1704             : 
    1705           6 :     m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
    1706           6 :     poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    1707             : 
    1708           6 :     m_nFontDefIndex = poPointHdr->m_nFontId;  // Font index
    1709           6 :     poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    1710             : 
    1711             :     /*-----------------------------------------------------------------
    1712             :      * Create and fill geometry object
    1713             :      *----------------------------------------------------------------*/
    1714           6 :     double dX = 0.0;
    1715           6 :     double dY = 0.0;
    1716           6 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1717           6 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1718             : 
    1719           6 :     SetGeometryDirectly(poGeometry);
    1720             : 
    1721           6 :     SetMBR(dX, dY, dX, dY);
    1722           6 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1723             :               poObjHdr->m_nMaxY);
    1724             : 
    1725           6 :     return 0;
    1726             : }
    1727             : 
    1728             : /**********************************************************************
    1729             :  *                   TABCustomPoint::WriteGeometryToMAPFile()
    1730             :  *
    1731             :  * Write the geometry and representation (color, etc...) part of the
    1732             :  * feature to the .MAP object pointed to by poMAPFile.
    1733             :  *
    1734             :  * It is assumed that poMAPFile currently points to a valid map object.
    1735             :  *
    1736             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1737             :  * been called.
    1738             :  **********************************************************************/
    1739           2 : int TABCustomPoint::WriteGeometryToMAPFile(
    1740             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1741             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1742             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1743             : {
    1744             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1745           2 :     if (bCoordBlockDataOnly)
    1746           0 :         return 0;
    1747             : 
    1748             :     /*-----------------------------------------------------------------
    1749             :      * We assume that ValidateMapInfoType() was called already and that
    1750             :      * the type in poObjHdr->m_nType is valid.
    1751             :      *----------------------------------------------------------------*/
    1752           2 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1753             : 
    1754             :     /*-----------------------------------------------------------------
    1755             :      * Fetch and validate geometry
    1756             :      *----------------------------------------------------------------*/
    1757           2 :     OGRGeometry *poGeom = GetGeometryRef();
    1758           2 :     OGRPoint *poPoint = nullptr;
    1759           2 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1760           2 :         poPoint = poGeom->toPoint();
    1761             :     else
    1762             :     {
    1763           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1764             :                  "TABCustomPoint: Missing or Invalid Geometry!");
    1765           0 :         return -1;
    1766             :     }
    1767             : 
    1768           2 :     GInt32 nX = 0;
    1769           2 :     GInt32 nY = 0;
    1770           2 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1771             : 
    1772             :     /*-----------------------------------------------------------------
    1773             :      * Copy object information
    1774             :      *----------------------------------------------------------------*/
    1775             :     TABMAPObjCustomPoint *poPointHdr =
    1776           2 :         cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
    1777             : 
    1778           2 :     poPointHdr->m_nX = nX;
    1779           2 :     poPointHdr->m_nY = nY;
    1780           2 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1781           2 :     poPointHdr->m_nUnknown_ = m_nUnknown_;
    1782           2 :     poPointHdr->m_nCustomStyle = m_nCustomStyle;  // 0x01=Show BG,
    1783             :                                                   // 0x02=Apply Color
    1784             : 
    1785           2 :     m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    1786           2 :     poPointHdr->m_nSymbolId =
    1787           2 :         static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    1788             : 
    1789           2 :     m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    1790           2 :     poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex);  // Font index
    1791             : 
    1792           2 :     if (CPLGetLastErrorType() == CE_Failure)
    1793           0 :         return -1;
    1794             : 
    1795           2 :     return 0;
    1796             : }
    1797             : 
    1798             : /**********************************************************************
    1799             :  *                   TABCustomPoint::GetSymbolStyleString()
    1800             :  *
    1801             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    1802             :  **********************************************************************/
    1803           7 : const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
    1804             : {
    1805             :     /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
    1806             :      * "apply color". */
    1807           7 :     const char *color = nullptr;
    1808           7 :     if (m_nCustomStyle & 0x02)
    1809           7 :         color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
    1810             :     else
    1811           0 :         color = "";
    1812             : 
    1813           7 :     int nAngle = static_cast<int>(dfAngle);
    1814             :     const char *pszStyle;
    1815           7 :     const char *pszExt = CPLGetExtension(GetSymbolNameRef());
    1816           7 :     char szLowerExt[8] = "";
    1817           7 :     const char *pszPtr = pszExt;
    1818             :     int i;
    1819             : 
    1820           7 :     for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
    1821             :     {
    1822           0 :         szLowerExt[i] =
    1823           0 :             static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
    1824             :     }
    1825           7 :     szLowerExt[i] = '\0';
    1826             : 
    1827           7 :     pszStyle = CPLSPrintf(
    1828             :         "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
    1829           7 :         nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
    1830             :         GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
    1831           7 :     return pszStyle;
    1832             : }
    1833             : 
    1834             : /**********************************************************************
    1835             :  *                   TABCustomPoint::SetSymbolFromStyle()
    1836             :  *
    1837             :  *  Set all Symbol var from a OGRStyleSymbol.
    1838             :  **********************************************************************/
    1839           2 : void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    1840             : {
    1841           2 :     ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
    1842             : 
    1843           2 :     GBool bIsNull = 0;
    1844             : 
    1845             :     // Try to set font glyph number
    1846           2 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    1847           2 :     if ((!bIsNull) && pszSymbolId &&
    1848           2 :         STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
    1849             :     {
    1850           2 :         const int nSymbolStyle = atoi(pszSymbolId + 19);
    1851           2 :         SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
    1852             : 
    1853           2 :         const char *pszPtr = pszSymbolId + 19;
    1854           4 :         while (*pszPtr != '-')
    1855             :         {
    1856           2 :             pszPtr++;
    1857             :         }
    1858           2 :         pszPtr++;
    1859             : 
    1860           2 :         char szSymbolName[256] = "";
    1861             :         int i;
    1862           2 :         for (i = 0;
    1863           8 :              i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
    1864             :              i++, pszPtr++)
    1865             :         {
    1866           6 :             szSymbolName[i] = *pszPtr;
    1867             :         }
    1868           2 :         szSymbolName[i] = '\0';
    1869           2 :         SetSymbolName(szSymbolName);
    1870             :     }
    1871           2 : }
    1872             : 
    1873             : /**********************************************************************
    1874             :  *                   TABCustomPoint::GetStyleString() const
    1875             :  *
    1876             :  * Return style string for this feature.
    1877             :  *
    1878             :  * Style String is built only once during the first call to GetStyleString().
    1879             :  **********************************************************************/
    1880           9 : const char *TABCustomPoint::GetStyleString() const
    1881             : {
    1882           9 :     if (m_pszStyleString == nullptr)
    1883             :     {
    1884           7 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    1885             :     }
    1886             : 
    1887           9 :     return m_pszStyleString;
    1888             : }
    1889             : 
    1890             : /*=====================================================================
    1891             :  *                      class TABPolyline
    1892             :  *====================================================================*/
    1893             : 
    1894             : /**********************************************************************
    1895             :  *                   TABPolyline::TABPolyline()
    1896             :  *
    1897             :  * Constructor.
    1898             :  **********************************************************************/
    1899        4057 : TABPolyline::TABPolyline(OGRFeatureDefn *poDefnIn)
    1900             :     : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
    1901        4057 :       m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
    1902             : {
    1903        4057 : }
    1904             : 
    1905             : /**********************************************************************
    1906             :  *                   TABPolyline::~TABPolyline()
    1907             :  *
    1908             :  * Destructor.
    1909             :  **********************************************************************/
    1910        8114 : TABPolyline::~TABPolyline()
    1911             : {
    1912        8114 : }
    1913             : 
    1914             : /**********************************************************************
    1915             :  *                     TABPolyline::CloneTABFeature()
    1916             :  *
    1917             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1918             :  *
    1919             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1920             :  * then copies any members specific to its own type.
    1921             :  **********************************************************************/
    1922           0 : TABFeature *TABPolyline::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    1923             : {
    1924             :     /*-----------------------------------------------------------------
    1925             :      * Alloc new feature and copy the base stuff
    1926             :      *----------------------------------------------------------------*/
    1927           0 :     TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
    1928             : 
    1929           0 :     CopyTABFeatureBase(poNew);
    1930             : 
    1931             :     /*-----------------------------------------------------------------
    1932             :      * And members specific to this class
    1933             :      *----------------------------------------------------------------*/
    1934             :     // ITABFeaturePen
    1935           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    1936             : 
    1937           0 :     poNew->m_bSmooth = m_bSmooth;
    1938           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    1939           0 :     poNew->m_dCenterX = m_dCenterX;
    1940           0 :     poNew->m_dCenterY = m_dCenterY;
    1941             : 
    1942           0 :     return poNew;
    1943             : }
    1944             : 
    1945             : /**********************************************************************
    1946             :  *                   TABPolyline::GetNumParts()
    1947             :  *
    1948             :  * Return the total number of parts in this object.
    1949             :  *
    1950             :  * Returns 0 if the geometry contained in the object is invalid or missing.
    1951             :  **********************************************************************/
    1952           0 : int TABPolyline::GetNumParts()
    1953             : {
    1954           0 :     int numParts = 0;
    1955             : 
    1956           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1957           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    1958             :     {
    1959             :         /*-------------------------------------------------------------
    1960             :          * Simple polyline
    1961             :          *------------------------------------------------------------*/
    1962           0 :         numParts = 1;
    1963             :     }
    1964           0 :     else if (poGeom &&
    1965           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1966             :     {
    1967             :         /*-------------------------------------------------------------
    1968             :          * Multiple polyline
    1969             :          *------------------------------------------------------------*/
    1970           0 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    1971           0 :         numParts = poMultiLine->getNumGeometries();
    1972             :     }
    1973             : 
    1974           0 :     return numParts;
    1975             : }
    1976             : 
    1977             : /**********************************************************************
    1978             :  *                   TABPolyline::GetPartRef()
    1979             :  *
    1980             :  * Returns a reference to the specified OGRLineString number, hiding the
    1981             :  * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
    1982             :  *
    1983             :  * Returns NULL if the geometry contained in the object is invalid or
    1984             :  * missing or if the specified part index is invalid.
    1985             :  **********************************************************************/
    1986           0 : OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
    1987             : {
    1988           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1989           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    1990             :         nPartIndex == 0)
    1991             :     {
    1992             :         /*-------------------------------------------------------------
    1993             :          * Simple polyline
    1994             :          *------------------------------------------------------------*/
    1995           0 :         return poGeom->toLineString();
    1996             :     }
    1997           0 :     else if (poGeom &&
    1998           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1999             :     {
    2000             :         /*-------------------------------------------------------------
    2001             :          * Multiple polyline
    2002             :          *------------------------------------------------------------*/
    2003           0 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2004           0 :         if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
    2005             :         {
    2006           0 :             return poMultiLine->getGeometryRef(nPartIndex);
    2007             :         }
    2008             :         else
    2009           0 :             return nullptr;
    2010             :     }
    2011             : 
    2012           0 :     return nullptr;
    2013             : }
    2014             : 
    2015             : /**********************************************************************
    2016             :  *                   TABPolyline::ValidateMapInfoType()
    2017             :  *
    2018             :  * Check the feature's geometry part and return the corresponding
    2019             :  * mapinfo object type code.  The m_nMapInfoType member will also
    2020             :  * be updated for further calls to GetMapInfoType();
    2021             :  *
    2022             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    2023             :  * is expected for this object class.
    2024             :  **********************************************************************/
    2025         238 : TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    2026             : {
    2027             :     /*-----------------------------------------------------------------
    2028             :      * Fetch and validate geometry
    2029             :      *----------------------------------------------------------------*/
    2030         238 :     OGRGeometry *poGeom = GetGeometryRef();
    2031         238 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2032             :     {
    2033             :         /*-------------------------------------------------------------
    2034             :          * Simple polyline
    2035             :          *------------------------------------------------------------*/
    2036         230 :         OGRLineString *poLine = poGeom->toLineString();
    2037         230 :         if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
    2038             :         {
    2039           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
    2040             :         }
    2041         230 :         else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
    2042             :         {
    2043           0 :             m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
    2044             :         }
    2045         230 :         else if (poLine->getNumPoints() > 2)
    2046             :         {
    2047         207 :             m_nMapInfoType = TAB_GEOM_PLINE;
    2048             :         }
    2049          46 :         else if ((poLine->getNumPoints() == 2) &&
    2050          23 :                  (m_bWriteTwoPointLineAsPolyline == TRUE))
    2051             :         {
    2052           0 :             m_nMapInfoType = TAB_GEOM_PLINE;
    2053             :         }
    2054          46 :         else if ((poLine->getNumPoints() == 2) &&
    2055          23 :                  (m_bWriteTwoPointLineAsPolyline == FALSE))
    2056             :         {
    2057          23 :             m_nMapInfoType = TAB_GEOM_LINE;
    2058             :         }
    2059             :         else
    2060             :         {
    2061           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    2062             :                      "TABPolyline: Geometry must contain at least 2 points.");
    2063           0 :             m_nMapInfoType = TAB_GEOM_NONE;
    2064             :         }
    2065             :     }
    2066          16 :     else if (poGeom &&
    2067           8 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2068             :     {
    2069             :         /*-------------------------------------------------------------
    2070             :          * Multiple polyline... validate all components
    2071             :          *------------------------------------------------------------*/
    2072           8 :         GInt32 numPointsTotal = 0;
    2073           8 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2074           8 :         int numLines = poMultiLine->getNumGeometries();
    2075             : 
    2076           8 :         m_nMapInfoType = TAB_GEOM_MULTIPLINE;
    2077             : 
    2078          18 :         for (int iLine = 0; iLine < numLines; iLine++)
    2079             :         {
    2080          10 :             poGeom = poMultiLine->getGeometryRef(iLine);
    2081          20 :             if (poGeom == nullptr ||
    2082          10 :                 wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    2083             :             {
    2084           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2085             :                          "TABPolyline: Object contains an invalid Geometry!");
    2086           0 :                 m_nMapInfoType = TAB_GEOM_NONE;
    2087           0 :                 numPointsTotal = 0;
    2088           0 :                 break;
    2089             :             }
    2090          10 :             OGRLineString *poLine = poGeom->toLineString();
    2091          10 :             numPointsTotal += poLine->getNumPoints();
    2092             :         }
    2093             : 
    2094           8 :         if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
    2095           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
    2096           8 :         else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
    2097           0 :             m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
    2098             :     }
    2099             :     else
    2100             :     {
    2101           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2102             :                  "TABPolyline: Missing or Invalid Geometry!");
    2103           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    2104             :     }
    2105             : 
    2106             :     /*-----------------------------------------------------------------
    2107             :      * Decide if coordinates should be compressed or not.
    2108             :      *
    2109             :      * __TODO__ We never write type LINE (2 points line) as compressed
    2110             :      * for the moment.  If we ever do it, then the decision to write
    2111             :      * a 2 point line in compressed coordinates or not should take into
    2112             :      * account the location of the object block MBR, so this would be
    2113             :      * better handled directly by TABMAPObjLine::WriteObject() since the
    2114             :      * object block center is not known until it is written to disk.
    2115             :      *----------------------------------------------------------------*/
    2116         238 :     if (m_nMapInfoType != TAB_GEOM_LINE)
    2117             :     {
    2118         215 :         ValidateCoordType(poMapFile);
    2119             :     }
    2120             :     else
    2121             :     {
    2122          23 :         UpdateMBR(poMapFile);
    2123             :     }
    2124             : 
    2125         238 :     return m_nMapInfoType;
    2126             : }
    2127             : 
    2128             : /**********************************************************************
    2129             :  *                   TABPolyline::ReadGeometryFromMAPFile()
    2130             :  *
    2131             :  * Fill the geometry and representation (color, etc...) part of the
    2132             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    2133             :  *
    2134             :  * It is assumed that poMAPFile currently points to the beginning of
    2135             :  * a map object.
    2136             :  *
    2137             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    2138             :  * been called.
    2139             :  **********************************************************************/
    2140        1387 : int TABPolyline::ReadGeometryFromMAPFile(
    2141             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    2142             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    2143             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    2144             : {
    2145        1387 :     GInt32 nX = 0;
    2146        1387 :     GInt32 nY = 0;
    2147        1387 :     double dX = 0.0;
    2148        1387 :     double dY = 0.0;
    2149        1387 :     double dXMin = 0.0;
    2150        1387 :     double dYMin = 0.0;
    2151        1387 :     double dXMax = 0.0;
    2152        1387 :     double dYMax = 0.0;
    2153        1387 :     OGRGeometry *poGeometry = nullptr;
    2154        1387 :     GBool bComprCoord = poObjHdr->IsCompressedType();
    2155        1387 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    2156             : 
    2157             :     /*-----------------------------------------------------------------
    2158             :      * Fetch and validate geometry type
    2159             :      *----------------------------------------------------------------*/
    2160        1387 :     m_nMapInfoType = poObjHdr->m_nType;
    2161             : 
    2162        1387 :     if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
    2163             :     {
    2164             :         /*=============================================================
    2165             :          * LINE (2 vertices)
    2166             :          *============================================================*/
    2167          21 :         TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
    2168             : 
    2169          21 :         m_bSmooth = FALSE;
    2170             : 
    2171          21 :         auto poLine = new OGRLineString();
    2172          21 :         poGeometry = poLine;
    2173          21 :         poLine->setNumPoints(2);
    2174             : 
    2175          21 :         poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
    2176             :                                 dYMin);
    2177          21 :         poLine->setPoint(0, dXMin, dYMin);
    2178             : 
    2179          21 :         poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
    2180             :                                 dYMax);
    2181          21 :         poLine->setPoint(1, dXMax, dYMax);
    2182             : 
    2183          21 :         if (!bCoordBlockDataOnly)
    2184             :         {
    2185          21 :             m_nPenDefIndex = poLineHdr->m_nPenId;  // Pen index
    2186          21 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2187          21 :         }
    2188             :     }
    2189        1366 :     else if (m_nMapInfoType == TAB_GEOM_PLINE ||
    2190        1361 :              m_nMapInfoType == TAB_GEOM_PLINE_C)
    2191             :     {
    2192             :         /*=============================================================
    2193             :          * PLINE ( > 2 vertices)
    2194             :          *============================================================*/
    2195             : 
    2196             :         /*-------------------------------------------------------------
    2197             :          * Copy data from poObjHdr
    2198             :          *------------------------------------------------------------*/
    2199        1356 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2200             : 
    2201        1356 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    2202        1356 :         const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
    2203        1356 :         if (nCoordDataSize > 1024 * 1024 &&
    2204           0 :             nCoordDataSize > poMapFile->GetFileSize())
    2205             :         {
    2206           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
    2207             :                      nCoordDataSize);
    2208           0 :             return -1;
    2209             :         }
    2210             :         // numLineSections = poPLineHdr->m_numLineSections; // Always 1
    2211        1356 :         m_bSmooth = poPLineHdr->m_bSmooth;
    2212             : 
    2213             :         // Centroid/label point
    2214        1356 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    2215             :                                 dX, dY);
    2216        1356 :         SetCenter(dX, dY);
    2217             : 
    2218             :         // Compressed coordinate origin (useful only in compressed case!)
    2219        1356 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    2220        1356 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    2221             : 
    2222             :         // MBR
    2223        1356 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    2224             :                                 dYMin);
    2225        1356 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    2226             :                                 dYMax);
    2227             : 
    2228        1356 :         if (!bCoordBlockDataOnly)
    2229             :         {
    2230        1230 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    2231        1230 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2232             :         }
    2233             : 
    2234             :         /*-------------------------------------------------------------
    2235             :          * Create Geometry and read coordinates
    2236             :          *------------------------------------------------------------*/
    2237        1356 :         const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
    2238             : 
    2239        1356 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2240         126 :             poCoordBlock = *ppoCoordBlock;
    2241             :         else
    2242        1230 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    2243        1356 :         if (poCoordBlock == nullptr)
    2244             :         {
    2245           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2246             :                      "Can't access coordinate block at offset %d",
    2247             :                      nCoordBlockPtr);
    2248           0 :             return -1;
    2249             :         }
    2250             : 
    2251        1356 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2252             : 
    2253        1356 :         auto poLine = new OGRLineString();
    2254        1356 :         poGeometry = poLine;
    2255        1356 :         poLine->setNumPoints(numPoints);
    2256             : 
    2257        1356 :         int nStatus = 0;
    2258       11126 :         for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2259             :         {
    2260        9770 :             nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
    2261        9770 :             if (nStatus != 0)
    2262           0 :                 break;
    2263        9770 :             poMapFile->Int2Coordsys(nX, nY, dX, dY);
    2264        9770 :             poLine->setPoint(i, dX, dY);
    2265             :         }
    2266             : 
    2267        1356 :         if (nStatus != 0)
    2268             :         {
    2269             :             // Failed ... error message has already been produced
    2270           0 :             delete poGeometry;
    2271           0 :             return nStatus;
    2272        1356 :         }
    2273             :     }
    2274          10 :     else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2275          10 :              m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2276           4 :              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2277           4 :              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2278           0 :              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2279           0 :              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
    2280             :     {
    2281             :         /*=============================================================
    2282             :          * PLINE MULTIPLE
    2283             :          *============================================================*/
    2284          10 :         const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    2285             : 
    2286             :         /*-------------------------------------------------------------
    2287             :          * Copy data from poObjHdr
    2288             :          *------------------------------------------------------------*/
    2289          10 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2290             : 
    2291          10 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    2292             :         /* GInt32 nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
    2293          10 :         GInt32 numLineSections = poPLineHdr->m_numLineSections;
    2294          10 :         m_bSmooth = poPLineHdr->m_bSmooth;
    2295             : 
    2296             :         // Centroid/label point
    2297          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    2298             :                                 dX, dY);
    2299          10 :         SetCenter(dX, dY);
    2300             : 
    2301             :         // Compressed coordinate origin (useful only in compressed case!)
    2302          10 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    2303          10 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    2304             : 
    2305             :         // MBR
    2306          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    2307             :                                 dYMin);
    2308          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    2309             :                                 dYMax);
    2310             : 
    2311          10 :         if (!bCoordBlockDataOnly)
    2312             :         {
    2313          10 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    2314          10 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2315             :         }
    2316             : 
    2317          10 :         const int nMinSizeOfSection = 24;
    2318          10 :         if (numLineSections > INT_MAX / nMinSizeOfSection)
    2319             :         {
    2320           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    2321           0 :             return -1;
    2322             :         }
    2323          10 :         const GUInt32 nMinimumBytesForSections =
    2324          10 :             nMinSizeOfSection * numLineSections;
    2325          10 :         if (nMinimumBytesForSections > 1024 * 1024 &&
    2326           0 :             nMinimumBytesForSections > poMapFile->GetFileSize())
    2327             :         {
    2328           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    2329           0 :             return -1;
    2330             :         }
    2331             : 
    2332             :         /*-------------------------------------------------------------
    2333             :          * Read data from the coord. block
    2334             :          *------------------------------------------------------------*/
    2335             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    2336          10 :             VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
    2337          10 :         if (pasSecHdrs == nullptr)
    2338           0 :             return -1;
    2339             : 
    2340          10 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2341           4 :             poCoordBlock = *ppoCoordBlock;
    2342             :         else
    2343           6 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    2344             : 
    2345          10 :         GInt32 numPointsTotal = 0;
    2346          20 :         if (poCoordBlock == nullptr ||
    2347          10 :             poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
    2348             :                                            numLineSections, pasSecHdrs,
    2349             :                                            numPointsTotal) != 0)
    2350             :         {
    2351           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2352             :                      "Failed reading coordinate data at offset %d",
    2353             :                      nCoordBlockPtr);
    2354           0 :             CPLFree(pasSecHdrs);
    2355           0 :             return -1;
    2356             :         }
    2357             : 
    2358          10 :         const GUInt32 nMinimumBytesForPoints =
    2359          10 :             (bComprCoord ? 4 : 8) * numPointsTotal;
    2360          10 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    2361           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    2362             :         {
    2363           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
    2364           0 :             CPLFree(pasSecHdrs);
    2365           0 :             return -1;
    2366             :         }
    2367             : 
    2368          10 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2369             : 
    2370             :         GInt32 *panXY = static_cast<GInt32 *>(
    2371          10 :             VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
    2372          10 :         if (panXY == nullptr)
    2373             :         {
    2374           0 :             CPLFree(pasSecHdrs);
    2375           0 :             return -1;
    2376             :         }
    2377             : 
    2378          10 :         if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
    2379             :             0)
    2380             :         {
    2381           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2382             :                      "Failed reading coordinate data at offset %d",
    2383             :                      nCoordBlockPtr);
    2384           0 :             CPLFree(pasSecHdrs);
    2385           0 :             CPLFree(panXY);
    2386           0 :             return -1;
    2387             :         }
    2388             : 
    2389             :         /*-------------------------------------------------------------
    2390             :          * Create a Geometry collection with one line geometry for
    2391             :          * each coordinates section
    2392             :          * If object contains only one section, then return a simple LineString
    2393             :          *------------------------------------------------------------*/
    2394          10 :         OGRMultiLineString *poMultiLine = nullptr;
    2395          10 :         if (numLineSections > 1)
    2396             :         {
    2397           6 :             poMultiLine = new OGRMultiLineString();
    2398           6 :             poGeometry = poMultiLine;
    2399             :         }
    2400             : 
    2401          26 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    2402             :         {
    2403          16 :             const int numSectionVertices = pasSecHdrs[iSection].numVertices;
    2404          16 :             GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
    2405             : 
    2406          16 :             auto poLine = new OGRLineString();
    2407          16 :             poLine->setNumPoints(numSectionVertices);
    2408             : 
    2409          48 :             for (int i = 0; i < numSectionVertices; i++)
    2410             :             {
    2411          32 :                 poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
    2412          32 :                 poLine->setPoint(i, dX, dY);
    2413          32 :                 pnXYPtr += 2;
    2414             :             }
    2415             : 
    2416          16 :             if (poGeometry == nullptr)
    2417           4 :                 poGeometry = poLine;
    2418          12 :             else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
    2419             :             {
    2420           0 :                 CPLAssert(false);  // Just in case lower-level lib is modified
    2421             :             }
    2422             :         }
    2423             : 
    2424          10 :         CPLFree(pasSecHdrs);
    2425          10 :         CPLFree(panXY);
    2426             :     }
    2427             :     else
    2428             :     {
    2429           0 :         CPLError(
    2430             :             CE_Failure, CPLE_AssertionFailed,
    2431             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    2432           0 :             m_nMapInfoType, m_nMapInfoType);
    2433           0 :         return -1;
    2434             :     }
    2435             : 
    2436        1387 :     SetGeometryDirectly(poGeometry);
    2437             : 
    2438        1387 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    2439        1387 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    2440             :               poObjHdr->m_nMaxY);
    2441             : 
    2442             :     /* Return a ref to coord block so that caller can continue reading
    2443             :      * after the end of this object (used by TABCollection and index splitting)
    2444             :      */
    2445        1387 :     if (ppoCoordBlock)
    2446         130 :         *ppoCoordBlock = poCoordBlock;
    2447             : 
    2448        1387 :     return 0;
    2449             : }
    2450             : 
    2451             : /**********************************************************************
    2452             :  *                   TABPolyline::WriteGeometryToMAPFile()
    2453             :  *
    2454             :  * Write the geometry and representation (color, etc...) part of the
    2455             :  * feature to the .MAP object pointed to by poMAPFile.
    2456             :  *
    2457             :  * It is assumed that poMAPFile currently points to a valid map object.
    2458             :  *
    2459             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    2460             :  * been called.
    2461             :  **********************************************************************/
    2462         364 : int TABPolyline::WriteGeometryToMAPFile(
    2463             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    2464             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    2465             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    2466             : {
    2467         364 :     GInt32 nX = 0;
    2468         364 :     GInt32 nY = 0;
    2469         364 :     OGRLineString *poLine = nullptr;
    2470         364 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    2471             : 
    2472             :     /*-----------------------------------------------------------------
    2473             :      * We assume that ValidateMapInfoType() was called already and that
    2474             :      * the type in poObjHdr->m_nType is valid.
    2475             :      *----------------------------------------------------------------*/
    2476         364 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    2477         364 :     CPLErrorReset();
    2478             : 
    2479             :     /*-----------------------------------------------------------------
    2480             :      * Fetch and validate geometry
    2481             :      *----------------------------------------------------------------*/
    2482         364 :     OGRGeometry *poGeom = GetGeometryRef();
    2483             : 
    2484        1069 :     if ((m_nMapInfoType == TAB_GEOM_LINE ||
    2485         364 :          m_nMapInfoType == TAB_GEOM_LINE_C) &&
    2486         751 :         poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    2487          23 :         (poLine = poGeom->toLineString())->getNumPoints() == 2)
    2488             :     {
    2489             :         /*=============================================================
    2490             :          * LINE (2 vertices)
    2491             :          *============================================================*/
    2492          23 :         TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
    2493             : 
    2494          23 :         poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
    2495          23 :                                 poLineHdr->m_nX1, poLineHdr->m_nY1);
    2496          23 :         poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
    2497          23 :                                 poLineHdr->m_nX2, poLineHdr->m_nY2);
    2498          23 :         poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
    2499             :                           poLineHdr->m_nY2);
    2500             : 
    2501          23 :         if (!bCoordBlockDataOnly)
    2502             :         {
    2503          23 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2504          23 :             poLineHdr->m_nPenId =
    2505          23 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2506             :         }
    2507             :     }
    2508        1020 :     else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
    2509         341 :               m_nMapInfoType == TAB_GEOM_PLINE_C) &&
    2510         682 :              poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2511             :     {
    2512             :         /*=============================================================
    2513             :          * PLINE ( > 2 vertices and less than 32767 vertices)
    2514             :          *============================================================*/
    2515         333 :         GBool bCompressed = poObjHdr->IsCompressedType();
    2516             : 
    2517             :         /*-------------------------------------------------------------
    2518             :          * Process geometry first...
    2519             :          *------------------------------------------------------------*/
    2520         333 :         poLine = poGeom->toLineString();
    2521         333 :         const int numPoints = poLine->getNumPoints();
    2522         333 :         CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
    2523             : 
    2524         333 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2525         126 :             poCoordBlock = *ppoCoordBlock;
    2526             :         else
    2527         207 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    2528         333 :         poCoordBlock->StartNewFeature();
    2529         333 :         const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    2530         333 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2531             : 
    2532         333 :         int nStatus = 0;
    2533        5039 :         for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2534             :         {
    2535        4706 :             poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
    2536        4706 :             if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
    2537             :                 0)
    2538             :             {
    2539             :                 // Failed ... error message has already been produced
    2540           0 :                 return nStatus;
    2541             :             }
    2542             :         }
    2543             : 
    2544         333 :         const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    2545             : 
    2546             :         /*-------------------------------------------------------------
    2547             :          * Copy info to poObjHdr
    2548             :          *------------------------------------------------------------*/
    2549         333 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2550             : 
    2551         333 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    2552         333 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    2553         333 :         poPLineHdr->m_numLineSections = 1;
    2554             : 
    2555         333 :         poPLineHdr->m_bSmooth = m_bSmooth;
    2556             : 
    2557             :         // MBR
    2558         333 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    2559             : 
    2560             :         // Polyline center/label point
    2561         333 :         double dX = 0.0;
    2562         333 :         double dY = 0.0;
    2563         333 :         if (GetCenter(dX, dY) != -1)
    2564             :         {
    2565         333 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    2566         333 :                                     poPLineHdr->m_nLabelY);
    2567             :         }
    2568             :         else
    2569             :         {
    2570           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    2571           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    2572             :         }
    2573             : 
    2574             :         // Compressed coordinate origin (useful only in compressed case!)
    2575         333 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    2576         333 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    2577             : 
    2578         333 :         if (!bCoordBlockDataOnly)
    2579             :         {
    2580         207 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2581         207 :             poPLineHdr->m_nPenId =
    2582         207 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2583             :         }
    2584             :     }
    2585          24 :     else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2586           8 :               m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2587           0 :               m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2588           0 :               m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2589           0 :               m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2590           8 :               m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
    2591          16 :              poGeom &&
    2592           8 :              (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
    2593           0 :               wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
    2594             :     {
    2595             :         /*=============================================================
    2596             :          * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
    2597             :          *============================================================*/
    2598             : 
    2599           8 :         CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2600             :                   m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2601             :                   m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2602             :                   m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2603             :                   m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2604             :                   m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
    2605             : 
    2606           8 :         int nStatus = 0;
    2607           8 :         OGREnvelope sEnvelope;
    2608           8 :         GBool bCompressed = poObjHdr->IsCompressedType();
    2609             : 
    2610             :         /*-------------------------------------------------------------
    2611             :          * Process geometry first...
    2612             :          *------------------------------------------------------------*/
    2613           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2614           0 :             poCoordBlock = *ppoCoordBlock;
    2615             :         else
    2616           8 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    2617           8 :         poCoordBlock->StartNewFeature();
    2618           8 :         const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    2619           8 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2620             : 
    2621           8 :         OGRMultiLineString *poMultiLine = nullptr;
    2622           8 :         GInt32 numLines = 1;
    2623           8 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2624             :         {
    2625           8 :             poMultiLine = poGeom->toMultiLineString();
    2626           8 :             numLines = poMultiLine->getNumGeometries();
    2627             :         }
    2628             :         // else
    2629             :         // {
    2630             :         //     poMultiLine = NULL;
    2631             :         //     numLines = 1;
    2632             :         // }
    2633             : 
    2634             :         /*-------------------------------------------------------------
    2635             :          * Build and write array of coord sections headers
    2636             :          *------------------------------------------------------------*/
    2637             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    2638           8 :             VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
    2639           8 :         if (pasSecHdrs == nullptr)
    2640             :         {
    2641           0 :             return -1;
    2642             :         }
    2643             : 
    2644             :         /*-------------------------------------------------------------
    2645             :          * In calculation of nDataOffset, we have to take into account that
    2646             :          * V450 header section uses int32 instead of int16 for numVertices
    2647             :          * and we add another 2 bytes to align with a 4 bytes boundary.
    2648             :          *------------------------------------------------------------*/
    2649           8 :         int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    2650             : 
    2651           8 :         const int nTotalHdrSizeUncompressed =
    2652           8 :             (nVersion >= 450 ? 28 : 24) * numLines;
    2653             : 
    2654           8 :         GInt32 numPointsTotal = 0;
    2655          18 :         for (int iLine = 0; iLine < numLines; iLine++)
    2656             :         {
    2657          10 :             if (poMultiLine)
    2658          10 :                 poGeom = poMultiLine->getGeometryRef(iLine);
    2659             : 
    2660          20 :             if (poGeom &&
    2661          10 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2662             :             {
    2663          10 :                 poLine = poGeom->toLineString();
    2664          10 :                 const GInt32 numPoints = poLine->getNumPoints();
    2665          10 :                 poLine->getEnvelope(&sEnvelope);
    2666             : 
    2667          10 :                 pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
    2668          10 :                 pasSecHdrs[iLine].numHoles = 0;  // It is a line!
    2669             : 
    2670          10 :                 poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
    2671          10 :                                         pasSecHdrs[iLine].nXMin,
    2672          10 :                                         pasSecHdrs[iLine].nYMin);
    2673          10 :                 poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
    2674          10 :                                         pasSecHdrs[iLine].nXMax,
    2675          10 :                                         pasSecHdrs[iLine].nYMax);
    2676          10 :                 pasSecHdrs[iLine].nDataOffset =
    2677          10 :                     nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
    2678          10 :                 pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
    2679             : 
    2680          10 :                 numPointsTotal += numPoints;
    2681             :             }
    2682             :             else
    2683             :             {
    2684           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2685             :                          "TABPolyline: Object contains an invalid Geometry!");
    2686           0 :                 nStatus = -1;
    2687             :             }
    2688             :         }
    2689             : 
    2690           8 :         if (nStatus == 0)
    2691           8 :             nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
    2692             :                                                       pasSecHdrs, bCompressed);
    2693             : 
    2694           8 :         CPLFree(pasSecHdrs);
    2695           8 :         pasSecHdrs = nullptr;
    2696             : 
    2697           8 :         if (nStatus != 0)
    2698           0 :             return nStatus;  // Error has already been reported.
    2699             : 
    2700             :         /*-------------------------------------------------------------
    2701             :          * Then write the coordinates themselves...
    2702             :          *------------------------------------------------------------*/
    2703          18 :         for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
    2704             :         {
    2705          10 :             if (poMultiLine)
    2706          10 :                 poGeom = poMultiLine->getGeometryRef(iLine);
    2707             : 
    2708          20 :             if (poGeom &&
    2709          10 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2710             :             {
    2711          10 :                 poLine = poGeom->toLineString();
    2712          10 :                 GInt32 numPoints = poLine->getNumPoints();
    2713             : 
    2714          30 :                 for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2715             :                 {
    2716          20 :                     poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
    2717             :                                             nX, nY);
    2718          20 :                     if ((nStatus = poCoordBlock->WriteIntCoord(
    2719          20 :                              nX, nY, bCompressed)) != 0)
    2720             :                     {
    2721             :                         // Failed ... error message has already been produced
    2722           0 :                         return nStatus;
    2723             :                     }
    2724             :                 }
    2725             :             }
    2726             :             else
    2727             :             {
    2728           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2729             :                          "TABPolyline: Object contains an invalid Geometry!");
    2730           0 :                 return -1;
    2731             :             }
    2732             :         }
    2733             : 
    2734           8 :         const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    2735             : 
    2736             :         /*-------------------------------------------------------------
    2737             :          * ... and finally copy info to poObjHdr
    2738             :          *------------------------------------------------------------*/
    2739           8 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2740             : 
    2741           8 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    2742           8 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    2743           8 :         poPLineHdr->m_numLineSections = numLines;
    2744             : 
    2745           8 :         poPLineHdr->m_bSmooth = m_bSmooth;
    2746             : 
    2747             :         // MBR
    2748           8 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    2749             : 
    2750             :         // Polyline center/label point
    2751           8 :         double dX = 0.0;
    2752           8 :         double dY = 0.0;
    2753           8 :         if (GetCenter(dX, dY) != -1)
    2754             :         {
    2755           8 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    2756           8 :                                     poPLineHdr->m_nLabelY);
    2757             :         }
    2758             :         else
    2759             :         {
    2760           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    2761           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    2762             :         }
    2763             : 
    2764             :         // Compressed coordinate origin (useful only in compressed case!)
    2765           8 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    2766           8 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    2767             : 
    2768           8 :         if (!bCoordBlockDataOnly)
    2769             :         {
    2770           8 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2771           8 :             poPLineHdr->m_nPenId =
    2772           8 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2773             :         }
    2774             :     }
    2775             :     else
    2776             :     {
    2777           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2778             :                  "TABPolyline: Object contains an invalid Geometry!");
    2779           0 :         return -1;
    2780             :     }
    2781             : 
    2782         364 :     if (CPLGetLastErrorType() == CE_Failure)
    2783           0 :         return -1;
    2784             : 
    2785             :     /* Return a ref to coord block so that caller can continue writing
    2786             :      * after the end of this object (used by index splitting)
    2787             :      */
    2788         364 :     if (ppoCoordBlock)
    2789         126 :         *ppoCoordBlock = poCoordBlock;
    2790             : 
    2791         364 :     return 0;
    2792             : }
    2793             : 
    2794             : /**********************************************************************
    2795             :  *                   TABPolyline::GetStyleString() const
    2796             :  *
    2797             :  * Return style string for this feature.
    2798             :  *
    2799             :  * Style String is built only once during the first call to GetStyleString().
    2800             :  **********************************************************************/
    2801          44 : const char *TABPolyline::GetStyleString() const
    2802             : {
    2803          44 :     if (m_pszStyleString == nullptr)
    2804             :     {
    2805          35 :         m_pszStyleString = CPLStrdup(GetPenStyleString());
    2806             :     }
    2807             : 
    2808          44 :     return m_pszStyleString;
    2809             : }
    2810             : 
    2811             : /**********************************************************************
    2812             :  *                   TABPolyline::DumpMIF()
    2813             :  *
    2814             :  * Dump feature geometry in a format similar to .MIF PLINEs.
    2815             :  **********************************************************************/
    2816           0 : void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
    2817             : {
    2818           0 :     OGRMultiLineString *poMultiLine = nullptr;
    2819           0 :     OGRLineString *poLine = nullptr;
    2820             :     int i, numPoints;
    2821             : 
    2822           0 :     if (fpOut == nullptr)
    2823           0 :         fpOut = stdout;
    2824             : 
    2825             :     /*-----------------------------------------------------------------
    2826             :      * Fetch and validate geometry
    2827             :      *----------------------------------------------------------------*/
    2828           0 :     OGRGeometry *poGeom = GetGeometryRef();
    2829           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2830             :     {
    2831             :         /*-------------------------------------------------------------
    2832             :          * Generate output for simple polyline
    2833             :          *------------------------------------------------------------*/
    2834           0 :         poLine = poGeom->toLineString();
    2835           0 :         numPoints = poLine->getNumPoints();
    2836           0 :         fprintf(fpOut, "PLINE %d\n", numPoints);
    2837           0 :         for (i = 0; i < numPoints; i++)
    2838           0 :             fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
    2839             :     }
    2840           0 :     else if (poGeom &&
    2841           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2842             :     {
    2843             :         /*-------------------------------------------------------------
    2844             :          * Generate output for multiple polyline
    2845             :          *------------------------------------------------------------*/
    2846             :         int iLine, numLines;
    2847           0 :         poMultiLine = poGeom->toMultiLineString();
    2848           0 :         numLines = poMultiLine->getNumGeometries();
    2849           0 :         fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
    2850           0 :         for (iLine = 0; iLine < numLines; iLine++)
    2851             :         {
    2852           0 :             poGeom = poMultiLine->getGeometryRef(iLine);
    2853           0 :             if (poGeom &&
    2854           0 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2855             :             {
    2856           0 :                 poLine = poGeom->toLineString();
    2857           0 :                 numPoints = poLine->getNumPoints();
    2858           0 :                 fprintf(fpOut, " %d\n", numPoints);
    2859           0 :                 for (i = 0; i < numPoints; i++)
    2860           0 :                     fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
    2861             :                             poLine->getY(i));
    2862             :             }
    2863             :             else
    2864             :             {
    2865           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2866             :                          "TABPolyline: Object contains an invalid Geometry!");
    2867           0 :                 return;
    2868             :             }
    2869             :         }
    2870             :     }
    2871             :     else
    2872             :     {
    2873           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2874             :                  "TABPolyline: Missing or Invalid Geometry!");
    2875           0 :         return;
    2876             :     }
    2877             : 
    2878           0 :     if (m_bCenterIsSet)
    2879           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    2880             : 
    2881             :     // Finish with PEN/BRUSH/etc. clauses
    2882           0 :     DumpPenDef();
    2883             : 
    2884           0 :     fflush(fpOut);
    2885             : }
    2886             : 
    2887             : /**********************************************************************
    2888             :  *                   TABPolyline::GetCenter()
    2889             :  *
    2890             :  * Returns the center point of the line.  Compute one if it was not
    2891             :  * explicitly set:
    2892             :  *
    2893             :  * In MapInfo, for a simple or multiple polyline (pline), the center point
    2894             :  * in the object definition is supposed to be either the center point of
    2895             :  * the pline or the first section of a multiple pline (if an odd number of
    2896             :  * points in the pline or first section), or the midway point between the
    2897             :  * two central points (if an even number of points involved).
    2898             :  *
    2899             :  * Returns 0 on success, -1 on error.
    2900             :  **********************************************************************/
    2901         341 : int TABPolyline::GetCenter(double &dX, double &dY)
    2902             : {
    2903         341 :     if (!m_bCenterIsSet)
    2904             :     {
    2905         215 :         OGRLineString *poLine = nullptr;
    2906             : 
    2907         215 :         OGRGeometry *poGeom = GetGeometryRef();
    2908         215 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2909             :         {
    2910         207 :             poLine = poGeom->toLineString();
    2911             :         }
    2912          16 :         else if (poGeom &&
    2913           8 :                  wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2914             :         {
    2915           8 :             OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2916           8 :             if (poMultiLine->getNumGeometries() > 0)
    2917           8 :                 poLine = poMultiLine->getGeometryRef(0);
    2918             :         }
    2919             : 
    2920         215 :         if (poLine && poLine->getNumPoints() > 0)
    2921             :         {
    2922         215 :             int i = poLine->getNumPoints() / 2;
    2923         215 :             if (poLine->getNumPoints() % 2 == 0)
    2924             :             {
    2925             :                 // Return the midway between the 2 center points
    2926          15 :                 m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
    2927          15 :                 m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
    2928             :             }
    2929             :             else
    2930             :             {
    2931             :                 // Return the center point
    2932         200 :                 m_dCenterX = poLine->getX(i);
    2933         200 :                 m_dCenterY = poLine->getY(i);
    2934             :             }
    2935         215 :             m_bCenterIsSet = TRUE;
    2936             :         }
    2937             :     }
    2938             : 
    2939         341 :     if (!m_bCenterIsSet)
    2940           0 :         return -1;
    2941             : 
    2942         341 :     dX = m_dCenterX;
    2943         341 :     dY = m_dCenterY;
    2944         341 :     return 0;
    2945             : }
    2946             : 
    2947             : /**********************************************************************
    2948             :  *                   TABPolyline::SetCenter()
    2949             :  *
    2950             :  * Set the X,Y coordinates to use as center point for the line.
    2951             :  **********************************************************************/
    2952        1366 : void TABPolyline::SetCenter(double dX, double dY)
    2953             : {
    2954        1366 :     m_dCenterX = dX;
    2955        1366 :     m_dCenterY = dY;
    2956        1366 :     m_bCenterIsSet = TRUE;
    2957        1366 : }
    2958             : 
    2959             : /**********************************************************************
    2960             :  *                   TABPolyline::TwoPointLineAsPolyline()
    2961             :  *
    2962             :  * Returns the value of m_bWriteTwoPointLineAsPolyline
    2963             :  **********************************************************************/
    2964           0 : GBool TABPolyline::TwoPointLineAsPolyline()
    2965             : {
    2966           0 :     return m_bWriteTwoPointLineAsPolyline;
    2967             : }
    2968             : 
    2969             : /**********************************************************************
    2970             :  *                   TABPolyline::TwoPointLineAsPolyline()
    2971             :  *
    2972             :  * Sets the value of m_bWriteTwoPointLineAsPolyline
    2973             :  **********************************************************************/
    2974           0 : void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
    2975             : {
    2976           0 :     m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
    2977           0 : }
    2978             : 
    2979             : /*=====================================================================
    2980             :  *                      class TABRegion
    2981             :  *====================================================================*/
    2982             : 
    2983             : /**********************************************************************
    2984             :  *                   TABRegion::TABRegion()
    2985             :  *
    2986             :  * Constructor.
    2987             :  **********************************************************************/
    2988        1154 : TABRegion::TABRegion(OGRFeatureDefn *poDefnIn)
    2989             :     : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
    2990        1154 :       m_dCenterX(0.0), m_dCenterY(0.0)
    2991             : {
    2992        1154 : }
    2993             : 
    2994             : /**********************************************************************
    2995             :  *                   TABRegion::~TABRegion()
    2996             :  *
    2997             :  * Destructor.
    2998             :  **********************************************************************/
    2999        2308 : TABRegion::~TABRegion()
    3000             : {
    3001        2308 : }
    3002             : 
    3003             : /**********************************************************************
    3004             :  *                     TABRegion::CloneTABFeature()
    3005             :  *
    3006             :  * Duplicate feature, including stuff specific to each TABFeature type.
    3007             :  *
    3008             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    3009             :  * then copies any members specific to its own type.
    3010             :  **********************************************************************/
    3011           0 : TABFeature *TABRegion::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    3012             : {
    3013             :     /*-----------------------------------------------------------------
    3014             :      * Alloc new feature and copy the base stuff
    3015             :      *----------------------------------------------------------------*/
    3016           0 :     TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
    3017             : 
    3018           0 :     CopyTABFeatureBase(poNew);
    3019             : 
    3020             :     /*-----------------------------------------------------------------
    3021             :      * And members specific to this class
    3022             :      *----------------------------------------------------------------*/
    3023             :     // ITABFeaturePen
    3024           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    3025             : 
    3026             :     // ITABFeatureBrush
    3027           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    3028             : 
    3029           0 :     poNew->m_bSmooth = m_bSmooth;
    3030           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    3031           0 :     poNew->m_dCenterX = m_dCenterX;
    3032           0 :     poNew->m_dCenterY = m_dCenterY;
    3033             : 
    3034           0 :     return poNew;
    3035             : }
    3036             : 
    3037             : /**********************************************************************
    3038             :  *                   TABRegion::ValidateMapInfoType()
    3039             :  *
    3040             :  * Check the feature's geometry part and return the corresponding
    3041             :  * mapinfo object type code.  The m_nMapInfoType member will also
    3042             :  * be updated for further calls to GetMapInfoType();
    3043             :  *
    3044             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    3045             :  * is expected for this object class.
    3046             :  **********************************************************************/
    3047          88 : TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    3048             : {
    3049             :     /*-----------------------------------------------------------------
    3050             :      * Fetch and validate geometry
    3051             :      *----------------------------------------------------------------*/
    3052          88 :     OGRGeometry *poGeom = GetGeometryRef();
    3053          94 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3054           6 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3055             :     {
    3056          88 :         GInt32 numPointsTotal = 0;
    3057          88 :         GInt32 numRings = GetNumRings();
    3058         176 :         for (int i = 0; i < numRings; i++)
    3059             :         {
    3060          88 :             OGRLinearRing *poRing = GetRingRef(i);
    3061          88 :             if (poRing)
    3062          88 :                 numPointsTotal += poRing->getNumPoints();
    3063             :         }
    3064          88 :         if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
    3065           0 :             m_nMapInfoType = TAB_GEOM_V800_REGION;
    3066          88 :         else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
    3067           0 :             m_nMapInfoType = TAB_GEOM_V450_REGION;
    3068             :         else
    3069          88 :             m_nMapInfoType = TAB_GEOM_REGION;
    3070             :     }
    3071             :     else
    3072             :     {
    3073           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3074             :                  "TABRegion: Missing or Invalid Geometry!");
    3075           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    3076             :     }
    3077             : 
    3078             :     /*-----------------------------------------------------------------
    3079             :      * Decide if coordinates should be compressed or not.
    3080             :      *----------------------------------------------------------------*/
    3081          88 :     ValidateCoordType(poMapFile);
    3082             : 
    3083          88 :     return m_nMapInfoType;
    3084             : }
    3085             : 
    3086             : /**********************************************************************
    3087             :  *                   TABRegion::ReadGeometryFromMAPFile()
    3088             :  *
    3089             :  * Fill the geometry and representation (color, etc...) part of the
    3090             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    3091             :  *
    3092             :  * It is assumed that poMAPFile currently points to the beginning of
    3093             :  * a map object.
    3094             :  *
    3095             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    3096             :  * been called.
    3097             :  **********************************************************************/
    3098         503 : int TABRegion::ReadGeometryFromMAPFile(
    3099             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    3100             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    3101             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    3102             : {
    3103         503 :     double dXMin = 0.0;
    3104         503 :     double dYMin = 0.0;
    3105         503 :     double dXMax = 0.0;
    3106         503 :     double dYMax = 0.0;
    3107         503 :     OGRGeometry *poGeometry = nullptr;
    3108         503 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    3109             : 
    3110             :     /*-----------------------------------------------------------------
    3111             :      * Fetch and validate geometry type
    3112             :      *----------------------------------------------------------------*/
    3113         503 :     m_nMapInfoType = poObjHdr->m_nType;
    3114             : 
    3115         503 :     if (m_nMapInfoType == TAB_GEOM_REGION ||
    3116         448 :         m_nMapInfoType == TAB_GEOM_REGION_C ||
    3117           4 :         m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3118           4 :         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3119           0 :         m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3120           0 :         m_nMapInfoType == TAB_GEOM_V800_REGION_C)
    3121             :     {
    3122             :         /*=============================================================
    3123             :          * REGION (Similar to PLINE MULTIPLE)
    3124             :          *============================================================*/
    3125             :         GInt32 /* nCoordDataSize, */ numPointsTotal;
    3126         503 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3127         503 :         OGRPolygon *poPolygon = nullptr;
    3128         503 :         GBool bComprCoord = poObjHdr->IsCompressedType();
    3129         503 :         int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    3130             : 
    3131             :         /*-------------------------------------------------------------
    3132             :          * Copy data from poObjHdr
    3133             :          *------------------------------------------------------------*/
    3134         503 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    3135             : 
    3136         503 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    3137             :         /* nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
    3138         503 :         GInt32 numLineSections = poPLineHdr->m_numLineSections;
    3139         503 :         m_bSmooth = poPLineHdr->m_bSmooth;
    3140             : 
    3141             :         // Centroid/label point
    3142         503 :         double dX = 0.0;
    3143         503 :         double dY = 0.0;
    3144         503 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    3145             :                                 dX, dY);
    3146         503 :         SetCenter(dX, dY);
    3147             : 
    3148             :         // Compressed coordinate origin (useful only in compressed case!)
    3149         503 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    3150         503 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    3151             : 
    3152             :         // MBR
    3153         503 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    3154             :                                 dYMin);
    3155         503 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    3156             :                                 dYMax);
    3157             : 
    3158         503 :         if (!bCoordBlockDataOnly)
    3159             :         {
    3160         503 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    3161         503 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    3162         503 :             m_nBrushDefIndex = poPLineHdr->m_nBrushId;  // Brush index
    3163         503 :             poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    3164             :         }
    3165             : 
    3166             :         /*-------------------------------------------------------------
    3167             :          * Read data from the coord. block
    3168             :          *------------------------------------------------------------*/
    3169             : 
    3170         503 :         const int nMinSizeOfSection = 24;
    3171         503 :         if (numLineSections > INT_MAX / nMinSizeOfSection)
    3172             :         {
    3173           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    3174           0 :             return -1;
    3175             :         }
    3176         503 :         const GUInt32 nMinimumBytesForSections =
    3177         503 :             nMinSizeOfSection * numLineSections;
    3178         503 :         if (nMinimumBytesForSections > 1024 * 1024 &&
    3179           0 :             nMinimumBytesForSections > poMapFile->GetFileSize())
    3180             :         {
    3181           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    3182           0 :             return -1;
    3183             :         }
    3184             : 
    3185             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    3186         503 :             VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
    3187         503 :         if (pasSecHdrs == nullptr)
    3188           0 :             return -1;
    3189             : 
    3190         503 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    3191           4 :             poCoordBlock = *ppoCoordBlock;
    3192             :         else
    3193         499 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    3194             : 
    3195         503 :         if (poCoordBlock)
    3196         503 :             poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    3197             : 
    3198        1006 :         if (poCoordBlock == nullptr ||
    3199         503 :             poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
    3200             :                                            numLineSections, pasSecHdrs,
    3201             :                                            numPointsTotal) != 0)
    3202             :         {
    3203           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3204             :                      "Failed reading coordinate data at offset %d",
    3205             :                      nCoordBlockPtr);
    3206           0 :             CPLFree(pasSecHdrs);
    3207           0 :             return -1;
    3208             :         }
    3209             : 
    3210         503 :         const GUInt32 nMinimumBytesForPoints =
    3211         503 :             (bComprCoord ? 4 : 8) * numPointsTotal;
    3212         503 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    3213           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    3214             :         {
    3215           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
    3216           0 :             CPLFree(pasSecHdrs);
    3217           0 :             return -1;
    3218             :         }
    3219             : 
    3220             :         GInt32 *panXY = static_cast<GInt32 *>(
    3221         503 :             VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
    3222         503 :         if (panXY == nullptr)
    3223             :         {
    3224           0 :             CPLFree(pasSecHdrs);
    3225           0 :             return -1;
    3226             :         }
    3227             : 
    3228         503 :         if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
    3229             :             0)
    3230             :         {
    3231           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3232             :                      "Failed reading coordinate data at offset %d",
    3233             :                      nCoordBlockPtr);
    3234           0 :             CPLFree(pasSecHdrs);
    3235           0 :             CPLFree(panXY);
    3236           0 :             return -1;
    3237             :         }
    3238             : 
    3239             :         /*-------------------------------------------------------------
    3240             :          * Decide if we should return an OGRPolygon or an OGRMultiPolygon
    3241             :          * depending on the number of outer rings found in CoordSecHdr blocks.
    3242             :          * The CoodSecHdr block for each outer ring in the region has a flag
    3243             :          * indicating the number of inner rings that follow.
    3244             :          * In older versions of the format, the count of inner rings was
    3245             :          * always zero, so in this case we would always return MultiPolygons.
    3246             :          *
    3247             :          * Note: The current implementation assumes that there cannot be
    3248             :          * holes inside holes (i.e. multiple levels of inner rings)... if
    3249             :          * that case was encountered then we would return an OGRMultiPolygon
    3250             :          * in which the topological relationship between the rings would
    3251             :          * be lost.
    3252             :          *------------------------------------------------------------*/
    3253         503 :         int numOuterRings = 0;
    3254        1006 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    3255             :         {
    3256             :             // Count this as an outer ring.
    3257         503 :             numOuterRings++;
    3258             :             // Skip inner rings... so loop continues on an outer ring.
    3259         503 :             iSection += pasSecHdrs[iSection].numHoles;
    3260             :         }
    3261             : 
    3262         503 :         if (numOuterRings > 1)
    3263             :         {
    3264           0 :             poMultiPolygon = new OGRMultiPolygon;
    3265           0 :             poGeometry = poMultiPolygon;
    3266             :         }
    3267             :         else
    3268             :         {
    3269         503 :             poGeometry = nullptr;  // Will be set later
    3270             :         }
    3271             : 
    3272             :         /*-------------------------------------------------------------
    3273             :          * OK, build the OGRGeometry object.
    3274             :          *------------------------------------------------------------*/
    3275         503 :         int numHolesToRead = 0;
    3276         503 :         poPolygon = nullptr;
    3277        1006 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    3278             :         {
    3279             : 
    3280         503 :             if (poPolygon == nullptr)
    3281         503 :                 poPolygon = new OGRPolygon();
    3282             : 
    3283         503 :             if (numHolesToRead < 1)
    3284         503 :                 numHolesToRead = pasSecHdrs[iSection].numHoles;
    3285             :             else
    3286           0 :                 numHolesToRead--;
    3287             : 
    3288         503 :             int numSectionVertices = pasSecHdrs[iSection].numVertices;
    3289         503 :             GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
    3290             : 
    3291         503 :             OGRLinearRing *poRing = new OGRLinearRing();
    3292         503 :             poRing->setNumPoints(numSectionVertices);
    3293             : 
    3294       13753 :             for (int i = 0; i < numSectionVertices; i++)
    3295             :             {
    3296       13250 :                 poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
    3297       13250 :                 poRing->setPoint(i, dX, dY);
    3298       13250 :                 pnXYPtr += 2;
    3299             :             }
    3300             : 
    3301         503 :             poPolygon->addRingDirectly(poRing);
    3302         503 :             poRing = nullptr;
    3303             : 
    3304         503 :             if (numHolesToRead < 1)
    3305             :             {
    3306         503 :                 if (numOuterRings > 1)
    3307             :                 {
    3308           0 :                     poMultiPolygon->addGeometryDirectly(poPolygon);
    3309             :                 }
    3310             :                 else
    3311             :                 {
    3312         503 :                     poGeometry = poPolygon;
    3313         503 :                     CPLAssert(iSection == numLineSections - 1);
    3314             :                 }
    3315             : 
    3316         503 :                 poPolygon = nullptr;  // We'll alloc a new polygon next loop.
    3317             :             }
    3318             :         }
    3319         503 :         delete poPolygon;  // should only trigger on corrupted files
    3320             : 
    3321         503 :         CPLFree(pasSecHdrs);
    3322         503 :         CPLFree(panXY);
    3323             :     }
    3324             :     else
    3325             :     {
    3326           0 :         CPLError(
    3327             :             CE_Failure, CPLE_AssertionFailed,
    3328             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    3329           0 :             m_nMapInfoType, m_nMapInfoType);
    3330           0 :         return -1;
    3331             :     }
    3332             : 
    3333         503 :     SetGeometryDirectly(poGeometry);
    3334             : 
    3335         503 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    3336         503 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    3337             :               poObjHdr->m_nMaxY);
    3338             : 
    3339             :     /* Return a ref to coord block so that caller can continue reading
    3340             :      * after the end of this object (used by TABCollection and index splitting)
    3341             :      */
    3342         503 :     if (ppoCoordBlock)
    3343           4 :         *ppoCoordBlock = poCoordBlock;
    3344             : 
    3345         503 :     return 0;
    3346             : }
    3347             : 
    3348             : /**********************************************************************
    3349             :  *                   TABRegion::WriteGeometryToMAPFile()
    3350             :  *
    3351             :  * Write the geometry and representation (color, etc...) part of the
    3352             :  * feature to the .MAP object pointed to by poMAPFile.
    3353             :  *
    3354             :  * It is assumed that poMAPFile currently points to a valid map object.
    3355             :  *
    3356             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    3357             :  * been called.
    3358             :  **********************************************************************/
    3359          88 : int TABRegion::WriteGeometryToMAPFile(
    3360             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    3361             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    3362             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    3363             : {
    3364             :     /*-----------------------------------------------------------------
    3365             :      * We assume that ValidateMapInfoType() was called already and that
    3366             :      * the type in poObjHdr->m_nType is valid.
    3367             :      *----------------------------------------------------------------*/
    3368          88 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    3369             : 
    3370             :     /*-----------------------------------------------------------------
    3371             :      * Fetch and validate geometry
    3372             :      *----------------------------------------------------------------*/
    3373          88 :     OGRGeometry *poGeom = GetGeometryRef();
    3374          88 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    3375             : 
    3376         258 :     if ((m_nMapInfoType == TAB_GEOM_REGION ||
    3377          82 :          m_nMapInfoType == TAB_GEOM_REGION_C ||
    3378           0 :          m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3379           0 :          m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3380           0 :          m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3381          88 :          m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
    3382         176 :         poGeom &&
    3383          88 :         (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3384           6 :          wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3385             :     {
    3386             :         /*=============================================================
    3387             :          * REGIONs are similar to PLINE MULTIPLE
    3388             :          *
    3389             :          * We accept both OGRPolygons (with one or multiple rings) and
    3390             :          * OGRMultiPolygons as input.
    3391             :          *============================================================*/
    3392          88 :         GBool bCompressed = poObjHdr->IsCompressedType();
    3393             : 
    3394             :         /*-------------------------------------------------------------
    3395             :          * Process geometry first...
    3396             :          *------------------------------------------------------------*/
    3397          88 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    3398           0 :             poCoordBlock = *ppoCoordBlock;
    3399             :         else
    3400          88 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    3401          88 :         poCoordBlock->StartNewFeature();
    3402          88 :         GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    3403          88 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    3404             : 
    3405             : #ifdef TABDUMP
    3406             :         printf(/*ok*/
    3407             :                "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
    3408             :                m_nComprOrgX, m_nComprOrgY);
    3409             : #endif
    3410             :         /*-------------------------------------------------------------
    3411             :          * Fetch total number of rings and build array of coord
    3412             :          * sections headers.
    3413             :          *------------------------------------------------------------*/
    3414          88 :         TABMAPCoordSecHdr *pasSecHdrs = nullptr;
    3415          88 :         int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
    3416          88 :         int nStatus = numRingsTotal == 0 ? -1 : 0;
    3417             : 
    3418             :         /*-------------------------------------------------------------
    3419             :          * Write the Coord. Section Header
    3420             :          *------------------------------------------------------------*/
    3421          88 :         const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    3422             : 
    3423          88 :         if (nStatus == 0)
    3424          88 :             nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
    3425             :                                                       pasSecHdrs, bCompressed);
    3426             : 
    3427          88 :         CPLFree(pasSecHdrs);
    3428          88 :         pasSecHdrs = nullptr;
    3429             : 
    3430          88 :         if (nStatus != 0)
    3431           0 :             return nStatus;  // Error has already been reported.
    3432             : 
    3433             :         /*-------------------------------------------------------------
    3434             :          * Go through all the rings in our OGRMultiPolygon or OGRPolygon
    3435             :          * to write the coordinates themselves...
    3436             :          *------------------------------------------------------------*/
    3437             : 
    3438          88 :         GInt32 nX = 0, nY = 0;
    3439         176 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3440             :         {
    3441          88 :             OGRLinearRing *poRing = GetRingRef(iRing);
    3442          88 :             if (poRing == nullptr)
    3443             :             {
    3444           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    3445             :                          "TABRegion: Object Geometry contains NULL rings!");
    3446           0 :                 return -1;
    3447             :             }
    3448             : 
    3449          88 :             int numPoints = poRing->getNumPoints();
    3450             : 
    3451        2515 :             for (int i = 0; nStatus == 0 && i < numPoints; i++)
    3452             :             {
    3453        2427 :                 poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
    3454             :                                         nY);
    3455        2427 :                 if ((nStatus =
    3456        2427 :                          poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
    3457             :                 {
    3458             :                     // Failed ... error message has already been produced
    3459           0 :                     return nStatus;
    3460             :                 }
    3461             :             }
    3462             :         } /* for iRing*/
    3463             : 
    3464          88 :         GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    3465             : 
    3466             :         /*-------------------------------------------------------------
    3467             :          * ... and finally copy info to poObjHdr
    3468             :          *------------------------------------------------------------*/
    3469          88 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    3470             : 
    3471          88 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    3472          88 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    3473          88 :         poPLineHdr->m_numLineSections = numRingsTotal;
    3474             : 
    3475          88 :         poPLineHdr->m_bSmooth = m_bSmooth;
    3476             : 
    3477             :         // MBR
    3478          88 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    3479             : 
    3480             :         // Region center/label point
    3481          88 :         double dX = 0.0;
    3482          88 :         double dY = 0.0;
    3483          88 :         if (GetCenter(dX, dY) != -1)
    3484             :         {
    3485          88 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    3486          88 :                                     poPLineHdr->m_nLabelY);
    3487             :         }
    3488             :         else
    3489             :         {
    3490           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    3491           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    3492             :         }
    3493             : 
    3494             :         // Compressed coordinate origin (useful only in compressed case!)
    3495          88 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    3496          88 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    3497             : 
    3498          88 :         if (!bCoordBlockDataOnly)
    3499             :         {
    3500          88 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    3501          88 :             poPLineHdr->m_nPenId =
    3502          88 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    3503             : 
    3504          88 :             m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    3505          88 :             poPLineHdr->m_nBrushId =
    3506          88 :                 static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    3507             :         }
    3508             :     }
    3509             :     else
    3510             :     {
    3511           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3512             :                  "TABRegion: Object contains an invalid Geometry!");
    3513           0 :         return -1;
    3514             :     }
    3515             : 
    3516          88 :     if (CPLGetLastErrorType() == CE_Failure)
    3517           0 :         return -1;
    3518             : 
    3519             :     /* Return a ref to coord block so that caller can continue writing
    3520             :      * after the end of this object (used by index splitting)
    3521             :      */
    3522          88 :     if (ppoCoordBlock)
    3523           0 :         *ppoCoordBlock = poCoordBlock;
    3524             : 
    3525          88 :     return 0;
    3526             : }
    3527             : 
    3528             : /**********************************************************************
    3529             :  *                   TABRegion::GetNumRings()
    3530             :  *
    3531             :  * Return the total number of rings in this object making it look like
    3532             :  * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
    3533             :  * of rings... hides the complexity of handling OGRMultiPolygons vs
    3534             :  * OGRPolygons, etc.
    3535             :  *
    3536             :  * Returns 0 if the geometry contained in the object is invalid or missing.
    3537             :  **********************************************************************/
    3538         109 : int TABRegion::GetNumRings()
    3539             : {
    3540         109 :     return ComputeNumRings(nullptr, nullptr);
    3541             : }
    3542             : 
    3543         197 : int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
    3544             :                                TABMAPFile *poMapFile)
    3545             : {
    3546         197 :     int numRingsTotal = 0;
    3547         197 :     int iLastSect = 0;
    3548             : 
    3549         197 :     if (ppasSecHdrs)
    3550          88 :         *ppasSecHdrs = nullptr;
    3551             : 
    3552         197 :     OGRGeometry *poGeom = GetGeometryRef();
    3553             : 
    3554         209 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3555          12 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3556             :     {
    3557             :         /*-------------------------------------------------------------
    3558             :          * Calculate total number of rings...
    3559             :          *------------------------------------------------------------*/
    3560         197 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3561             :         {
    3562          24 :             for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
    3563             :             {
    3564          12 :                 numRingsTotal += poPolygon->getNumInteriorRings() + 1;
    3565             : 
    3566          12 :                 if (ppasSecHdrs && poMapFile)
    3567             :                 {
    3568           6 :                     if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
    3569           6 :                                       iLastSect) != 0)
    3570           0 :                         return 0;  // An error happened, return count=0
    3571             :                 }
    3572             :             }  // for
    3573             :         }
    3574             :         else
    3575             :         {
    3576         185 :             OGRPolygon *poPolygon = poGeom->toPolygon();
    3577         185 :             numRingsTotal = poPolygon->getNumInteriorRings() + 1;
    3578             : 
    3579         185 :             if (ppasSecHdrs && poMapFile)
    3580             :             {
    3581          82 :                 if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
    3582          82 :                                   iLastSect) != 0)
    3583           0 :                     return 0;  // An error happened, return count=0
    3584             :             }
    3585             :         }
    3586             :     }
    3587             : 
    3588             :     /*-----------------------------------------------------------------
    3589             :      * If we're generating section header blocks, then init the
    3590             :      * coordinate offset values.
    3591             :      *
    3592             :      * In calculation of nDataOffset, we have to take into account that
    3593             :      * V450 header section uses int32 instead of int16 for numVertices
    3594             :      * and we add another 2 bytes to align with a 4 bytes boundary.
    3595             :      *------------------------------------------------------------*/
    3596         197 :     const int nTotalHdrSizeUncompressed =
    3597         394 :         (m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3598         197 :          m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3599         197 :          m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3600         197 :          m_nMapInfoType == TAB_GEOM_V800_REGION_C)
    3601         394 :             ? 28 * numRingsTotal
    3602             :             : 24 * numRingsTotal;
    3603             : 
    3604         197 :     if (ppasSecHdrs)
    3605             :     {
    3606          88 :         int numPointsTotal = 0;
    3607          88 :         CPLAssert(iLastSect == numRingsTotal);
    3608         176 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3609             :         {
    3610          88 :             (*ppasSecHdrs)[iRing].nDataOffset =
    3611          88 :                 nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
    3612          88 :             (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
    3613             : 
    3614          88 :             numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
    3615             :         }
    3616             :     }
    3617             : 
    3618         197 :     return numRingsTotal;
    3619             : }
    3620             : 
    3621             : /**********************************************************************
    3622             :  *                   TABRegion::AppendSecHdrs()
    3623             :  *
    3624             :  * (Private method)
    3625             :  *
    3626             :  * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
    3627             :  **********************************************************************/
    3628          88 : int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
    3629             :                              TABMAPCoordSecHdr *&pasSecHdrs,
    3630             :                              TABMAPFile *poMapFile, int &iLastRing)
    3631             : {
    3632             :     /*-------------------------------------------------------------
    3633             :      * Add a pasSecHdrs[] entry for each ring in this polygon.
    3634             :      * Note that the structs won't be fully initialized.
    3635             :      *------------------------------------------------------------*/
    3636          88 :     int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
    3637             : 
    3638          88 :     pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    3639          88 :         CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
    3640             :                                    sizeof(TABMAPCoordSecHdr)));
    3641             : 
    3642         176 :     for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
    3643             :     {
    3644          88 :         OGRLinearRing *poRing = nullptr;
    3645          88 :         OGREnvelope sEnvelope;
    3646             : 
    3647          88 :         if (iRing == 0)
    3648          88 :             poRing = poPolygon->getExteriorRing();
    3649             :         else
    3650           0 :             poRing = poPolygon->getInteriorRing(iRing - 1);
    3651             : 
    3652          88 :         if (poRing == nullptr)
    3653             :         {
    3654           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    3655             :                      "Assertion Failed: Encountered NULL ring in OGRPolygon");
    3656           0 :             return -1;
    3657             :         }
    3658             : 
    3659          88 :         poRing->getEnvelope(&sEnvelope);
    3660             : 
    3661          88 :         pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
    3662             : 
    3663          88 :         if (iRing == 0)
    3664          88 :             pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
    3665             :         else
    3666           0 :             pasSecHdrs[iLastRing].numHoles = 0;
    3667             : 
    3668          88 :         poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
    3669          88 :                                 pasSecHdrs[iLastRing].nXMin,
    3670          88 :                                 pasSecHdrs[iLastRing].nYMin);
    3671          88 :         poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
    3672          88 :                                 pasSecHdrs[iLastRing].nXMax,
    3673          88 :                                 pasSecHdrs[iLastRing].nYMax);
    3674             : 
    3675          88 :         iLastRing++;
    3676             :     } /* for iRing*/
    3677             : 
    3678          88 :     return 0;
    3679             : }
    3680             : 
    3681             : /**********************************************************************
    3682             :  *                   TABRegion::GetRingRef()
    3683             :  *
    3684             :  * Returns a reference to the specified ring number making it look like
    3685             :  * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
    3686             :  * of rings... hides the complexity of handling OGRMultiPolygons vs
    3687             :  * OGRPolygons, etc.
    3688             :  *
    3689             :  * Returns NULL if the geometry contained in the object is invalid or
    3690             :  * missing or if the specified ring index is invalid.
    3691             :  **********************************************************************/
    3692         197 : OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
    3693             : {
    3694         197 :     OGRLinearRing *poRing = nullptr;
    3695             : 
    3696         197 :     OGRGeometry *poGeom = GetGeometryRef();
    3697             : 
    3698         209 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3699          12 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3700             :     {
    3701             :         /*-------------------------------------------------------------
    3702             :          * Establish number of polygons based on geometry type
    3703             :          *------------------------------------------------------------*/
    3704         197 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3705         197 :         int iCurRing = 0;
    3706         197 :         int numOGRPolygons = 0;
    3707             : 
    3708         197 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3709             :         {
    3710          12 :             poMultiPolygon = poGeom->toMultiPolygon();
    3711          12 :             numOGRPolygons = poMultiPolygon->getNumGeometries();
    3712             :         }
    3713             :         else
    3714             :         {
    3715         185 :             numOGRPolygons = 1;
    3716             :         }
    3717             : 
    3718             :         /*-------------------------------------------------------------
    3719             :          * Loop through polygons until we find the requested ring.
    3720             :          *------------------------------------------------------------*/
    3721         197 :         iCurRing = 0;
    3722         394 :         for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
    3723             :              iPoly++)
    3724             :         {
    3725         197 :             OGRPolygon *poPolygon = nullptr;
    3726         197 :             if (poMultiPolygon)
    3727          12 :                 poPolygon = poMultiPolygon->getGeometryRef(iPoly);
    3728             :             else
    3729         185 :                 poPolygon = poGeom->toPolygon();
    3730             : 
    3731         197 :             int numIntRings = poPolygon->getNumInteriorRings();
    3732             : 
    3733         197 :             if (iCurRing == nRequestedRingIndex)
    3734             :             {
    3735         197 :                 poRing = poPolygon->getExteriorRing();
    3736             :             }
    3737           0 :             else if (nRequestedRingIndex > iCurRing &&
    3738           0 :                      nRequestedRingIndex - (iCurRing + 1) < numIntRings)
    3739             :             {
    3740           0 :                 poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
    3741           0 :                                                     (iCurRing + 1));
    3742             :             }
    3743         197 :             iCurRing += numIntRings + 1;
    3744             :         }
    3745             :     }
    3746             : 
    3747         197 :     return poRing;
    3748             : }
    3749             : 
    3750             : /**********************************************************************
    3751             :  *                   TABRegion::RingIsHole()
    3752             :  *
    3753             :  * Return false if the requested ring index is the first of a polygon
    3754             :  **********************************************************************/
    3755           0 : GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
    3756             : {
    3757           0 :     OGRGeometry *poGeom = GetGeometryRef();
    3758             : 
    3759           0 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3760           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3761             :     {
    3762             :         /*-------------------------------------------------------------
    3763             :          * Establish number of polygons based on geometry type
    3764             :          *------------------------------------------------------------*/
    3765           0 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3766           0 :         int iCurRing = 0;
    3767           0 :         int numOGRPolygons = 0;
    3768             : 
    3769           0 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3770             :         {
    3771           0 :             poMultiPolygon = poGeom->toMultiPolygon();
    3772           0 :             numOGRPolygons = poMultiPolygon->getNumGeometries();
    3773             :         }
    3774             :         else
    3775             :         {
    3776           0 :             numOGRPolygons = 1;
    3777             :         }
    3778             : 
    3779             :         /*-------------------------------------------------------------
    3780             :          * Loop through polygons until we find the requested ring.
    3781             :          *------------------------------------------------------------*/
    3782           0 :         iCurRing = 0;
    3783           0 :         for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
    3784             :         {
    3785           0 :             OGRPolygon *poPolygon = nullptr;
    3786           0 :             if (poMultiPolygon)
    3787           0 :                 poPolygon = poMultiPolygon->getGeometryRef(iPoly);
    3788             :             else
    3789           0 :                 poPolygon = poGeom->toPolygon();
    3790             : 
    3791           0 :             int numIntRings = poPolygon->getNumInteriorRings();
    3792             : 
    3793           0 :             if (iCurRing == nRequestedRingIndex)
    3794             :             {
    3795           0 :                 return FALSE;
    3796             :             }
    3797           0 :             else if (nRequestedRingIndex > iCurRing &&
    3798           0 :                      nRequestedRingIndex - (iCurRing + 1) < numIntRings)
    3799             :             {
    3800           0 :                 return TRUE;
    3801             :             }
    3802           0 :             iCurRing += numIntRings + 1;
    3803             :         }
    3804             :     }
    3805             : 
    3806           0 :     return FALSE;
    3807             : }
    3808             : 
    3809             : /**********************************************************************
    3810             :  *                   TABRegion::GetStyleString() const
    3811             :  *
    3812             :  * Return style string for this feature.
    3813             :  *
    3814             :  * Style String is built only once during the first call to GetStyleString().
    3815             :  **********************************************************************/
    3816          57 : const char *TABRegion::GetStyleString() const
    3817             : {
    3818          57 :     if (m_pszStyleString == nullptr)
    3819             :     {
    3820             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    3821             :         // to use temporary buffers
    3822          35 :         char *pszPen = CPLStrdup(GetPenStyleString());
    3823          35 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    3824             : 
    3825          35 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    3826             : 
    3827          35 :         CPLFree(pszPen);
    3828          35 :         CPLFree(pszBrush);
    3829             :     }
    3830             : 
    3831          57 :     return m_pszStyleString;
    3832             : }
    3833             : 
    3834             : /**********************************************************************
    3835             :  *                   TABRegion::DumpMIF()
    3836             :  *
    3837             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    3838             :  **********************************************************************/
    3839           0 : void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
    3840             : {
    3841           0 :     if (fpOut == nullptr)
    3842           0 :         fpOut = stdout;
    3843             : 
    3844             :     /*-----------------------------------------------------------------
    3845             :      * Fetch and validate geometry
    3846             :      *----------------------------------------------------------------*/
    3847           0 :     OGRGeometry *poGeom = GetGeometryRef();
    3848           0 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3849           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3850             :     {
    3851             :         /*-------------------------------------------------------------
    3852             :          * Generate output for region
    3853             :          *
    3854             :          * Note that we want to handle both OGRPolygons and OGRMultiPolygons
    3855             :          * that's why we use the GetNumRings()/GetRingRef() interface.
    3856             :          *------------------------------------------------------------*/
    3857           0 :         int numRingsTotal = GetNumRings();
    3858             : 
    3859           0 :         fprintf(fpOut, "REGION %d\n", numRingsTotal);
    3860             : 
    3861           0 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3862             :         {
    3863           0 :             OGRLinearRing *poRing = GetRingRef(iRing);
    3864             : 
    3865           0 :             if (poRing == nullptr)
    3866             :             {
    3867           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    3868             :                          "TABRegion: Object Geometry contains NULL rings!");
    3869           0 :                 return;
    3870             :             }
    3871             : 
    3872           0 :             const int numPoints = poRing->getNumPoints();
    3873           0 :             fprintf(fpOut, " %d\n", numPoints);
    3874           0 :             for (int i = 0; i < numPoints; i++)
    3875           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    3876             :                         poRing->getY(i));
    3877             :         }
    3878             :     }
    3879             :     else
    3880             :     {
    3881           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3882             :                  "TABRegion: Missing or Invalid Geometry!");
    3883           0 :         return;
    3884             :     }
    3885             : 
    3886           0 :     if (m_bCenterIsSet)
    3887           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    3888             : 
    3889             :     // Finish with PEN/BRUSH/etc. clauses
    3890           0 :     DumpPenDef();
    3891           0 :     DumpBrushDef();
    3892             : 
    3893           0 :     fflush(fpOut);
    3894             : }
    3895             : 
    3896             : /**********************************************************************
    3897             :  *                   TABRegion::GetCenter()
    3898             :  *
    3899             :  * Returns the center/label point of the region.
    3900             :  * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
    3901             :  * before.
    3902             :  *
    3903             :  * Returns 0 on success, -1 on error.
    3904             :  **********************************************************************/
    3905          88 : int TABRegion::GetCenter(double &dX, double &dY)
    3906             : {
    3907          88 :     if (!m_bCenterIsSet)
    3908             :     {
    3909             :         /*-------------------------------------------------------------
    3910             :          * Calculate label point.  If we have a multipolygon then we use
    3911             :          * the first OGRPolygon in the feature to calculate the point.
    3912             :          *------------------------------------------------------------*/
    3913          88 :         OGRGeometry *poGeom = GetGeometryRef();
    3914          88 :         if (poGeom == nullptr)
    3915           0 :             return -1;
    3916             : 
    3917          88 :         OGRPolygon *poPolygon = nullptr;
    3918             : 
    3919          88 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3920             :         {
    3921           6 :             OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
    3922           6 :             if (poMultiPolygon->getNumGeometries() > 0)
    3923           6 :                 poPolygon = poMultiPolygon->getGeometryRef(0);
    3924             :         }
    3925          82 :         else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    3926             :         {
    3927          82 :             poPolygon = poGeom->toPolygon();
    3928             :         }
    3929             : 
    3930          88 :         OGRPoint oLabelPoint;
    3931         176 :         if (poPolygon != nullptr &&
    3932          88 :             OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
    3933             :         {
    3934          88 :             m_dCenterX = oLabelPoint.getX();
    3935          88 :             m_dCenterY = oLabelPoint.getY();
    3936             :         }
    3937             :         else
    3938             :         {
    3939           0 :             OGREnvelope oEnv;
    3940           0 :             poGeom->getEnvelope(&oEnv);
    3941           0 :             m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
    3942           0 :             m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
    3943             :         }
    3944             : 
    3945          88 :         m_bCenterIsSet = TRUE;
    3946             :     }
    3947             : 
    3948          88 :     if (!m_bCenterIsSet)
    3949           0 :         return -1;
    3950             : 
    3951          88 :     dX = m_dCenterX;
    3952          88 :     dY = m_dCenterY;
    3953          88 :     return 0;
    3954             : }
    3955             : 
    3956             : /**********************************************************************
    3957             :  *                   TABRegion::SetCenter()
    3958             :  *
    3959             :  * Set the X,Y coordinates to use as center/label point for the region.
    3960             :  **********************************************************************/
    3961         504 : void TABRegion::SetCenter(double dX, double dY)
    3962             : {
    3963         504 :     m_dCenterX = dX;
    3964         504 :     m_dCenterY = dY;
    3965         504 :     m_bCenterIsSet = TRUE;
    3966         504 : }
    3967             : 
    3968             : /*=====================================================================
    3969             :  *                      class TABRectangle
    3970             :  *====================================================================*/
    3971             : 
    3972             : /**********************************************************************
    3973             :  *                   TABRectangle::TABRectangle()
    3974             :  *
    3975             :  * Constructor.
    3976             :  **********************************************************************/
    3977         949 : TABRectangle::TABRectangle(OGRFeatureDefn *poDefnIn)
    3978             :     : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
    3979         949 :       m_dRoundYRadius(0.0)
    3980             : {
    3981         949 : }
    3982             : 
    3983             : /**********************************************************************
    3984             :  *                   TABRectangle::~TABRectangle()
    3985             :  *
    3986             :  * Destructor.
    3987             :  **********************************************************************/
    3988        1898 : TABRectangle::~TABRectangle()
    3989             : {
    3990        1898 : }
    3991             : 
    3992             : /**********************************************************************
    3993             :  *                     TABRectangle::CloneTABFeature()
    3994             :  *
    3995             :  * Duplicate feature, including stuff specific to each TABFeature type.
    3996             :  *
    3997             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    3998             :  * then copies any members specific to its own type.
    3999             :  **********************************************************************/
    4000           0 : TABFeature *TABRectangle::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    4001             : {
    4002             :     /*-----------------------------------------------------------------
    4003             :      * Alloc new feature and copy the base stuff
    4004             :      *----------------------------------------------------------------*/
    4005             :     TABRectangle *poNew =
    4006           0 :         new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
    4007             : 
    4008           0 :     CopyTABFeatureBase(poNew);
    4009             : 
    4010             :     /*-----------------------------------------------------------------
    4011             :      * And members specific to this class
    4012             :      *----------------------------------------------------------------*/
    4013             :     // ITABFeaturePen
    4014           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4015             : 
    4016             :     // ITABFeatureBrush
    4017           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    4018             : 
    4019           0 :     poNew->m_bRoundCorners = m_bRoundCorners;
    4020           0 :     poNew->m_dRoundXRadius = m_dRoundXRadius;
    4021           0 :     poNew->m_dRoundYRadius = m_dRoundYRadius;
    4022             : 
    4023           0 :     return poNew;
    4024             : }
    4025             : 
    4026             : /**********************************************************************
    4027             :  *                   TABRectangle::ValidateMapInfoType()
    4028             :  *
    4029             :  * Check the feature's geometry part and return the corresponding
    4030             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4031             :  * be updated for further calls to GetMapInfoType();
    4032             :  *
    4033             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4034             :  * is expected for this object class.
    4035             :  **********************************************************************/
    4036           0 : TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4037             : {
    4038             :     /*-----------------------------------------------------------------
    4039             :      * Fetch and validate geometry
    4040             :      *----------------------------------------------------------------*/
    4041           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4042           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4043             :     {
    4044           0 :         if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    4045           0 :             m_nMapInfoType = TAB_GEOM_ROUNDRECT;
    4046             :         else
    4047           0 :             m_nMapInfoType = TAB_GEOM_RECT;
    4048             :     }
    4049             :     else
    4050             :     {
    4051           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4052             :                  "TABRectangle: Missing or Invalid Geometry!");
    4053           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4054             :     }
    4055             : 
    4056             :     /*-----------------------------------------------------------------
    4057             :      * Decide if coordinates should be compressed or not.
    4058             :      *----------------------------------------------------------------*/
    4059             :     // __TODO__ For now we always write uncompressed for this class...
    4060             :     // ValidateCoordType(poMapFile);
    4061           0 :     UpdateMBR(poMapFile);
    4062             : 
    4063           0 :     return m_nMapInfoType;
    4064             : }
    4065             : 
    4066             : /**********************************************************************
    4067             :  *                   TABRectangle::UpdateMBR()
    4068             :  *
    4069             :  * Update the feature MBR members using the geometry
    4070             :  *
    4071             :  * Returns 0 on success, or -1 if there is no geometry in object
    4072             :  **********************************************************************/
    4073           0 : int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4074             : {
    4075           0 :     OGREnvelope sEnvelope;
    4076             : 
    4077             :     /*-----------------------------------------------------------------
    4078             :      * Fetch and validate geometry
    4079             :      *----------------------------------------------------------------*/
    4080           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4081           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4082           0 :         poGeom->getEnvelope(&sEnvelope);
    4083             :     else
    4084             :     {
    4085           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4086             :                  "TABRectangle: Missing or Invalid Geometry!");
    4087           0 :         return -1;
    4088             :     }
    4089             : 
    4090             :     /*-----------------------------------------------------------------
    4091             :      * Note that we will simply use the rectangle's MBR and don't really
    4092             :      * read the polygon geometry... this should be OK unless the
    4093             :      * polygon geometry was not really a rectangle.
    4094             :      *----------------------------------------------------------------*/
    4095           0 :     m_dXMin = sEnvelope.MinX;
    4096           0 :     m_dYMin = sEnvelope.MinY;
    4097           0 :     m_dXMax = sEnvelope.MaxX;
    4098           0 :     m_dYMax = sEnvelope.MaxY;
    4099             : 
    4100           0 :     if (poMapFile)
    4101             :     {
    4102           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4103           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4104             :     }
    4105             : 
    4106           0 :     return 0;
    4107             : }
    4108             : 
    4109             : /**********************************************************************
    4110             :  *                   TABRectangle::ReadGeometryFromMAPFile()
    4111             :  *
    4112             :  * Fill the geometry and representation (color, etc...) part of the
    4113             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4114             :  *
    4115             :  * It is assumed that poMAPFile currently points to the beginning of
    4116             :  * a map object.
    4117             :  *
    4118             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4119             :  * been called.
    4120             :  **********************************************************************/
    4121           8 : int TABRectangle::ReadGeometryFromMAPFile(
    4122             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4123             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4124             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4125             : {
    4126             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4127           8 :     if (bCoordBlockDataOnly)
    4128           0 :         return 0;
    4129             : 
    4130             :     /*-----------------------------------------------------------------
    4131             :      * Fetch and validate geometry type
    4132             :      *----------------------------------------------------------------*/
    4133           8 :     m_nMapInfoType = poObjHdr->m_nType;
    4134             : 
    4135           8 :     if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
    4136           4 :         m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
    4137           0 :         m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
    4138             :     {
    4139           0 :         CPLError(
    4140             :             CE_Failure, CPLE_AssertionFailed,
    4141             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    4142           0 :             m_nMapInfoType, m_nMapInfoType);
    4143           0 :         return -1;
    4144             :     }
    4145             : 
    4146             :     /*-----------------------------------------------------------------
    4147             :      * Read object information
    4148             :      *----------------------------------------------------------------*/
    4149             :     TABMAPObjRectEllipse *poRectHdr =
    4150           8 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4151             : 
    4152             :     // Read the corners radius
    4153             : 
    4154           8 :     if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
    4155           4 :         m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
    4156             :     {
    4157             :         // Read the corner's diameters
    4158           4 :         poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
    4159           4 :                                     poRectHdr->m_nCornerHeight, m_dRoundXRadius,
    4160           4 :                                     m_dRoundYRadius);
    4161             : 
    4162             :         // Divide by 2 since we store the corner's radius
    4163           4 :         m_dRoundXRadius /= 2.0;
    4164           4 :         m_dRoundYRadius /= 2.0;
    4165             : 
    4166           4 :         m_bRoundCorners = TRUE;
    4167             :     }
    4168             :     else
    4169             :     {
    4170           4 :         m_bRoundCorners = FALSE;
    4171           4 :         m_dRoundXRadius = 0.0;
    4172           4 :         m_dRoundYRadius = 0.0;
    4173             :     }
    4174             : 
    4175             :     // A rectangle is defined by its MBR
    4176             : 
    4177           8 :     double dXMin = 0.0;
    4178           8 :     double dYMin = 0.0;
    4179           8 :     double dXMax = 0.0;
    4180           8 :     double dYMax = 0.0;
    4181           8 :     poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
    4182             :                             dYMin);
    4183           8 :     poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
    4184             :                             dYMax);
    4185             : 
    4186           8 :     m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
    4187           8 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    4188             : 
    4189           8 :     m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
    4190           8 :     poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    4191             : 
    4192             :     /*-----------------------------------------------------------------
    4193             :      * Call SetMBR() and GetMBR() now to make sure that min values are
    4194             :      * really smaller than max values.
    4195             :      *----------------------------------------------------------------*/
    4196           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    4197           8 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4198             : 
    4199             :     /* Copy int MBR to feature class members */
    4200           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    4201             :               poObjHdr->m_nMaxY);
    4202             : 
    4203             :     /*-----------------------------------------------------------------
    4204             :      * Create and fill geometry object
    4205             :      *----------------------------------------------------------------*/
    4206           8 :     OGRPolygon *poPolygon = new OGRPolygon;
    4207           8 :     OGRLinearRing *poRing = new OGRLinearRing();
    4208           8 :     if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    4209             :     {
    4210             :         /*-------------------------------------------------------------
    4211             :          * For rounded rectangles, we generate arcs with 45 line
    4212             :          * segments for each corner.  We start with lower-left corner
    4213             :          * and proceed counterclockwise
    4214             :          * We also have to make sure that rounding radius is not too
    4215             :          * large for the MBR in the generated polygon... however, we
    4216             :          * always return the true X/Y radius (not adjusted) since this
    4217             :          * is the way MapInfo seems to do it when a radius bigger than
    4218             :          * the MBR is passed from TBA to MIF.
    4219             :          *------------------------------------------------------------*/
    4220             :         const double dXRadius =
    4221           4 :             std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
    4222             :         const double dYRadius =
    4223           4 :             std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
    4224           4 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
    4225             :                        dYRadius, M_PI, 3.0 * M_PI / 2.0);
    4226           4 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
    4227             :                        dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
    4228           4 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
    4229             :                        dYRadius, 0.0, M_PI / 2.0);
    4230           4 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
    4231             :                        dYRadius, M_PI / 2.0, M_PI);
    4232             : 
    4233           4 :         TABCloseRing(poRing);
    4234             :     }
    4235             :     else
    4236             :     {
    4237           4 :         poRing->addPoint(dXMin, dYMin);
    4238           4 :         poRing->addPoint(dXMax, dYMin);
    4239           4 :         poRing->addPoint(dXMax, dYMax);
    4240           4 :         poRing->addPoint(dXMin, dYMax);
    4241           4 :         poRing->addPoint(dXMin, dYMin);
    4242             :     }
    4243             : 
    4244           8 :     poPolygon->addRingDirectly(poRing);
    4245           8 :     SetGeometryDirectly(poPolygon);
    4246             : 
    4247           8 :     return 0;
    4248             : }
    4249             : 
    4250             : /**********************************************************************
    4251             :  *                   TABRectangle::WriteGeometryToMAPFile()
    4252             :  *
    4253             :  * Write the geometry and representation (color, etc...) part of the
    4254             :  * feature to the .MAP object pointed to by poMAPFile.
    4255             :  *
    4256             :  * It is assumed that poMAPFile currently points to a valid map object.
    4257             :  *
    4258             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4259             :  * been called.
    4260             :  **********************************************************************/
    4261           0 : int TABRectangle::WriteGeometryToMAPFile(
    4262             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4263             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4264             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4265             : {
    4266             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4267           0 :     if (bCoordBlockDataOnly)
    4268           0 :         return 0;
    4269             : 
    4270             :     /*-----------------------------------------------------------------
    4271             :      * We assume that ValidateMapInfoType() was called already and that
    4272             :      * the type in poObjHdr->m_nType is valid.
    4273             :      *----------------------------------------------------------------*/
    4274           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    4275             : 
    4276             :     /*-----------------------------------------------------------------
    4277             :      * Fetch and validate geometry and update MBR
    4278             :      * Note that we will simply use the geometry's MBR and don't really
    4279             :      * read the polygon geometry... this should be OK unless the
    4280             :      * polygon geometry was not really a rectangle.
    4281             :      *----------------------------------------------------------------*/
    4282           0 :     if (UpdateMBR(poMapFile) != 0)
    4283           0 :         return -1; /* Error already reported */
    4284             : 
    4285             :     /*-----------------------------------------------------------------
    4286             :      * Copy object information
    4287             :      *----------------------------------------------------------------*/
    4288             :     TABMAPObjRectEllipse *poRectHdr =
    4289           0 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4290             : 
    4291           0 :     if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
    4292           0 :         m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
    4293             :     {
    4294           0 :         poMapFile->Coordsys2IntDist(
    4295           0 :             m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
    4296           0 :             poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
    4297             :     }
    4298             :     else
    4299             :     {
    4300           0 :         poRectHdr->m_nCornerWidth = 0;
    4301           0 :         poRectHdr->m_nCornerHeight = 0;
    4302             :     }
    4303             : 
    4304             :     // A rectangle is defined by its MBR (values were set in UpdateMBR())
    4305           0 :     poRectHdr->m_nMinX = m_nXMin;
    4306           0 :     poRectHdr->m_nMinY = m_nYMin;
    4307           0 :     poRectHdr->m_nMaxX = m_nXMax;
    4308           0 :     poRectHdr->m_nMaxY = m_nYMax;
    4309             : 
    4310           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    4311           0 :     poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    4312             : 
    4313           0 :     m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    4314           0 :     poRectHdr->m_nBrushId =
    4315           0 :         static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    4316             : 
    4317           0 :     if (CPLGetLastErrorType() == CE_Failure)
    4318           0 :         return -1;
    4319             : 
    4320           0 :     return 0;
    4321             : }
    4322             : 
    4323             : /**********************************************************************
    4324             :  *                   TABRectangle::GetStyleString() const
    4325             :  *
    4326             :  * Return style string for this feature.
    4327             :  *
    4328             :  * Style String is built only once during the first call to GetStyleString().
    4329             :  **********************************************************************/
    4330          18 : const char *TABRectangle::GetStyleString() const
    4331             : {
    4332          18 :     if (m_pszStyleString == nullptr)
    4333             :     {
    4334             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    4335             :         // to use temporary buffers
    4336          10 :         char *pszPen = CPLStrdup(GetPenStyleString());
    4337          10 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    4338             : 
    4339          10 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    4340             : 
    4341          10 :         CPLFree(pszPen);
    4342          10 :         CPLFree(pszBrush);
    4343             :     }
    4344             : 
    4345          18 :     return m_pszStyleString;
    4346             : }
    4347             : 
    4348             : /**********************************************************************
    4349             :  *                   TABRectangle::DumpMIF()
    4350             :  *
    4351             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    4352             :  **********************************************************************/
    4353           0 : void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
    4354             : {
    4355           0 :     if (fpOut == nullptr)
    4356           0 :         fpOut = stdout;
    4357             : 
    4358             :     /*-----------------------------------------------------------------
    4359             :      * Output RECT or ROUNDRECT parameters
    4360             :      *----------------------------------------------------------------*/
    4361           0 :     double dXMin = 0.0;
    4362           0 :     double dYMin = 0.0;
    4363           0 :     double dXMax = 0.0;
    4364           0 :     double dYMax = 0.0;
    4365           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4366             : 
    4367           0 :     if (m_bRoundCorners)
    4368           0 :         fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g    %.15g %.15g)\n",
    4369             :                 dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
    4370             :     else
    4371           0 :         fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
    4372             :                 dYMax);
    4373             : 
    4374             :     /*-----------------------------------------------------------------
    4375             :      * Fetch and validate geometry
    4376             :      *----------------------------------------------------------------*/
    4377           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4378           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4379             :     {
    4380             :         /*-------------------------------------------------------------
    4381             :          * Generate rectangle output as a region
    4382             :          * We could also output as a RECT or ROUNDRECT in a real MIF generator
    4383             :          *------------------------------------------------------------*/
    4384           0 :         OGRPolygon *poPolygon = poGeom->toPolygon();
    4385           0 :         int numIntRings = poPolygon->getNumInteriorRings();
    4386           0 :         fprintf(fpOut, "REGION %d\n", numIntRings + 1);
    4387             :         // In this loop, iRing=-1 for the outer ring.
    4388           0 :         for (int iRing = -1; iRing < numIntRings; iRing++)
    4389             :         {
    4390           0 :             OGRLinearRing *poRing = nullptr;
    4391             : 
    4392           0 :             if (iRing == -1)
    4393           0 :                 poRing = poPolygon->getExteriorRing();
    4394             :             else
    4395           0 :                 poRing = poPolygon->getInteriorRing(iRing);
    4396             : 
    4397           0 :             if (poRing == nullptr)
    4398             :             {
    4399           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    4400             :                          "TABRectangle: Object Geometry contains NULL rings!");
    4401           0 :                 return;
    4402             :             }
    4403             : 
    4404           0 :             const int numPoints = poRing->getNumPoints();
    4405           0 :             fprintf(fpOut, " %d\n", numPoints);
    4406           0 :             for (int i = 0; i < numPoints; i++)
    4407           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    4408             :                         poRing->getY(i));
    4409             :         }
    4410             :     }
    4411             :     else
    4412             :     {
    4413           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4414             :                  "TABRectangle: Missing or Invalid Geometry!");
    4415           0 :         return;
    4416             :     }
    4417             : 
    4418             :     // Finish with PEN/BRUSH/etc. clauses
    4419           0 :     DumpPenDef();
    4420           0 :     DumpBrushDef();
    4421             : 
    4422           0 :     fflush(fpOut);
    4423             : }
    4424             : 
    4425             : /*=====================================================================
    4426             :  *                      class TABEllipse
    4427             :  *====================================================================*/
    4428             : 
    4429             : /**********************************************************************
    4430             :  *                   TABEllipse::TABEllipse()
    4431             :  *
    4432             :  * Constructor.
    4433             :  **********************************************************************/
    4434         401 : TABEllipse::TABEllipse(OGRFeatureDefn *poDefnIn)
    4435             :     : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
    4436         401 :       m_dYRadius(0.0)
    4437             : {
    4438         401 : }
    4439             : 
    4440             : /**********************************************************************
    4441             :  *                   TABEllipse::~TABEllipse()
    4442             :  *
    4443             :  * Destructor.
    4444             :  **********************************************************************/
    4445         802 : TABEllipse::~TABEllipse()
    4446             : {
    4447         802 : }
    4448             : 
    4449             : /**********************************************************************
    4450             :  *                     TABEllipse::CloneTABFeature()
    4451             :  *
    4452             :  * Duplicate feature, including stuff specific to each TABFeature type.
    4453             :  *
    4454             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    4455             :  * then copies any members specific to its own type.
    4456             :  **********************************************************************/
    4457           0 : TABFeature *TABEllipse::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    4458             : {
    4459             :     /*-----------------------------------------------------------------
    4460             :      * Alloc new feature and copy the base stuff
    4461             :      *----------------------------------------------------------------*/
    4462           0 :     TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
    4463             : 
    4464           0 :     CopyTABFeatureBase(poNew);
    4465             : 
    4466             :     /*-----------------------------------------------------------------
    4467             :      * And members specific to this class
    4468             :      *----------------------------------------------------------------*/
    4469             :     // ITABFeaturePen
    4470           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4471             : 
    4472             :     // ITABFeatureBrush
    4473           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    4474             : 
    4475           0 :     poNew->m_dCenterX = m_dCenterX;
    4476           0 :     poNew->m_dCenterY = m_dCenterY;
    4477           0 :     poNew->m_dXRadius = m_dXRadius;
    4478           0 :     poNew->m_dYRadius = m_dYRadius;
    4479             : 
    4480           0 :     return poNew;
    4481             : }
    4482             : 
    4483             : /**********************************************************************
    4484             :  *                   TABEllipse::ValidateMapInfoType()
    4485             :  *
    4486             :  * Check the feature's geometry part and return the corresponding
    4487             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4488             :  * be updated for further calls to GetMapInfoType();
    4489             :  *
    4490             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4491             :  * is expected for this object class.
    4492             :  **********************************************************************/
    4493           0 : TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4494             : {
    4495             :     /*-----------------------------------------------------------------
    4496             :      * Fetch and validate geometry
    4497             :      *----------------------------------------------------------------*/
    4498           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4499           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    4500           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4501             :     {
    4502           0 :         m_nMapInfoType = TAB_GEOM_ELLIPSE;
    4503             :     }
    4504             :     else
    4505             :     {
    4506           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4507             :                  "TABEllipse: Missing or Invalid Geometry!");
    4508           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4509             :     }
    4510             : 
    4511             :     /*-----------------------------------------------------------------
    4512             :      * Decide if coordinates should be compressed or not.
    4513             :      *----------------------------------------------------------------*/
    4514             :     // __TODO__ For now we always write uncompressed for this class...
    4515             :     // ValidateCoordType(poMapFile);
    4516           0 :     UpdateMBR(poMapFile);
    4517             : 
    4518           0 :     return m_nMapInfoType;
    4519             : }
    4520             : 
    4521             : /**********************************************************************
    4522             :  *                   TABEllipse::UpdateMBR()
    4523             :  *
    4524             :  * Update the feature MBR members using the geometry
    4525             :  *
    4526             :  * Returns 0 on success, or -1 if there is no geometry in object
    4527             :  **********************************************************************/
    4528           0 : int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4529             : {
    4530           0 :     OGREnvelope sEnvelope;
    4531             : 
    4532             :     /*-----------------------------------------------------------------
    4533             :      * Fetch and validate geometry... Polygon and point are accepted.
    4534             :      * Note that we will simply use the ellipse's MBR and don't really
    4535             :      * read the polygon geometry... this should be OK unless the
    4536             :      * polygon geometry was not really an ellipse.
    4537             :      *----------------------------------------------------------------*/
    4538           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4539           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    4540           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4541           0 :         poGeom->getEnvelope(&sEnvelope);
    4542             :     else
    4543             :     {
    4544           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4545             :                  "TABEllipse: Missing or Invalid Geometry!");
    4546           0 :         return -1;
    4547             :     }
    4548             : 
    4549             :     /*-----------------------------------------------------------------
    4550             :      * We use the center of the MBR as the ellipse center, and the
    4551             :      * X/Y radius to define the MBR size.  If X/Y radius are null then
    4552             :      * we'll try to use the MBR to recompute them.
    4553             :      *----------------------------------------------------------------*/
    4554           0 :     const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
    4555           0 :     const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
    4556           0 :     if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
    4557             :     {
    4558           0 :         m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
    4559           0 :         m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
    4560             :     }
    4561             : 
    4562           0 :     m_dXMin = dXCenter - m_dXRadius;
    4563           0 :     m_dYMin = dYCenter - m_dYRadius;
    4564           0 :     m_dXMax = dXCenter + m_dXRadius;
    4565           0 :     m_dYMax = dYCenter + m_dYRadius;
    4566             : 
    4567           0 :     if (poMapFile)
    4568             :     {
    4569           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4570           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4571             :     }
    4572             : 
    4573           0 :     return 0;
    4574             : }
    4575             : 
    4576             : /**********************************************************************
    4577             :  *                   TABEllipse::ReadGeometryFromMAPFile()
    4578             :  *
    4579             :  * Fill the geometry and representation (color, etc...) part of the
    4580             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4581             :  *
    4582             :  * It is assumed that poMAPFile currently points to the beginning of
    4583             :  * a map object.
    4584             :  *
    4585             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4586             :  * been called.
    4587             :  **********************************************************************/
    4588           4 : int TABEllipse::ReadGeometryFromMAPFile(
    4589             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4590             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4591             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4592             : {
    4593             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4594           4 :     if (bCoordBlockDataOnly)
    4595           0 :         return 0;
    4596             : 
    4597             :     /*-----------------------------------------------------------------
    4598             :      * Fetch and validate geometry type
    4599             :      *----------------------------------------------------------------*/
    4600           4 :     m_nMapInfoType = poObjHdr->m_nType;
    4601             : 
    4602           4 :     if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
    4603           0 :         m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
    4604             :     {
    4605           0 :         CPLError(
    4606             :             CE_Failure, CPLE_AssertionFailed,
    4607             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    4608           0 :             m_nMapInfoType, m_nMapInfoType);
    4609           0 :         return -1;
    4610             :     }
    4611             : 
    4612             :     /*-----------------------------------------------------------------
    4613             :      * Read object information
    4614             :      *----------------------------------------------------------------*/
    4615             :     TABMAPObjRectEllipse *poRectHdr =
    4616           4 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4617             : 
    4618             :     // An ellipse is defined by its MBR
    4619             : 
    4620           4 :     double dXMin = 0.0;
    4621           4 :     double dYMin = 0.0;
    4622           4 :     double dXMax = 0.0;
    4623           4 :     double dYMax = 0.0;
    4624           4 :     poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
    4625             :                             dYMin);
    4626           4 :     poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
    4627             :                             dYMax);
    4628             : 
    4629           4 :     m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
    4630           4 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    4631             : 
    4632           4 :     m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
    4633           4 :     poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    4634             : 
    4635             :     /*-----------------------------------------------------------------
    4636             :      * Save info about the ellipse def. inside class members
    4637             :      *----------------------------------------------------------------*/
    4638           4 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    4639           4 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    4640           4 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    4641           4 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    4642             : 
    4643           4 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    4644             : 
    4645           4 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    4646             :               poObjHdr->m_nMaxY);
    4647             : 
    4648             :     /*-----------------------------------------------------------------
    4649             :      * Create and fill geometry object
    4650             :      *----------------------------------------------------------------*/
    4651           4 :     OGRPolygon *poPolygon = new OGRPolygon;
    4652           4 :     OGRLinearRing *poRing = new OGRLinearRing();
    4653             : 
    4654             :     /*-----------------------------------------------------------------
    4655             :      * For the OGR geometry, we generate an ellipse with 2 degrees line
    4656             :      * segments.
    4657             :      *----------------------------------------------------------------*/
    4658           4 :     TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
    4659             :                    0.0, 2.0 * M_PI);
    4660           4 :     TABCloseRing(poRing);
    4661             : 
    4662           4 :     poPolygon->addRingDirectly(poRing);
    4663           4 :     SetGeometryDirectly(poPolygon);
    4664             : 
    4665           4 :     return 0;
    4666             : }
    4667             : 
    4668             : /**********************************************************************
    4669             :  *                   TABEllipse::WriteGeometryToMAPFile()
    4670             :  *
    4671             :  * Write the geometry and representation (color, etc...) part of the
    4672             :  * feature to the .MAP object pointed to by poMAPFile.
    4673             :  *
    4674             :  * It is assumed that poMAPFile currently points to a valid map object.
    4675             :  *
    4676             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4677             :  * been called.
    4678             :  **********************************************************************/
    4679           0 : int TABEllipse::WriteGeometryToMAPFile(
    4680             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4681             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4682             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4683             : {
    4684             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4685           0 :     if (bCoordBlockDataOnly)
    4686           0 :         return 0;
    4687             : 
    4688             :     /*-----------------------------------------------------------------
    4689             :      * We assume that ValidateMapInfoType() was called already and that
    4690             :      * the type in poObjHdr->m_nType is valid.
    4691             :      *----------------------------------------------------------------*/
    4692           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    4693             : 
    4694             :     /*-----------------------------------------------------------------
    4695             :      * Fetch and validate geometry... Polygon and point are accepted.
    4696             :      * Note that we will simply use the ellipse's MBR and don't really
    4697             :      * read the polygon geometry... this should be OK unless the
    4698             :      * polygon geometry was not really an ellipse.
    4699             :      *
    4700             :      * We use the center of the MBR as the ellipse center, and the
    4701             :      * X/Y radius to define the MBR size.  If X/Y radius are null then
    4702             :      * we'll try to use the MBR to recompute them.
    4703             :      *----------------------------------------------------------------*/
    4704           0 :     if (UpdateMBR(poMapFile) != 0)
    4705           0 :         return -1; /* Error already reported */
    4706             : 
    4707             :     /*-----------------------------------------------------------------
    4708             :      * Copy object information
    4709             :      *----------------------------------------------------------------*/
    4710             :     TABMAPObjRectEllipse *poRectHdr =
    4711           0 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4712             : 
    4713             :     // Reset RoundRect Corner members... just in case (unused for ellipse)
    4714           0 :     poRectHdr->m_nCornerWidth = 0;
    4715           0 :     poRectHdr->m_nCornerHeight = 0;
    4716             : 
    4717             :     // An ellipse is defined by its MBR (values were set in UpdateMBR())
    4718           0 :     poRectHdr->m_nMinX = m_nXMin;
    4719           0 :     poRectHdr->m_nMinY = m_nYMin;
    4720           0 :     poRectHdr->m_nMaxX = m_nXMax;
    4721           0 :     poRectHdr->m_nMaxY = m_nYMax;
    4722             : 
    4723           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    4724           0 :     poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    4725             : 
    4726           0 :     m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    4727           0 :     poRectHdr->m_nBrushId =
    4728           0 :         static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    4729             : 
    4730           0 :     if (CPLGetLastErrorType() == CE_Failure)
    4731           0 :         return -1;
    4732             : 
    4733           0 :     return 0;
    4734             : }
    4735             : 
    4736             : /**********************************************************************
    4737             :  *                   TABEllipse::GetStyleString() const
    4738             :  *
    4739             :  * Return style string for this feature.
    4740             :  *
    4741             :  * Style String is built only once during the first call to GetStyleString().
    4742             :  **********************************************************************/
    4743           9 : const char *TABEllipse::GetStyleString() const
    4744             : {
    4745           9 :     if (m_pszStyleString == nullptr)
    4746             :     {
    4747             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    4748             :         // to use temporary buffers
    4749           5 :         char *pszPen = CPLStrdup(GetPenStyleString());
    4750           5 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    4751             : 
    4752           5 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    4753             : 
    4754           5 :         CPLFree(pszPen);
    4755           5 :         CPLFree(pszBrush);
    4756             :     }
    4757             : 
    4758           9 :     return m_pszStyleString;
    4759             : }
    4760             : 
    4761             : /**********************************************************************
    4762             :  *                   TABEllipse::DumpMIF()
    4763             :  *
    4764             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    4765             :  **********************************************************************/
    4766           0 : void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
    4767             : {
    4768           0 :     if (fpOut == nullptr)
    4769           0 :         fpOut = stdout;
    4770             : 
    4771             :     /*-----------------------------------------------------------------
    4772             :      * Output ELLIPSE parameters
    4773             :      *----------------------------------------------------------------*/
    4774           0 :     double dXMin = 0.0;
    4775           0 :     double dYMin = 0.0;
    4776           0 :     double dXMax = 0.0;
    4777           0 :     double dYMax = 0.0;
    4778           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4779           0 :     fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
    4780             :             dYMax);
    4781             : 
    4782             :     /*-----------------------------------------------------------------
    4783             :      * Fetch and validate geometry
    4784             :      *----------------------------------------------------------------*/
    4785           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4786           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4787             :     {
    4788             :         /*-------------------------------------------------------------
    4789             :          * Generate ellipse output as a region
    4790             :          * We could also output as an ELLIPSE in a real MIF generator
    4791             :          *------------------------------------------------------------*/
    4792           0 :         OGRPolygon *poPolygon = poGeom->toPolygon();
    4793           0 :         int numIntRings = poPolygon->getNumInteriorRings();
    4794           0 :         fprintf(fpOut, "REGION %d\n", numIntRings + 1);
    4795             :         // In this loop, iRing=-1 for the outer ring.
    4796           0 :         for (int iRing = -1; iRing < numIntRings; iRing++)
    4797             :         {
    4798           0 :             OGRLinearRing *poRing = nullptr;
    4799             : 
    4800           0 :             if (iRing == -1)
    4801           0 :                 poRing = poPolygon->getExteriorRing();
    4802             :             else
    4803           0 :                 poRing = poPolygon->getInteriorRing(iRing);
    4804             : 
    4805           0 :             if (poRing == nullptr)
    4806             :             {
    4807           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    4808             :                          "TABEllipse: Object Geometry contains NULL rings!");
    4809           0 :                 return;
    4810             :             }
    4811             : 
    4812           0 :             int numPoints = poRing->getNumPoints();
    4813           0 :             fprintf(fpOut, " %d\n", numPoints);
    4814           0 :             for (int i = 0; i < numPoints; i++)
    4815           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    4816             :                         poRing->getY(i));
    4817             :         }
    4818             :     }
    4819             :     else
    4820             :     {
    4821           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4822             :                  "TABEllipse: Missing or Invalid Geometry!");
    4823           0 :         return;
    4824             :     }
    4825             : 
    4826             :     // Finish with PEN/BRUSH/etc. clauses
    4827           0 :     DumpPenDef();
    4828           0 :     DumpBrushDef();
    4829             : 
    4830           0 :     fflush(fpOut);
    4831             : }
    4832             : 
    4833             : /*=====================================================================
    4834             :  *                      class TABArc
    4835             :  *====================================================================*/
    4836             : 
    4837             : /**********************************************************************
    4838             :  *                   TABArc::TABArc()
    4839             :  *
    4840             :  * Constructor.
    4841             :  **********************************************************************/
    4842         680 : TABArc::TABArc(OGRFeatureDefn *poDefnIn)
    4843             :     : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
    4844         680 :       m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
    4845             : {
    4846         680 : }
    4847             : 
    4848             : /**********************************************************************
    4849             :  *                   TABArc::~TABArc()
    4850             :  *
    4851             :  * Destructor.
    4852             :  **********************************************************************/
    4853        1360 : TABArc::~TABArc()
    4854             : {
    4855        1360 : }
    4856             : 
    4857             : /**********************************************************************
    4858             :  *                     TABArc::CloneTABFeature()
    4859             :  *
    4860             :  * Duplicate feature, including stuff specific to each TABFeature type.
    4861             :  *
    4862             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    4863             :  * then copies any members specific to its own type.
    4864             :  **********************************************************************/
    4865           0 : TABFeature *TABArc::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    4866             : {
    4867             :     /*-----------------------------------------------------------------
    4868             :      * Alloc new feature and copy the base stuff
    4869             :      *----------------------------------------------------------------*/
    4870           0 :     TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
    4871             : 
    4872           0 :     CopyTABFeatureBase(poNew);
    4873             : 
    4874             :     /*-----------------------------------------------------------------
    4875             :      * And members specific to this class
    4876             :      *----------------------------------------------------------------*/
    4877             :     // ITABFeaturePen
    4878           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4879             : 
    4880           0 :     poNew->SetStartAngle(GetStartAngle());
    4881           0 :     poNew->SetEndAngle(GetEndAngle());
    4882             : 
    4883           0 :     poNew->m_dCenterX = m_dCenterX;
    4884           0 :     poNew->m_dCenterY = m_dCenterY;
    4885           0 :     poNew->m_dXRadius = m_dXRadius;
    4886           0 :     poNew->m_dYRadius = m_dYRadius;
    4887             : 
    4888           0 :     return poNew;
    4889             : }
    4890             : 
    4891             : /**********************************************************************
    4892             :  *                   TABArc::ValidateMapInfoType()
    4893             :  *
    4894             :  * Check the feature's geometry part and return the corresponding
    4895             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4896             :  * be updated for further calls to GetMapInfoType();
    4897             :  *
    4898             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4899             :  * is expected for this object class.
    4900             :  **********************************************************************/
    4901           0 : TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4902             : {
    4903             :     /*-----------------------------------------------------------------
    4904             :      * Fetch and validate geometry
    4905             :      *----------------------------------------------------------------*/
    4906           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4907           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
    4908           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4909             :     {
    4910           0 :         m_nMapInfoType = TAB_GEOM_ARC;
    4911             :     }
    4912             :     else
    4913             :     {
    4914           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4915             :                  "TABArc: Missing or Invalid Geometry!");
    4916           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4917             :     }
    4918             : 
    4919             :     /*-----------------------------------------------------------------
    4920             :      * Decide if coordinates should be compressed or not.
    4921             :      *----------------------------------------------------------------*/
    4922             :     // __TODO__ For now we always write uncompressed for this class...
    4923             :     // ValidateCoordType(poMapFile);
    4924           0 :     UpdateMBR(poMapFile);
    4925             : 
    4926           0 :     return m_nMapInfoType;
    4927             : }
    4928             : 
    4929             : /**********************************************************************
    4930             :  *                   TABArc::UpdateMBR()
    4931             :  *
    4932             :  * Update the feature MBR members using the geometry
    4933             :  *
    4934             :  * Returns 0 on success, or -1 if there is no geometry in object
    4935             :  **********************************************************************/
    4936           0 : int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4937             : {
    4938           0 :     OGREnvelope sEnvelope;
    4939             : 
    4940           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4941           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
    4942             :     {
    4943             :         /*-------------------------------------------------------------
    4944             :          * POLYGON geometry:
    4945             :          * Note that we will simply use the ellipse's MBR and don't really
    4946             :          * read the polygon geometry... this should be OK unless the
    4947             :          * polygon geometry was not really an ellipse.
    4948             :          * In the case of a polygon geometry. the m_dCenterX/Y values MUST
    4949             :          * have been set by the caller.
    4950             :          *------------------------------------------------------------*/
    4951           0 :         poGeom->getEnvelope(&sEnvelope);
    4952             :     }
    4953           0 :     else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4954             :     {
    4955             :         /*-------------------------------------------------------------
    4956             :          * In the case of a POINT GEOMETRY, we will make sure the
    4957             :          * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
    4958             :          *
    4959             :          * In this case we have to reconstruct the arc inside a temporary
    4960             :          * geometry object in order to find its real MBR.
    4961             :          *------------------------------------------------------------*/
    4962           0 :         OGRPoint *poPoint = poGeom->toPoint();
    4963           0 :         m_dCenterX = poPoint->getX();
    4964           0 :         m_dCenterY = poPoint->getY();
    4965             : 
    4966           0 :         OGRLineString oTmpLine;
    4967           0 :         int numPts = 0;
    4968           0 :         if (m_dEndAngle < m_dStartAngle)
    4969           0 :             numPts = static_cast<int>(
    4970           0 :                 std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
    4971             :         else
    4972           0 :             numPts = static_cast<int>(
    4973           0 :                 std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
    4974           0 :         numPts = std::max(2, numPts);
    4975             : 
    4976           0 :         TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    4977           0 :                        m_dYRadius, m_dStartAngle * M_PI / 180.0,
    4978           0 :                        m_dEndAngle * M_PI / 180.0);
    4979             : 
    4980           0 :         oTmpLine.getEnvelope(&sEnvelope);
    4981             :     }
    4982             :     else
    4983             :     {
    4984           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4985             :                  "TABArc: Missing or Invalid Geometry!");
    4986           0 :         return -1;
    4987             :     }
    4988             : 
    4989             :     // Update the Arc's MBR
    4990           0 :     m_dXMin = sEnvelope.MinX;
    4991           0 :     m_dYMin = sEnvelope.MinY;
    4992           0 :     m_dXMax = sEnvelope.MaxX;
    4993           0 :     m_dYMax = sEnvelope.MaxY;
    4994             : 
    4995           0 :     if (poMapFile)
    4996             :     {
    4997           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4998           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4999             :     }
    5000             : 
    5001           0 :     return 0;
    5002             : }
    5003             : 
    5004             : /**********************************************************************
    5005             :  *                   TABArc::ReadGeometryFromMAPFile()
    5006             :  *
    5007             :  * Fill the geometry and representation (color, etc...) part of the
    5008             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    5009             :  *
    5010             :  * It is assumed that poMAPFile currently points to the beginning of
    5011             :  * a map object.
    5012             :  *
    5013             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5014             :  * been called.
    5015             :  **********************************************************************/
    5016           8 : int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
    5017             :                                     TABMAPObjHdr *poObjHdr,
    5018             :                                     GBool bCoordBlockDataOnly /*=FALSE*/,
    5019             :                                     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    5020             : {
    5021             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    5022           8 :     if (bCoordBlockDataOnly)
    5023           0 :         return 0;
    5024             : 
    5025             :     /*-----------------------------------------------------------------
    5026             :      * Fetch and validate geometry type
    5027             :      *----------------------------------------------------------------*/
    5028           8 :     m_nMapInfoType = poObjHdr->m_nType;
    5029             : 
    5030           8 :     if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
    5031             :     {
    5032           0 :         CPLError(
    5033             :             CE_Failure, CPLE_AssertionFailed,
    5034             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    5035           0 :             m_nMapInfoType, m_nMapInfoType);
    5036           0 :         return -1;
    5037             :     }
    5038             : 
    5039             :     /*-----------------------------------------------------------------
    5040             :      * Read object information
    5041             :      *----------------------------------------------------------------*/
    5042           8 :     TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
    5043             : 
    5044             :     /*-------------------------------------------------------------
    5045             :      * Start/End angles
    5046             :      * Since the angles are specified for integer coordinates, and
    5047             :      * that these coordinates can have the X axis reversed, we have to
    5048             :      * adjust the angle values for the change in the X axis
    5049             :      * direction.
    5050             :      *
    5051             :      * This should be necessary only when X axis is flipped.
    5052             :      * __TODO__ Why is order of start/end values reversed as well???
    5053             :      *------------------------------------------------------------*/
    5054             : 
    5055             :     /*-------------------------------------------------------------
    5056             :      * OK, Arc angles again!!!!!!!!!!!!
    5057             :      * After some tests in 1999-11, it appeared that the angle values
    5058             :      * ALWAYS had to be flipped (read order= end angle followed by
    5059             :      * start angle), no matter which quadrant the file is in.
    5060             :      * This does not make any sense, so I suspect that there is something
    5061             :      * that we are missing here!
    5062             :      *
    5063             :      * 2000-01-14.... Again!!!  Based on some sample data files:
    5064             :      *  File         Ver Quadr  ReflXAxis  Read_Order   Adjust_Angle
    5065             :      * test_symb.tab 300    2        1      end,start    X=yes Y=no
    5066             :      * alltypes.tab: 300    1        0      start,end    X=no  Y=no
    5067             :      * arcs.tab:     300    2        0      end,start    X=yes Y=no
    5068             :      *
    5069             :      * Until we prove it wrong, the rule would be:
    5070             :      *  -> Quadrant 1 and 3, angles order = start, end
    5071             :      *  -> Quadrant 2 and 4, angles order = end, start
    5072             :      * + Always adjust angles for x and y axis based on quadrant.
    5073             :      *
    5074             :      * This was confirmed using some more files in which the quadrant was
    5075             :      * manually changed, but whether these are valid results is
    5076             :      * disputable.
    5077             :      *
    5078             :      * The ReflectXAxis flag seems to have no effect here...
    5079             :      *------------------------------------------------------------*/
    5080             : 
    5081             :     /*-------------------------------------------------------------
    5082             :      * In version 100 .tab files (version 400 .map), it is possible
    5083             :      * to have a quadrant value of 0 and it should be treated the
    5084             :      * same way as quadrant 3
    5085             :      *------------------------------------------------------------*/
    5086           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
    5087           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5088           0 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5089             :     {
    5090             :         // Quadrants 1 and 3 ... read order = start, end
    5091           8 :         m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
    5092           8 :         m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
    5093             :     }
    5094             :     else
    5095             :     {
    5096             :         // Quadrants 2 and 4 ... read order = end, start
    5097           0 :         m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
    5098           0 :         m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
    5099             :     }
    5100             : 
    5101           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
    5102          16 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5103           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5104             :     {
    5105             :         // X axis direction is flipped... adjust angle
    5106           0 :         m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
    5107           0 :                                                  : (540.0 - m_dStartAngle);
    5108           0 :         m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
    5109           0 :                                              : (540.0 - m_dEndAngle);
    5110             :     }
    5111             : 
    5112           8 :     if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
    5113             :     {
    5114           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5115             :                  "Wrong start and end angles: %f %f", m_dStartAngle,
    5116             :                  m_dEndAngle);
    5117           0 :         return -1;
    5118             :     }
    5119             : 
    5120           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5121          16 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
    5122           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5123             :     {
    5124             :         // Y axis direction is flipped... this reverses angle direction
    5125             :         // Unfortunately we never found any file that contains this case,
    5126             :         // but this should be the behavior to expect!!!
    5127             :         //
    5128             :         // 2000-01-14: some files in which quadrant was set to 3 and 4
    5129             :         // manually seemed to confirm that this is the right thing to do.
    5130           0 :         m_dStartAngle = 360.0 - m_dStartAngle;
    5131           0 :         m_dEndAngle = 360.0 - m_dEndAngle;
    5132             :     }
    5133             : 
    5134             :     // An arc is defined by its defining ellipse's MBR:
    5135             : 
    5136           8 :     double dXMin = 0.0;
    5137           8 :     double dYMin = 0.0;
    5138           8 :     double dXMax = 0.0;
    5139           8 :     double dYMax = 0.0;
    5140             : 
    5141           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
    5142             :                             poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
    5143           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
    5144             :                             poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
    5145             : 
    5146           8 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    5147           8 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    5148           8 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    5149           8 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    5150             : 
    5151             :     // Read the Arc's MBR and use that as this feature's MBR
    5152           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
    5153           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
    5154           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    5155             : 
    5156           8 :     m_nPenDefIndex = poArcHdr->m_nPenId;  // Pen index
    5157           8 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    5158             : 
    5159             :     /*-----------------------------------------------------------------
    5160             :      * Create and fill geometry object
    5161             :      * For the OGR geometry, we generate an arc with 2 degrees line
    5162             :      * segments.
    5163             :      *----------------------------------------------------------------*/
    5164           8 :     OGRLineString *poLine = new OGRLineString;
    5165             : 
    5166             :     const int numPts = std::max(
    5167          16 :         2,
    5168           8 :         (m_dEndAngle < m_dStartAngle
    5169           8 :              ? static_cast<int>(
    5170           0 :                    std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
    5171           8 :              : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
    5172           8 :                                 1)));
    5173             : 
    5174           8 :     TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    5175           8 :                    m_dYRadius, m_dStartAngle * M_PI / 180.0,
    5176           8 :                    m_dEndAngle * M_PI / 180.0);
    5177             : 
    5178           8 :     SetGeometryDirectly(poLine);
    5179             : 
    5180           8 :     return 0;
    5181             : }
    5182             : 
    5183             : /**********************************************************************
    5184             :  *                   TABArc::WriteGeometryToMAPFile()
    5185             :  *
    5186             :  * Write the geometry and representation (color, etc...) part of the
    5187             :  * feature to the .MAP object pointed to by poMAPFile.
    5188             :  *
    5189             :  * It is assumed that poMAPFile currently points to a valid map object.
    5190             :  *
    5191             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5192             :  * been called.
    5193             :  **********************************************************************/
    5194           0 : int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    5195             :                                    TABMAPObjHdr *poObjHdr,
    5196             :                                    GBool bCoordBlockDataOnly /*=FALSE*/,
    5197             :                                    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    5198             : {
    5199             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    5200           0 :     if (bCoordBlockDataOnly)
    5201           0 :         return 0;
    5202             : 
    5203             :     /*-----------------------------------------------------------------
    5204             :      * We assume that ValidateMapInfoType() was called already and that
    5205             :      * the type in poObjHdr->m_nType is valid.
    5206             :      *----------------------------------------------------------------*/
    5207           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    5208             : 
    5209             :     /*-----------------------------------------------------------------
    5210             :      * Fetch and validate geometry
    5211             :      * In the case of ARCs, this is all done inside UpdateMBR()
    5212             :      *----------------------------------------------------------------*/
    5213           0 :     if (UpdateMBR(poMapFile) != 0)
    5214           0 :         return -1; /* Error already reported */
    5215             : 
    5216             :     /*-----------------------------------------------------------------
    5217             :      * Copy object information
    5218             :      *----------------------------------------------------------------*/
    5219           0 :     TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
    5220             : 
    5221             :     /*-------------------------------------------------------------
    5222             :      * Start/End angles
    5223             :      * Since we ALWAYS produce files in quadrant 1 then we can
    5224             :      * ignore the special angle conversion required by flipped axis.
    5225             :      *
    5226             :      * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
    5227             :      *------------------------------------------------------------*/
    5228           0 :     CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
    5229             : 
    5230           0 :     poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
    5231           0 :     poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
    5232             : 
    5233             :     // An arc is defined by its defining ellipse's MBR:
    5234           0 :     poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
    5235           0 :                             poArcHdr->m_nArcEllipseMinX,
    5236           0 :                             poArcHdr->m_nArcEllipseMinY);
    5237           0 :     poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
    5238           0 :                             poArcHdr->m_nArcEllipseMaxX,
    5239           0 :                             poArcHdr->m_nArcEllipseMaxY);
    5240             : 
    5241             :     // Pass the Arc's actual MBR (values were set in UpdateMBR())
    5242           0 :     poArcHdr->m_nMinX = m_nXMin;
    5243           0 :     poArcHdr->m_nMinY = m_nYMin;
    5244           0 :     poArcHdr->m_nMaxX = m_nXMax;
    5245           0 :     poArcHdr->m_nMaxY = m_nYMax;
    5246             : 
    5247           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    5248           0 :     poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    5249             : 
    5250           0 :     if (CPLGetLastErrorType() == CE_Failure)
    5251           0 :         return -1;
    5252             : 
    5253           0 :     return 0;
    5254             : }
    5255             : 
    5256             : /**********************************************************************
    5257             :  *                   TABArc::SetStart/EndAngle()
    5258             :  *
    5259             :  * Set the start/end angle values in degrees, making sure the values are
    5260             :  * always in the range [0..360]
    5261             :  **********************************************************************/
    5262           0 : void TABArc::SetStartAngle(double dAngle)
    5263             : {
    5264           0 :     dAngle = fmod(dAngle, 360.0);
    5265           0 :     if (dAngle < 0.0)
    5266           0 :         dAngle += 360.0;
    5267             : 
    5268           0 :     m_dStartAngle = dAngle;
    5269           0 : }
    5270             : 
    5271           0 : void TABArc::SetEndAngle(double dAngle)
    5272             : {
    5273           0 :     dAngle = fmod(dAngle, 360.0);
    5274           0 :     if (dAngle < 0.0)
    5275           0 :         dAngle += 360.0;
    5276             : 
    5277           0 :     m_dEndAngle = dAngle;
    5278           0 : }
    5279             : 
    5280             : /**********************************************************************
    5281             :  *                   TABArc::GetStyleString() const
    5282             :  *
    5283             :  * Return style string for this feature.
    5284             :  *
    5285             :  * Style String is built only once during the first call to GetStyleString().
    5286             :  **********************************************************************/
    5287          14 : const char *TABArc::GetStyleString() const
    5288             : {
    5289          14 :     if (m_pszStyleString == nullptr)
    5290             :     {
    5291          10 :         m_pszStyleString = CPLStrdup(GetPenStyleString());
    5292             :     }
    5293             : 
    5294          14 :     return m_pszStyleString;
    5295             : }
    5296             : 
    5297             : /**********************************************************************
    5298             :  *                   TABArc::DumpMIF()
    5299             :  *
    5300             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    5301             :  **********************************************************************/
    5302           0 : void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
    5303             : {
    5304           0 :     if (fpOut == nullptr)
    5305           0 :         fpOut = stdout;
    5306             : 
    5307             :     /*-----------------------------------------------------------------
    5308             :      * Output ARC parameters
    5309             :      *----------------------------------------------------------------*/
    5310           0 :     fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g   %d %d)\n",
    5311           0 :             m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
    5312           0 :             m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
    5313           0 :             static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
    5314             : 
    5315             :     /*-----------------------------------------------------------------
    5316             :      * Fetch and validate geometry
    5317             :      *----------------------------------------------------------------*/
    5318           0 :     OGRGeometry *poGeom = GetGeometryRef();
    5319           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    5320             :     {
    5321             :         /*-------------------------------------------------------------
    5322             :          * Generate arc output as a simple polyline
    5323             :          * We could also output as an ELLIPSE in a real MIF generator
    5324             :          *------------------------------------------------------------*/
    5325           0 :         OGRLineString *poLine = poGeom->toLineString();
    5326           0 :         const int numPoints = poLine->getNumPoints();
    5327           0 :         fprintf(fpOut, "PLINE %d\n", numPoints);
    5328           0 :         for (int i = 0; i < numPoints; i++)
    5329           0 :             fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
    5330             :     }
    5331             :     else
    5332             :     {
    5333           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5334             :                  "TABArc: Missing or Invalid Geometry!");
    5335           0 :         return;
    5336             :     }
    5337             : 
    5338             :     // Finish with PEN/BRUSH/etc. clauses
    5339           0 :     DumpPenDef();
    5340             : 
    5341           0 :     fflush(fpOut);
    5342             : }
    5343             : 
    5344             : /*=====================================================================
    5345             :  *                      class TABText
    5346             :  *====================================================================*/
    5347             : 
    5348             : /**********************************************************************
    5349             :  *                   TABText::TABText()
    5350             :  *
    5351             :  * Constructor.
    5352             :  **********************************************************************/
    5353         309 : TABText::TABText(OGRFeatureDefn *poDefnIn)
    5354             :     : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
    5355             :       m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
    5356             :       m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
    5357             :       m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
    5358         309 :       m_nFontStyle(0)
    5359             : {
    5360         309 : }
    5361             : 
    5362             : /**********************************************************************
    5363             :  *                   TABText::~TABText()
    5364             :  *
    5365             :  * Destructor.
    5366             :  **********************************************************************/
    5367         618 : TABText::~TABText()
    5368             : {
    5369         309 :     CPLFree(m_pszString);
    5370         618 : }
    5371             : 
    5372             : /**********************************************************************
    5373             :  *                     TABText::CloneTABFeature()
    5374             :  *
    5375             :  * Duplicate feature, including stuff specific to each TABFeature type.
    5376             :  *
    5377             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    5378             :  * then copies any members specific to its own type.
    5379             :  **********************************************************************/
    5380           0 : TABFeature *TABText::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    5381             : {
    5382             :     /*-----------------------------------------------------------------
    5383             :      * Alloc new feature and copy the base stuff
    5384             :      *----------------------------------------------------------------*/
    5385           0 :     TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
    5386             : 
    5387           0 :     CopyTABFeatureBase(poNew);
    5388             : 
    5389             :     /*-----------------------------------------------------------------
    5390             :      * And members specific to this class
    5391             :      *----------------------------------------------------------------*/
    5392             :     // ITABFeaturePen
    5393           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    5394             : 
    5395             :     // ITABFeatureFont
    5396           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    5397             : 
    5398           0 :     poNew->SetTextString(GetTextString());
    5399           0 :     poNew->SetTextAngle(GetTextAngle());
    5400           0 :     poNew->SetTextBoxHeight(GetTextBoxHeight());
    5401           0 :     poNew->SetTextBoxWidth(GetTextBoxWidth());
    5402           0 :     poNew->SetFontStyleTABValue(GetFontStyleTABValue());
    5403           0 :     poNew->SetFontBGColor(GetFontBGColor());
    5404           0 :     poNew->SetFontFGColor(GetFontFGColor());
    5405           0 :     poNew->SetFontOColor(GetFontOColor());
    5406           0 :     poNew->SetFontSColor(GetFontSColor());
    5407             : 
    5408           0 :     poNew->SetTextJustification(GetTextJustification());
    5409           0 :     poNew->SetTextSpacing(GetTextSpacing());
    5410             :     // Note: Text arrow/line coordinates are not transported... but
    5411             :     //       we ignore them most of the time anyways.
    5412           0 :     poNew->SetTextLineType(TABTLNoLine);
    5413             : 
    5414           0 :     return poNew;
    5415             : }
    5416             : 
    5417             : /**********************************************************************
    5418             :  *                   TABText::ValidateMapInfoType()
    5419             :  *
    5420             :  * Check the feature's geometry part and return the corresponding
    5421             :  * mapinfo object type code.  The m_nMapInfoType member will also
    5422             :  * be updated for further calls to GetMapInfoType();
    5423             :  *
    5424             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    5425             :  * is expected for this object class.
    5426             :  **********************************************************************/
    5427           4 : TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    5428             : {
    5429             :     /*-----------------------------------------------------------------
    5430             :      * Fetch and validate geometry
    5431             :      *----------------------------------------------------------------*/
    5432           4 :     OGRGeometry *poGeom = GetGeometryRef();
    5433           4 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5434             :     {
    5435           4 :         m_nMapInfoType = TAB_GEOM_TEXT;
    5436             :     }
    5437             :     else
    5438             :     {
    5439           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5440             :                  "TABText: Missing or Invalid Geometry!");
    5441           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    5442             :     }
    5443             : 
    5444             :     /*-----------------------------------------------------------------
    5445             :      * Decide if coordinates should be compressed or not.
    5446             :      *----------------------------------------------------------------*/
    5447             :     // __TODO__ For now we always write uncompressed for this class...
    5448             :     // ValidateCoordType(poMapFile);
    5449           4 :     UpdateMBR(poMapFile);
    5450             : 
    5451           4 :     return m_nMapInfoType;
    5452             : }
    5453             : 
    5454             : /**********************************************************************
    5455             :  *                   TABText::ReadGeometryFromMAPFile()
    5456             :  *
    5457             :  * Fill the geometry and representation (color, etc...) part of the
    5458             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    5459             :  *
    5460             :  * It is assumed that poMAPFile currently points to the beginning of
    5461             :  * a map object.
    5462             :  *
    5463             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5464             :  * been called.
    5465             :  **********************************************************************/
    5466           8 : int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
    5467             :                                      TABMAPObjHdr *poObjHdr,
    5468             :                                      GBool bCoordBlockDataOnly /*=FALSE*/,
    5469             :                                      TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    5470             : {
    5471             :     /*-----------------------------------------------------------------
    5472             :      * Fetch and validate geometry type
    5473             :      *----------------------------------------------------------------*/
    5474           8 :     m_nMapInfoType = poObjHdr->m_nType;
    5475             : 
    5476           8 :     if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
    5477             :     {
    5478           0 :         CPLError(
    5479             :             CE_Failure, CPLE_AssertionFailed,
    5480             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    5481           0 :             m_nMapInfoType, m_nMapInfoType);
    5482           0 :         return -1;
    5483             :     }
    5484             : 
    5485             :     /*=============================================================
    5486             :      * TEXT
    5487             :      *============================================================*/
    5488             : 
    5489             :     /*-----------------------------------------------------------------
    5490             :      * Read object information
    5491             :      *----------------------------------------------------------------*/
    5492           8 :     TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
    5493             : 
    5494           8 :     const GInt32 nCoordBlockPtr =
    5495             :         poTextHdr->m_nCoordBlockPtr;                     // String position
    5496           8 :     const int nStringLen = poTextHdr->m_nCoordDataSize;  // String length
    5497           8 :     m_nTextAlignment = poTextHdr->m_nTextAlignment;      // just./spacing/arrow
    5498             : 
    5499             :     /*-------------------------------------------------------------
    5500             :      * Text Angle, in tenths of degree.
    5501             :      * Contrary to arc start/end angles, no conversion based on
    5502             :      * origin quadrant is required here.
    5503             :      *------------------------------------------------------------*/
    5504           8 :     m_dAngle = poTextHdr->m_nAngle / 10.0;
    5505             : 
    5506           8 :     m_nFontStyle = poTextHdr->m_nFontStyle;  // Font style
    5507             : 
    5508           8 :     m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
    5509           8 :                        poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
    5510           8 :     m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
    5511           8 :                        poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
    5512           8 :     m_rgbOutline = m_rgbBackground;
    5513             :     // In MapInfo, the shadow color is always gray (128,128,128)
    5514           8 :     m_rgbShadow = 0x808080;
    5515             : 
    5516             :     // arrow endpoint
    5517           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
    5518           8 :                             m_dfLineEndX, m_dfLineEndY);
    5519           8 :     m_bLineEndSet = TRUE;
    5520             : 
    5521             :     // Text Height
    5522           8 :     double dJunk = 0.0;
    5523           8 :     poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
    5524             : 
    5525           8 :     if (!bCoordBlockDataOnly)
    5526             :     {
    5527           8 :         m_nFontDefIndex = poTextHdr->m_nFontId;  // Font name index
    5528           8 :         poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    5529             :     }
    5530             : 
    5531             :     // MBR after rotation
    5532           8 :     double dXMin = 0.0;
    5533           8 :     double dYMin = 0.0;
    5534           8 :     double dXMax = 0.0;
    5535           8 :     double dYMax = 0.0;
    5536           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
    5537             :                             dYMin);
    5538           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
    5539             :                             dYMax);
    5540             : 
    5541           8 :     if (!bCoordBlockDataOnly)
    5542             :     {
    5543           8 :         m_nPenDefIndex = poTextHdr->m_nPenId;  // Pen index for line
    5544           8 :         poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    5545             :     }
    5546             : 
    5547             :     /*-------------------------------------------------------------
    5548             :      * Read text string from the coord. block
    5549             :      * Note that the string may contain binary '\n' and '\\' chars
    5550             :      * that we keep to an unescaped form internally. This is to
    5551             :      * be like OGR drivers. See bug 1107 for details.
    5552             :      *------------------------------------------------------------*/
    5553             :     char *pszTmpString =
    5554           8 :         static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
    5555             : 
    5556           8 :     if (nStringLen > 0)
    5557             :     {
    5558           8 :         TABMAPCoordBlock *poCoordBlock = nullptr;
    5559             : 
    5560           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    5561           0 :             poCoordBlock = *ppoCoordBlock;
    5562             :         else
    5563           8 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    5564          16 :         if (poCoordBlock == nullptr ||
    5565           8 :             poCoordBlock->ReadBytes(
    5566             :                 nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
    5567             :         {
    5568           0 :             CPLError(CE_Failure, CPLE_FileIO,
    5569             :                      "Failed reading text string at offset %d", nCoordBlockPtr);
    5570           0 :             CPLFree(pszTmpString);
    5571           0 :             return -1;
    5572             :         }
    5573             : 
    5574             :         /* Return a ref to coord block so that caller can continue reading
    5575             :          * after the end of this object (used by index splitting)
    5576             :          */
    5577           8 :         if (ppoCoordBlock)
    5578           0 :             *ppoCoordBlock = poCoordBlock;
    5579             :     }
    5580             : 
    5581           8 :     pszTmpString[nStringLen] = '\0';
    5582             : 
    5583           8 :     if (!poMapFile->GetEncoding().empty())
    5584             :     {
    5585             :         char *pszUtf8String =
    5586           1 :             CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
    5587           1 :         CPLFree(pszTmpString);
    5588           1 :         pszTmpString = pszUtf8String;
    5589             :     }
    5590             : 
    5591           8 :     CPLFree(m_pszString);
    5592           8 :     m_pszString = pszTmpString;  // This string was Escaped before 20050714
    5593             : 
    5594             :     /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
    5595             :      */
    5596           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    5597           8 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    5598             : 
    5599             :     /* Copy int MBR to feature class members */
    5600           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    5601             :               poObjHdr->m_nMaxY);
    5602             : 
    5603             :     /*-----------------------------------------------------------------
    5604             :      * Create an OGRPoint Geometry...
    5605             :      * The point X,Y values will be the coords of the lower-left corner before
    5606             :      * rotation is applied.  (Note that the rotation in MapInfo is done around
    5607             :      * the upper-left corner)
    5608             :      * We need to calculate the true lower left corner of the text based
    5609             :      * on the MBR after rotation, the text height and the rotation angle.
    5610             :      *----------------------------------------------------------------*/
    5611           8 :     double dSin = sin(m_dAngle * M_PI / 180.0);
    5612           8 :     double dCos = cos(m_dAngle * M_PI / 180.0);
    5613           8 :     double dX = 0.0;
    5614           8 :     double dY = 0.0;
    5615           8 :     if (dSin > 0.0 && dCos > 0.0)
    5616             :     {
    5617           7 :         dX = dXMin + m_dHeight * dSin;
    5618           7 :         dY = dYMin;
    5619             :     }
    5620           1 :     else if (dSin > 0.0 && dCos < 0.0)
    5621             :     {
    5622           0 :         dX = dXMax;
    5623           0 :         dY = dYMin - m_dHeight * dCos;
    5624             :     }
    5625           1 :     else if (dSin < 0.0 && dCos < 0.0)
    5626             :     {
    5627           0 :         dX = dXMax + m_dHeight * dSin;
    5628           0 :         dY = dYMax;
    5629             :     }
    5630             :     else  // dSin < 0 && dCos > 0
    5631             :     {
    5632           1 :         dX = dXMin;
    5633           1 :         dY = dYMax - m_dHeight * dCos;
    5634             :     }
    5635             : 
    5636           8 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    5637             : 
    5638           8 :     SetGeometryDirectly(poGeometry);
    5639             : 
    5640             :     /*-----------------------------------------------------------------
    5641             :      * Compute Text Width: the width of the Text MBR before rotation
    5642             :      * in ground units... unfortunately this value is not stored in the
    5643             :      * file, so we have to compute it with the MBR after rotation and
    5644             :      * the height of the MBR before rotation:
    5645             :      * With  W = Width of MBR before rotation
    5646             :      *       H = Height of MBR before rotation
    5647             :      *       dX = Width of MBR after rotation
    5648             :      *       dY = Height of MBR after rotation
    5649             :      *       teta = rotation angle
    5650             :      *
    5651             :      *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
    5652             :      *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
    5653             :      *
    5654             :      * and for other teta values, use:
    5655             :      *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
    5656             :      *----------------------------------------------------------------*/
    5657           8 :     dSin = std::abs(dSin);
    5658           8 :     dCos = std::abs(dCos);
    5659           8 :     if (m_dHeight == 0.0)
    5660           0 :         m_dWidth = 0.0;
    5661           8 :     else if (dCos > dSin)
    5662           8 :         m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
    5663           8 :                    (m_dHeight * dCos);
    5664             :     else
    5665           0 :         m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
    5666           0 :                    (m_dHeight * dSin);
    5667           8 :     m_dWidth = std::abs(m_dWidth);
    5668             : 
    5669           8 :     return 0;
    5670             : }
    5671             : 
    5672             : /**********************************************************************
    5673             :  *                   TABText::WriteGeometryToMAPFile()
    5674             :  *
    5675             :  * Write the geometry and representation (color, etc...) part of the
    5676             :  * feature to the .MAP object pointed to by poMAPFile.
    5677             :  *
    5678             :  * It is assumed that poMAPFile currently points to a valid map object.
    5679             :  *
    5680             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5681             :  * been called.
    5682             :  **********************************************************************/
    5683           4 : int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    5684             :                                     TABMAPObjHdr *poObjHdr,
    5685             :                                     GBool bCoordBlockDataOnly /*=FALSE*/,
    5686             :                                     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    5687             : {
    5688             :     GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
    5689             : 
    5690             :     /*-----------------------------------------------------------------
    5691             :      * We assume that ValidateMapInfoType() was called already and that
    5692             :      * the type in poObjHdr->m_nType is valid.
    5693             :      *----------------------------------------------------------------*/
    5694           4 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    5695             : 
    5696             :     /*-----------------------------------------------------------------
    5697             :      * Fetch and validate geometry
    5698             :      *----------------------------------------------------------------*/
    5699           4 :     OGRGeometry *poGeom = GetGeometryRef();
    5700           4 :     OGRPoint *poPoint = nullptr;
    5701           4 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5702           4 :         poPoint = poGeom->toPoint();
    5703             :     else
    5704             :     {
    5705           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5706             :                  "TABText: Missing or Invalid Geometry!");
    5707           0 :         return -1;
    5708             :     }
    5709             : 
    5710           4 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    5711             : 
    5712             :     /*-----------------------------------------------------------------
    5713             :      * Write string to a coord block first...
    5714             :      * Note that the string may contain unescaped '\n' and '\\'
    5715             :      * that we have to keep like that for the MAP file.
    5716             :      * See MapTools bug 1107 for more details.
    5717             :      *----------------------------------------------------------------*/
    5718           4 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    5719           4 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    5720           0 :         poCoordBlock = *ppoCoordBlock;
    5721             :     else
    5722           4 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    5723           4 :     poCoordBlock->StartNewFeature();
    5724           4 :     GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    5725             : 
    5726             :     // This string was escaped before 20050714
    5727           8 :     CPLString oTmpString(m_pszString ? m_pszString : "");
    5728           4 :     if (!poMapFile->GetEncoding().empty())
    5729             :     {
    5730           0 :         oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
    5731             :     }
    5732             : 
    5733           4 :     int nStringLen = static_cast<int>(oTmpString.length());
    5734             : 
    5735           4 :     if (nStringLen > 0)
    5736             :     {
    5737           3 :         poCoordBlock->WriteBytes(
    5738           3 :             nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
    5739             :     }
    5740             :     else
    5741             :     {
    5742           1 :         nCoordBlockPtr = 0;
    5743             :     }
    5744             : 
    5745             :     /*-----------------------------------------------------------------
    5746             :      * Copy object information
    5747             :      *----------------------------------------------------------------*/
    5748           4 :     TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
    5749             : 
    5750           4 :     poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr;    // String position
    5751           4 :     poTextHdr->m_nCoordDataSize = nStringLen;        // String length
    5752           4 :     poTextHdr->m_nTextAlignment = m_nTextAlignment;  // just./spacing/arrow
    5753             : 
    5754             :     /*-----------------------------------------------------------------
    5755             :      * Text Angle, (written in tenths of degrees)
    5756             :      * Contrary to arc start/end angles, no conversion based on
    5757             :      * origin quadrant is required here.
    5758             :      *----------------------------------------------------------------*/
    5759           4 :     poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
    5760             : 
    5761           4 :     poTextHdr->m_nFontStyle = m_nFontStyle;  // Font style/effect
    5762             : 
    5763           4 :     poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
    5764           4 :     poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
    5765           4 :     poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
    5766             : 
    5767           4 :     poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
    5768           4 :     poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
    5769           4 :     poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
    5770             : 
    5771             :     /*-----------------------------------------------------------------
    5772             :      * The OGRPoint's X,Y values were the coords of the lower-left corner
    5773             :      * before rotation was applied.  (Note that the rotation in MapInfo is
    5774             :      * done around the upper-left corner)
    5775             :      * The Feature's MBR is the MBR of the text after rotation... that's
    5776             :      * what MapInfo uses to define the text location.
    5777             :      *----------------------------------------------------------------*/
    5778           4 :     double dXMin = 0.0;
    5779           4 :     double dYMin = 0.0;
    5780           4 :     double dXMax = 0.0;
    5781           4 :     double dYMax = 0.0;
    5782             :     // Make sure Feature MBR is in sync with other params
    5783             : 
    5784           4 :     UpdateMBR();
    5785           4 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    5786             : 
    5787           4 :     poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
    5788           4 :     poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
    5789             : 
    5790             :     // Label line end point
    5791           4 :     double dX = 0.0;
    5792           4 :     double dY = 0.0;
    5793           4 :     GetTextLineEndPoint(dX, dY);  // Make sure a default line end point is set
    5794           4 :     poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
    5795           4 :                             poTextHdr->m_nLineEndY);
    5796             : 
    5797             :     // Text Height
    5798           4 :     poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
    5799           4 :     poTextHdr->m_nHeight = nY;
    5800             : 
    5801           4 :     if (!bCoordBlockDataOnly)
    5802             :     {
    5803             :         // Font name
    5804           4 :         m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    5805           4 :         poTextHdr->m_nFontId =
    5806           4 :             static_cast<GByte>(m_nFontDefIndex);  // Font name index
    5807             :     }
    5808             : 
    5809             :     // MBR after rotation
    5810           4 :     poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
    5811             : 
    5812           4 :     if (!bCoordBlockDataOnly)
    5813             :     {
    5814           4 :         m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    5815           4 :         poTextHdr->m_nPenId =
    5816           4 :             static_cast<GByte>(m_nPenDefIndex);  // Pen index for line/arrow
    5817             :     }
    5818             : 
    5819           4 :     if (CPLGetLastErrorType() == CE_Failure)
    5820           0 :         return -1;
    5821             : 
    5822             :     /* Return a ref to coord block so that caller can continue writing
    5823             :      * after the end of this object (used by index splitting)
    5824             :      */
    5825           4 :     if (ppoCoordBlock)
    5826           0 :         *ppoCoordBlock = poCoordBlock;
    5827             : 
    5828           4 :     return 0;
    5829             : }
    5830             : 
    5831             : /**********************************************************************
    5832             :  *                   TABText::GetTextString()
    5833             :  *
    5834             :  * Return ref to text string value.
    5835             :  *
    5836             :  * Returned string is a reference to the internal string buffer and should
    5837             :  * not be modified or freed by the caller.
    5838             :  **********************************************************************/
    5839         300 : const char *TABText::GetTextString() const
    5840             : {
    5841         300 :     if (m_pszString == nullptr)
    5842           0 :         return "";
    5843             : 
    5844         300 :     return m_pszString;
    5845             : }
    5846             : 
    5847             : /**********************************************************************
    5848             :  *                   TABText::SetTextString()
    5849             :  *
    5850             :  * Set new text string value.
    5851             :  *
    5852             :  * Note: The text string may contain "\n" chars or "\\" chars
    5853             :  * and we expect to receive them in a 2 chars escaped form as
    5854             :  * described in the MIF format specs.
    5855             :  **********************************************************************/
    5856           3 : void TABText::SetTextString(const char *pszNewStr)
    5857             : {
    5858           3 :     CPLFree(m_pszString);
    5859           3 :     m_pszString = CPLStrdup(pszNewStr);
    5860           3 : }
    5861             : 
    5862             : /**********************************************************************
    5863             :  *                   TABText::GetTextAngle()
    5864             :  *
    5865             :  * Return text angle in degrees.
    5866             :  **********************************************************************/
    5867          10 : double TABText::GetTextAngle() const
    5868             : {
    5869          10 :     return m_dAngle;
    5870             : }
    5871             : 
    5872         211 : void TABText::SetTextAngle(double dAngle)
    5873             : {
    5874             :     // Make sure angle is in the range [0..360]
    5875         211 :     dAngle = fmod(dAngle, 360.0);
    5876         211 :     if (dAngle < 0.0)
    5877           0 :         dAngle += 360.0;
    5878         211 :     m_dAngle = dAngle;
    5879         211 :     UpdateMBR();
    5880         211 : }
    5881             : 
    5882             : /**********************************************************************
    5883             :  *                   TABText::GetTextBoxHeight()
    5884             :  *
    5885             :  * Return text height in Y axis coord. units of the text box before rotation.
    5886             :  **********************************************************************/
    5887          10 : double TABText::GetTextBoxHeight() const
    5888             : {
    5889          10 :     return m_dHeight;
    5890             : }
    5891             : 
    5892           3 : void TABText::SetTextBoxHeight(double dHeight)
    5893             : {
    5894           3 :     m_dHeight = dHeight;
    5895           3 :     UpdateMBR();
    5896           3 : }
    5897             : 
    5898             : /**********************************************************************
    5899             :  *                   TABText::GetTextBoxWidth()
    5900             :  *
    5901             :  * Return text width in X axis coord. units. of the text box before rotation.
    5902             :  *
    5903             :  * If value has not been set, then we force a default value that assumes
    5904             :  * that one char's box width is 60% of its height... and we ignore
    5905             :  * the multiline case.  This should not matter when the user PROPERLY sets
    5906             :  * the value.
    5907             :  **********************************************************************/
    5908          12 : double TABText::GetTextBoxWidth() const
    5909             : {
    5910          12 :     if (m_dWidth == 0.0 && m_pszString)
    5911             :     {
    5912           3 :         m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
    5913             :     }
    5914          12 :     return m_dWidth;
    5915             : }
    5916             : 
    5917           0 : void TABText::SetTextBoxWidth(double dWidth)
    5918             : {
    5919           0 :     m_dWidth = dWidth;
    5920           0 :     UpdateMBR();
    5921           0 : }
    5922             : 
    5923             : /**********************************************************************
    5924             :  *                   TABText::GetTextLineEndPoint()
    5925             :  *
    5926             :  * Return X,Y coordinates of the text label line end point.
    5927             :  * Default is the center of the text MBR.
    5928             :  **********************************************************************/
    5929           4 : void TABText::GetTextLineEndPoint(double &dX, double &dY)
    5930             : {
    5931           4 :     if (!m_bLineEndSet)
    5932             :     {
    5933             :         // Set default location at center of text MBR
    5934           4 :         double dXMin = 0.0;
    5935           4 :         double dYMin = 0.0;
    5936           4 :         double dXMax = 0.0;
    5937           4 :         double dYMax = 0.0;
    5938           4 :         UpdateMBR();
    5939           4 :         GetMBR(dXMin, dYMin, dXMax, dYMax);
    5940           4 :         m_dfLineEndX = (dXMin + dXMax) / 2.0;
    5941           4 :         m_dfLineEndY = (dYMin + dYMax) / 2.0;
    5942           4 :         m_bLineEndSet = TRUE;
    5943             :     }
    5944             : 
    5945             :     // Return values
    5946           4 :     dX = m_dfLineEndX;
    5947           4 :     dY = m_dfLineEndY;
    5948           4 : }
    5949             : 
    5950         178 : void TABText::SetTextLineEndPoint(double dX, double dY)
    5951             : {
    5952         178 :     m_dfLineEndX = dX;
    5953         178 :     m_dfLineEndY = dY;
    5954         178 :     m_bLineEndSet = TRUE;
    5955         178 : }
    5956             : 
    5957             : /**********************************************************************
    5958             :  *                   TABText::UpdateMBR()
    5959             :  *
    5960             :  * Update the feature MBR using the text origin (OGRPoint geometry), the
    5961             :  * rotation angle, and the Width/height before rotation.
    5962             :  *
    5963             :  * This function cannot perform properly unless all the above have been set.
    5964             :  *
    5965             :  * Returns 0 on success, or -1 if there is no geometry in object
    5966             :  **********************************************************************/
    5967         226 : int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    5968             : {
    5969         226 :     OGRGeometry *poGeom = GetGeometryRef();
    5970         226 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5971             :     {
    5972          12 :         OGRPoint *poPoint = poGeom->toPoint();
    5973             : 
    5974          12 :         const double dX0 = poPoint->getX();
    5975          12 :         const double dY0 = poPoint->getY();
    5976             : 
    5977          12 :         const double dSin = sin(m_dAngle * M_PI / 180.0);
    5978          12 :         const double dCos = cos(m_dAngle * M_PI / 180.0);
    5979             : 
    5980          12 :         GetTextBoxWidth();  // Force default width value if necessary.
    5981             : 
    5982          12 :         const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
    5983          12 :         const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
    5984             : 
    5985          12 :         SetMBR(dX0, dY0, dX0, dY0);
    5986          60 :         for (int i = 0; i < 4; i++)
    5987             :         {
    5988             :             // Rotate one of the box corners
    5989          48 :             const double dX1 =
    5990          48 :                 dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
    5991          48 :             const double dY1 =
    5992          48 :                 dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
    5993             : 
    5994             :             // And update feature MBR with rotated coordinate
    5995          48 :             if (dX1 < m_dXMin)
    5996           9 :                 m_dXMin = dX1;
    5997          48 :             if (dX1 > m_dXMax)
    5998           9 :                 m_dXMax = dX1;
    5999          48 :             if (dY1 < m_dYMin)
    6000           0 :                 m_dYMin = dY1;
    6001          48 :             if (dY1 > m_dYMax)
    6002          18 :                 m_dYMax = dY1;
    6003             :         }
    6004             : 
    6005          12 :         if (poMapFile)
    6006             :         {
    6007           4 :             poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    6008           4 :             poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    6009             :         }
    6010             : 
    6011          12 :         return 0;
    6012             :     }
    6013             : 
    6014         214 :     return -1;
    6015             : }
    6016             : 
    6017             : /**********************************************************************
    6018             :  *                   TABText::GetFontBGColor()
    6019             :  *
    6020             :  * Return background color.
    6021             :  **********************************************************************/
    6022           8 : GInt32 TABText::GetFontBGColor() const
    6023             : {
    6024           8 :     return m_rgbBackground;
    6025             : }
    6026             : 
    6027         258 : void TABText::SetFontBGColor(GInt32 rgbColor)
    6028             : {
    6029         258 :     m_rgbBackground = rgbColor;
    6030         258 : }
    6031             : 
    6032             : /**********************************************************************
    6033             :  *                   TABText::GetFontOColor()
    6034             :  *
    6035             :  * Return outline color.
    6036             :  **********************************************************************/
    6037           1 : GInt32 TABText::GetFontOColor() const
    6038             : {
    6039           1 :     return m_rgbOutline;
    6040             : }
    6041             : 
    6042           1 : void TABText::SetFontOColor(GInt32 rgbColor)
    6043             : {
    6044           1 :     m_rgbOutline = rgbColor;
    6045           1 : }
    6046             : 
    6047             : /**********************************************************************
    6048             :  *                   TABText::GetFontSColor()
    6049             :  *
    6050             :  * Return shadow color.
    6051             :  **********************************************************************/
    6052           0 : GInt32 TABText::GetFontSColor() const
    6053             : {
    6054           0 :     return m_rgbShadow;
    6055             : }
    6056             : 
    6057           0 : void TABText::SetFontSColor(GInt32 rgbColor)
    6058             : {
    6059           0 :     m_rgbShadow = rgbColor;
    6060           0 : }
    6061             : 
    6062             : /**********************************************************************
    6063             :  *                   TABText::GetFontFGColor()
    6064             :  *
    6065             :  * Return foreground color.
    6066             :  **********************************************************************/
    6067          10 : GInt32 TABText::GetFontFGColor() const
    6068             : {
    6069          10 :     return m_rgbForeground;
    6070             : }
    6071             : 
    6072         265 : void TABText::SetFontFGColor(GInt32 rgbColor)
    6073             : {
    6074         265 :     m_rgbForeground = rgbColor;
    6075         265 : }
    6076             : 
    6077             : /**********************************************************************
    6078             :  *                   TABText::GetTextJustification()
    6079             :  *
    6080             :  * Return text justification.  Default is TABTJLeft
    6081             :  **********************************************************************/
    6082          10 : TABTextJust TABText::GetTextJustification() const
    6083             : {
    6084          10 :     TABTextJust eJust = TABTJLeft;
    6085             : 
    6086          10 :     if (m_nTextAlignment & 0x0200)
    6087           9 :         eJust = TABTJCenter;
    6088           1 :     else if (m_nTextAlignment & 0x0400)
    6089           0 :         eJust = TABTJRight;
    6090             : 
    6091          10 :     return eJust;
    6092             : }
    6093             : 
    6094         222 : void TABText::SetTextJustification(TABTextJust eJustification)
    6095             : {
    6096             :     // Flush current value... default is TABTJLeft
    6097         222 :     m_nTextAlignment &= ~0x0600;
    6098             :     // ... and set new one.
    6099         222 :     if (eJustification == TABTJCenter)
    6100         222 :         m_nTextAlignment |= 0x0200;
    6101           0 :     else if (eJustification == TABTJRight)
    6102           0 :         m_nTextAlignment |= 0x0400;
    6103         222 : }
    6104             : 
    6105             : /**********************************************************************
    6106             :  *                   TABText::GetTextSpacing()
    6107             :  *
    6108             :  * Return text vertical spacing factor.  Default is TABTSSingle
    6109             :  **********************************************************************/
    6110           0 : TABTextSpacing TABText::GetTextSpacing() const
    6111             : {
    6112           0 :     TABTextSpacing eSpacing = TABTSSingle;
    6113             : 
    6114           0 :     if (m_nTextAlignment & 0x0800)
    6115           0 :         eSpacing = TABTS1_5;
    6116           0 :     else if (m_nTextAlignment & 0x1000)
    6117           0 :         eSpacing = TABTSDouble;
    6118             : 
    6119           0 :     return eSpacing;
    6120             : }
    6121             : 
    6122         237 : void TABText::SetTextSpacing(TABTextSpacing eSpacing)
    6123             : {
    6124             :     // Flush current value... default is TABTSSingle
    6125         237 :     m_nTextAlignment &= ~0x1800;
    6126             :     // ... and set new one.
    6127         237 :     if (eSpacing == TABTS1_5)
    6128           0 :         m_nTextAlignment |= 0x0800;
    6129         237 :     else if (eSpacing == TABTSDouble)
    6130         237 :         m_nTextAlignment |= 0x1000;
    6131         237 : }
    6132             : 
    6133             : /**********************************************************************
    6134             :  *                   TABText::GetTextLineType()
    6135             :  *
    6136             :  * Return text line (arrow) type.  Default is TABTLNoLine
    6137             :  **********************************************************************/
    6138           0 : TABTextLineType TABText::GetTextLineType() const
    6139             : {
    6140           0 :     TABTextLineType eLine = TABTLNoLine;
    6141             : 
    6142           0 :     if (m_nTextAlignment & 0x2000)
    6143           0 :         eLine = TABTLSimple;
    6144           0 :     else if (m_nTextAlignment & 0x4000)
    6145           0 :         eLine = TABTLArrow;
    6146             : 
    6147           0 :     return eLine;
    6148             : }
    6149             : 
    6150         178 : void TABText::SetTextLineType(TABTextLineType eLineType)
    6151             : {
    6152             :     // Flush current value... default is TABTLNoLine
    6153         178 :     m_nTextAlignment &= ~0x6000;
    6154             :     // ... and set new one.
    6155         178 :     if (eLineType == TABTLSimple)
    6156         178 :         m_nTextAlignment |= 0x2000;
    6157           0 :     else if (eLineType == TABTLArrow)
    6158           0 :         m_nTextAlignment |= 0x4000;
    6159         178 : }
    6160             : 
    6161             : /**********************************************************************
    6162             :  *                   TABText::QueryFontStyle()
    6163             :  *
    6164             :  * Return TRUE if the specified font style attribute is turned ON,
    6165             :  * or FALSE otherwise.  See enum TABFontStyle for the list of styles
    6166             :  * that can be queried on.
    6167             :  **********************************************************************/
    6168         385 : GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
    6169             : {
    6170         385 :     return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
    6171             : }
    6172             : 
    6173         264 : void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
    6174             : {
    6175         264 :     if (bStyleOn)
    6176         264 :         m_nFontStyle |= static_cast<int>(eStyleToToggle);
    6177             :     else
    6178           0 :         m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
    6179         264 : }
    6180             : 
    6181             : /**********************************************************************
    6182             :  *                   TABText::GetFontStyleMIFValue()
    6183             :  *
    6184             :  * Return the Font Style value for this object using the style values
    6185             :  * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
    6186             :  *
    6187             :  * The reason why we have to differentiate between the TAB and the MIF font
    6188             :  * style values is that in TAB, TABFSBox is included in the style value
    6189             :  * as code 0x100, but in MIF it is not included, instead it is implied by
    6190             :  * the presence of the BG color in the FONT() clause (the BG color is
    6191             :  * present only when TABFSBox or TABFSHalo is set).
    6192             :  * This also has the effect of shifting all the other style values > 0x100
    6193             :  * by 1 byte.
    6194             :  **********************************************************************/
    6195           0 : int TABText::GetFontStyleMIFValue() const
    6196             : {
    6197             :     // The conversion is simply to remove bit 0x100 from the value and shift
    6198             :     // down all values past this bit.
    6199           0 :     return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
    6200             : }
    6201             : 
    6202         261 : void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
    6203             : {
    6204         261 :     m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
    6205             :     // When BG color is set, then either BOX or HALO should be set.
    6206         261 :     if (bBGColorSet && !QueryFontStyle(TABFSHalo))
    6207         254 :         ToggleFontStyle(TABFSBox, TRUE);
    6208         261 : }
    6209             : 
    6210          10 : int TABText::IsFontBGColorUsed() const
    6211             : {
    6212             :     // Font BG color is used only when BOX is set.
    6213          10 :     return QueryFontStyle(TABFSBox);
    6214             : }
    6215             : 
    6216          10 : int TABText::IsFontOColorUsed() const
    6217             : {
    6218             :     // Font outline color is used only when HALO is set.
    6219          10 :     return QueryFontStyle(TABFSHalo);
    6220             : }
    6221             : 
    6222          10 : int TABText::IsFontSColorUsed() const
    6223             : {
    6224             :     // Font shadow color is used only when Shadow is set.
    6225          10 :     return QueryFontStyle(TABFSShadow);
    6226             : }
    6227             : 
    6228          10 : int TABText::IsFontBold() const
    6229             : {
    6230             :     // Font bold is used only when Bold is set.
    6231          10 :     return QueryFontStyle(TABFSBold);
    6232             : }
    6233             : 
    6234          10 : int TABText::IsFontItalic() const
    6235             : {
    6236             :     // Font italic is used only when Italic is set.
    6237          10 :     return QueryFontStyle(TABFSItalic);
    6238             : }
    6239             : 
    6240          10 : int TABText::IsFontUnderline() const
    6241             : {
    6242             :     // Font underline is used only when Underline is set.
    6243          10 :     return QueryFontStyle(TABFSUnderline);
    6244             : }
    6245             : 
    6246             : /**********************************************************************
    6247             :  *                   TABText::GetLabelStyleString()
    6248             :  *
    6249             :  * This is not the correct location, it should be in ITABFeatureFont,
    6250             :  * but it is really more easy to put it here.  This fct return a complete
    6251             :  * string for the representation with the string to display
    6252             :  **********************************************************************/
    6253          10 : const char *TABText::GetLabelStyleString() const
    6254             : {
    6255          10 :     const char *pszStyle = nullptr;
    6256          10 :     int nStringLen = static_cast<int>(strlen(GetTextString()));
    6257             :     // ALL Caps, Extpanded need to modify the string value
    6258             :     char *pszTextString =
    6259          10 :         static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
    6260             :     /* char szPattern[20]; */
    6261          10 :     int nJustification = 1;
    6262             : 
    6263          10 :     strcpy(pszTextString, GetTextString());
    6264             :     /* szPattern[0] = '\0'; */
    6265             : 
    6266          10 :     switch (GetTextJustification())
    6267             :     {
    6268           9 :         case TABTJCenter:
    6269           9 :             nJustification = 2;
    6270           9 :             break;
    6271           0 :         case TABTJRight:
    6272           0 :             nJustification = 3;
    6273           0 :             break;
    6274           1 :         case TABTJLeft:
    6275             :         default:
    6276           1 :             nJustification = 1;
    6277           1 :             break;
    6278             :     }
    6279             : 
    6280             :     // Compute real font size, taking number of lines ("\\n", "\n") and line
    6281             :     // spacing into account.
    6282          10 :     int numLines = 1;
    6283          61 :     for (int i = 0; pszTextString[i];
    6284          51 :          numLines +=
    6285         102 :          ((pszTextString[i] == '\n' ||
    6286          51 :            (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
    6287           0 :           pszTextString[i + 1] != '\0'),
    6288             :              ++i)
    6289             :         ;
    6290             : 
    6291          10 :     double dHeight = GetTextBoxHeight() / numLines;
    6292             : 
    6293             :     // In all cases, take out 20% of font height to account for line spacing
    6294          10 :     if (numLines > 1)
    6295             :     {
    6296           0 :         switch (GetTextSpacing())
    6297             :         {
    6298           0 :             case TABTS1_5:
    6299           0 :                 dHeight *= (0.80 * 0.69);
    6300           0 :                 break;
    6301           0 :             case TABTSDouble:
    6302           0 :                 dHeight *= (0.66 * 0.69);
    6303           0 :                 break;
    6304           0 :             default:
    6305           0 :                 dHeight *= 0.69;
    6306             :         }
    6307             :     }
    6308             :     else
    6309             :     {
    6310          10 :         dHeight *= 0.69;
    6311             :     }
    6312             : 
    6313          10 :     if (QueryFontStyle(TABFSAllCaps))
    6314           0 :         for (int i = 0; pszTextString[i]; ++i)
    6315           0 :             if (isalpha(static_cast<unsigned char>(pszTextString[i])))
    6316           0 :                 pszTextString[i] = static_cast<char>(
    6317           0 :                     CPLToupper(static_cast<unsigned char>(pszTextString[i])));
    6318             : 
    6319             :     /* Escape the double quote chars and expand the text */
    6320          10 :     char *pszTmpTextString = nullptr;
    6321             : 
    6322          10 :     if (QueryFontStyle(TABFSExpanded))
    6323             :         pszTmpTextString = static_cast<char *>(
    6324           0 :             CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
    6325             :     else
    6326             :         pszTmpTextString = static_cast<char *>(
    6327          10 :             CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
    6328             : 
    6329          10 :     int j = 0;
    6330          61 :     for (int i = 0; i < nStringLen; ++i, ++j)
    6331             :     {
    6332          51 :         if (pszTextString[i] == '"')
    6333             :         {
    6334           0 :             pszTmpTextString[j] = '\\';
    6335           0 :             pszTmpTextString[j + 1] = pszTextString[i];
    6336           0 :             ++j;
    6337             :         }
    6338             :         else
    6339          51 :             pszTmpTextString[j] = pszTextString[i];
    6340             : 
    6341          51 :         if (QueryFontStyle(TABFSExpanded))
    6342             :         {
    6343           0 :             pszTmpTextString[j + 1] = ' ';
    6344           0 :             ++j;
    6345             :         }
    6346             :     }
    6347             : 
    6348          10 :     pszTmpTextString[j] = '\0';
    6349          10 :     CPLFree(pszTextString);
    6350             :     pszTextString = static_cast<char *>(
    6351          10 :         CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
    6352          10 :     strcpy(pszTextString, pszTmpTextString);
    6353          10 :     CPLFree(pszTmpTextString);
    6354             : 
    6355             :     const char *pszBGColor =
    6356          10 :         IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
    6357             :     const char *pszOColor =
    6358          10 :         IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
    6359             :     const char *pszSColor =
    6360          10 :         IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
    6361          10 :     const char *pszBold = IsFontBold() ? ",bo:1" : "";
    6362          10 :     const char *pszItalic = IsFontItalic() ? ",it:1" : "";
    6363          10 :     const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
    6364             : 
    6365          10 :     pszStyle = CPLSPrintf(
    6366             :         "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
    6367             :         pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
    6368             :         pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
    6369             :         GetFontNameRef());
    6370             : 
    6371          10 :     CPLFree(pszTextString);
    6372          10 :     return pszStyle;
    6373             : }
    6374             : 
    6375             : /**********************************************************************
    6376             :  *                   TABText::GetStyleString() const
    6377             :  *
    6378             :  * Return style string for this feature.
    6379             :  *
    6380             :  * Style String is built only once during the first call to GetStyleString().
    6381             :  **********************************************************************/
    6382          10 : const char *TABText::GetStyleString() const
    6383             : {
    6384          10 :     if (m_pszStyleString == nullptr)
    6385             :     {
    6386          10 :         m_pszStyleString = CPLStrdup(GetLabelStyleString());
    6387             :     }
    6388             : 
    6389          10 :     return m_pszStyleString;
    6390             : }
    6391             : 
    6392           4 : void TABText::SetLabelFromStyleString(const char *pszStyleString)
    6393             : {
    6394             :     // Use the Style Manager to retrieve all the information we need.
    6395           4 :     auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
    6396           0 :     std::unique_ptr<OGRStyleTool> poStylePart;
    6397             : 
    6398             :     // Init the StyleMgr with the StyleString.
    6399           4 :     poStyleMgr->InitStyleString(pszStyleString);
    6400             : 
    6401             :     // Retrieve the Symbol info.
    6402           4 :     const int numParts = poStyleMgr->GetPartCount();
    6403           4 :     for (int i = 0; i < numParts; i++)
    6404             :     {
    6405           4 :         poStylePart.reset(poStyleMgr->GetPart(i));
    6406           4 :         if (poStylePart == nullptr)
    6407             :         {
    6408           0 :             continue;
    6409             :         }
    6410             : 
    6411           4 :         if (poStylePart->GetType() == OGRSTCLabel)
    6412             :         {
    6413           4 :             break;
    6414             :         }
    6415             :         else
    6416             :         {
    6417           0 :             poStylePart.reset();
    6418             :         }
    6419             :     }
    6420             : 
    6421             :     // If the no Symbol found, do nothing.
    6422           4 :     if (poStylePart == nullptr)
    6423             :     {
    6424           0 :         return;
    6425             :     }
    6426             : 
    6427           4 :     auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
    6428             : 
    6429           4 :     GBool bIsNull = 0;
    6430           4 :     const char *pszText = poLabelStyle->TextString(bIsNull);
    6431           4 :     if (!bIsNull && pszText)
    6432             :     {
    6433           3 :         SetTextString(pszText);
    6434             : 
    6435           3 :         poLabelStyle->SetUnit(OGRSTUMM);
    6436           3 :         double dfSize = poLabelStyle->Size(bIsNull);
    6437           3 :         if (!bIsNull)
    6438             :         {
    6439           3 :             dfSize /= 1000;
    6440             : 
    6441             :             // Compute text box height, taking number of lines ("\\n", "\n") and
    6442             :             // line spacing into account.
    6443           3 :             int numLines = 1;
    6444          18 :             for (int i = 0; pszText[i];
    6445          45 :                  numLines += ((pszText[i] == '\n' ||
    6446          15 :                                (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
    6447           0 :                               pszText[i + 1] != '\0'),
    6448             :                      ++i)
    6449             :                 ;
    6450             : 
    6451             :             // Cf GetLabelStyleString() for 0.69. We should likely also take
    6452             :             // into account line spacing if we knew how to compute it.
    6453           3 :             SetTextBoxHeight(dfSize / 0.69 * numLines);
    6454             :         }
    6455             :     }
    6456             : 
    6457           4 :     if (poLabelStyle->Bold(bIsNull))
    6458           3 :         ToggleFontStyle(TABFSBold, true);
    6459             : 
    6460           4 :     if (poLabelStyle->Italic(bIsNull))
    6461           1 :         ToggleFontStyle(TABFSItalic, true);
    6462             : 
    6463           4 :     if (poLabelStyle->Underline(bIsNull))
    6464           1 :         ToggleFontStyle(TABFSUnderline, true);
    6465             : 
    6466           4 :     const char *pszFontName = poLabelStyle->FontName(bIsNull);
    6467           4 :     if (!bIsNull && pszFontName)
    6468           4 :         SetFontName(pszFontName);
    6469             : 
    6470             :     // Set the ForeColor
    6471           4 :     const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
    6472           4 :     if (bIsNull)
    6473           0 :         pszForeColor = nullptr;
    6474           4 :     if (pszForeColor)
    6475             :     {
    6476           4 :         if (pszForeColor[0] == '#')
    6477           4 :             pszForeColor++;
    6478           8 :         CPLString osForeColor(pszForeColor);
    6479           4 :         if (strlen(pszForeColor) > 6)
    6480           1 :             osForeColor.resize(6);
    6481           4 :         const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
    6482           4 :         SetFontFGColor(static_cast<GInt32>(nColor));
    6483             :     }
    6484             : 
    6485             :     // Set the BackgroundColor
    6486           4 :     const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
    6487           4 :     if (bIsNull)
    6488           0 :         pszBackColor = nullptr;
    6489           4 :     if (pszBackColor)
    6490             :     {
    6491           4 :         if (pszBackColor[0] == '#')
    6492           4 :             pszBackColor++;
    6493           8 :         CPLString osBackColor(pszBackColor);
    6494           4 :         if (strlen(pszBackColor) > 6)
    6495           1 :             osBackColor.resize(6);
    6496           4 :         const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
    6497           4 :         ToggleFontStyle(TABFSBox, true);
    6498           4 :         SetFontBGColor(static_cast<GInt32>(nColor));
    6499             :     }
    6500             : 
    6501             :     // Set the OutlineColor
    6502           4 :     const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
    6503           4 :     if (bIsNull)
    6504           3 :         pszOutlineColor = nullptr;
    6505           4 :     if (pszOutlineColor)
    6506             :     {
    6507           1 :         if (pszOutlineColor[0] == '#')
    6508           1 :             pszOutlineColor++;
    6509           2 :         CPLString osOutlineColor(pszOutlineColor);
    6510           1 :         if (strlen(pszOutlineColor) > 6)
    6511           0 :             osOutlineColor.resize(6);
    6512             :         const int nColor =
    6513           1 :             static_cast<int>(strtol(osOutlineColor, nullptr, 16));
    6514           1 :         ToggleFontStyle(TABFSHalo, true);
    6515           1 :         SetFontOColor(static_cast<GInt32>(nColor));
    6516             :     }
    6517             : 
    6518             : #if 0
    6519             :     // Commented out since it is hardcoded to 0x808080.
    6520             :     // Set the ShadowColor
    6521             :     const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
    6522             :     if(bIsNull) pszShadowColor = nullptr;
    6523             :     if(pszShadowColor)
    6524             :     {
    6525             :         if(pszShadowColor[0] == '#')
    6526             :             pszShadowColor++;
    6527             :         CPLString osShadowColor(pszShadowColor);
    6528             :         if( strlen(pszShadowColor) > 6 )
    6529             :             osShadowColor.resize(6);
    6530             :         const int nColor =
    6531             :             static_cast<int>(strtol(osShadowColor, nullptr, 16));
    6532             :         ToggleFontStyle(TABFSShadow, true);
    6533             :         SetFontSColor(static_cast<GInt32>(nColor));
    6534             :     }
    6535             : #endif
    6536             : 
    6537           4 :     const double dfAngle = poLabelStyle->Angle(bIsNull);
    6538           4 :     if (!bIsNull)
    6539           3 :         SetTextAngle(dfAngle);
    6540             : 
    6541           4 :     const int nAnchor = poLabelStyle->Anchor(bIsNull);
    6542           4 :     if (!bIsNull)
    6543             :     {
    6544           3 :         switch ((nAnchor - 1) % 3)
    6545             :         {
    6546           0 :             case 0:
    6547           0 :                 SetTextJustification(TABTJLeft);
    6548           0 :                 break;
    6549           3 :             case 1:
    6550           3 :                 SetTextJustification(TABTJCenter);
    6551           3 :                 break;
    6552           0 :             default /* 2 */:
    6553           0 :                 SetTextJustification(TABTJRight);
    6554           0 :                 break;
    6555             :         }
    6556             :     }
    6557             : }
    6558             : 
    6559             : /**********************************************************************
    6560             :  *                   TABText::DumpMIF()
    6561             :  *
    6562             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    6563             :  **********************************************************************/
    6564           0 : void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
    6565             : {
    6566           0 :     if (fpOut == nullptr)
    6567           0 :         fpOut = stdout;
    6568             : 
    6569             :     /*-----------------------------------------------------------------
    6570             :      * Fetch and validate geometry
    6571             :      *----------------------------------------------------------------*/
    6572           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6573           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6574             :     {
    6575             :         /*-------------------------------------------------------------
    6576             :          * Generate output for text object
    6577             :          *------------------------------------------------------------*/
    6578           0 :         OGRPoint *poPoint = poGeom->toPoint();
    6579             : 
    6580           0 :         fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
    6581           0 :                 m_pszString ? m_pszString : "", poPoint->getX(),
    6582             :                 poPoint->getY());
    6583             : 
    6584           0 :         fprintf(fpOut, "  m_pszString = '%s'\n", m_pszString);
    6585           0 :         fprintf(fpOut, "  m_dAngle    = %.15g\n", m_dAngle);
    6586           0 :         fprintf(fpOut, "  m_dHeight   = %.15g\n", m_dHeight);
    6587           0 :         fprintf(fpOut, "  m_rgbForeground  = 0x%6.6x (%d)\n", m_rgbForeground,
    6588             :                 m_rgbForeground);
    6589           0 :         fprintf(fpOut, "  m_rgbBackground  = 0x%6.6x (%d)\n", m_rgbBackground,
    6590             :                 m_rgbBackground);
    6591           0 :         fprintf(fpOut, "  m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
    6592           0 :         fprintf(fpOut, "  m_nFontStyle     = 0x%4.4x\n", m_nFontStyle);
    6593             :     }
    6594             :     else
    6595             :     {
    6596           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6597             :                  "TABText: Missing or Invalid Geometry!");
    6598           0 :         return;
    6599             :     }
    6600             : 
    6601             :     // Finish with PEN/BRUSH/etc. clauses
    6602           0 :     DumpPenDef();
    6603           0 :     DumpFontDef();
    6604             : 
    6605           0 :     fflush(fpOut);
    6606             : }
    6607             : 
    6608             : /*=====================================================================
    6609             :  *                      class TABMultiPoint
    6610             :  *====================================================================*/
    6611             : 
    6612             : /**********************************************************************
    6613             :  *                   TABMultiPoint::TABMultiPoint()
    6614             :  *
    6615             :  * Constructor.
    6616             :  **********************************************************************/
    6617         191 : TABMultiPoint::TABMultiPoint(OGRFeatureDefn *poDefnIn)
    6618             :     : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
    6619         191 :       m_dCenterY(0.0)
    6620             : {
    6621         191 : }
    6622             : 
    6623             : /**********************************************************************
    6624             :  *                   TABMultiPoint::~TABMultiPoint()
    6625             :  *
    6626             :  * Destructor.
    6627             :  **********************************************************************/
    6628         382 : TABMultiPoint::~TABMultiPoint()
    6629             : {
    6630         382 : }
    6631             : 
    6632             : /**********************************************************************
    6633             :  *                     TABMultiPoint::CloneTABFeature()
    6634             :  *
    6635             :  * Duplicate feature, including stuff specific to each TABFeature type.
    6636             :  *
    6637             :  * This method calls the generic TABFeature::CloneTABFeature() and
    6638             :  * then copies any members specific to its own type.
    6639             :  **********************************************************************/
    6640           0 : TABFeature *TABMultiPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    6641             : {
    6642             :     /*-----------------------------------------------------------------
    6643             :      * Alloc new feature and copy the base stuff
    6644             :      *----------------------------------------------------------------*/
    6645             :     TABMultiPoint *poNew =
    6646           0 :         new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
    6647             : 
    6648           0 :     CopyTABFeatureBase(poNew);
    6649             : 
    6650             :     /*-----------------------------------------------------------------
    6651             :      * And members specific to this class
    6652             :      *----------------------------------------------------------------*/
    6653             :     // ITABFeatureSymbol
    6654           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    6655             : 
    6656           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    6657           0 :     poNew->m_dCenterX = m_dCenterX;
    6658           0 :     poNew->m_dCenterY = m_dCenterY;
    6659             : 
    6660           0 :     return poNew;
    6661             : }
    6662             : 
    6663             : /**********************************************************************
    6664             :  *                   TABMultiPoint::ValidateMapInfoType()
    6665             :  *
    6666             :  * Check the feature's geometry part and return the corresponding
    6667             :  * mapinfo object type code.  The m_nMapInfoType member will also
    6668             :  * be updated for further calls to GetMapInfoType();
    6669             :  *
    6670             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    6671             :  * is expected for this object class.
    6672             :  **********************************************************************/
    6673           0 : TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    6674             : {
    6675             :     /*-----------------------------------------------------------------
    6676             :      * Fetch and validate geometry
    6677             :      *----------------------------------------------------------------*/
    6678           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6679           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6680             :     {
    6681           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    6682             : 
    6683           0 :         if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
    6684           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
    6685             :         else
    6686           0 :             m_nMapInfoType = TAB_GEOM_MULTIPOINT;
    6687             :     }
    6688             :     else
    6689             :     {
    6690           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6691             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6692           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    6693             :     }
    6694             : 
    6695             :     /*-----------------------------------------------------------------
    6696             :      * Decide if coordinates should be compressed or not.
    6697             :      *----------------------------------------------------------------*/
    6698           0 :     ValidateCoordType(poMapFile);
    6699             : 
    6700           0 :     return m_nMapInfoType;
    6701             : }
    6702             : 
    6703             : /**********************************************************************
    6704             :  *                   TABMultiPoint::ReadGeometryFromMAPFile()
    6705             :  *
    6706             :  * Fill the geometry and representation (color, etc...) part of the
    6707             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    6708             :  *
    6709             :  * It is assumed that poMAPFile currently points to the beginning of
    6710             :  * a map object.
    6711             :  *
    6712             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    6713             :  * been called.
    6714             :  **********************************************************************/
    6715           8 : int TABMultiPoint::ReadGeometryFromMAPFile(
    6716             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    6717             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    6718             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    6719             : {
    6720           8 :     double dXMin = 0.0;
    6721           8 :     double dYMin = 0.0;
    6722           8 :     double dXMax = 0.0;
    6723           8 :     double dYMax = 0.0;
    6724           8 :     OGRGeometry *poGeometry = nullptr;
    6725           8 :     GBool bComprCoord = poObjHdr->IsCompressedType();
    6726           8 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    6727             : 
    6728             :     /*-----------------------------------------------------------------
    6729             :      * Fetch and validate geometry type
    6730             :      *----------------------------------------------------------------*/
    6731           8 :     m_nMapInfoType = poObjHdr->m_nType;
    6732             : 
    6733             :     /*-----------------------------------------------------------------
    6734             :      * Read object information
    6735             :      *----------------------------------------------------------------*/
    6736           8 :     if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
    6737           8 :         m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
    6738           0 :         m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
    6739           0 :         m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
    6740             :     {
    6741             :         /*-------------------------------------------------------------
    6742             :          * Copy data from poObjHdr
    6743             :          *------------------------------------------------------------*/
    6744             :         TABMAPObjMultiPoint *poMPointHdr =
    6745           8 :             cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
    6746             : 
    6747           8 :         const GUInt32 nMinimumBytesForPoints =
    6748           8 :             (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
    6749           8 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    6750           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    6751             :         {
    6752           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
    6753           0 :             return -1;
    6754             :         }
    6755             : 
    6756             :         // MBR
    6757           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
    6758             :                                 dXMin, dYMin);
    6759           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
    6760             :                                 dXMax, dYMax);
    6761             : 
    6762           8 :         if (!bCoordBlockDataOnly)
    6763             :         {
    6764           8 :             m_nSymbolDefIndex = poMPointHdr->m_nSymbolId;  // Symbol index
    6765           8 :             poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    6766             :         }
    6767             : 
    6768           8 :         double dX = 0.0;
    6769           8 :         double dY = 0.0;
    6770             :         // Centroid/label point
    6771           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
    6772             :                                 dX, dY);
    6773           8 :         SetCenter(dX, dY);
    6774             : 
    6775             :         // Compressed coordinate origin (useful only in compressed case!)
    6776           8 :         m_nComprOrgX = poMPointHdr->m_nComprOrgX;
    6777           8 :         m_nComprOrgY = poMPointHdr->m_nComprOrgY;
    6778             : 
    6779             :         /*-------------------------------------------------------------
    6780             :          * Read Point Coordinates
    6781             :          *------------------------------------------------------------*/
    6782           8 :         OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
    6783           8 :         poGeometry = poMultiPoint;
    6784             : 
    6785           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    6786           4 :             poCoordBlock = *ppoCoordBlock;
    6787             :         else
    6788             :             poCoordBlock =
    6789           4 :                 poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
    6790           8 :         if (poCoordBlock == nullptr)
    6791             :         {
    6792           0 :             delete poGeometry;
    6793           0 :             return -1;
    6794             :         }
    6795           8 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    6796             : 
    6797          24 :         for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
    6798             :         {
    6799          16 :             GInt32 nX = 0;
    6800          16 :             GInt32 nY = 0;
    6801          16 :             if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
    6802             :             {
    6803           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    6804             :                          "Failed reading coordinate data at offset %d",
    6805             :                          poMPointHdr->m_nCoordBlockPtr);
    6806           0 :                 delete poGeometry;
    6807           0 :                 return -1;
    6808             :             }
    6809             : 
    6810          16 :             poMapFile->Int2Coordsys(nX, nY, dX, dY);
    6811          16 :             OGRPoint *poPoint = new OGRPoint(dX, dY);
    6812             : 
    6813          16 :             if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
    6814             :             {
    6815           0 :                 CPLAssert(false);  // Just in case lower-level lib is modified
    6816             :             }
    6817           8 :         }
    6818             :     }
    6819             :     else
    6820             :     {
    6821           0 :         CPLError(
    6822             :             CE_Failure, CPLE_AssertionFailed,
    6823             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    6824           0 :             m_nMapInfoType, m_nMapInfoType);
    6825           0 :         return -1;
    6826             :     }
    6827             : 
    6828           8 :     SetGeometryDirectly(poGeometry);
    6829             : 
    6830           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    6831             : 
    6832             :     /* Copy int MBR to feature class members */
    6833           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    6834             :               poObjHdr->m_nMaxY);
    6835             : 
    6836             :     /* Return a ref to coord block so that caller can continue reading
    6837             :      * after the end of this object (used by TABCollection and index splitting)
    6838             :      */
    6839           8 :     if (ppoCoordBlock)
    6840           4 :         *ppoCoordBlock = poCoordBlock;
    6841             : 
    6842           8 :     return 0;
    6843             : }
    6844             : 
    6845             : /**********************************************************************
    6846             :  *                   TABMultiPoint::WriteGeometryToMAPFile()
    6847             :  *
    6848             :  * Write the geometry and representation (color, etc...) part of the
    6849             :  * feature to the .MAP object pointed to by poMAPFile.
    6850             :  *
    6851             :  * It is assumed that poMAPFile currently points to a valid map object.
    6852             :  *
    6853             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    6854             :  * been called.
    6855             :  **********************************************************************/
    6856           0 : int TABMultiPoint::WriteGeometryToMAPFile(
    6857             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    6858             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    6859             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    6860             : {
    6861             :     GInt32 nX, nY;
    6862             : 
    6863             :     /*-----------------------------------------------------------------
    6864             :      * We assume that ValidateMapInfoType() was called already and that
    6865             :      * the type in poObjHdr->m_nType is valid.
    6866             :      *----------------------------------------------------------------*/
    6867           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    6868             : 
    6869             :     TABMAPObjMultiPoint *poMPointHdr =
    6870           0 :         cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
    6871             : 
    6872             :     /*-----------------------------------------------------------------
    6873             :      * Fetch and validate geometry
    6874             :      *----------------------------------------------------------------*/
    6875           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6876           0 :     OGRMultiPoint *poMPoint = nullptr;
    6877           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6878           0 :         poMPoint = poGeom->toMultiPoint();
    6879             :     else
    6880             :     {
    6881           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6882             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6883           0 :         return -1;
    6884             :     }
    6885             : 
    6886           0 :     poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
    6887             : 
    6888             :     /*-----------------------------------------------------------------
    6889             :      * Write data to coordinate block
    6890             :      *----------------------------------------------------------------*/
    6891           0 :     const GBool bCompressed = poObjHdr->IsCompressedType();
    6892             : 
    6893           0 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    6894           0 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    6895           0 :         poCoordBlock = *ppoCoordBlock;
    6896             :     else
    6897           0 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    6898           0 :     poCoordBlock->StartNewFeature();
    6899           0 :     poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    6900           0 :     poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    6901             : 
    6902           0 :     for (int iPoint = 0, nStatus = 0;
    6903           0 :          nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
    6904             :     {
    6905           0 :         poGeom = poMPoint->getGeometryRef(iPoint);
    6906             : 
    6907           0 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6908             :         {
    6909           0 :             OGRPoint *poPoint = poGeom->toPoint();
    6910             : 
    6911           0 :             poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    6912           0 :             if (iPoint == 0)
    6913             :             {
    6914             :                 // Default to the first point, we may use explicit value below
    6915           0 :                 poMPointHdr->m_nLabelX = nX;
    6916           0 :                 poMPointHdr->m_nLabelY = nY;
    6917             :             }
    6918             : 
    6919           0 :             if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
    6920             :                 0)
    6921             :             {
    6922             :                 // Failed ... error message has already been produced
    6923           0 :                 return nStatus;
    6924             :             }
    6925             :         }
    6926             :         else
    6927             :         {
    6928           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    6929             :                      "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
    6930           0 :             return -1;
    6931             :         }
    6932             :     }
    6933             : 
    6934             :     /*-----------------------------------------------------------------
    6935             :      * Copy object information
    6936             :      *----------------------------------------------------------------*/
    6937             : 
    6938             :     // Compressed coordinate origin (useful only in compressed case!)
    6939           0 :     poMPointHdr->m_nComprOrgX = m_nComprOrgX;
    6940           0 :     poMPointHdr->m_nComprOrgY = m_nComprOrgY;
    6941             : 
    6942           0 :     poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    6943           0 :     poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    6944             : 
    6945             :     // Center/label point (default value already set above)
    6946           0 :     double dX = 0.0;
    6947           0 :     double dY = 0.0;
    6948           0 :     if (GetCenter(dX, dY) != -1)
    6949             :     {
    6950           0 :         poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
    6951           0 :                                 poMPointHdr->m_nLabelY);
    6952             :     }
    6953             : 
    6954           0 :     if (!bCoordBlockDataOnly)
    6955             :     {
    6956           0 :         m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    6957           0 :         poMPointHdr->m_nSymbolId =
    6958           0 :             static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    6959             :     }
    6960             : 
    6961           0 :     if (CPLGetLastErrorType() == CE_Failure)
    6962           0 :         return -1;
    6963             : 
    6964             :     /* Return a ref to coord block so that caller can continue writing
    6965             :      * after the end of this object (used by index splitting)
    6966             :      */
    6967           0 :     if (ppoCoordBlock)
    6968           0 :         *ppoCoordBlock = poCoordBlock;
    6969             : 
    6970           0 :     return 0;
    6971             : }
    6972             : 
    6973             : /**********************************************************************
    6974             :  *                   TABMultiPoint::GetXY()
    6975             :  *
    6976             :  * Return this point's X,Y coordinates.
    6977             :  **********************************************************************/
    6978           0 : int TABMultiPoint::GetXY(int i, double &dX, double &dY)
    6979             : {
    6980             :     /*-----------------------------------------------------------------
    6981             :      * Fetch and validate geometry
    6982             :      *----------------------------------------------------------------*/
    6983           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6984           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6985             :     {
    6986           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    6987             : 
    6988           0 :         if (i >= 0 && i < poMPoint->getNumGeometries() &&
    6989           0 :             (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
    6990           0 :             wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6991             :         {
    6992           0 :             OGRPoint *poPoint = poGeom->toPoint();
    6993             : 
    6994           0 :             dX = poPoint->getX();
    6995           0 :             dY = poPoint->getY();
    6996             :         }
    6997             :     }
    6998             :     else
    6999             :     {
    7000           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7001             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7002           0 :         dX = 0.0;
    7003           0 :         dY = 0.0;
    7004           0 :         return -1;
    7005             :     }
    7006             : 
    7007           0 :     return 0;
    7008             : }
    7009             : 
    7010             : /**********************************************************************
    7011             :  *                   TABMultiPoint::GetNumPoints()
    7012             :  *
    7013             :  * Return the number of points in this multipoint object
    7014             :  **********************************************************************/
    7015           0 : int TABMultiPoint::GetNumPoints()
    7016             : {
    7017             :     /*-----------------------------------------------------------------
    7018             :      * Fetch and validate geometry
    7019             :      *----------------------------------------------------------------*/
    7020           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7021           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    7022             :     {
    7023           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    7024             : 
    7025           0 :         return poMPoint->getNumGeometries();
    7026             :     }
    7027             :     else
    7028             :     {
    7029           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7030             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7031           0 :         return 0;
    7032             :     }
    7033             : }
    7034             : 
    7035             : /**********************************************************************
    7036             :  *                   TABMultiPoint::GetStyleString() const
    7037             :  *
    7038             :  * Return style string for this feature.
    7039             :  *
    7040             :  * Style String is built only once during the first call to GetStyleString().
    7041             :  **********************************************************************/
    7042           3 : const char *TABMultiPoint::GetStyleString() const
    7043             : {
    7044           3 :     if (m_pszStyleString == nullptr)
    7045             :     {
    7046           3 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    7047             :     }
    7048             : 
    7049           3 :     return m_pszStyleString;
    7050             : }
    7051             : 
    7052             : /**********************************************************************
    7053             :  *                   TABMultiPoint::GetCenter()
    7054             :  *
    7055             :  * Returns the center point (or label point?) of the object.  Compute one
    7056             :  * if it was not explicitly set:
    7057             :  *
    7058             :  * The default seems to be to use the first point in the collection as
    7059             :  * the center.. so we'll use that.
    7060             :  *
    7061             :  * Returns 0 on success, -1 on error.
    7062             :  **********************************************************************/
    7063           0 : int TABMultiPoint::GetCenter(double &dX, double &dY)
    7064             : {
    7065           0 :     if (!m_bCenterIsSet && GetNumPoints() > 0)
    7066             :     {
    7067             :         // The default seems to be to use the first point in the collection
    7068             :         // as the center... so we'll use that.
    7069           0 :         if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
    7070           0 :             m_bCenterIsSet = TRUE;
    7071             :     }
    7072             : 
    7073           0 :     if (!m_bCenterIsSet)
    7074           0 :         return -1;
    7075             : 
    7076           0 :     dX = m_dCenterX;
    7077           0 :     dY = m_dCenterY;
    7078           0 :     return 0;
    7079             : }
    7080             : 
    7081             : /**********************************************************************
    7082             :  *                   TABMultiPoint::SetCenter()
    7083             :  *
    7084             :  * Set the X,Y coordinates to use as center point (or label point?)
    7085             :  **********************************************************************/
    7086         173 : void TABMultiPoint::SetCenter(double dX, double dY)
    7087             : {
    7088         173 :     m_dCenterX = dX;
    7089         173 :     m_dCenterY = dY;
    7090         173 :     m_bCenterIsSet = TRUE;
    7091         173 : }
    7092             : 
    7093             : /**********************************************************************
    7094             :  *                   TABMultiPoint::DumpMIF()
    7095             :  *
    7096             :  * Dump feature geometry in a format similar to .MIF POINTs.
    7097             :  **********************************************************************/
    7098           0 : void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
    7099             : {
    7100           0 :     if (fpOut == nullptr)
    7101           0 :         fpOut = stdout;
    7102             : 
    7103             :     /*-----------------------------------------------------------------
    7104             :      * Fetch and validate geometry
    7105             :      *----------------------------------------------------------------*/
    7106           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7107           0 :     OGRMultiPoint *poMPoint = nullptr;
    7108           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    7109           0 :         poMPoint = poGeom->toMultiPoint();
    7110             :     else
    7111             :     {
    7112           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7113             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7114           0 :         return;
    7115             :     }
    7116             : 
    7117             :     /*-----------------------------------------------------------------
    7118             :      * Generate output
    7119             :      *----------------------------------------------------------------*/
    7120           0 :     fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
    7121             : 
    7122           0 :     for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
    7123             :     {
    7124           0 :         poGeom = poMPoint->getGeometryRef(iPoint);
    7125             : 
    7126           0 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    7127             :         {
    7128           0 :             OGRPoint *poPoint = poGeom->toPoint();
    7129           0 :             fprintf(fpOut, "  %.15g %.15g\n", poPoint->getX(), poPoint->getY());
    7130             :         }
    7131             :         else
    7132             :         {
    7133           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    7134             :                      "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
    7135           0 :             return;
    7136             :         }
    7137             :     }
    7138             : 
    7139           0 :     DumpSymbolDef(fpOut);
    7140             : 
    7141           0 :     if (m_bCenterIsSet)
    7142           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    7143             : 
    7144           0 :     fflush(fpOut);
    7145             : }
    7146             : 
    7147             : /*=====================================================================
    7148             :  *                      class TABCollection
    7149             :  *====================================================================*/
    7150             : 
    7151             : /**********************************************************************
    7152             :  *                   TABCollection::TABCollection()
    7153             :  *
    7154             :  * Constructor.
    7155             :  **********************************************************************/
    7156         103 : TABCollection::TABCollection(OGRFeatureDefn *poDefnIn)
    7157             :     : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
    7158         103 :       m_poMpoint(nullptr)
    7159             : {
    7160         103 : }
    7161             : 
    7162             : /**********************************************************************
    7163             :  *                   TABCollection::~TABCollection()
    7164             :  *
    7165             :  * Destructor.
    7166             :  **********************************************************************/
    7167         206 : TABCollection::~TABCollection()
    7168             : {
    7169         103 :     EmptyCollection();
    7170         206 : }
    7171             : 
    7172             : /**********************************************************************
    7173             :  *                   TABCollection::EmptyCollection()
    7174             :  *
    7175             :  * Delete/free all collection components.
    7176             :  **********************************************************************/
    7177         204 : void TABCollection::EmptyCollection()
    7178             : {
    7179             : 
    7180         204 :     if (m_poRegion)
    7181             :     {
    7182          54 :         delete m_poRegion;
    7183          54 :         m_poRegion = nullptr;
    7184             :     }
    7185             : 
    7186         204 :     if (m_poPline)
    7187             :     {
    7188          37 :         delete m_poPline;
    7189          37 :         m_poPline = nullptr;
    7190             :     }
    7191             : 
    7192         204 :     if (m_poMpoint)
    7193             :     {
    7194           6 :         delete m_poMpoint;
    7195           6 :         m_poMpoint = nullptr;
    7196             :     }
    7197             : 
    7198             :     // Empty OGR Geometry Collection as well
    7199         204 :     SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
    7200         204 : }
    7201             : 
    7202             : /**********************************************************************
    7203             :  *                     TABCollection::CloneTABFeature()
    7204             :  *
    7205             :  * Duplicate feature, including stuff specific to each TABFeature type.
    7206             :  *
    7207             :  * This method calls the generic TABFeature::CloneTABFeature() and
    7208             :  * then copies any members specific to its own type.
    7209             :  **********************************************************************/
    7210           0 : TABFeature *TABCollection::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    7211             : {
    7212             :     /*-----------------------------------------------------------------
    7213             :      * Alloc new feature and copy the base stuff
    7214             :      *----------------------------------------------------------------*/
    7215             :     TABCollection *poNew =
    7216           0 :         new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
    7217             : 
    7218           0 :     CopyTABFeatureBase(poNew);
    7219             : 
    7220             :     /*-----------------------------------------------------------------
    7221             :      * And members specific to this class
    7222             :      *----------------------------------------------------------------*/
    7223             : 
    7224           0 :     if (m_poRegion)
    7225           0 :         poNew->SetRegionDirectly(
    7226           0 :             cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
    7227             : 
    7228           0 :     if (m_poPline)
    7229           0 :         poNew->SetPolylineDirectly(
    7230           0 :             cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
    7231             : 
    7232           0 :     if (m_poMpoint)
    7233           0 :         poNew->SetMultiPointDirectly(
    7234           0 :             cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
    7235             : 
    7236           0 :     return poNew;
    7237             : }
    7238             : 
    7239             : /**********************************************************************
    7240             :  *                   TABCollection::ValidateMapInfoType()
    7241             :  *
    7242             :  * Check the feature's geometry part and return the corresponding
    7243             :  * mapinfo object type code.  The m_nMapInfoType member will also
    7244             :  * be updated for further calls to GetMapInfoType();
    7245             :  *
    7246             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    7247             :  * is expected for this object class.
    7248             :  **********************************************************************/
    7249           0 : TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    7250             : {
    7251           0 :     int nRegionType = TAB_GEOM_NONE;
    7252           0 :     int nPLineType = TAB_GEOM_NONE;
    7253           0 :     int nMPointType = TAB_GEOM_NONE;
    7254           0 :     int nVersion = 650;
    7255             : 
    7256             :     /*-----------------------------------------------------------------
    7257             :      * Fetch and validate geometry
    7258             :      *----------------------------------------------------------------*/
    7259           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7260           0 :     if (poGeom &&
    7261           0 :         wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
    7262             :     {
    7263           0 :         m_nMapInfoType = TAB_GEOM_COLLECTION;
    7264             :     }
    7265             :     else
    7266             :     {
    7267           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7268             :                  "TABCollection: Missing or Invalid Geometry!");
    7269           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    7270             :     }
    7271             : 
    7272             :     /*-----------------------------------------------------------------
    7273             :      * Decide if coordinates should be compressed or not.
    7274             :      *----------------------------------------------------------------*/
    7275           0 :     GBool bComprCoord = ValidateCoordType(poMapFile);
    7276             : 
    7277             :     /*-----------------------------------------------------------------
    7278             :      * Since all members of the collection share the same compressed coord
    7279             :      * origin, we should force the compressed origin in all components
    7280             :      * to be the same.
    7281             :      * This also implies that ValidateMapInfoType() should *NOT* be called
    7282             :      * again until the collection components are written by WriteGeom...()
    7283             :      *----------------------------------------------------------------*/
    7284             : 
    7285             :     // First pass to figure collection type...
    7286           0 :     if (m_poRegion)
    7287             :     {
    7288           0 :         m_poRegion->ValidateCoordType(poMapFile);
    7289           0 :         nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
    7290           0 :         if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
    7291           0 :             nVersion = TAB_GEOM_GET_VERSION(nRegionType);
    7292             :     }
    7293             : 
    7294           0 :     if (m_poPline)
    7295             :     {
    7296           0 :         m_poPline->ValidateCoordType(poMapFile);
    7297           0 :         nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
    7298           0 :         if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
    7299           0 :             nVersion = TAB_GEOM_GET_VERSION(nPLineType);
    7300             :     }
    7301             : 
    7302           0 :     if (m_poMpoint)
    7303             :     {
    7304           0 :         m_poMpoint->ValidateCoordType(poMapFile);
    7305           0 :         nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
    7306           0 :         if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
    7307           0 :             nVersion = TAB_GEOM_GET_VERSION(nMPointType);
    7308             :     }
    7309             : 
    7310             :     // Need to upgrade native type of collection?
    7311           0 :     if (nVersion == 800)
    7312             :     {
    7313           0 :         m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
    7314             :     }
    7315             : 
    7316             :     // Make another pass updating native type and coordinates type and origin
    7317             :     // of each component
    7318           0 :     if (m_poRegion && nRegionType != TAB_GEOM_NONE)
    7319             :     {
    7320           0 :         GInt32 nXMin = 0;
    7321           0 :         GInt32 nYMin = 0;
    7322           0 :         GInt32 nXMax = 0;
    7323           0 :         GInt32 nYMax = 0;
    7324           0 :         m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7325           0 :         m_poRegion->ForceCoordTypeAndOrigin(
    7326             :             (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
    7327             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7328             :             nYMax);
    7329             :     }
    7330             : 
    7331           0 :     if (m_poPline && nPLineType != TAB_GEOM_NONE)
    7332             :     {
    7333             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    7334           0 :         m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7335           0 :         m_poPline->ForceCoordTypeAndOrigin(
    7336             :             (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
    7337             :                              : TAB_GEOM_V450_MULTIPLINE),
    7338             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7339             :             nYMax);
    7340             :     }
    7341             : 
    7342           0 :     if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
    7343             :     {
    7344             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    7345           0 :         m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7346           0 :         m_poMpoint->ForceCoordTypeAndOrigin(
    7347             :             (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
    7348             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7349             :             nYMax);
    7350             :     }
    7351             : 
    7352           0 :     return m_nMapInfoType;
    7353             : }
    7354             : 
    7355             : /**********************************************************************
    7356             :  *                   TABCollection::ReadLabelAndMBR()
    7357             :  *
    7358             :  * Reads the label and MBR elements of the header of a collection component
    7359             :  *
    7360             :  * Returns 0 on success, -1 on failure.
    7361             :  **********************************************************************/
    7362          12 : int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
    7363             :                                    GBool bComprCoord, GInt32 nComprOrgX,
    7364             :                                    GInt32 nComprOrgY, GInt32 &pnMinX,
    7365             :                                    GInt32 &pnMinY, GInt32 &pnMaxX,
    7366             :                                    GInt32 &pnMaxY, GInt32 &pnLabelX,
    7367             :                                    GInt32 &pnLabelY)
    7368             : {
    7369             :     //
    7370             :     // The sections in the collection's coord blocks start with center/label
    7371             :     // point + MBR that are normally found in the object data blocks
    7372             :     // of regular region/pline/mulitpoint objects.
    7373             :     //
    7374             : 
    7375          12 :     if (bComprCoord)
    7376             :     {
    7377             :         // Region center/label point, relative to compr. coord. origin
    7378             :         // No it is not relative to the Object block center
    7379          12 :         pnLabelX = poCoordBlock->ReadInt16();
    7380          12 :         pnLabelY = poCoordBlock->ReadInt16();
    7381             : 
    7382          12 :         TABSaturatedAdd(pnLabelX, nComprOrgX);
    7383          12 :         TABSaturatedAdd(pnLabelY, nComprOrgY);
    7384             : 
    7385          12 :         pnMinX = poCoordBlock->ReadInt16();  // Read MBR
    7386          12 :         pnMinY = poCoordBlock->ReadInt16();
    7387          12 :         pnMaxX = poCoordBlock->ReadInt16();
    7388          12 :         pnMaxY = poCoordBlock->ReadInt16();
    7389          12 :         TABSaturatedAdd(pnMinX, nComprOrgX);
    7390          12 :         TABSaturatedAdd(pnMinY, nComprOrgY);
    7391          12 :         TABSaturatedAdd(pnMaxX, nComprOrgX);
    7392          12 :         TABSaturatedAdd(pnMaxY, nComprOrgY);
    7393             :     }
    7394             :     else
    7395             :     {
    7396             :         // Region center/label point, relative to compr. coord. origin
    7397             :         // No it is not relative to the Object block center
    7398           0 :         pnLabelX = poCoordBlock->ReadInt32();
    7399           0 :         pnLabelY = poCoordBlock->ReadInt32();
    7400             : 
    7401           0 :         pnMinX = poCoordBlock->ReadInt32();  // Read MBR
    7402           0 :         pnMinY = poCoordBlock->ReadInt32();
    7403           0 :         pnMaxX = poCoordBlock->ReadInt32();
    7404           0 :         pnMaxY = poCoordBlock->ReadInt32();
    7405             :     }
    7406             : 
    7407          12 :     return 0;
    7408             : }
    7409             : 
    7410             : /**********************************************************************
    7411             :  *                   TABCollection::WriteLabelAndMBR()
    7412             :  *
    7413             :  * Writes the label and MBR elements of the header of a collection component
    7414             :  *
    7415             :  * Returns 0 on success, -1 on failure.
    7416             :  **********************************************************************/
    7417           0 : int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
    7418             :                                     GBool bComprCoord, GInt32 nMinX,
    7419             :                                     GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
    7420             :                                     GInt32 nLabelX, GInt32 nLabelY)
    7421             : {
    7422             :     //
    7423             :     // The sections in the collection's coord blocks start with center/label
    7424             :     // point + MBR that are normally found in the object data blocks
    7425             :     // of regular region/pline/mulitpoint objects.
    7426             :     //
    7427             : 
    7428           0 :     int nStatus = 0;
    7429           0 :     if ((nStatus =
    7430           0 :              poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
    7431           0 :         (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
    7432           0 :             0 ||
    7433           0 :         (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
    7434             :     {
    7435             :         // Failed ... error message has already been produced
    7436           0 :         return nStatus;
    7437             :     }
    7438             : 
    7439           0 :     return 0;
    7440             : }
    7441             : 
    7442             : /**********************************************************************
    7443             :  *                   TABCollection::ReadGeometryFromMAPFile()
    7444             :  *
    7445             :  * Fill the geometry and representation (color, etc...) part of the
    7446             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    7447             :  *
    7448             :  * It is assumed that poMAPFile currently points to the beginning of
    7449             :  * a map object.
    7450             :  *
    7451             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    7452             :  * been called.
    7453             :  **********************************************************************/
    7454           4 : int TABCollection::ReadGeometryFromMAPFile(
    7455             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    7456             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    7457             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    7458             : {
    7459           4 :     const GBool bComprCoord = poObjHdr->IsCompressedType();
    7460             : 
    7461             :     /*-----------------------------------------------------------------
    7462             :      * Fetch and validate geometry type
    7463             :      *----------------------------------------------------------------*/
    7464           4 :     m_nMapInfoType = poObjHdr->m_nType;
    7465             : 
    7466           4 :     if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
    7467           4 :         m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
    7468           0 :         m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
    7469           0 :         m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
    7470             :     {
    7471           0 :         CPLError(
    7472             :             CE_Failure, CPLE_AssertionFailed,
    7473             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    7474           0 :             m_nMapInfoType, m_nMapInfoType);
    7475           0 :         return -1;
    7476             :     }
    7477             : 
    7478           4 :     int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    7479             : 
    7480             :     // Make sure collection is empty
    7481           4 :     EmptyCollection();
    7482             : 
    7483             :     /*-------------------------------------------------------------
    7484             :      * Copy data from poObjHdr
    7485             :      *------------------------------------------------------------*/
    7486             :     TABMAPObjCollection *poCollHdr =
    7487           4 :         cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
    7488             : 
    7489             :     // MBR
    7490           4 :     double dXMin = 0.0;
    7491           4 :     double dYMin = 0.0;
    7492           4 :     double dXMax = 0.0;
    7493           4 :     double dYMax = 0.0;
    7494           4 :     poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
    7495             :                             dYMin);
    7496           4 :     poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
    7497             :                             dYMax);
    7498             : 
    7499           4 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    7500             : 
    7501           4 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    7502             :               poObjHdr->m_nMaxY);
    7503             : 
    7504           4 :     int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
    7505           4 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    7506           4 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    7507           0 :         poCoordBlock = *ppoCoordBlock;
    7508             :     else
    7509           4 :         poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
    7510             : 
    7511             :     // Compressed coordinate origin (useful only in compressed case!)
    7512           4 :     m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7513           4 :     m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7514             : 
    7515             :     /*-----------------------------------------------------------------
    7516             :      * Region Component
    7517             :      *----------------------------------------------------------------*/
    7518           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
    7519             :     {
    7520             :         //
    7521             :         // Build fake coord section header to pass to TABRegion::ReadGeom...()
    7522             :         //
    7523           4 :         TABMAPObjPLine oRegionHdr;
    7524             : 
    7525           4 :         oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7526           4 :         oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7527             : 
    7528             :         //
    7529             :         // The region section in the coord block starts with center/label
    7530             :         // point + MBR that are normally found in the object data blocks
    7531             :         // of regular region objects.
    7532             :         //
    7533             : 
    7534             :         // In V800 the mini-header starts with a copy of num_parts
    7535           4 :         if (nVersion >= 800)
    7536             :         {
    7537             :             // int numParts = poCoordBlock->ReadInt32();
    7538           0 :             CPLAssert(poCoordBlock->ReadInt32() ==
    7539             :                       poCollHdr->m_nNumRegSections);
    7540             :         }
    7541             : 
    7542           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
    7543             :                         oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
    7544             :                         oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
    7545             :                         oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
    7546             :                         oRegionHdr.m_nLabelY);
    7547             : 
    7548             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7549           4 :         oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7550             : 
    7551           4 :         if (bComprCoord)
    7552           4 :             oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
    7553             :         else
    7554           0 :             oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
    7555           4 :         if (nVersion == 800)
    7556           0 :             oRegionHdr.m_nType = static_cast<TABGeomType>(
    7557           0 :                 oRegionHdr.m_nType +
    7558             :                 (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
    7559             : 
    7560           4 :         oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
    7561           4 :         oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
    7562           4 :         oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
    7563           4 :         oRegionHdr.m_bSmooth = 0;  // TODO
    7564             : 
    7565             :         //
    7566             :         // Use a TABRegion to read/store the Region coord data
    7567             :         //
    7568           4 :         m_poRegion = new TABRegion(GetDefnRef());
    7569           4 :         if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
    7570             :                                                 bCoordBlockDataOnly,
    7571           4 :                                                 &poCoordBlock) != 0)
    7572           0 :             return -1;
    7573             : 
    7574             :         // Set new coord block ptr for next object
    7575             :         /*if (poCoordBlock)
    7576             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7577             :     }
    7578             : 
    7579             :     /*-----------------------------------------------------------------
    7580             :      * PLine Component
    7581             :      *----------------------------------------------------------------*/
    7582           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
    7583             :     {
    7584             :         //
    7585             :         // Build fake coord section header to pass to TABPolyline::ReadGeom..()
    7586             :         //
    7587           4 :         TABMAPObjPLine oPLineHdr;
    7588             : 
    7589           4 :         oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7590           4 :         oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7591             : 
    7592             :         //
    7593             :         // The pline section in the coord block starts with center/label
    7594             :         // point + MBR that are normally found in the object data blocks
    7595             :         // of regular pline objects.
    7596             :         //
    7597             : 
    7598             :         // In V800 the mini-header starts with a copy of num_parts
    7599           4 :         if (nVersion >= 800)
    7600             :         {
    7601             :             // int numParts = poCoordBlock->ReadInt32();
    7602           0 :             CPLAssert(poCoordBlock->ReadInt32() ==
    7603             :                       poCollHdr->m_nNumPLineSections);
    7604             :         }
    7605             : 
    7606           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
    7607             :                         oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
    7608             :                         oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
    7609             :                         oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
    7610             : 
    7611             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7612           4 :         oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7613             : 
    7614           4 :         if (bComprCoord)
    7615           4 :             oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
    7616             :         else
    7617           0 :             oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
    7618           4 :         if (nVersion == 800)
    7619           0 :             oPLineHdr.m_nType = static_cast<TABGeomType>(
    7620           0 :                 oPLineHdr.m_nType +
    7621             :                 (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
    7622             : 
    7623           4 :         oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
    7624           4 :         oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
    7625           4 :         oPLineHdr.m_bSmooth = 0;  // TODO
    7626             : 
    7627             :         //
    7628             :         // Use a TABPolyline to read/store the Polyline coord data
    7629             :         //
    7630           4 :         m_poPline = new TABPolyline(GetDefnRef());
    7631           4 :         if (m_poPline->ReadGeometryFromMAPFile(
    7632           4 :                 poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
    7633           0 :             return -1;
    7634             : 
    7635             :         // Set new coord block ptr for next object
    7636             :         /*if (poCoordBlock)
    7637             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7638             :     }
    7639             : 
    7640             :     /*-----------------------------------------------------------------
    7641             :      * MultiPoint Component
    7642             :      *----------------------------------------------------------------*/
    7643           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
    7644             :     {
    7645             :         //
    7646             :         // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
    7647             :         //
    7648           4 :         TABMAPObjMultiPoint oMPointHdr;
    7649             : 
    7650           4 :         oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7651           4 :         oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7652             : 
    7653             :         //
    7654             :         // The pline section in the coord block starts with center/label
    7655             :         // point + MBR that are normally found in the object data blocks
    7656             :         // of regular pline objects.
    7657             :         //
    7658           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
    7659             :                         oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
    7660             :                         oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
    7661             :                         oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
    7662             :                         oMPointHdr.m_nLabelY);
    7663             : 
    7664             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7665           4 :         oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7666             : 
    7667           4 :         if (bComprCoord)
    7668           4 :             oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
    7669             :         else
    7670           0 :             oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
    7671           4 :         if (nVersion == 800)
    7672           0 :             oMPointHdr.m_nType = static_cast<TABGeomType>(
    7673           0 :                 oMPointHdr.m_nType +
    7674             :                 (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
    7675             : 
    7676           4 :         oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
    7677           4 :         oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
    7678             : 
    7679             :         //
    7680             :         // Use a TABMultiPoint to read/store the coord data
    7681             :         //
    7682           4 :         m_poMpoint = new TABMultiPoint(GetDefnRef());
    7683           4 :         if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
    7684             :                                                 bCoordBlockDataOnly,
    7685           4 :                                                 &poCoordBlock) != 0)
    7686           0 :             return -1;
    7687             : 
    7688             :         // Set new coord block ptr for next object (not really useful here)
    7689             :         /*if (poCoordBlock)
    7690             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7691             :     }
    7692             : 
    7693             :     /*-----------------------------------------------------------------
    7694             :      * Set the main OGRFeature Geometry
    7695             :      * (this is actually duplicating geometries from each member)
    7696             :      *----------------------------------------------------------------*/
    7697           4 :     if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
    7698           0 :         return -1;
    7699             : 
    7700             :     /* Return a ref to coord block so that caller can continue reading
    7701             :      * after the end of this object (used by index splitting)
    7702             :      */
    7703           4 :     if (ppoCoordBlock)
    7704           0 :         *ppoCoordBlock = poCoordBlock;
    7705             : 
    7706           4 :     return 0;
    7707             : }
    7708             : 
    7709             : /**********************************************************************
    7710             :  *                   TABCollection::WriteGeometryToMAPFile()
    7711             :  *
    7712             :  * Write the geometry and representation (color, etc...) part of the
    7713             :  * feature to the .MAP object pointed to by poMAPFile.
    7714             :  *
    7715             :  * It is assumed that poMAPFile currently points to a valid map object.
    7716             :  *
    7717             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    7718             :  * been called.
    7719             :  **********************************************************************/
    7720           0 : int TABCollection::WriteGeometryToMAPFile(
    7721             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    7722             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    7723             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    7724             : {
    7725             :     /*-----------------------------------------------------------------
    7726             :      * Note that the current implementation does not allow setting the
    7727             :      * Geometry via OGRFeature::SetGeometry(). The geometries must be set
    7728             :      * via the SetRegion/Pline/MpointDirectly() methods which will take
    7729             :      * care of keeping the OGRFeature's geometry in sync.
    7730             :      *
    7731             :      * TODO: If we ever want to support sync'ing changes from the OGRFeature's
    7732             :      * geometry to the m_poRegion/Pline/Mpoint then a call should be added
    7733             :      * here, or perhaps in ValidateMapInfoType(), or even better in
    7734             :      * custom TABCollection::SetGeometry*()... but then this last option
    7735             :      * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
    7736             :      *----------------------------------------------------------------*/
    7737             : 
    7738             :     /*-----------------------------------------------------------------
    7739             :      * We assume that ValidateMapInfoType() was called already and that
    7740             :      * the type in poObjHdr->m_nType is valid.
    7741             :      *----------------------------------------------------------------*/
    7742           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    7743             : 
    7744             :     TABMAPObjCollection *poCollHdr =
    7745           0 :         cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
    7746             : 
    7747             :     /*-----------------------------------------------------------------
    7748             :      * Write data to coordinate block for each component...
    7749             :      *
    7750             :      * Note that at this point, the caller (TABFile) has called
    7751             :      * TABCollection::ValidateMapInfoType() which in turn has called
    7752             :      * each component's respective ValidateMapInfoType() and
    7753             :      * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
    7754             :      * their respective WriteGeometryToMapFile() called.
    7755             :      *----------------------------------------------------------------*/
    7756           0 :     const GBool bCompressed = poObjHdr->IsCompressedType();
    7757             :     // TODO: ??? Do we need to track overall collection coord data size???
    7758           0 :     int nTotalFeatureDataSize = 0;
    7759             : 
    7760           0 :     const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    7761             : 
    7762           0 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    7763           0 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    7764           0 :         poCoordBlock = *ppoCoordBlock;
    7765             :     else
    7766           0 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    7767           0 :     poCoordBlock->StartNewFeature();
    7768           0 :     poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7769           0 :     poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    7770             : 
    7771             :     /*-----------------------------------------------------------------
    7772             :      * Region component
    7773             :      *----------------------------------------------------------------*/
    7774           0 :     if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
    7775             :     {
    7776           0 :         CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
    7777             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
    7778             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
    7779             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
    7780             : 
    7781           0 :         TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
    7782           0 :             TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
    7783             : 
    7784             :         // Update count of objects by type in header
    7785           0 :         if (!bCoordBlockDataOnly)
    7786           0 :             poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
    7787             : 
    7788             :         // Write a placeholder for centroid/label point and MBR mini-header
    7789             :         // and we'll come back later to write the real values.
    7790             :         //
    7791             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7792             :         // StartNewFeature() as well, so we need to track the current
    7793             :         // value before calling it
    7794             : 
    7795           0 :         poCoordBlock->StartNewFeature();
    7796           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7797             : 
    7798             :         // In V800 the mini-header starts with a copy of num_parts
    7799           0 :         if (nVersion >= 800)
    7800             :         {
    7801           0 :             poCoordBlock->WriteInt32(0);
    7802             :         }
    7803           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7804           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7805             : 
    7806           0 :         if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
    7807             :                                                bCoordBlockDataOnly,
    7808           0 :                                                &poCoordBlock) != 0)
    7809             :         {
    7810           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7811             :                      "Failed writing Region part in collection.");
    7812           0 :             delete poRegionHdr;
    7813           0 :             return -1;
    7814             :         }
    7815             : 
    7816           0 :         nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
    7817             : 
    7818             :         // Come back to write the real values in the mini-header
    7819           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7820           0 :         poCoordBlock->StartNewFeature();
    7821             : 
    7822           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7823             :         {
    7824           0 :             delete poRegionHdr;
    7825           0 :             return -1;
    7826             :         }
    7827             : 
    7828             :         // In V800 the mini-header starts with a copy of num_parts
    7829           0 :         if (nVersion >= 800)
    7830             :         {
    7831           0 :             poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
    7832             :         }
    7833           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
    7834             :                          poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
    7835             :                          poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
    7836             :                          poRegionHdr->m_nLabelY);
    7837             : 
    7838             :         // And finally move the pointer back to the end of this component
    7839           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    7840             :         {
    7841           0 :             delete poRegionHdr;
    7842           0 :             return -1;
    7843             :         }
    7844             : 
    7845             :         // Copy other header members to the main collection header
    7846             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    7847             :         //       mini-header???
    7848           0 :         poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
    7849           0 :         poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
    7850             : 
    7851           0 :         if (!bCoordBlockDataOnly)
    7852             :         {
    7853           0 :             poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
    7854           0 :             poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
    7855             :             // TODO: Smooth flag         = poRegionHdr->m_bSmooth;
    7856             :         }
    7857             : 
    7858           0 :         delete poRegionHdr;
    7859             :     }
    7860             :     else
    7861             :     {
    7862             :         // No Region component. Set corresponding header fields to 0
    7863             : 
    7864           0 :         poCollHdr->m_nRegionDataSize = 0;
    7865           0 :         poCollHdr->m_nNumRegSections = 0;
    7866           0 :         poCollHdr->m_nRegionPenId = 0;
    7867           0 :         poCollHdr->m_nRegionBrushId = 0;
    7868             :     }
    7869             : 
    7870             :     /*-----------------------------------------------------------------
    7871             :      * PLine component
    7872             :      *----------------------------------------------------------------*/
    7873           0 :     if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
    7874             :     {
    7875           0 :         CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
    7876             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
    7877             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
    7878             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
    7879             : 
    7880           0 :         TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
    7881           0 :             TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
    7882             : 
    7883             :         // Update count of objects by type in header
    7884           0 :         if (!bCoordBlockDataOnly)
    7885           0 :             poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
    7886             : 
    7887             :         // Write a placeholder for centroid/label point and MBR mini-header
    7888             :         // and we'll come back later to write the real values.
    7889             :         //
    7890             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7891             :         // StartNewFeature() as well, so we need to track the current
    7892             :         // value before calling it
    7893             : 
    7894           0 :         poCoordBlock->StartNewFeature();
    7895           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7896             : 
    7897             :         // In V800 the mini-header starts with a copy of num_parts
    7898           0 :         if (nVersion >= 800)
    7899             :         {
    7900           0 :             poCoordBlock->WriteInt32(0);
    7901             :         }
    7902           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7903           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7904             : 
    7905           0 :         if (m_poPline->WriteGeometryToMAPFile(
    7906           0 :                 poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
    7907             :         {
    7908           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7909             :                      "Failed writing Region part in collection.");
    7910           0 :             delete poPlineHdr;
    7911           0 :             return -1;
    7912             :         }
    7913             : 
    7914           0 :         nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
    7915             : 
    7916             :         // Come back to write the real values in the mini-header
    7917           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7918           0 :         poCoordBlock->StartNewFeature();
    7919             : 
    7920           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7921             :         {
    7922           0 :             delete poPlineHdr;
    7923           0 :             return -1;
    7924             :         }
    7925             : 
    7926             :         // In V800 the mini-header starts with a copy of num_parts
    7927           0 :         if (nVersion >= 800)
    7928             :         {
    7929           0 :             poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
    7930             :         }
    7931           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
    7932             :                          poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
    7933             :                          poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
    7934             :                          poPlineHdr->m_nLabelY);
    7935             : 
    7936             :         // And finally move the pointer back to the end of this component
    7937           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    7938             :         {
    7939           0 :             delete poPlineHdr;
    7940           0 :             return -1;
    7941             :         }
    7942             : 
    7943             :         // Copy other header members to the main collection header
    7944             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    7945             :         //       mini-header???
    7946           0 :         poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
    7947           0 :         poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
    7948           0 :         if (!bCoordBlockDataOnly)
    7949             :         {
    7950           0 :             poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
    7951             :             // TODO: Smooth flag           = poPlineHdr->m_bSmooth;
    7952             :         }
    7953             : 
    7954           0 :         delete poPlineHdr;
    7955             :     }
    7956             :     else
    7957             :     {
    7958             :         // No Polyline component. Set corresponding header fields to 0
    7959             : 
    7960           0 :         poCollHdr->m_nPolylineDataSize = 0;
    7961           0 :         poCollHdr->m_nNumPLineSections = 0;
    7962           0 :         poCollHdr->m_nPolylinePenId = 0;
    7963             :     }
    7964             : 
    7965             :     /*-----------------------------------------------------------------
    7966             :      * MultiPoint component
    7967             :      *----------------------------------------------------------------*/
    7968           0 :     if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
    7969             :     {
    7970           0 :         CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
    7971             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
    7972             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
    7973             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
    7974             : 
    7975             :         TABMAPObjMultiPoint *poMpointHdr =
    7976           0 :             cpl::down_cast<TABMAPObjMultiPoint *>(
    7977           0 :                 TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
    7978             : 
    7979             :         // Update count of objects by type in header
    7980           0 :         if (!bCoordBlockDataOnly)
    7981           0 :             poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
    7982             : 
    7983             :         // Write a placeholder for centroid/label point and MBR mini-header
    7984             :         // and we'll come back later to write the real values.
    7985             :         //
    7986             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7987             :         // StartNewFeature() as well, so we need to track the current
    7988             :         // value before calling it
    7989             : 
    7990           0 :         poCoordBlock->StartNewFeature();
    7991           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7992             : 
    7993           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7994           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7995             : 
    7996           0 :         if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
    7997             :                                                bCoordBlockDataOnly,
    7998           0 :                                                &poCoordBlock) != 0)
    7999             :         {
    8000           0 :             CPLError(CE_Failure, CPLE_FileIO,
    8001             :                      "Failed writing Region part in collection.");
    8002           0 :             delete poMpointHdr;
    8003           0 :             return -1;
    8004             :         }
    8005             : 
    8006           0 :         nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
    8007             : 
    8008             :         // Come back to write the real values in the mini-header
    8009           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    8010           0 :         poCoordBlock->StartNewFeature();
    8011             : 
    8012           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    8013             :         {
    8014           0 :             delete poMpointHdr;
    8015           0 :             return -1;
    8016             :         }
    8017             : 
    8018           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
    8019             :                          poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
    8020             :                          poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
    8021             :                          poMpointHdr->m_nLabelY);
    8022             : 
    8023             :         // And finally move the pointer back to the end of this component
    8024           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    8025             :         {
    8026           0 :             delete poMpointHdr;
    8027           0 :             return -1;
    8028             :         }
    8029             : 
    8030             :         // Copy other header members to the main collection header
    8031             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    8032             :         //       mini-header???
    8033           0 :         poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
    8034           0 :         poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
    8035           0 :         if (!bCoordBlockDataOnly)
    8036             :         {
    8037           0 :             poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
    8038             :         }
    8039             : 
    8040           0 :         delete poMpointHdr;
    8041             :     }
    8042             :     else
    8043             :     {
    8044             :         // No Multipoint component. Set corresponding header fields to 0
    8045             : 
    8046           0 :         poCollHdr->m_nMPointDataSize = 0;
    8047           0 :         poCollHdr->m_nNumMultiPoints = 0;
    8048           0 :         poCollHdr->m_nMultiPointSymbolId = 0;
    8049             :     }
    8050             : 
    8051             :     /*-----------------------------------------------------------------
    8052             :      * Copy object information
    8053             :      *----------------------------------------------------------------*/
    8054             : 
    8055             :     // Compressed coordinate origin (useful only in compressed case!)
    8056           0 :     poCollHdr->m_nComprOrgX = m_nComprOrgX;
    8057           0 :     poCollHdr->m_nComprOrgY = m_nComprOrgY;
    8058             : 
    8059           0 :     poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
    8060             : 
    8061           0 :     poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    8062             : 
    8063           0 :     if (CPLGetLastErrorType() == CE_Failure)
    8064           0 :         return -1;
    8065             : 
    8066             :     /* Return a ref to coord block so that caller can continue writing
    8067             :      * after the end of this object (used by index splitting)
    8068             :      */
    8069           0 :     if (ppoCoordBlock)
    8070           0 :         *ppoCoordBlock = poCoordBlock;
    8071             : 
    8072           0 :     return 0;
    8073             : }
    8074             : 
    8075             : /**********************************************************************
    8076             :  *                   TABCollection::SyncOGRGeometryCollection()
    8077             :  *
    8078             :  * Copy the region/pline/multipoint's geometries to the OGRFeature's
    8079             :  * geometry.
    8080             :  **********************************************************************/
    8081         208 : int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
    8082             :                                              GBool bSyncPline,
    8083             :                                              GBool bSyncMpoint)
    8084             : {
    8085         208 :     OGRGeometry *poThisGeom = GetGeometryRef();
    8086         208 :     OGRGeometryCollection *poGeomColl = nullptr;
    8087             : 
    8088             :     // poGeometry is defined in the OGRFeature class
    8089         208 :     if (poThisGeom == nullptr)
    8090             :     {
    8091         103 :         poGeomColl = new OGRGeometryCollection();
    8092             :     }
    8093         105 :     else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
    8094             :     {
    8095         105 :         poGeomColl = poThisGeom->toGeometryCollection();
    8096             :     }
    8097             :     else
    8098             :     {
    8099           0 :         CPLError(
    8100             :             CE_Failure, CPLE_AssertionFailed,
    8101             :             "TABCollection: Invalid Geometry. Type must be OGRCollection.");
    8102           0 :         return -1;
    8103             :     }
    8104             : 
    8105             :     /*-----------------------------------------------------------------
    8106             :      * Start by removing geometries that need to be replaced
    8107             :      * In theory there should be a single geometry of each type, but
    8108             :      * just in case, we'll loop over the whole collection and delete all
    8109             :      * instances of each type if there are some.
    8110             :      *----------------------------------------------------------------*/
    8111         208 :     int numGeometries = poGeomColl->getNumGeometries();
    8112         220 :     for (int i = 0; i < numGeometries; i++)
    8113             :     {
    8114          12 :         OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
    8115          12 :         if (!poGeom)
    8116           0 :             continue;
    8117             : 
    8118          24 :         if ((bSyncRegion &&
    8119          12 :              (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    8120           6 :               wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
    8121           6 :             (bSyncPline &&
    8122           6 :              (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
    8123          30 :               wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
    8124           6 :             (bSyncMpoint &&
    8125           6 :              (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
    8126             :         {
    8127             :             // Remove this geometry
    8128          12 :             poGeomColl->removeGeometry(i);
    8129             : 
    8130             :             // Unless this was the last geometry, we need to restart
    8131             :             // scanning the collection since we modified it
    8132          12 :             if (i != numGeometries - 1)
    8133             :             {
    8134           6 :                 i = 0;
    8135           6 :                 numGeometries = poGeomColl->getNumGeometries();
    8136             :             }
    8137             :         }
    8138             :     }
    8139             : 
    8140             :     /*-----------------------------------------------------------------
    8141             :      * Copy TAB Feature geometries to OGRGeometryCollection
    8142             :      *----------------------------------------------------------------*/
    8143         208 :     if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
    8144           4 :         poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
    8145             : 
    8146         208 :     if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
    8147           4 :         poGeomColl->addGeometry(m_poPline->GetGeometryRef());
    8148             : 
    8149         208 :     if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
    8150           4 :         poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
    8151             : 
    8152         208 :     if (poThisGeom == nullptr)
    8153         103 :         SetGeometryDirectly(poGeomColl);
    8154             : 
    8155         208 :     return 0;
    8156             : }
    8157             : 
    8158             : /**********************************************************************
    8159             :  *                   TABCollection::SetRegionDirectly()
    8160             :  *
    8161             :  * Set the region component of the collection, deleting the current
    8162             :  * region component if there is one. The object is then owned by the
    8163             :  * TABCollection object. Passing NULL just deletes it.
    8164             :  *
    8165             :  * Note that an intentional side-effect is that calling this method
    8166             :  * with the same poRegion pointer that is already owned by this object
    8167             :  * will force resync'ing the OGR Geometry member.
    8168             :  **********************************************************************/
    8169           0 : int TABCollection::SetRegionDirectly(TABRegion *poRegion)
    8170             : {
    8171           0 :     if (m_poRegion && m_poRegion != poRegion)
    8172           0 :         delete m_poRegion;
    8173           0 :     m_poRegion = poRegion;
    8174             : 
    8175             :     // Update OGRGeometryCollection component as well
    8176           0 :     return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
    8177             : }
    8178             : 
    8179             : /**********************************************************************
    8180             :  *                   TABCollection::SetPolylineDirectly()
    8181             :  *
    8182             :  * Set the polyline component of the collection, deleting the current
    8183             :  * polyline component if there is one. The object is then owned by the
    8184             :  * TABCollection object. Passing NULL just deletes it.
    8185             :  *
    8186             :  * Note that an intentional side-effect is that calling this method
    8187             :  * with the same poPline pointer that is already owned by this object
    8188             :  * will force resync'ing the OGR Geometry member.
    8189             :  **********************************************************************/
    8190           0 : int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
    8191             : {
    8192           0 :     if (m_poPline && m_poPline != poPline)
    8193           0 :         delete m_poPline;
    8194           0 :     m_poPline = poPline;
    8195             : 
    8196             :     // Update OGRGeometryCollection component as well
    8197           0 :     return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
    8198             : }
    8199             : 
    8200             : /**********************************************************************
    8201             :  *                   TABCollection::SetMultiPointDirectly()
    8202             :  *
    8203             :  * Set the multipoint component of the collection, deleting the current
    8204             :  * multipoint component if there is one. The object is then owned by the
    8205             :  * TABCollection object. Passing NULL just deletes it.
    8206             :  *
    8207             :  * Note that an intentional side-effect is that calling this method
    8208             :  * with the same poMpoint pointer that is already owned by this object
    8209             :  * will force resync'ing the OGR Geometry member.
    8210             :  **********************************************************************/
    8211           0 : int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
    8212             : {
    8213           0 :     if (m_poMpoint && m_poMpoint != poMpoint)
    8214           0 :         delete m_poMpoint;
    8215           0 :     m_poMpoint = poMpoint;
    8216             : 
    8217             :     // Update OGRGeometryCollection component as well
    8218           0 :     return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
    8219             : }
    8220             : 
    8221             : /**********************************************************************
    8222             :  *                   TABCollection::GetStyleString() const
    8223             :  *
    8224             :  * Return style string for this feature.
    8225             :  *
    8226             :  * Style String is built only once during the first call to GetStyleString().
    8227             :  **********************************************************************/
    8228           3 : const char *TABCollection::GetStyleString() const
    8229             : {
    8230           3 :     if (m_pszStyleString == nullptr)
    8231             :     {
    8232           3 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    8233             :     }
    8234             : 
    8235           3 :     return m_pszStyleString;
    8236             : }
    8237             : 
    8238             : /**********************************************************************
    8239             :  *                   TABCollection::DumpMIF()
    8240             :  *
    8241             :  * Dump feature geometry
    8242             :  **********************************************************************/
    8243           0 : void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
    8244             : {
    8245           0 :     if (fpOut == nullptr)
    8246           0 :         fpOut = stdout;
    8247             : 
    8248             :     /*-----------------------------------------------------------------
    8249             :      * Generate output
    8250             :      *----------------------------------------------------------------*/
    8251           0 :     int numParts = 0;
    8252           0 :     if (m_poRegion)
    8253           0 :         numParts++;
    8254           0 :     if (m_poPline)
    8255           0 :         numParts++;
    8256           0 :     if (m_poMpoint)
    8257           0 :         numParts++;
    8258             : 
    8259           0 :     fprintf(fpOut, "COLLECTION %d\n", numParts);
    8260             : 
    8261           0 :     if (m_poRegion)
    8262           0 :         m_poRegion->DumpMIF(fpOut);
    8263             : 
    8264           0 :     if (m_poPline)
    8265           0 :         m_poPline->DumpMIF(fpOut);
    8266             : 
    8267           0 :     if (m_poMpoint)
    8268           0 :         m_poMpoint->DumpMIF(fpOut);
    8269             : 
    8270           0 :     DumpSymbolDef(fpOut);
    8271             : 
    8272           0 :     fflush(fpOut);
    8273           0 : }
    8274             : 
    8275             : /*=====================================================================
    8276             :  *                      class TABDebugFeature
    8277             :  *====================================================================*/
    8278             : 
    8279             : /**********************************************************************
    8280             :  *                   TABDebugFeature::TABDebugFeature()
    8281             :  *
    8282             :  * Constructor.
    8283             :  **********************************************************************/
    8284           0 : TABDebugFeature::TABDebugFeature(OGRFeatureDefn *poDefnIn)
    8285           0 :     : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
    8286             : {
    8287           0 :     memset(m_abyBuf, 0, sizeof(m_abyBuf));
    8288           0 : }
    8289             : 
    8290             : /**********************************************************************
    8291             :  *                   TABDebugFeature::~TABDebugFeature()
    8292             :  *
    8293             :  * Destructor.
    8294             :  **********************************************************************/
    8295           0 : TABDebugFeature::~TABDebugFeature()
    8296             : {
    8297           0 : }
    8298             : 
    8299             : /**********************************************************************
    8300             :  *                   TABDebugFeature::ReadGeometryFromMAPFile()
    8301             :  *
    8302             :  * Fill the geometry and representation (color, etc...) part of the
    8303             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    8304             :  *
    8305             :  * It is assumed that poMAPFile currently points to the beginning of
    8306             :  * a map object.
    8307             :  *
    8308             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    8309             :  * been called.
    8310             :  **********************************************************************/
    8311           0 : int TABDebugFeature::ReadGeometryFromMAPFile(
    8312             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    8313             :     GBool /*bCoordBlockDataOnly=FALSE*/,
    8314             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    8315             : {
    8316             :     /*-----------------------------------------------------------------
    8317             :      * Fetch geometry type
    8318             :      *----------------------------------------------------------------*/
    8319           0 :     m_nMapInfoType = poObjHdr->m_nType;
    8320             : 
    8321           0 :     TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
    8322           0 :     TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
    8323             : 
    8324             :     /*-----------------------------------------------------------------
    8325             :      * If object type has coords in a type 3 block, then its position
    8326             :      * follows
    8327             :      *----------------------------------------------------------------*/
    8328           0 :     if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
    8329             :     {
    8330           0 :         m_nCoordDataPtr = poObjBlock->ReadInt32();
    8331           0 :         m_nCoordDataSize = poObjBlock->ReadInt32();
    8332             :     }
    8333             :     else
    8334             :     {
    8335           0 :         m_nCoordDataPtr = -1;
    8336           0 :         m_nCoordDataSize = 0;
    8337             :     }
    8338             : 
    8339           0 :     m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
    8340           0 :     if (m_nSize > 0)
    8341             :     {
    8342           0 :         poObjBlock->GotoByteRel(-5);  // Go back to beginning of header
    8343           0 :         poObjBlock->ReadBytes(
    8344           0 :             std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
    8345             :     }
    8346             : 
    8347           0 :     return 0;
    8348             : }
    8349             : 
    8350             : /**********************************************************************
    8351             :  *                   TABDebugFeature::WriteGeometryToMAPFile()
    8352             :  *
    8353             :  * Write the geometry and representation (color, etc...) part of the
    8354             :  * feature to the .MAP object pointed to by poMAPFile.
    8355             :  *
    8356             :  * It is assumed that poMAPFile currently points to a valid map object.
    8357             :  *
    8358             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    8359             :  * been called.
    8360             :  **********************************************************************/
    8361           0 : int TABDebugFeature::WriteGeometryToMAPFile(
    8362             :     TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
    8363             :     GBool /*bCoordBlockDataOnly=FALSE*/,
    8364             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    8365             : {
    8366             :     // Nothing to do here!
    8367             : 
    8368           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    8369             :              "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
    8370             : 
    8371           0 :     return -1;
    8372             : }
    8373             : 
    8374             : /**********************************************************************
    8375             :  *                   TABDebugFeature::DumpMIF()
    8376             :  *
    8377             :  * Dump feature contents... available only in DEBUG mode.
    8378             :  **********************************************************************/
    8379           0 : void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
    8380             : {
    8381           0 :     if (fpOut == nullptr)
    8382           0 :         fpOut = stdout;
    8383             : 
    8384           0 :     fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
    8385           0 :             GetMapInfoType());
    8386           0 :     fprintf(fpOut, "  Object size: %d bytes\n", m_nSize);
    8387           0 :     fprintf(fpOut, "  m_nCoordDataPtr  = %d\n", m_nCoordDataPtr);
    8388           0 :     fprintf(fpOut, "  m_nCoordDataSize = %d\n", m_nCoordDataSize);
    8389           0 :     fprintf(fpOut, "  ");
    8390             : 
    8391           0 :     for (int i = 0; i < m_nSize; i++)
    8392           0 :         fprintf(fpOut, " %2.2x", m_abyBuf[i]);
    8393             : 
    8394           0 :     fprintf(fpOut, "  \n");
    8395             : 
    8396           0 :     fflush(fpOut);
    8397           0 : }
    8398             : 
    8399             : /*=====================================================================
    8400             :  *                      class ITABFeaturePen
    8401             :  *====================================================================*/
    8402             : 
    8403             : /**********************************************************************
    8404             :  *                   ITABFeaturePen::ITABFeaturePen()
    8405             :  **********************************************************************/
    8406             : 
    8407             : // MI default is PEN(1, 2, 0)
    8408             : static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
    8409             : 
    8410        7550 : ITABFeaturePen::ITABFeaturePen()
    8411        7550 :     : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
    8412             : {
    8413        7550 : }
    8414             : 
    8415             : /**********************************************************************
    8416             :  *                   ITABFeaturePen::GetPenWidthPixel()
    8417             :  *                   ITABFeaturePen::SetPenWidthPixel()
    8418             :  *                   ITABFeaturePen::GetPenWidthPoint()
    8419             :  *                   ITABFeaturePen::SetPenWidthPoint()
    8420             :  *
    8421             :  * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
    8422             :  * in points (value from 0.1 to 203.7 points). The default pen width
    8423             :  * in MapInfo is 1 pixel.  Pen width in points exist only in file version 450.
    8424             :  *
    8425             :  * The following methods hide the way the pen width is stored in the files.
    8426             :  *
    8427             :  * In order to establish if a given pen def had its width specified in
    8428             :  * pixels or in points, one should first call GetPenWidthPoint(), and if
    8429             :  * it returns 0 then the Pixel width should be used instead:
    8430             :  *    if (GetPenWidthPoint() == 0)
    8431             :  *       ... use pen width in points ...
    8432             :  *    else
    8433             :  *       ... use Pixel width from GetPenWidthPixel()
    8434             :  *
    8435             :  * Note that the reverse is not true: the default pixel width is always 1,
    8436             :  * even when the pen width was actually set in points.
    8437             :  **********************************************************************/
    8438             : 
    8439          95 : GByte ITABFeaturePen::GetPenWidthPixel() const
    8440             : {
    8441          95 :     return m_sPenDef.nPixelWidth;
    8442             : }
    8443             : 
    8444          27 : void ITABFeaturePen::SetPenWidthPixel(GByte val)
    8445             : {
    8446          27 :     const GByte nPixelWidthMin = 1;
    8447          27 :     const GByte nPixelWidthMax = 7;
    8448          27 :     m_sPenDef.nPixelWidth =
    8449          27 :         std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
    8450          27 :     m_sPenDef.nPointWidth = 0;
    8451          27 : }
    8452             : 
    8453           0 : double ITABFeaturePen::GetPenWidthPoint() const
    8454             : {
    8455             :     // We store point width internally as tenths of points
    8456           0 :     return m_sPenDef.nPointWidth / 10.0;
    8457             : }
    8458             : 
    8459           0 : void ITABFeaturePen::SetPenWidthPoint(double val)
    8460             : {
    8461           0 :     m_sPenDef.nPointWidth =
    8462           0 :         std::min(std::max(static_cast<int>(val * 10), 1), 2037);
    8463           0 :     m_sPenDef.nPixelWidth = 1;
    8464           0 : }
    8465             : 
    8466             : /**********************************************************************
    8467             :  *                   ITABFeaturePen::GetPenWidthMIF()
    8468             :  *                   ITABFeaturePen::SetPenWidthMIF()
    8469             :  *
    8470             :  * The MIF representation for pen width is either a value from 1 to 7
    8471             :  * for a pen width in pixels, or a value from 11 to 2047 for a pen
    8472             :  * width in points = 10 + (point_width*10)
    8473             :  **********************************************************************/
    8474          21 : int ITABFeaturePen::GetPenWidthMIF() const
    8475             : {
    8476          21 :     return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
    8477          21 :                                       : m_sPenDef.nPixelWidth);
    8478             : }
    8479             : 
    8480        2421 : void ITABFeaturePen::SetPenWidthMIF(int val)
    8481             : {
    8482        2421 :     if (val > 10)
    8483             :     {
    8484           0 :         m_sPenDef.nPointWidth = std::min((val - 10), 2037);
    8485           0 :         m_sPenDef.nPixelWidth = 0;
    8486             :     }
    8487             :     else
    8488             :     {
    8489        2421 :         m_sPenDef.nPixelWidth =
    8490        2421 :             static_cast<GByte>(std::min(std::max(val, 1), 7));
    8491        2421 :         m_sPenDef.nPointWidth = 0;
    8492             :     }
    8493        2421 : }
    8494             : 
    8495             : /**********************************************************************
    8496             :  *                   ITABFeaturePen::GetPenStyleString()
    8497             :  *
    8498             :  *  Return a PEN() string. All representations info for the pen are here.
    8499             :  **********************************************************************/
    8500          95 : const char *ITABFeaturePen::GetPenStyleString() const
    8501             : {
    8502          95 :     const char *pszStyle = nullptr;
    8503          95 :     int nOGRStyle = 0;
    8504             :     char szPattern[20];
    8505             : 
    8506          95 :     szPattern[0] = '\0';
    8507             : 
    8508             :     // For now, I only add the 25 first styles
    8509          95 :     switch (GetPenPattern())
    8510             :     {
    8511           0 :         case 1:
    8512           0 :             nOGRStyle = 1;
    8513           0 :             break;
    8514          95 :         case 2:
    8515          95 :             nOGRStyle = 0;
    8516          95 :             break;
    8517           0 :         case 3:
    8518           0 :             nOGRStyle = 3;
    8519           0 :             strcpy(szPattern, "1 1");
    8520           0 :             break;
    8521           0 :         case 4:
    8522           0 :             nOGRStyle = 3;
    8523           0 :             strcpy(szPattern, "2 1");
    8524           0 :             break;
    8525           0 :         case 5:
    8526           0 :             nOGRStyle = 3;
    8527           0 :             strcpy(szPattern, "3 1");
    8528           0 :             break;
    8529           0 :         case 6:
    8530           0 :             nOGRStyle = 3;
    8531           0 :             strcpy(szPattern, "6 1");
    8532           0 :             break;
    8533           0 :         case 7:
    8534           0 :             nOGRStyle = 4;
    8535           0 :             strcpy(szPattern, "12 2");
    8536           0 :             break;
    8537           0 :         case 8:
    8538           0 :             nOGRStyle = 4;
    8539           0 :             strcpy(szPattern, "24 4");
    8540           0 :             break;
    8541           0 :         case 9:
    8542           0 :             nOGRStyle = 3;
    8543           0 :             strcpy(szPattern, "4 3");
    8544           0 :             break;
    8545           0 :         case 10:
    8546           0 :             nOGRStyle = 5;
    8547           0 :             strcpy(szPattern, "1 4");
    8548           0 :             break;
    8549           0 :         case 11:
    8550           0 :             nOGRStyle = 3;
    8551           0 :             strcpy(szPattern, "4 6");
    8552           0 :             break;
    8553           0 :         case 12:
    8554           0 :             nOGRStyle = 3;
    8555           0 :             strcpy(szPattern, "6 4");
    8556           0 :             break;
    8557           0 :         case 13:
    8558           0 :             nOGRStyle = 4;
    8559           0 :             strcpy(szPattern, "12 12");
    8560           0 :             break;
    8561           0 :         case 14:
    8562           0 :             nOGRStyle = 6;
    8563           0 :             strcpy(szPattern, "8 2 1 2");
    8564           0 :             break;
    8565           0 :         case 15:
    8566           0 :             nOGRStyle = 6;
    8567           0 :             strcpy(szPattern, "12 1 1 1");
    8568           0 :             break;
    8569           0 :         case 16:
    8570           0 :             nOGRStyle = 6;
    8571           0 :             strcpy(szPattern, "12 1 3 1");
    8572           0 :             break;
    8573           0 :         case 17:
    8574           0 :             nOGRStyle = 6;
    8575           0 :             strcpy(szPattern, "24 6 4 6");
    8576           0 :             break;
    8577           0 :         case 18:
    8578           0 :             nOGRStyle = 7;
    8579           0 :             strcpy(szPattern, "24 3 3 3 3 3");
    8580           0 :             break;
    8581           0 :         case 19:
    8582           0 :             nOGRStyle = 7;
    8583           0 :             strcpy(szPattern, "24 3 3 3 3 3 3 3");
    8584           0 :             break;
    8585           0 :         case 20:
    8586           0 :             nOGRStyle = 7;
    8587           0 :             strcpy(szPattern, "6 3 1 3 1 3");
    8588           0 :             break;
    8589           0 :         case 21:
    8590           0 :             nOGRStyle = 7;
    8591           0 :             strcpy(szPattern, "12 2 1 2 1 2");
    8592           0 :             break;
    8593           0 :         case 22:
    8594           0 :             nOGRStyle = 7;
    8595           0 :             strcpy(szPattern, "12 2 1 2 1 2 1 2");
    8596           0 :             break;
    8597           0 :         case 23:
    8598           0 :             nOGRStyle = 6;
    8599           0 :             strcpy(szPattern, "4 1 1 1");
    8600           0 :             break;
    8601           0 :         case 24:
    8602           0 :             nOGRStyle = 7;
    8603           0 :             strcpy(szPattern, "4 1 1 1 1");
    8604           0 :             break;
    8605           0 :         case 25:
    8606           0 :             nOGRStyle = 6;
    8607           0 :             strcpy(szPattern, "4 1 1 1 2 1 1 1");
    8608           0 :             break;
    8609             : 
    8610           0 :         default:
    8611           0 :             nOGRStyle = 0;
    8612           0 :             break;
    8613             :     }
    8614             : 
    8615             :     // note - MapInfo renders all lines using a round pen cap and round pen join
    8616             :     // which are not the default values for OGR pen cap/join styles. So we need
    8617             :     // to explicitly include the cap/j parameters in these strings
    8618          95 :     if (strlen(szPattern) != 0)
    8619             :     {
    8620           0 :         if (m_sPenDef.nPointWidth > 0)
    8621           0 :             pszStyle = CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
    8622             :                                   "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
    8623           0 :                                   static_cast<int>(GetPenWidthPoint()),
    8624           0 :                                   m_sPenDef.rgbColor, GetPenPattern(),
    8625             :                                   nOGRStyle, szPattern);
    8626             :         else
    8627           0 :             pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
    8628             :                                   "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
    8629           0 :                                   GetPenWidthPixel(), m_sPenDef.rgbColor,
    8630           0 :                                   GetPenPattern(), nOGRStyle, szPattern);
    8631             :     }
    8632             :     else
    8633             :     {
    8634          95 :         if (m_sPenDef.nPointWidth > 0)
    8635             :             pszStyle =
    8636           0 :                 CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\""
    8637             :                            "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
    8638           0 :                            static_cast<int>(GetPenWidthPoint()),
    8639           0 :                            m_sPenDef.rgbColor, GetPenPattern(), nOGRStyle);
    8640             :         else
    8641          95 :             pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
    8642             :                                   "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
    8643          95 :                                   GetPenWidthPixel(), m_sPenDef.rgbColor,
    8644          95 :                                   GetPenPattern(), nOGRStyle);
    8645             :     }
    8646             : 
    8647          95 :     return pszStyle;
    8648             : }
    8649             : 
    8650             : /**********************************************************************
    8651             :  *                   ITABFeaturePen::SetPenFromStyleString()
    8652             :  *
    8653             :  *  Init the Pen properties from a style string.
    8654             :  **********************************************************************/
    8655          30 : void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
    8656             : {
    8657          30 :     GBool bIsNull = 0;
    8658             : 
    8659             :     // Use the Style Manager to retrieve all the information we need.
    8660          30 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    8661          30 :     OGRStyleTool *poStylePart = nullptr;
    8662             : 
    8663             :     // Init the StyleMgr with the StyleString.
    8664          30 :     poStyleMgr->InitStyleString(pszStyleString);
    8665             : 
    8666             :     // Retrieve the Pen info.
    8667          30 :     const int numParts = poStyleMgr->GetPartCount();
    8668          47 :     for (int i = 0; i < numParts; i++)
    8669             :     {
    8670          45 :         poStylePart = poStyleMgr->GetPart(i);
    8671          45 :         if (poStylePart == nullptr)
    8672           0 :             continue;
    8673             : 
    8674          45 :         if (poStylePart->GetType() == OGRSTCPen)
    8675             :         {
    8676          28 :             break;
    8677             :         }
    8678             :         else
    8679             :         {
    8680          17 :             delete poStylePart;
    8681          17 :             poStylePart = nullptr;
    8682             :         }
    8683             :     }
    8684             : 
    8685             :     // If the no Pen found, do nothing.
    8686          30 :     if (poStylePart == nullptr)
    8687             :     {
    8688           2 :         delete poStyleMgr;
    8689           2 :         return;
    8690             :     }
    8691             : 
    8692          28 :     OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
    8693             : 
    8694             :     // With Pen, we always want to output points or pixels (which are the same,
    8695             :     // so just use points).
    8696             :     //
    8697             :     // It's very important to set the output unit of the feature.
    8698             :     // The default value is meter. If we don't do it all numerical values
    8699             :     // will be assumed to be converted from the input unit to meter when we
    8700             :     // will get them via GetParam...() functions.
    8701             :     // See OGRStyleTool::Parse() for more details.
    8702          28 :     poPenStyle->SetUnit(OGRSTUPoints, 1);
    8703             : 
    8704             :     // Get the Pen Id or pattern
    8705          28 :     const char *pszPenName = poPenStyle->Id(bIsNull);
    8706          28 :     if (bIsNull)
    8707           1 :         pszPenName = nullptr;
    8708             : 
    8709             :     // Set the width
    8710          28 :     if (poPenStyle->Width(bIsNull) != 0.0)
    8711             :     {
    8712          27 :         const double nPenWidth = poPenStyle->Width(bIsNull);
    8713             :         // Width < 10 is a pixel
    8714          27 :         if (nPenWidth > 10)
    8715           0 :             SetPenWidthPoint(nPenWidth);
    8716             :         else
    8717          27 :             SetPenWidthPixel(static_cast<GByte>(nPenWidth));
    8718             :     }
    8719             : 
    8720             :     // Set the color
    8721          28 :     const char *pszPenColor = poPenStyle->Color(bIsNull);
    8722          28 :     if (pszPenColor != nullptr)
    8723             :     {
    8724          28 :         if (pszPenColor[0] == '#')
    8725          28 :             pszPenColor++;
    8726             :         // The Pen color is an Hexa string that need to be convert in a int
    8727             :         const GInt32 nPenColor =
    8728          28 :             static_cast<int>(strtol(pszPenColor, nullptr, 16));
    8729          28 :         SetPenColor(nPenColor);
    8730             :     }
    8731             : 
    8732          28 :     const char *pszPenPattern = nullptr;
    8733             : 
    8734             :     // Set the Id of the Pen, use Pattern if necessary.
    8735          28 :     if (pszPenName &&
    8736          27 :         (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
    8737             :     {
    8738          27 :         const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
    8739          27 :         if (pszPenId != nullptr)
    8740             :         {
    8741          27 :             const int nPenId = atoi(pszPenId + 12);
    8742          27 :             SetPenPattern(static_cast<GByte>(nPenId));
    8743             :         }
    8744             :         else
    8745             :         {
    8746           0 :             pszPenId = strstr(pszPenName, "ogr-pen-");
    8747           0 :             if (pszPenId != nullptr)
    8748             :             {
    8749           0 :                 int nPenId = atoi(pszPenId + 8);
    8750           0 :                 if (nPenId == 0)
    8751           0 :                     nPenId = 2;
    8752           0 :                 SetPenPattern(static_cast<GByte>(nPenId));
    8753             :             }
    8754          27 :         }
    8755             :     }
    8756             :     else
    8757             :     {
    8758             :         // If no Pen Id, use the Pen Pattern to retrieve the Id.
    8759           1 :         pszPenPattern = poPenStyle->Pattern(bIsNull);
    8760           1 :         if (bIsNull)
    8761           1 :             pszPenPattern = nullptr;
    8762             :         else
    8763             :         {
    8764           0 :             if (strcmp(pszPenPattern, "1 1") == 0)
    8765           0 :                 SetPenPattern(3);
    8766           0 :             else if (strcmp(pszPenPattern, "2 1") == 0)
    8767           0 :                 SetPenPattern(4);
    8768           0 :             else if (strcmp(pszPenPattern, "3 1") == 0)
    8769           0 :                 SetPenPattern(5);
    8770           0 :             else if (strcmp(pszPenPattern, "6 1") == 0)
    8771           0 :                 SetPenPattern(6);
    8772           0 :             else if (strcmp(pszPenPattern, "12 2") == 0)
    8773           0 :                 SetPenPattern(7);
    8774           0 :             else if (strcmp(pszPenPattern, "24 4") == 0)
    8775           0 :                 SetPenPattern(8);
    8776           0 :             else if (strcmp(pszPenPattern, "4 3") == 0)
    8777           0 :                 SetPenPattern(9);
    8778           0 :             else if (strcmp(pszPenPattern, "1 4") == 0)
    8779           0 :                 SetPenPattern(10);
    8780           0 :             else if (strcmp(pszPenPattern, "4 6") == 0)
    8781           0 :                 SetPenPattern(11);
    8782           0 :             else if (strcmp(pszPenPattern, "6 4") == 0)
    8783           0 :                 SetPenPattern(12);
    8784           0 :             else if (strcmp(pszPenPattern, "12 12") == 0)
    8785           0 :                 SetPenPattern(13);
    8786           0 :             else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
    8787           0 :                 SetPenPattern(14);
    8788           0 :             else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
    8789           0 :                 SetPenPattern(15);
    8790           0 :             else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
    8791           0 :                 SetPenPattern(16);
    8792           0 :             else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
    8793           0 :                 SetPenPattern(17);
    8794           0 :             else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
    8795           0 :                 SetPenPattern(18);
    8796           0 :             else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
    8797           0 :                 SetPenPattern(19);
    8798           0 :             else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
    8799           0 :                 SetPenPattern(20);
    8800           0 :             else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
    8801           0 :                 SetPenPattern(21);
    8802           0 :             else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
    8803           0 :                 SetPenPattern(22);
    8804           0 :             else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
    8805           0 :                 SetPenPattern(23);
    8806           0 :             else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
    8807           0 :                 SetPenPattern(24);
    8808           0 :             else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
    8809           0 :                 SetPenPattern(25);
    8810             :         }
    8811             :     }
    8812             : 
    8813          28 :     delete poStyleMgr;
    8814          28 :     delete poStylePart;
    8815             : 
    8816          28 :     return;
    8817             : }
    8818             : 
    8819             : /**********************************************************************
    8820             :  *                   ITABFeaturePen::DumpPenDef()
    8821             :  *
    8822             :  * Dump pen definition information.
    8823             :  **********************************************************************/
    8824           0 : void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
    8825             : {
    8826           0 :     if (fpOut == nullptr)
    8827           0 :         fpOut = stdout;
    8828             : 
    8829           0 :     fprintf(fpOut, "  m_nPenDefIndex         = %d\n", m_nPenDefIndex);
    8830           0 :     fprintf(fpOut, "  m_sPenDef.nRefCount    = %d\n", m_sPenDef.nRefCount);
    8831           0 :     fprintf(fpOut, "  m_sPenDef.nPixelWidth  = %u\n", m_sPenDef.nPixelWidth);
    8832           0 :     fprintf(fpOut, "  m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
    8833           0 :     fprintf(fpOut, "  m_sPenDef.nPointWidth  = %d\n", m_sPenDef.nPointWidth);
    8834           0 :     fprintf(fpOut, "  m_sPenDef.rgbColor     = 0x%6.6x (%d)\n",
    8835             :             m_sPenDef.rgbColor, m_sPenDef.rgbColor);
    8836             : 
    8837           0 :     fflush(fpOut);
    8838           0 : }
    8839             : 
    8840             : /*=====================================================================
    8841             :  *                      class ITABFeatureBrush
    8842             :  *====================================================================*/
    8843             : 
    8844             : /**********************************************************************
    8845             :  *                   ITABFeatureBrush::ITABFeatureBrush()
    8846             :  **********************************************************************/
    8847             : 
    8848             : // MI default is BRUSH(2, 16777215, 16777215)
    8849             : static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
    8850             : 
    8851        2504 : ITABFeatureBrush::ITABFeatureBrush()
    8852        2504 :     : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
    8853             : {
    8854        2504 : }
    8855             : 
    8856             : /**********************************************************************
    8857             :  *                   ITABFeatureBrush::GetBrushStyleString()
    8858             :  *
    8859             :  *  Return a Brush() string. All representations info for the Brush are here.
    8860             :  **********************************************************************/
    8861          50 : const char *ITABFeatureBrush::GetBrushStyleString() const
    8862             : {
    8863          50 :     const char *pszStyle = nullptr;
    8864          50 :     int nOGRStyle = 0;
    8865             :     /* char szPattern[20]; */
    8866             :     //* szPattern[0] = '\0'; */
    8867             : 
    8868          50 :     if (m_sBrushDef.nFillPattern == 1)
    8869          49 :         nOGRStyle = 1;
    8870           1 :     else if (m_sBrushDef.nFillPattern == 3)
    8871           0 :         nOGRStyle = 2;
    8872           1 :     else if (m_sBrushDef.nFillPattern == 4)
    8873           0 :         nOGRStyle = 3;
    8874           1 :     else if (m_sBrushDef.nFillPattern == 5)
    8875           0 :         nOGRStyle = 5;
    8876           1 :     else if (m_sBrushDef.nFillPattern == 6)
    8877           0 :         nOGRStyle = 4;
    8878           1 :     else if (m_sBrushDef.nFillPattern == 7)
    8879           0 :         nOGRStyle = 6;
    8880           1 :     else if (m_sBrushDef.nFillPattern == 8)
    8881           0 :         nOGRStyle = 7;
    8882             : 
    8883          50 :     if (GetBrushTransparent())
    8884             :     {
    8885             :         /* Omit BG Color for transparent brushes */
    8886           2 :         pszStyle = CPLSPrintf(
    8887             :             "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
    8888           2 :             m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
    8889             :     }
    8890             :     else
    8891             :     {
    8892          48 :         pszStyle = CPLSPrintf(
    8893             :             "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
    8894          48 :             m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
    8895          48 :             m_sBrushDef.nFillPattern, nOGRStyle);
    8896             :     }
    8897             : 
    8898          50 :     return pszStyle;
    8899             : }
    8900             : 
    8901             : /**********************************************************************
    8902             :  *                   ITABFeatureBrush::SetBrushFromStyleString()
    8903             :  *
    8904             :  *  Set all Brush elements from a StyleString.
    8905             :  *  Use StyleMgr to do so.
    8906             :  **********************************************************************/
    8907          17 : void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
    8908             : {
    8909          17 :     GBool bIsNull = 0;
    8910             : 
    8911             :     // Use the Style Manager to retrieve all the information we need.
    8912          17 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    8913          17 :     OGRStyleTool *poStylePart = nullptr;
    8914             : 
    8915             :     // Init the StyleMgr with the StyleString.
    8916          17 :     poStyleMgr->InitStyleString(pszStyleString);
    8917             : 
    8918             :     // Retrieve the Brush info.
    8919          17 :     const int numParts = poStyleMgr->GetPartCount();
    8920          17 :     for (int i = 0; i < numParts; i++)
    8921             :     {
    8922          17 :         poStylePart = poStyleMgr->GetPart(i);
    8923          17 :         if (poStylePart == nullptr)
    8924           0 :             continue;
    8925             : 
    8926          17 :         if (poStylePart->GetType() == OGRSTCBrush)
    8927             :         {
    8928          17 :             break;
    8929             :         }
    8930             :         else
    8931             :         {
    8932           0 :             delete poStylePart;
    8933           0 :             poStylePart = nullptr;
    8934             :         }
    8935             :     }
    8936             : 
    8937             :     // If the no Brush found, do nothing.
    8938          17 :     if (poStylePart == nullptr)
    8939             :     {
    8940           0 :         delete poStyleMgr;
    8941           0 :         return;
    8942             :     }
    8943             : 
    8944          17 :     OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
    8945             : 
    8946             :     // Set the Brush Id (FillPattern)
    8947          17 :     const char *pszBrushId = poBrushStyle->Id(bIsNull);
    8948          17 :     if (bIsNull)
    8949           2 :         pszBrushId = nullptr;
    8950          17 :     bool bHasBrushId = false;
    8951             : 
    8952          17 :     if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
    8953           0 :                        strstr(pszBrushId, "ogr-brush-")))
    8954             :     {
    8955          15 :         if (strstr(pszBrushId, "mapinfo-brush-"))
    8956             :         {
    8957          15 :             const int nBrushId = atoi(pszBrushId + 14);
    8958          15 :             SetBrushPattern(static_cast<GByte>(nBrushId));
    8959          15 :             bHasBrushId = true;
    8960             :         }
    8961           0 :         else if (strstr(pszBrushId, "ogr-brush-"))
    8962             :         {
    8963           0 :             int nBrushId = atoi(pszBrushId + 10);
    8964           0 :             if (nBrushId > 1)
    8965           0 :                 nBrushId++;
    8966           0 :             SetBrushPattern(static_cast<GByte>(nBrushId));
    8967           0 :             bHasBrushId = true;
    8968             :         }
    8969             :     }
    8970             : 
    8971             :     // Set the BackColor, if not set, then it is transparent
    8972          17 :     const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
    8973          17 :     if (bIsNull)
    8974           1 :         pszBrushColor = nullptr;
    8975             : 
    8976          17 :     if (pszBrushColor)
    8977             :     {
    8978          16 :         if (pszBrushColor[0] == '#')
    8979          16 :             pszBrushColor++;
    8980          16 :         if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
    8981           1 :             pszBrushColor[7] == '0')
    8982             :         {
    8983           1 :             SetBrushTransparent(1);
    8984             :         }
    8985             :         else
    8986             :         {
    8987          30 :             CPLString osBrushColor(pszBrushColor);
    8988          15 :             if (strlen(pszBrushColor) > 6)
    8989           0 :                 osBrushColor.resize(6);
    8990             :             const int nBrushColor =
    8991          15 :                 static_cast<int>(strtol(osBrushColor, nullptr, 16));
    8992          15 :             SetBrushBGColor(static_cast<GInt32>(nBrushColor));
    8993             :         }
    8994             :     }
    8995             :     else
    8996             :     {
    8997           1 :         SetBrushTransparent(1);
    8998             :     }
    8999             : 
    9000             :     // Set the ForeColor
    9001          17 :     pszBrushColor = poBrushStyle->ForeColor(bIsNull);
    9002          17 :     if (bIsNull)
    9003           0 :         pszBrushColor = nullptr;
    9004             : 
    9005          17 :     if (pszBrushColor)
    9006             :     {
    9007          17 :         if (pszBrushColor[0] == '#')
    9008          17 :             pszBrushColor++;
    9009          17 :         if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
    9010           1 :             pszBrushColor[7] == '0')
    9011             :         {
    9012           1 :             if (!bHasBrushId)
    9013           1 :                 SetBrushPattern(static_cast<GByte>(1));  // No-fill
    9014             :         }
    9015             :         else
    9016             :         {
    9017          16 :             if (!bHasBrushId)
    9018           1 :                 SetBrushPattern(static_cast<GByte>(2));  // Solid-fill
    9019             :         }
    9020             : 
    9021          34 :         CPLString osBrushColor(pszBrushColor);
    9022          17 :         if (strlen(pszBrushColor) > 6)
    9023           1 :             osBrushColor.resize(6);
    9024             :         const int nBrushColor =
    9025          17 :             static_cast<int>(strtol(osBrushColor, nullptr, 16));
    9026          17 :         SetBrushFGColor(static_cast<GInt32>(nBrushColor));
    9027             :     }
    9028             : 
    9029          17 :     delete poStyleMgr;
    9030          17 :     delete poStylePart;
    9031             : 
    9032          17 :     return;
    9033             : }
    9034             : 
    9035             : /**********************************************************************
    9036             :  *                   ITABFeatureBrush::DumpBrushDef()
    9037             :  *
    9038             :  * Dump Brush definition information.
    9039             :  **********************************************************************/
    9040           0 : void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
    9041             : {
    9042           0 :     if (fpOut == nullptr)
    9043           0 :         fpOut = stdout;
    9044             : 
    9045           0 :     fprintf(fpOut, "  m_nBrushDefIndex         = %d\n", m_nBrushDefIndex);
    9046           0 :     fprintf(fpOut, "  m_sBrushDef.nRefCount    = %d\n", m_sBrushDef.nRefCount);
    9047           0 :     fprintf(fpOut, "  m_sBrushDef.nFillPattern = %d\n",
    9048           0 :             static_cast<int>(m_sBrushDef.nFillPattern));
    9049           0 :     fprintf(fpOut, "  m_sBrushDef.bTransparentFill = %d\n",
    9050           0 :             static_cast<int>(m_sBrushDef.bTransparentFill));
    9051           0 :     fprintf(fpOut, "  m_sBrushDef.rgbFGColor   = 0x%6.6x (%d)\n",
    9052             :             m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
    9053           0 :     fprintf(fpOut, "  m_sBrushDef.rgbBGColor   = 0x%6.6x (%d)\n",
    9054             :             m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
    9055             : 
    9056           0 :     fflush(fpOut);
    9057           0 : }
    9058             : 
    9059             : /*=====================================================================
    9060             :  *                      class ITABFeatureFont
    9061             :  *====================================================================*/
    9062             : 
    9063             : /**********************************************************************
    9064             :  *                   ITABFeatureFont::ITABFeatureFont()
    9065             :  **********************************************************************/
    9066             : 
    9067             : // MI default is Font("Arial", 0, 0, 0)
    9068             : static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
    9069             : 
    9070        1638 : ITABFeatureFont::ITABFeatureFont()
    9071        1638 :     : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
    9072             : {
    9073        1638 : }
    9074             : 
    9075             : /**********************************************************************
    9076             :  *                   ITABFeatureFont::SetFontName()
    9077             :  **********************************************************************/
    9078        1582 : void ITABFeatureFont::SetFontName(const char *pszName)
    9079             : {
    9080        1582 :     strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
    9081        1582 :     m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
    9082        1582 : }
    9083             : 
    9084             : /**********************************************************************
    9085             :  *                   ITABFeatureFont::DumpFontDef()
    9086             :  *
    9087             :  * Dump Font definition information.
    9088             :  **********************************************************************/
    9089           0 : void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
    9090             : {
    9091           0 :     if (fpOut == nullptr)
    9092           0 :         fpOut = stdout;
    9093             : 
    9094           0 :     fprintf(fpOut, "  m_nFontDefIndex       = %d\n", m_nFontDefIndex);
    9095           0 :     fprintf(fpOut, "  m_sFontDef.nRefCount  = %d\n", m_sFontDef.nRefCount);
    9096           0 :     fprintf(fpOut, "  m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
    9097             : 
    9098           0 :     fflush(fpOut);
    9099           0 : }
    9100             : 
    9101             : /*=====================================================================
    9102             :  *                      class ITABFeatureSymbol
    9103             :  *====================================================================*/
    9104             : 
    9105             : /**********************************************************************
    9106             :  *                   ITABFeatureSymbol::ITABFeatureSymbol()
    9107             :  **********************************************************************/
    9108             : 
    9109             : // MI default is Symbol(35, 0, 12)
    9110             : static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
    9111             : 
    9112      531864 : ITABFeatureSymbol::ITABFeatureSymbol()
    9113      531864 :     : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
    9114             : {
    9115      531864 : }
    9116             : 
    9117             : /**********************************************************************
    9118             :  *                   ITABFeatureSymbol::GetSymbolStyleString()
    9119             :  *
    9120             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    9121             :  **********************************************************************/
    9122          33 : const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
    9123             : {
    9124          33 :     const char *pszStyle = nullptr;
    9125          33 :     int nOGRStyle = 0;
    9126             :     /* char szPattern[20]; */
    9127          33 :     int nAngle = 0;
    9128             :     /* szPattern[0] = '\0'; */
    9129             : 
    9130          33 :     switch (m_sSymbolDef.nSymbolNo)
    9131             :     {
    9132           0 :         case 31:
    9133             :             // this is actually a "null" symbol in MapInfo!
    9134           0 :             nOGRStyle = 0;
    9135           0 :             break;
    9136           0 :         case 32:  // filled square
    9137           0 :             nOGRStyle = 5;
    9138           0 :             break;
    9139           0 :         case 33:  // filled diamond
    9140           0 :             nAngle = 45;
    9141           0 :             nOGRStyle = 5;
    9142           0 :             break;
    9143           0 :         case 34:  // filled circle
    9144           0 :             nOGRStyle = 3;
    9145           0 :             break;
    9146          33 :         case 35:  // filled star
    9147          33 :             nOGRStyle = 9;
    9148          33 :             break;
    9149           0 :         case 36:  // filled upward pointing triangle
    9150           0 :             nOGRStyle = 7;
    9151           0 :             break;
    9152           0 :         case 37:  // filled downward pointing triangle
    9153           0 :             nAngle = 180;
    9154           0 :             nOGRStyle = 7;
    9155           0 :             break;
    9156           0 :         case 38:  // hollow square
    9157           0 :             nOGRStyle = 4;
    9158           0 :             break;
    9159           0 :         case 39:  // hollow diamond
    9160           0 :             nAngle = 45;
    9161           0 :             nOGRStyle = 4;
    9162           0 :             break;
    9163           0 :         case 40:  // hollow circle
    9164           0 :             nOGRStyle = 2;
    9165           0 :             break;
    9166           0 :         case 41:  // hollow star
    9167           0 :             nOGRStyle = 8;
    9168           0 :             break;
    9169           0 :         case 42:  // hollow upward pointing triangle
    9170           0 :             nOGRStyle = 6;
    9171           0 :             break;
    9172           0 :         case 43:  // hollow downward pointing triangle
    9173           0 :             nAngle = 180;
    9174           0 :             nOGRStyle = 6;
    9175           0 :             break;
    9176           0 :         case 44:  // filled square (with shadow)
    9177           0 :             nOGRStyle = 5;
    9178           0 :             break;
    9179           0 :         case 45:  // filled upward triangle (with shadow)
    9180           0 :             nOGRStyle = 7;
    9181           0 :             break;
    9182           0 :         case 46:  // filled circle (with shadow)
    9183           0 :             nOGRStyle = 3;
    9184           0 :             break;
    9185           0 :         case 49:  // crossed lines
    9186           0 :             nOGRStyle = 0;
    9187           0 :             break;
    9188           0 :         case 50:  // X crossed lines
    9189           0 :             nOGRStyle = 1;
    9190           0 :             break;
    9191             :     }
    9192             : 
    9193          33 :     nAngle += static_cast<int>(dfAngle);
    9194             : 
    9195          66 :     pszStyle = CPLSPrintf(
    9196             :         "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
    9197          33 :         m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
    9198             :         nOGRStyle);
    9199             : 
    9200          33 :     return pszStyle;
    9201             : }
    9202             : 
    9203             : /**********************************************************************
    9204             :  *                   ITABFeatureSymbol::SetSymbolFromStyleString()
    9205             :  *
    9206             :  *  Set all Symbol var from a OGRStyleSymbol.
    9207             :  **********************************************************************/
    9208         109 : void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    9209             : {
    9210         109 :     GBool bIsNull = 0;
    9211             : 
    9212             :     // Set the Symbol Id (SymbolNo)
    9213         109 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    9214         109 :     if (bIsNull)
    9215           0 :         pszSymbolId = nullptr;
    9216             : 
    9217         109 :     if (pszSymbolId)
    9218             :     {
    9219         109 :         if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
    9220             :         {
    9221         105 :             const int nSymbolId = atoi(pszSymbolId + 12);
    9222         105 :             SetSymbolNo(static_cast<GByte>(nSymbolId));
    9223             :         }
    9224           4 :         else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
    9225             :         {
    9226           0 :             const int nSymbolId = atoi(pszSymbolId + 8);
    9227             : 
    9228             :             // The OGR symbol is not the MapInfo one
    9229             :             // Here's some mapping
    9230           0 :             switch (nSymbolId)
    9231             :             {
    9232           0 :                 case 0:
    9233           0 :                     SetSymbolNo(49);
    9234           0 :                     break;
    9235           0 :                 case 1:
    9236           0 :                     SetSymbolNo(50);
    9237           0 :                     break;
    9238           0 :                 case 2:
    9239           0 :                     SetSymbolNo(40);
    9240           0 :                     break;
    9241           0 :                 case 3:
    9242           0 :                     SetSymbolNo(34);
    9243           0 :                     break;
    9244           0 :                 case 4:
    9245           0 :                     SetSymbolNo(38);
    9246           0 :                     break;
    9247           0 :                 case 5:
    9248           0 :                     SetSymbolNo(32);
    9249           0 :                     break;
    9250           0 :                 case 6:
    9251           0 :                     SetSymbolNo(42);
    9252           0 :                     break;
    9253           0 :                 case 7:
    9254           0 :                     SetSymbolNo(36);
    9255           0 :                     break;
    9256           0 :                 case 8:
    9257           0 :                     SetSymbolNo(41);
    9258           0 :                     break;
    9259           0 :                 case 9:
    9260           0 :                     SetSymbolNo(35);
    9261           0 :                     break;
    9262           0 :                 case 10:  // vertical bar -- no mapinfo equivalent, so use
    9263             :                           // crosshairs as closest match
    9264           0 :                     SetSymbolNo(49);
    9265           0 :                     break;
    9266             :             }
    9267             :         }
    9268             :     }
    9269             : 
    9270             :     // Set SymbolSize
    9271         109 :     const double dSymbolSize = poSymbolStyle->Size(bIsNull);
    9272         109 :     if (dSymbolSize != 0.0)
    9273             :     {
    9274         109 :         SetSymbolSize(static_cast<GInt16>(dSymbolSize));
    9275             :     }
    9276             : 
    9277             :     // Set Symbol Color
    9278         109 :     const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
    9279         109 :     if (pszSymbolColor)
    9280             :     {
    9281         109 :         if (pszSymbolColor[0] == '#')
    9282         109 :             pszSymbolColor++;
    9283             :         int nSymbolColor =
    9284         109 :             static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
    9285         109 :         SetSymbolColor(static_cast<GInt32>(nSymbolColor));
    9286             :     }
    9287         109 : }
    9288             : 
    9289             : /**********************************************************************
    9290             :  *                   ITABFeatureSymbol::SetSymbolFromStyleString()
    9291             :  *
    9292             :  *  Set all Symbol var from a StyleString. Use StyleMgr to do so.
    9293             :  **********************************************************************/
    9294         109 : void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
    9295             : {
    9296             :     // Use the Style Manager to retrieve all the information we need.
    9297         109 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    9298         109 :     OGRStyleTool *poStylePart = nullptr;
    9299             : 
    9300             :     // Init the StyleMgr with the StyleString.
    9301         109 :     poStyleMgr->InitStyleString(pszStyleString);
    9302             : 
    9303             :     // Retrieve the Symbol info.
    9304         109 :     const int numParts = poStyleMgr->GetPartCount();
    9305         109 :     for (int i = 0; i < numParts; i++)
    9306             :     {
    9307         109 :         poStylePart = poStyleMgr->GetPart(i);
    9308         109 :         if (poStylePart == nullptr)
    9309           0 :             continue;
    9310             : 
    9311         109 :         if (poStylePart->GetType() == OGRSTCSymbol)
    9312             :         {
    9313         109 :             break;
    9314             :         }
    9315             :         else
    9316             :         {
    9317           0 :             delete poStylePart;
    9318           0 :             poStylePart = nullptr;
    9319             :         }
    9320             :     }
    9321             : 
    9322             :     // If the no Symbol found, do nothing.
    9323         109 :     if (poStylePart == nullptr)
    9324             :     {
    9325           0 :         delete poStyleMgr;
    9326           0 :         return;
    9327             :     }
    9328             : 
    9329             :     OGRStyleSymbol *poSymbolStyle =
    9330         109 :         cpl::down_cast<OGRStyleSymbol *>(poStylePart);
    9331             : 
    9332             :     // With Symbol, we always want to output points
    9333             :     //
    9334             :     // It's very important to set the output unit of the feature.
    9335             :     // The default value is meter. If we don't do it all numerical values
    9336             :     // will be assumed to be converted from the input unit to meter when we
    9337             :     // will get them via GetParam...() functions.
    9338             :     // See OGRStyleTool::Parse() for more details.
    9339         109 :     poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
    9340             : 
    9341         109 :     SetSymbolFromStyle(poSymbolStyle);
    9342             : 
    9343         109 :     delete poStyleMgr;
    9344         109 :     delete poStylePart;
    9345             : 
    9346         109 :     return;
    9347             : }
    9348             : 
    9349             : /**********************************************************************
    9350             :  *                   ITABFeatureSymbol::GetSymbolFeatureClass()
    9351             :  *
    9352             :  *  Return the feature class needed to represent the style string.
    9353             :  **********************************************************************/
    9354             : TABFeatureClass
    9355         109 : ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
    9356             : {
    9357             :     // Use the Style Manager to retrieve all the information we need.
    9358         109 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    9359         109 :     OGRStyleTool *poStylePart = nullptr;
    9360             : 
    9361             :     // Init the StyleMgr with the StyleString.
    9362         109 :     poStyleMgr->InitStyleString(pszStyleString);
    9363             : 
    9364             :     // Retrieve the Symbol info.
    9365         109 :     const int numParts = poStyleMgr->GetPartCount();
    9366         109 :     for (int i = 0; i < numParts; i++)
    9367             :     {
    9368         109 :         poStylePart = poStyleMgr->GetPart(i);
    9369         109 :         if (poStylePart == nullptr)
    9370             :         {
    9371           0 :             continue;
    9372             :         }
    9373             : 
    9374         109 :         if (poStylePart->GetType() == OGRSTCSymbol)
    9375             :         {
    9376         109 :             break;
    9377             :         }
    9378             :         else
    9379             :         {
    9380           0 :             delete poStylePart;
    9381           0 :             poStylePart = nullptr;
    9382             :         }
    9383             :     }
    9384             : 
    9385         109 :     TABFeatureClass result = TABFCPoint;
    9386             : 
    9387             :     // If the no Symbol found, do nothing.
    9388         109 :     if (poStylePart == nullptr)
    9389             :     {
    9390           0 :         delete poStyleMgr;
    9391           0 :         return result;
    9392             :     }
    9393             : 
    9394             :     OGRStyleSymbol *poSymbolStyle =
    9395         109 :         cpl::down_cast<OGRStyleSymbol *>(poStylePart);
    9396             : 
    9397         109 :     GBool bIsNull = 0;
    9398             : 
    9399             :     // Set the Symbol Id (SymbolNo)
    9400         109 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    9401         109 :     if (bIsNull)
    9402           0 :         pszSymbolId = nullptr;
    9403             : 
    9404         109 :     if (pszSymbolId)
    9405             :     {
    9406         109 :         if (STARTS_WITH(pszSymbolId, "font-sym-"))
    9407             :         {
    9408           2 :             result = TABFCFontPoint;
    9409             :         }
    9410         107 :         else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
    9411             :         {
    9412           2 :             result = TABFCCustomPoint;
    9413             :         }
    9414             :     }
    9415             : 
    9416         109 :     delete poStyleMgr;
    9417         109 :     delete poStylePart;
    9418             : 
    9419         109 :     return result;
    9420             : }
    9421             : 
    9422             : /**********************************************************************
    9423             :  *                   ITABFeatureSymbol::DumpSymbolDef()
    9424             :  *
    9425             :  * Dump Symbol definition information.
    9426             :  **********************************************************************/
    9427           0 : void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
    9428             : {
    9429           0 :     if (fpOut == nullptr)
    9430           0 :         fpOut = stdout;
    9431             : 
    9432           0 :     fprintf(fpOut, "  m_nSymbolDefIndex       = %d\n", m_nSymbolDefIndex);
    9433           0 :     fprintf(fpOut, "  m_sSymbolDef.nRefCount  = %d\n", m_sSymbolDef.nRefCount);
    9434           0 :     fprintf(fpOut, "  m_sSymbolDef.nSymbolNo  = %d\n", m_sSymbolDef.nSymbolNo);
    9435           0 :     fprintf(fpOut, "  m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
    9436           0 :     fprintf(fpOut, "  m_sSymbolDef._unknown_  = %d\n",
    9437           0 :             static_cast<int>(m_sSymbolDef._nUnknownValue_));
    9438           0 :     fprintf(fpOut, "  m_sSymbolDef.rgbColor   = 0x%6.6x (%d)\n",
    9439             :             m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
    9440             : 
    9441           0 :     fflush(fpOut);
    9442           0 : }

Generated by: LCOV version 1.14