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: 2109 3738 56.4 %
Date: 2024-11-21 22:18:42 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             :  * SPDX-License-Identifier: MIT
      14             :  **********************************************************************/
      15             : 
      16             : #include "cpl_port.h"
      17             : #include "mitab.h"
      18             : #include "mitab_geometry.h"
      19             : #include "mitab_utils.h"
      20             : 
      21             : #include <cctype>
      22             : #include <cmath>
      23             : #include <cstdio>
      24             : #include <cstdlib>
      25             : #include <cstring>
      26             : #include <algorithm>
      27             : #include <utility>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_string.h"
      32             : #include "cpl_vsi.h"
      33             : #include "mitab.h"
      34             : #include "mitab_geometry.h"
      35             : #include "mitab_priv.h"
      36             : #include "mitab_utils.h"
      37             : #include "ogr_core.h"
      38             : #include "ogr_feature.h"
      39             : #include "ogr_featurestyle.h"
      40             : #include "ogr_geometry.h"
      41             : 
      42             : /*=====================================================================
      43             :  *                      class TABFeature
      44             :  *====================================================================*/
      45             : 
      46             : /**********************************************************************
      47             :  *                   TABFeature::TABFeature()
      48             :  *
      49             :  * Constructor.
      50             :  **********************************************************************/
      51      552482 : TABFeature::TABFeature(OGRFeatureDefn *poDefnIn)
      52             :     : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
      53             :       m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
      54      552482 :       m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
      55             : {
      56      552482 : }
      57             : 
      58             : /**********************************************************************
      59             :  *                   TABFeature::~TABFeature()
      60             :  *
      61             :  * Destructor.
      62             :  **********************************************************************/
      63      552746 : TABFeature::~TABFeature()
      64             : {
      65      552746 : }
      66             : 
      67             : /**********************************************************************
      68             :  *                     TABFeature::CreateFromMapInfoType()
      69             :  *
      70             :  * Factory that creates a TABFeature of the right class for the specified
      71             :  * MapInfo Type
      72             :  *
      73             :  **********************************************************************/
      74      529600 : TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
      75             :                                               OGRFeatureDefn *poDefn)
      76             : {
      77      529600 :     TABFeature *poFeature = nullptr;
      78             : 
      79             :     /*-----------------------------------------------------------------
      80             :      * Create new feature object of the right type
      81             :      *----------------------------------------------------------------*/
      82      529600 :     switch (nMapInfoType)
      83             :     {
      84         154 :         case TAB_GEOM_NONE:
      85         154 :             poFeature = new TABFeature(poDefn);
      86         154 :             break;
      87      527310 :         case TAB_GEOM_SYMBOL_C:
      88             :         case TAB_GEOM_SYMBOL:
      89      527310 :             poFeature = new TABPoint(poDefn);
      90      527310 :             break;
      91           6 :         case TAB_GEOM_FONTSYMBOL_C:
      92             :         case TAB_GEOM_FONTSYMBOL:
      93           6 :             poFeature = new TABFontPoint(poDefn);
      94           6 :             break;
      95           6 :         case TAB_GEOM_CUSTOMSYMBOL_C:
      96             :         case TAB_GEOM_CUSTOMSYMBOL:
      97           6 :             poFeature = new TABCustomPoint(poDefn);
      98           6 :             break;
      99        1578 :         case TAB_GEOM_LINE_C:
     100             :         case TAB_GEOM_LINE:
     101             :         case TAB_GEOM_PLINE_C:
     102             :         case TAB_GEOM_PLINE:
     103             :         case TAB_GEOM_MULTIPLINE_C:
     104             :         case TAB_GEOM_MULTIPLINE:
     105             :         case TAB_GEOM_V450_MULTIPLINE_C:
     106             :         case TAB_GEOM_V450_MULTIPLINE:
     107             :         case TAB_GEOM_V800_MULTIPLINE_C:
     108             :         case TAB_GEOM_V800_MULTIPLINE:
     109        1578 :             poFeature = new TABPolyline(poDefn);
     110        1578 :             break;
     111           8 :         case TAB_GEOM_ARC_C:
     112             :         case TAB_GEOM_ARC:
     113           8 :             poFeature = new TABArc(poDefn);
     114           8 :             break;
     115             : 
     116         510 :         case TAB_GEOM_REGION_C:
     117             :         case TAB_GEOM_REGION:
     118             :         case TAB_GEOM_V450_REGION_C:
     119             :         case TAB_GEOM_V450_REGION:
     120             :         case TAB_GEOM_V800_REGION_C:
     121             :         case TAB_GEOM_V800_REGION:
     122         510 :             poFeature = new TABRegion(poDefn);
     123         510 :             break;
     124           8 :         case TAB_GEOM_RECT_C:
     125             :         case TAB_GEOM_RECT:
     126             :         case TAB_GEOM_ROUNDRECT_C:
     127             :         case TAB_GEOM_ROUNDRECT:
     128           8 :             poFeature = new TABRectangle(poDefn);
     129           8 :             break;
     130           4 :         case TAB_GEOM_ELLIPSE_C:
     131             :         case TAB_GEOM_ELLIPSE:
     132           4 :             poFeature = new TABEllipse(poDefn);
     133           4 :             break;
     134           8 :         case TAB_GEOM_TEXT_C:
     135             :         case TAB_GEOM_TEXT:
     136           8 :             poFeature = new TABText(poDefn);
     137           8 :             break;
     138           4 :         case TAB_GEOM_MULTIPOINT_C:
     139             :         case TAB_GEOM_MULTIPOINT:
     140             :         case TAB_GEOM_V800_MULTIPOINT_C:
     141             :         case TAB_GEOM_V800_MULTIPOINT:
     142           4 :             poFeature = new TABMultiPoint(poDefn);
     143           4 :             break;
     144           4 :         case TAB_GEOM_COLLECTION_C:
     145             :         case TAB_GEOM_COLLECTION:
     146             :         case TAB_GEOM_V800_COLLECTION_C:
     147             :         case TAB_GEOM_V800_COLLECTION:
     148           4 :             poFeature = new TABCollection(poDefn);
     149           4 :             break;
     150           0 :         default:
     151             :             /*-------------------------------------------------------------
     152             :              * Unsupported feature type... we still return a valid feature
     153             :              * with NONE geometry after producing a Warning.
     154             :              * Callers can trap that case by checking CPLGetLastErrorNo()
     155             :              * against TAB_WarningFeatureTypeNotSupported
     156             :              *------------------------------------------------------------*/
     157             :             // poFeature = new TABDebugFeature(poDefn);
     158           0 :             poFeature = new TABFeature(poDefn);
     159             : 
     160           0 :             CPLError(
     161             :                 CE_Warning,
     162             :                 static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
     163             :                 "Unsupported object type %d (0x%2.2x).  Feature will be "
     164             :                 "returned with NONE geometry.",
     165             :                 nMapInfoType, nMapInfoType);
     166             :     }
     167             : 
     168      529600 :     return poFeature;
     169             : }
     170             : 
     171             : /**********************************************************************
     172             :  *                     TABFeature::CopyTABFeatureBase()
     173             :  *
     174             :  * Used by CloneTABFeature() to copy the basic (fields, geometry, etc.)
     175             :  * TABFeature members.
     176             :  *
     177             :  * The newly created feature is owned by the caller, and will have its own
     178             :  * reference to the OGRFeatureDefn.
     179             :  *
     180             :  * It is possible to create the clone with a different OGRFeatureDefn,
     181             :  * in this case, the fields won't be copied of course.
     182             :  *
     183             :  **********************************************************************/
     184           2 : void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature)
     185             : {
     186             :     /*-----------------------------------------------------------------
     187             :      * Copy fields only if OGRFeatureDefn is the same
     188             :      *----------------------------------------------------------------*/
     189           2 :     OGRFeatureDefn *poThisDefnRef = GetDefnRef();
     190             : 
     191           2 :     if (poThisDefnRef == poDestFeature->GetDefnRef())
     192             :     {
     193           0 :         for (int i = 0; i < poThisDefnRef->GetFieldCount(); i++)
     194             :         {
     195           0 :             poDestFeature->SetField(i, GetRawFieldRef(i));
     196             :         }
     197             :     }
     198             : 
     199             :     /*-----------------------------------------------------------------
     200             :      * Copy the geometry
     201             :      *----------------------------------------------------------------*/
     202           2 :     poDestFeature->SetGeometry(GetGeometryRef());
     203             : 
     204           2 :     double dXMin = 0.0;
     205           2 :     double dYMin = 0.0;
     206           2 :     double dXMax = 0.0;
     207           2 :     double dYMax = 0.0;
     208           2 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
     209           2 :     poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
     210             : 
     211           2 :     GInt32 nXMin = 0;
     212           2 :     GInt32 nYMin = 0;
     213           2 :     GInt32 nXMax = 0;
     214           2 :     GInt32 nYMax = 0;
     215           2 :     GetIntMBR(nXMin, nYMin, nXMax, nYMax);
     216           2 :     poDestFeature->SetIntMBR(nXMin, nYMin, nXMax, nYMax);
     217             : 
     218             :     // m_nMapInfoType is not carried but it is not required anyways.
     219             :     // it will default to TAB_GEOM_NONE
     220           2 : }
     221             : 
     222             : /**********************************************************************
     223             :  *                     TABFeature::CloneTABFeature()
     224             :  *
     225             :  * Duplicate feature, including stuff specific to each TABFeature type.
     226             :  *
     227             :  * The newly created feature is owned by the caller, and will have its own
     228             :  * reference to the OGRFeatureDefn.
     229             :  *
     230             :  * It is possible to create the clone with a different OGRFeatureDefn,
     231             :  * in this case, the fields won't be copied of course.
     232             :  *
     233             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
     234             :  * then copies any members specific to its own type.
     235             :  **********************************************************************/
     236           0 : TABFeature *TABFeature::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
     237             : {
     238             :     /*-----------------------------------------------------------------
     239             :      * Alloc new feature and copy the base stuff
     240             :      *----------------------------------------------------------------*/
     241           0 :     TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
     242             : 
     243           0 :     CopyTABFeatureBase(poNew);
     244             : 
     245             :     /*-----------------------------------------------------------------
     246             :      * And members specific to this class
     247             :      *----------------------------------------------------------------*/
     248             :     // Nothing to do for this class
     249             : 
     250           0 :     return poNew;
     251             : }
     252             : 
     253             : /**********************************************************************
     254             :  *                   TABFeature::SetMBR()
     255             :  *
     256             :  * Set the values for the MBR corners for this feature.
     257             :  **********************************************************************/
     258      536737 : void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
     259             : {
     260      536737 :     m_dXMin = std::min(dXMin, dXMax);
     261      536737 :     m_dYMin = std::min(dYMin, dYMax);
     262      536737 :     m_dXMax = std::max(dXMin, dXMax);
     263      536737 :     m_dYMax = std::max(dYMin, dYMax);
     264      536737 : }
     265             : 
     266             : /**********************************************************************
     267             :  *                   TABFeature::GetMBR()
     268             :  *
     269             :  * Return the values for the MBR corners for this feature.
     270             :  **********************************************************************/
     271        1227 : void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
     272             :                         double &dYMax)
     273             : {
     274        1227 :     dXMin = m_dXMin;
     275        1227 :     dYMin = m_dYMin;
     276        1227 :     dXMax = m_dXMax;
     277        1227 :     dYMax = m_dYMax;
     278        1227 : }
     279             : 
     280             : /**********************************************************************
     281             :  *                   TABFeature::SetIntMBR()
     282             :  *
     283             :  * Set the integer coordinates values of the MBR of this feature.
     284             :  **********************************************************************/
     285      529452 : void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
     286             :                            GInt32 nYMax)
     287             : {
     288      529452 :     m_nXMin = nXMin;
     289      529452 :     m_nYMin = nYMin;
     290      529452 :     m_nXMax = nXMax;
     291      529452 :     m_nYMax = nYMax;
     292      529452 : }
     293             : 
     294             : /**********************************************************************
     295             :  *                   TABFeature::GetIntMBR()
     296             :  *
     297             :  * Return the integer coordinates values of the MBR of this feature.
     298             :  **********************************************************************/
     299       15023 : void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
     300             :                            GInt32 &nYMax)
     301             : {
     302       15023 :     nXMin = m_nXMin;
     303       15023 :     nYMin = m_nYMin;
     304       15023 :     nXMax = m_nXMax;
     305       15023 :     nYMax = m_nYMax;
     306       15023 : }
     307             : 
     308             : /**********************************************************************
     309             :  *                   TABFeature::ReadRecordFromDATFile()
     310             :  *
     311             :  * Fill the fields part of the feature from the contents of the
     312             :  * table record pointed to by poDATFile.
     313             :  *
     314             :  * It is assumed that poDATFile currently points to the beginning of
     315             :  * the table record and that this feature's OGRFeatureDefn has been
     316             :  * properly initialized for this table.
     317             :  **********************************************************************/
     318      529432 : int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
     319             : {
     320      529432 :     CPLAssert(poDATFile);
     321             : 
     322      529432 :     const int numFields = poDATFile->GetNumFields();
     323             : 
     324     1060650 :     for (int iField = 0; iField < numFields; iField++)
     325             :     {
     326      531221 :         switch (poDATFile->GetFieldType(iField))
     327             :         {
     328        1308 :             case TABFChar:
     329             :             {
     330        1308 :                 int iWidth(poDATFile->GetFieldWidth(iField));
     331        2616 :                 CPLString osValue(poDATFile->ReadCharField(iWidth));
     332             : 
     333        1308 :                 if (!poDATFile->GetEncoding().empty())
     334             :                 {
     335         110 :                     osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
     336             :                 }
     337        1308 :                 SetField(iField, osValue);
     338        1308 :                 break;
     339             :             }
     340          28 :             case TABFDecimal:
     341             :             {
     342          28 :                 const double dValue = poDATFile->ReadDecimalField(
     343             :                     poDATFile->GetFieldWidth(iField));
     344          28 :                 SetField(iField, dValue);
     345          28 :                 break;
     346             :             }
     347      529212 :             case TABFInteger:
     348             :             {
     349      529212 :                 const int nValue = poDATFile->ReadIntegerField(
     350             :                     poDATFile->GetFieldWidth(iField));
     351      529212 :                 SetField(iField, nValue);
     352      529212 :                 break;
     353             :             }
     354           4 :             case TABFSmallInt:
     355             :             {
     356           4 :                 const int nValue = poDATFile->ReadSmallIntField(
     357           4 :                     poDATFile->GetFieldWidth(iField));
     358           4 :                 SetField(iField, nValue);
     359           4 :                 break;
     360             :             }
     361           6 :             case TABFLargeInt:
     362             :             {
     363           6 :                 const GInt64 nValue = poDATFile->ReadLargeIntField(
     364             :                     poDATFile->GetFieldWidth(iField));
     365           6 :                 SetField(iField, nValue);
     366           6 :                 break;
     367             :             }
     368         598 :             case TABFFloat:
     369             :             {
     370             :                 const double dValue =
     371         598 :                     poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
     372         598 :                 SetField(iField, dValue);
     373         598 :                 break;
     374             :             }
     375           6 :             case TABFLogical:
     376             :             {
     377           6 :                 const bool bValue = poDATFile->ReadLogicalField(
     378             :                     poDATFile->GetFieldWidth(iField));
     379           6 :                 SetField(iField, bValue ? 1 : 0);
     380           6 :                 break;
     381             :             }
     382          29 :             case TABFDate:
     383             :             {
     384             : #ifdef MITAB_USE_OFTDATETIME
     385          29 :                 int nYear = 0;
     386          29 :                 int nMonth = 0;
     387          29 :                 int nDay = 0;
     388          29 :                 const int status = poDATFile->ReadDateField(
     389             :                     poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
     390          29 :                 if (status == 0)
     391             :                 {
     392          12 :                     SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
     393             :                 }
     394             : #else
     395             :                 const char *pszValue =
     396             :                     poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
     397             :                 SetField(iField, pszValue);
     398             : #endif
     399          29 :                 break;
     400             :             }
     401           7 :             case TABFTime:
     402             :             {
     403             : #ifdef MITAB_USE_OFTDATETIME
     404           7 :                 int nHour = 0;
     405           7 :                 int nMin = 0;
     406           7 :                 int nMS = 0;
     407           7 :                 int nSec = 0;
     408             :                 const int status =
     409           7 :                     poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
     410             :                                              &nHour, &nMin, &nSec, &nMS);
     411           7 :                 if (status == 0)
     412             :                 {
     413           6 :                     int nYear = 0;
     414           6 :                     int nMonth = 0;
     415           6 :                     int nDay = 0;
     416           6 :                     SetField(iField, nYear, nMonth, nDay, nHour, nMin,
     417           6 :                              nSec + nMS / 1000.0f, 0);
     418             :                 }
     419             : #else
     420             :                 const char *pszValue =
     421             :                     poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
     422             :                 SetField(iField, pszValue);
     423             : #endif
     424           7 :                 break;
     425             :             }
     426          23 :             case TABFDateTime:
     427             :             {
     428             : #ifdef MITAB_USE_OFTDATETIME
     429          23 :                 int nYear = 0;
     430          23 :                 int nMonth = 0;
     431          23 :                 int nDay = 0;
     432          23 :                 int nHour = 0;
     433          23 :                 int nMin = 0;
     434          23 :                 int nMS = 0;
     435          23 :                 int nSec = 0;
     436          23 :                 const int status = poDATFile->ReadDateTimeField(
     437             :                     poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
     438             :                     &nHour, &nMin, &nSec, &nMS);
     439          23 :                 if (status == 0)
     440             :                 {
     441           6 :                     SetField(iField, nYear, nMonth, nDay, nHour, nMin,
     442           6 :                              nSec + nMS / 1000.0f, 0);
     443             :                 }
     444             : #else
     445             :                 const char *pszValue = poDATFile->ReadDateTimeField(
     446             :                     poDATFile->GetFieldWidth(iField));
     447             :                 SetField(iField, pszValue);
     448             : #endif
     449          23 :                 break;
     450             :             }
     451           0 :             default:
     452             :                 // Other type???  Impossible!
     453           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     454             :                          "Unsupported field type!");
     455             :         }
     456             :     }
     457             : 
     458      529432 :     return 0;
     459             : }
     460             : 
     461             : /**********************************************************************
     462             :  *                   TABFeature::WriteRecordToDATFile()
     463             :  *
     464             :  * Write the attribute part of the feature to the .DAT file.
     465             :  *
     466             :  * It is assumed that poDATFile currently points to the beginning of
     467             :  * the table record and that this feature's OGRFeatureDefn has been
     468             :  * properly initialized for this table.
     469             :  *
     470             :  * Returns 0 on success, -1 on error.
     471             :  **********************************************************************/
     472       15087 : int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
     473             :                                      TABINDFile *poINDFile, int *panIndexNo)
     474             : {
     475             : #ifdef MITAB_USE_OFTDATETIME
     476       15087 :     int nYear = 0;
     477       15087 :     int nMon = 0;
     478       15087 :     int nDay = 0;
     479       15087 :     int nHour = 0;
     480       15087 :     int nMin = 0;
     481       15087 :     int nTZFlag = 0;
     482       15087 :     float fSec = 0.0f;
     483             : #endif
     484             : 
     485       15087 :     CPLAssert(poDATFile);
     486             : 
     487       15087 :     const int numFields = poDATFile->GetNumFields();
     488             : 
     489       15087 :     poDATFile->MarkRecordAsExisting();
     490             : 
     491       15087 :     int nStatus = 0;
     492       30858 :     for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
     493             :     {
     494             :         // Hack for "extra" introduced field.
     495       15771 :         if (iField >= GetDefnRef()->GetFieldCount())
     496             :         {
     497           1 :             CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
     498             :                       iField == 0);
     499           1 :             nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
     500             :                                                    poINDFile, 0);
     501           1 :             continue;
     502             :         }
     503       15770 :         CPLAssert(panIndexNo != nullptr);
     504             : 
     505       15770 :         switch (poDATFile->GetFieldType(iField))
     506             :         {
     507         377 :             case TABFChar:
     508             :             {
     509         377 :                 CPLString osValue(GetFieldAsString(iField));
     510         377 :                 if (!poDATFile->GetEncoding().empty())
     511             :                 {
     512          30 :                     osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
     513             :                 }
     514         377 :                 nStatus = poDATFile->WriteCharField(
     515             :                     osValue, poDATFile->GetFieldWidth(iField), poINDFile,
     516         377 :                     panIndexNo[iField]);
     517             :             }
     518         377 :             break;
     519           5 :             case TABFDecimal:
     520           5 :                 nStatus = poDATFile->WriteDecimalField(
     521             :                     GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
     522             :                     poDATFile->GetFieldPrecision(iField), poINDFile,
     523           5 :                     panIndexNo[iField]);
     524           5 :                 break;
     525       14993 :             case TABFInteger:
     526       14993 :                 nStatus = poDATFile->WriteIntegerField(
     527       14993 :                     GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
     528       14993 :                 break;
     529           0 :             case TABFSmallInt:
     530           0 :                 nStatus = poDATFile->WriteSmallIntField(
     531           0 :                     static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
     532           0 :                     panIndexNo[iField]);
     533           0 :                 break;
     534           2 :             case TABFLargeInt:
     535           6 :                 nStatus = poDATFile->WriteLargeIntField(
     536           2 :                     static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
     537           2 :                     panIndexNo[iField]);
     538           2 :                 break;
     539         237 :             case TABFFloat:
     540         237 :                 nStatus = poDATFile->WriteFloatField(
     541         237 :                     GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
     542         237 :                 break;
     543           2 :             case TABFLogical:
     544             :                 nStatus =
     545           2 :                     poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
     546           2 :                                                  poINDFile, panIndexNo[iField]);
     547           2 :                 break;
     548          76 :             case TABFDate:
     549             : #ifdef MITAB_USE_OFTDATETIME
     550          76 :                 if (IsFieldSetAndNotNull(iField))
     551             :                 {
     552          59 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     553             :                                        &nMin, &fSec, &nTZFlag);
     554             :                 }
     555             :                 else
     556             :                 {
     557          17 :                     nYear = 0;
     558          17 :                     nMon = 0;
     559          17 :                     nDay = 0;
     560             :                 }
     561             : 
     562         152 :                 nStatus = poDATFile->WriteDateField(
     563          76 :                     nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
     564             : #else
     565             :                 nStatus = poDATFile->WriteDateField(
     566             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     567             : #endif
     568          76 :                 break;
     569           3 :             case TABFTime:
     570             : #ifdef MITAB_USE_OFTDATETIME
     571           3 :                 if (IsFieldSetAndNotNull(iField))
     572             :                 {
     573           2 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     574             :                                        &nMin, &fSec, &nTZFlag);
     575             :                 }
     576             :                 else
     577             :                 {
     578             :                     // Put negative values, so that WriteTimeField() forges
     579             :                     // a negative value, and ultimately write -1 in the binary
     580             :                     // field
     581           1 :                     nHour = -1;
     582           1 :                     nMin = -1;
     583           1 :                     fSec = -1;
     584             :                 }
     585           3 :                 nStatus = poDATFile->WriteTimeField(
     586             :                     nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
     587           3 :                     poINDFile, panIndexNo[iField]);
     588             : 
     589             : #else
     590             :                 nStatus = poDATFile->WriteTimeField(
     591             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     592             : #endif
     593           3 :                 break;
     594          75 :             case TABFDateTime:
     595             : #ifdef MITAB_USE_OFTDATETIME
     596          75 :                 if (IsFieldSetAndNotNull(iField))
     597             :                 {
     598          58 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     599             :                                        &nMin, &fSec, &nTZFlag);
     600             :                 }
     601             :                 else
     602             :                 {
     603          17 :                     nYear = 0;
     604          17 :                     nMon = 0;
     605          17 :                     nDay = 0;
     606          17 :                     nHour = 0;
     607          17 :                     nMin = 0;
     608          17 :                     fSec = 0;
     609             :                 }
     610             : 
     611          75 :                 nStatus = poDATFile->WriteDateTimeField(
     612             :                     nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
     613          75 :                     OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
     614             : #else
     615             :                 nStatus = poDATFile->WriteDateTimeField(
     616             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     617             : #endif
     618          75 :                 break;
     619           0 :             default:
     620             :                 // Other type???  Impossible!
     621           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     622             :                          "Unsupported field type!");
     623             :         }
     624             :     }
     625             : 
     626       15087 :     if (nStatus != 0)
     627           1 :         return nStatus;
     628             : 
     629       15086 :     if (poDATFile->CommitRecordToFile() != 0)
     630           0 :         return -1;
     631             : 
     632       15086 :     return 0;
     633             : }
     634             : 
     635             : /**********************************************************************
     636             :  *                   TABFeature::ReadGeometryFromMAPFile()
     637             :  *
     638             :  * In derived classes, this method should be reimplemented to
     639             :  * fill the geometry and representation (color, etc...) part of the
     640             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     641             :  *
     642             :  * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
     643             :  * currently points to the beginning of a map object.
     644             :  *
     645             :  * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
     646             :  * the CoordBlock data during splitting of object blocks. In this case we
     647             :  * need to process only the information related to the CoordBlock. One
     648             :  * important thing to avoid is reading/writing pen/brush/symbol definitions
     649             :  * as that would screw up their ref counters.
     650             :  *
     651             :  * ppoCoordBlock is used by TABCollection and by index splitting code
     652             :  * to provide a CoordBlock to use instead of the one from the poMAPFile and
     653             :  * return the current pointer at the end of the call.
     654             :  *
     655             :  * The current implementation does nothing since instances of TABFeature
     656             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     657             :  *
     658             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     659             :  * been called.
     660             :  **********************************************************************/
     661         154 : int TABFeature::ReadGeometryFromMAPFile(
     662             :     TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
     663             :     GBool /*bCoordBlockDataOnly=FALSE*/,
     664             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     665             : {
     666             :     // Nothing to do. Instances of TABFeature objects contain no geometry.
     667         154 :     return 0;
     668             : }
     669             : 
     670             : /**********************************************************************
     671             :  *                   TABFeature::UpdateMBR()
     672             :  *
     673             :  * Fetch envelope of poGeom and update MBR.
     674             :  * Integer coord MBR is updated only if poMapFile is not NULL.
     675             :  *
     676             :  * Returns 0 on success, or -1 if there is no geometry in object
     677             :  **********************************************************************/
     678       15017 : int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
     679             : {
     680       15017 :     OGRGeometry *poGeom = GetGeometryRef();
     681             : 
     682       15017 :     if (poGeom)
     683             :     {
     684       15017 :         OGREnvelope oEnv;
     685       15017 :         poGeom->getEnvelope(&oEnv);
     686             : 
     687       15017 :         m_dXMin = oEnv.MinX;
     688       15017 :         m_dYMin = oEnv.MinY;
     689       15017 :         m_dXMax = oEnv.MaxX;
     690       15017 :         m_dYMax = oEnv.MaxY;
     691             : 
     692       15017 :         if (poMapFile)
     693             :         {
     694       15017 :             poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
     695       15017 :             poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
     696             :             // Coordsy2Int can transform a min value to a max one and vice
     697             :             // versa.
     698       15017 :             if (m_nXMin > m_nXMax)
     699             :             {
     700           0 :                 std::swap(m_nXMin, m_nXMax);
     701             :             }
     702       15017 :             if (m_nYMin > m_nYMax)
     703             :             {
     704           0 :                 std::swap(m_nYMin, m_nYMax);
     705             :             }
     706             :         }
     707             : 
     708       15017 :         return 0;
     709             :     }
     710             : 
     711           0 :     return -1;
     712             : }
     713             : 
     714             : /**********************************************************************
     715             :  *                   TABFeature::ValidateCoordType()
     716             :  *
     717             :  * Checks the feature envelope to establish if the feature should be
     718             :  * written using Compressed coordinates or not and adjust m_nMapInfoType
     719             :  * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
     720             :  * m_nXMax, m_nYMax
     721             :  *
     722             :  * This function should be used only by the ValidateMapInfoType()
     723             :  * implementations.
     724             :  *
     725             :  * Returns TRUE if coord. should be compressed, FALSE otherwise
     726             :  **********************************************************************/
     727         303 : GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
     728             : {
     729         303 :     GBool bCompr = FALSE;
     730             : 
     731             :     /*-------------------------------------------------------------
     732             :      * Decide if coordinates should be compressed or not.
     733             :      *------------------------------------------------------------*/
     734         303 :     if (UpdateMBR(poMapFile) == 0)
     735             :     {
     736             :         /* Test for max range < 65535 here instead of < 65536 to avoid
     737             :          * compressed coordinate overflows in some boundary situations
     738             :          */
     739         303 :         if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
     740         294 :             (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
     741             :         {
     742         294 :             bCompr = TRUE;
     743             :         }
     744         303 :         m_nComprOrgX =
     745         303 :             static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
     746         303 :         m_nComprOrgY =
     747         303 :             static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
     748             :     }
     749             : 
     750             :     /*-------------------------------------------------------------
     751             :      * Adjust native type
     752             :      *------------------------------------------------------------*/
     753         303 :     if (bCompr && ((m_nMapInfoType % 3) == 2))
     754         294 :         m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
     755             :                                                   1);  // compr = 1, 4, 7, ...
     756           9 :     else if (!bCompr && ((m_nMapInfoType % 3) == 1))
     757           0 :         m_nMapInfoType = static_cast<TABGeomType>(
     758           0 :             m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
     759             : 
     760         303 :     return bCompr;
     761             : }
     762             : 
     763             : /**********************************************************************
     764             :  *                   TABFeature::ForceCoordTypeAndOrigin()
     765             :  *
     766             :  * This function is used by TABCollection::ValidateMapInfoType() to force
     767             :  * the coord type and compressed origin of all members of a collection
     768             :  * to be the same. (A replacement for ValidateCoordType() for this
     769             :  * specific case)
     770             :  **********************************************************************/
     771           0 : void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
     772             :                                          GInt32 nComprOrgX, GInt32 nComprOrgY,
     773             :                                          GInt32 nXMin, GInt32 nYMin,
     774             :                                          GInt32 nXMax, GInt32 nYMax)
     775             : {
     776             :     /*-------------------------------------------------------------
     777             :      * Set Compressed Origin and adjust native type
     778             :      *------------------------------------------------------------*/
     779           0 :     m_nComprOrgX = nComprOrgX;
     780           0 :     m_nComprOrgY = nComprOrgY;
     781             : 
     782           0 :     m_nMapInfoType = nMapInfoType;
     783             : 
     784           0 :     if (bCompr && ((m_nMapInfoType % 3) == 2))
     785           0 :         m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
     786             :                                                   1);  // compr = 1, 4, 7, ...
     787           0 :     else if (!bCompr && ((m_nMapInfoType % 3) == 1))
     788           0 :         m_nMapInfoType = static_cast<TABGeomType>(
     789           0 :             m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
     790             : 
     791           0 :     m_nXMin = nXMin;
     792           0 :     m_nYMin = nYMin;
     793           0 :     m_nXMax = nXMax;
     794           0 :     m_nYMax = nYMax;
     795           0 : }
     796             : 
     797             : /**********************************************************************
     798             :  *                   TABFeature::WriteGeometryToMAPFile()
     799             :  *
     800             :  *
     801             :  * In derived classes, this method should be reimplemented to
     802             :  * write the geometry and representation (color, etc...) part of the
     803             :  * feature to the .MAP object pointed to by poMAPFile.
     804             :  *
     805             :  * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
     806             :  * currently points to a valid map object.
     807             :  *
     808             :  * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
     809             :  * the CoordBlock data during splitting of object blocks. In this case we
     810             :  * need to process only the information related to the CoordBlock. One
     811             :  * important thing to avoid is reading/writing pen/brush/symbol definitions
     812             :  * as that would screw up their ref counters.
     813             :  *
     814             :  * ppoCoordBlock is used by TABCollection and by index splitting code
     815             :  * to provide a CoordBlock to use instead of the one from the poMAPFile and
     816             :  * return the current pointer at the end of the call.
     817             :  *
     818             :  * The current implementation does nothing since instances of TABFeature
     819             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     820             :  *
     821             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     822             :  * been called.
     823             :  **********************************************************************/
     824          60 : int TABFeature::WriteGeometryToMAPFile(
     825             :     TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
     826             :     GBool /*bCoordBlockDataOnly=FALSE*/,
     827             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     828             : {
     829             :     /*-----------------------------------------------------------------
     830             :      * Nothing to do... instances of TABFeature objects contain no geometry.
     831             :      *----------------------------------------------------------------*/
     832             : 
     833          60 :     return 0;
     834             : }
     835             : 
     836             : /**********************************************************************
     837             :  *                   TABFeature::DumpMID()
     838             :  *
     839             :  * Dump feature attributes in a format similar to .MID data records.
     840             :  **********************************************************************/
     841           0 : void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
     842             : {
     843           0 :     OGRFeatureDefn *l_poDefn = GetDefnRef();
     844             : 
     845           0 :     if (fpOut == nullptr)
     846           0 :         fpOut = stdout;
     847             : 
     848           0 :     for (int iField = 0; iField < GetFieldCount(); iField++)
     849             :     {
     850           0 :         OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
     851             : 
     852           0 :         fprintf(fpOut, "  %s (%s) = %s\n", poFDefn->GetNameRef(),
     853             :                 OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
     854             :                 GetFieldAsString(iField));
     855             :     }
     856             : 
     857           0 :     fflush(fpOut);
     858           0 : }
     859             : 
     860             : /**********************************************************************
     861             :  *                   TABFeature::DumpMIF()
     862             :  *
     863             :  * Dump feature geometry in a format similar to .MIF files.
     864             :  **********************************************************************/
     865           0 : void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
     866             : {
     867           0 :     if (fpOut == nullptr)
     868           0 :         fpOut = stdout;
     869             : 
     870             :     /*-----------------------------------------------------------------
     871             :      * Generate output... not much to do, feature contains no geometry.
     872             :      *----------------------------------------------------------------*/
     873           0 :     fprintf(fpOut, "NONE\n");
     874             : 
     875           0 :     fflush(fpOut);
     876           0 : }
     877             : 
     878             : /*=====================================================================
     879             :  *                      class TABPoint
     880             :  *====================================================================*/
     881             : 
     882             : /**********************************************************************
     883             :  *                   TABPoint::TABPoint()
     884             :  *
     885             :  * Constructor.
     886             :  **********************************************************************/
     887      544164 : TABPoint::TABPoint(OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
     888             : {
     889      544164 : }
     890             : 
     891             : /**********************************************************************
     892             :  *                   TABPoint::~TABPoint()
     893             :  *
     894             :  * Destructor.
     895             :  **********************************************************************/
     896     1086999 : TABPoint::~TABPoint()
     897             : {
     898     1086999 : }
     899             : 
     900             : /**********************************************************************
     901             :  *                     TABPoint::CloneTABFeature()
     902             :  *
     903             :  * Duplicate feature, including stuff specific to each TABFeature type.
     904             :  *
     905             :  * This method calls the generic TABFeature::CloneTABFeature() and
     906             :  * then copies any members specific to its own type.
     907             :  **********************************************************************/
     908           2 : TABFeature *TABPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
     909             : {
     910             :     /*-----------------------------------------------------------------
     911             :      * Alloc new feature and copy the base stuff
     912             :      *----------------------------------------------------------------*/
     913           2 :     TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
     914             : 
     915           2 :     CopyTABFeatureBase(poNew);
     916             : 
     917             :     /*-----------------------------------------------------------------
     918             :      * And members specific to this class
     919             :      *----------------------------------------------------------------*/
     920             :     // ITABFeatureSymbol
     921           2 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
     922             : 
     923           2 :     return poNew;
     924             : }
     925             : 
     926             : /**********************************************************************
     927             :  *                   TABPoint::ValidateMapInfoType()
     928             :  *
     929             :  * Check the feature's geometry part and return the corresponding
     930             :  * mapinfo object type code.  The m_nMapInfoType member will also
     931             :  * be updated for further calls to GetMapInfoType();
     932             :  *
     933             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
     934             :  * is expected for this object class.
     935             :  **********************************************************************/
     936       14691 : TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
     937             : {
     938             :     /*-----------------------------------------------------------------
     939             :      * Fetch and validate geometry
     940             :      * __TODO__ For now we always write in uncompressed format (until we
     941             :      * find that this is not correct... note that at this point the
     942             :      * decision to use compressed/uncompressed will likely be based on
     943             :      * the distance between the point and the object block center in
     944             :      * integer coordinates being > 32767 or not... remains to be verified)
     945             :      *----------------------------------------------------------------*/
     946       14691 :     OGRGeometry *poGeom = GetGeometryRef();
     947       14691 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     948             :     {
     949       14691 :         switch (GetFeatureClass())
     950             :         {
     951           2 :             case TABFCFontPoint:
     952           2 :                 m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
     953           2 :                 break;
     954           2 :             case TABFCCustomPoint:
     955           2 :                 m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
     956           2 :                 break;
     957       14687 :             case TABFCPoint:
     958             :             default:
     959       14687 :                 m_nMapInfoType = TAB_GEOM_SYMBOL;
     960       14687 :                 break;
     961             :         }
     962             :     }
     963             :     else
     964             :     {
     965           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     966             :                  "TABPoint: Missing or Invalid Geometry!");
     967           0 :         m_nMapInfoType = TAB_GEOM_NONE;
     968             :     }
     969             : 
     970       14691 :     UpdateMBR(poMapFile);
     971             : 
     972       14691 :     return m_nMapInfoType;
     973             : }
     974             : 
     975             : /**********************************************************************
     976             :  *                   TABPoint::ReadGeometryFromMAPFile()
     977             :  *
     978             :  * Fill the geometry and representation (color, etc...) part of the
     979             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     980             :  *
     981             :  * It is assumed that poMAPFile currently points to the beginning of
     982             :  * a map object.
     983             :  *
     984             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     985             :  * been called.
     986             :  **********************************************************************/
     987      527310 : int TABPoint::ReadGeometryFromMAPFile(
     988             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
     989             :     GBool bCoordBlockDataOnly /*=FALSE*/,
     990             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     991             : {
     992             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
     993      527310 :     if (bCoordBlockDataOnly)
     994           0 :         return 0;
     995             : 
     996             :     /*-----------------------------------------------------------------
     997             :      * Fetch and validate geometry type
     998             :      *----------------------------------------------------------------*/
     999      527310 :     m_nMapInfoType = poObjHdr->m_nType;
    1000             : 
    1001      527310 :     if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
    1002       12888 :         m_nMapInfoType != TAB_GEOM_SYMBOL_C)
    1003             :     {
    1004           0 :         CPLError(
    1005             :             CE_Failure, CPLE_AssertionFailed,
    1006             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1007           0 :             m_nMapInfoType, m_nMapInfoType);
    1008           0 :         return -1;
    1009             :     }
    1010             : 
    1011             :     /*-----------------------------------------------------------------
    1012             :      * Read object information
    1013             :      *----------------------------------------------------------------*/
    1014      527310 :     TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
    1015             : 
    1016      527310 :     m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
    1017             : 
    1018      527310 :     poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    1019             : 
    1020             :     /*-----------------------------------------------------------------
    1021             :      * Create and fill geometry object
    1022             :      *----------------------------------------------------------------*/
    1023      527310 :     double dX = 0.0;
    1024      527310 :     double dY = 0.0;
    1025             : 
    1026      527310 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1027      527310 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1028             : 
    1029      527310 :     SetGeometryDirectly(poGeometry);
    1030             : 
    1031      527310 :     SetMBR(dX, dY, dX, dY);
    1032      527310 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1033             :               poObjHdr->m_nMaxY);
    1034             : 
    1035      527310 :     return 0;
    1036             : }
    1037             : 
    1038             : /**********************************************************************
    1039             :  *                   TABPoint::WriteGeometryToMAPFile()
    1040             :  *
    1041             :  * Write the geometry and representation (color, etc...) part of the
    1042             :  * feature to the .MAP object pointed to by poMAPFile.
    1043             :  *
    1044             :  * It is assumed that poMAPFile currently points to a valid map object.
    1045             :  *
    1046             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1047             :  * been called.
    1048             :  **********************************************************************/
    1049       14687 : int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    1050             :                                      TABMAPObjHdr *poObjHdr,
    1051             :                                      GBool bCoordBlockDataOnly /*=FALSE*/,
    1052             :                                      TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1053             : {
    1054             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1055       14687 :     if (bCoordBlockDataOnly)
    1056           0 :         return 0;
    1057             : 
    1058             :     /*-----------------------------------------------------------------
    1059             :      * We assume that ValidateMapInfoType() was called already and that
    1060             :      * the type in poObjHdr->m_nType is valid.
    1061             :      *----------------------------------------------------------------*/
    1062       14687 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1063             : 
    1064             :     /*-----------------------------------------------------------------
    1065             :      * Fetch and validate geometry
    1066             :      *----------------------------------------------------------------*/
    1067       14687 :     OGRGeometry *poGeom = GetGeometryRef();
    1068       14687 :     OGRPoint *poPoint = nullptr;
    1069       14687 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1070       14687 :         poPoint = poGeom->toPoint();
    1071             :     else
    1072             :     {
    1073           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1074             :                  "TABPoint: Missing or Invalid Geometry!");
    1075           0 :         return -1;
    1076             :     }
    1077             : 
    1078       14687 :     GInt32 nX = 0;
    1079       14687 :     GInt32 nY = 0;
    1080       14687 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1081             : 
    1082             :     /*-----------------------------------------------------------------
    1083             :      * Copy object information
    1084             :      *----------------------------------------------------------------*/
    1085       14687 :     TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
    1086             : 
    1087       14687 :     poPointHdr->m_nX = nX;
    1088       14687 :     poPointHdr->m_nY = nY;
    1089       14687 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1090             : 
    1091       14687 :     m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    1092       14687 :     poPointHdr->m_nSymbolId =
    1093       14687 :         static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    1094             : 
    1095       14687 :     if (CPLGetLastErrorType() == CE_Failure)
    1096           0 :         return -1;
    1097             : 
    1098       14687 :     return 0;
    1099             : }
    1100             : 
    1101             : /**********************************************************************
    1102             :  *                   TABPoint::GetX()
    1103             :  *
    1104             :  * Return this point's X coordinate.
    1105             :  **********************************************************************/
    1106           0 : double TABPoint::GetX()
    1107             : {
    1108             : 
    1109             :     /*-----------------------------------------------------------------
    1110             :      * Fetch and validate geometry
    1111             :      *----------------------------------------------------------------*/
    1112           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1113           0 :     OGRPoint *poPoint = nullptr;
    1114           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1115           0 :         poPoint = poGeom->toPoint();
    1116             :     else
    1117             :     {
    1118           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1119             :                  "TABPoint: Missing or Invalid Geometry!");
    1120           0 :         return 0.0;
    1121             :     }
    1122             : 
    1123           0 :     return poPoint->getX();
    1124             : }
    1125             : 
    1126             : /**********************************************************************
    1127             :  *                   TABPoint::GetY()
    1128             :  *
    1129             :  * Return this point's Y coordinate.
    1130             :  **********************************************************************/
    1131           0 : double TABPoint::GetY()
    1132             : {
    1133             :     /*-----------------------------------------------------------------
    1134             :      * Fetch and validate geometry
    1135             :      *----------------------------------------------------------------*/
    1136           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1137           0 :     OGRPoint *poPoint = nullptr;
    1138           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1139           0 :         poPoint = poGeom->toPoint();
    1140             :     else
    1141             :     {
    1142           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1143             :                  "TABPoint: Missing or Invalid Geometry!");
    1144           0 :         return 0.0;
    1145             :     }
    1146             : 
    1147           0 :     return poPoint->getY();
    1148             : }
    1149             : 
    1150             : /**********************************************************************
    1151             :  *                   TABPoint::GetStyleString() const
    1152             :  *
    1153             :  * Return style string for this feature.
    1154             :  *
    1155             :  * Style String is built only once during the first call to GetStyleString().
    1156             :  **********************************************************************/
    1157         239 : const char *TABPoint::GetStyleString() const
    1158             : {
    1159         239 :     if (m_pszStyleString == nullptr)
    1160             :     {
    1161          27 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    1162             :     }
    1163             : 
    1164         239 :     return m_pszStyleString;
    1165             : }
    1166             : 
    1167             : /**********************************************************************
    1168             :  *                   TABPoint::DumpMIF()
    1169             :  *
    1170             :  * Dump feature geometry in a format similar to .MIF POINTs.
    1171             :  **********************************************************************/
    1172           0 : void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
    1173             : {
    1174           0 :     if (fpOut == nullptr)
    1175           0 :         fpOut = stdout;
    1176             : 
    1177             :     /*-----------------------------------------------------------------
    1178             :      * Fetch and validate geometry
    1179             :      *----------------------------------------------------------------*/
    1180           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1181           0 :     OGRPoint *poPoint = nullptr;
    1182           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1183           0 :         poPoint = poGeom->toPoint();
    1184             :     else
    1185             :     {
    1186           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1187             :                  "TABPoint: Missing or Invalid Geometry!");
    1188           0 :         return;
    1189             :     }
    1190             : 
    1191             :     /*-----------------------------------------------------------------
    1192             :      * Generate output
    1193             :      *----------------------------------------------------------------*/
    1194           0 :     fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
    1195             : 
    1196           0 :     DumpSymbolDef(fpOut);
    1197             : 
    1198             :     /*-----------------------------------------------------------------
    1199             :      * Handle stuff specific to derived classes
    1200             :      *----------------------------------------------------------------*/
    1201             :     // cppcheck-suppress knownConditionTrueFalse
    1202           0 :     if (GetFeatureClass() == TABFCFontPoint)
    1203             :     {
    1204           0 :         TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
    1205           0 :         fprintf(fpOut, "  m_nFontStyle     = 0x%2.2x (%d)\n",
    1206             :                 poFeature->GetFontStyleTABValue(),
    1207             :                 poFeature->GetFontStyleTABValue());
    1208             : 
    1209           0 :         poFeature->DumpFontDef(fpOut);
    1210             :     }
    1211             :     // cppcheck-suppress knownConditionTrueFalse
    1212           0 :     if (GetFeatureClass() == TABFCCustomPoint)
    1213             :     {
    1214           0 :         TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
    1215             : 
    1216           0 :         fprintf(fpOut, "  m_nUnknown_      = 0x%2.2x (%d)\n",
    1217           0 :                 poFeature->m_nUnknown_, poFeature->m_nUnknown_);
    1218           0 :         fprintf(fpOut, "  m_nCustomStyle   = 0x%2.2x (%d)\n",
    1219           0 :                 poFeature->GetCustomSymbolStyle(),
    1220           0 :                 poFeature->GetCustomSymbolStyle());
    1221             : 
    1222           0 :         poFeature->DumpFontDef(fpOut);
    1223             :     }
    1224             : 
    1225           0 :     fflush(fpOut);
    1226             : }
    1227             : 
    1228             : /*=====================================================================
    1229             :  *                      class TABFontPoint
    1230             :  *====================================================================*/
    1231             : 
    1232             : /**********************************************************************
    1233             :  *                   TABFontPoint::TABFontPoint()
    1234             :  *
    1235             :  * Constructor.
    1236             :  **********************************************************************/
    1237         643 : TABFontPoint::TABFontPoint(OGRFeatureDefn *poDefnIn)
    1238         643 :     : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
    1239             : {
    1240         643 : }
    1241             : 
    1242             : /**********************************************************************
    1243             :  *                   TABFontPoint::~TABFontPoint()
    1244             :  *
    1245             :  * Destructor.
    1246             :  **********************************************************************/
    1247        1286 : TABFontPoint::~TABFontPoint()
    1248             : {
    1249        1286 : }
    1250             : 
    1251             : /**********************************************************************
    1252             :  *                     TABFontPoint::CloneTABFeature()
    1253             :  *
    1254             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1255             :  *
    1256             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1257             :  * then copies any members specific to its own type.
    1258             :  **********************************************************************/
    1259           0 : TABFeature *TABFontPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    1260             : {
    1261             :     /*-----------------------------------------------------------------
    1262             :      * Alloc new feature and copy the base stuff
    1263             :      *----------------------------------------------------------------*/
    1264             :     TABFontPoint *poNew =
    1265           0 :         new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
    1266             : 
    1267           0 :     CopyTABFeatureBase(poNew);
    1268             : 
    1269             :     /*-----------------------------------------------------------------
    1270             :      * And members specific to this class
    1271             :      *----------------------------------------------------------------*/
    1272             :     // ITABFeatureSymbol
    1273           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    1274             : 
    1275             :     // ITABFeatureFont
    1276           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    1277             : 
    1278           0 :     poNew->SetSymbolAngle(GetSymbolAngle());
    1279           0 :     poNew->SetFontStyleTABValue(GetFontStyleTABValue());
    1280             : 
    1281           0 :     return poNew;
    1282             : }
    1283             : 
    1284             : /**********************************************************************
    1285             :  *                   TABFontPoint::ReadGeometryFromMAPFile()
    1286             :  *
    1287             :  * Fill the geometry and representation (color, etc...) part of the
    1288             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    1289             :  *
    1290             :  * It is assumed that poMAPFile currently points to the beginning of
    1291             :  * a map object.
    1292             :  *
    1293             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1294             :  * been called.
    1295             :  **********************************************************************/
    1296           6 : int TABFontPoint::ReadGeometryFromMAPFile(
    1297             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1298             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1299             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1300             : {
    1301             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1302           6 :     if (bCoordBlockDataOnly)
    1303           0 :         return 0;
    1304             : 
    1305             :     /*-----------------------------------------------------------------
    1306             :      * Fetch and validate geometry type
    1307             :      *----------------------------------------------------------------*/
    1308           6 :     m_nMapInfoType = poObjHdr->m_nType;
    1309             : 
    1310           6 :     if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
    1311           0 :         m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
    1312             :     {
    1313           0 :         CPLError(
    1314             :             CE_Failure, CPLE_AssertionFailed,
    1315             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1316           0 :             m_nMapInfoType, m_nMapInfoType);
    1317           0 :         return -1;
    1318             :     }
    1319             : 
    1320             :     /*-----------------------------------------------------------------
    1321             :      * Read object information
    1322             :      * NOTE: This symbol type does not contain a reference to a
    1323             :      * SymbolDef block in the file, but we still use the m_sSymbolDef
    1324             :      * structure to store the information inside the class so that the
    1325             :      * ITABFeatureSymbol methods work properly for the class user.
    1326             :      *----------------------------------------------------------------*/
    1327             :     TABMAPObjFontPoint *poPointHdr =
    1328           6 :         cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
    1329             : 
    1330           6 :     m_nSymbolDefIndex = -1;
    1331           6 :     m_sSymbolDef.nRefCount = 0;
    1332             : 
    1333           6 :     m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId;    // shape
    1334           6 :     m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize;  // point size
    1335             : 
    1336           6 :     m_nFontStyle = poPointHdr->m_nFontStyle;  // font style
    1337             : 
    1338           6 :     m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
    1339           6 :                             poPointHdr->m_nG * 256 + poPointHdr->m_nB;
    1340             : 
    1341             :     /*-------------------------------------------------------------
    1342             :      * Symbol Angle, in tenths of degree.
    1343             :      * Contrary to arc start/end angles, no conversion based on
    1344             :      * origin quadrant is required here.
    1345             :      *------------------------------------------------------------*/
    1346           6 :     m_dAngle = poPointHdr->m_nAngle / 10.0;
    1347             : 
    1348           6 :     m_nFontDefIndex = poPointHdr->m_nFontId;  // Font name index
    1349             : 
    1350           6 :     poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    1351             : 
    1352             :     /*-----------------------------------------------------------------
    1353             :      * Create and fill geometry object
    1354             :      *----------------------------------------------------------------*/
    1355           6 :     double dX = 0.0;
    1356           6 :     double dY = 0.0;
    1357           6 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1358           6 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1359             : 
    1360           6 :     SetGeometryDirectly(poGeometry);
    1361             : 
    1362           6 :     SetMBR(dX, dY, dX, dY);
    1363           6 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1364             :               poObjHdr->m_nMaxY);
    1365             : 
    1366           6 :     return 0;
    1367             : }
    1368             : 
    1369             : /**********************************************************************
    1370             :  *                   TABFontPoint::WriteGeometryToMAPFile()
    1371             :  *
    1372             :  * Write the geometry and representation (color, etc...) part of the
    1373             :  * feature to the .MAP object pointed to by poMAPFile.
    1374             :  *
    1375             :  * It is assumed that poMAPFile currently points to a valid map object.
    1376             :  *
    1377             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1378             :  * been called.
    1379             :  **********************************************************************/
    1380           2 : int TABFontPoint::WriteGeometryToMAPFile(
    1381             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1382             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1383             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1384             : {
    1385             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1386           2 :     if (bCoordBlockDataOnly)
    1387           0 :         return 0;
    1388             : 
    1389             :     /*-----------------------------------------------------------------
    1390             :      * We assume that ValidateMapInfoType() was called already and that
    1391             :      * the type in poObjHdr->m_nType is valid.
    1392             :      *----------------------------------------------------------------*/
    1393           2 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1394             : 
    1395             :     /*-----------------------------------------------------------------
    1396             :      * Fetch and validate geometry
    1397             :      *----------------------------------------------------------------*/
    1398           2 :     OGRGeometry *poGeom = GetGeometryRef();
    1399           2 :     OGRPoint *poPoint = nullptr;
    1400           2 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1401           2 :         poPoint = poGeom->toPoint();
    1402             :     else
    1403             :     {
    1404           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1405             :                  "TABFontPoint: Missing or Invalid Geometry!");
    1406           0 :         return -1;
    1407             :     }
    1408             : 
    1409           2 :     GInt32 nX = 0;
    1410           2 :     GInt32 nY = 0;
    1411           2 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1412             : 
    1413             :     /*-----------------------------------------------------------------
    1414             :      * Copy object information
    1415             :      * NOTE: This symbol type does not contain a reference to a
    1416             :      * SymbolDef block in the file, but we still use the m_sSymbolDef
    1417             :      * structure to store the information inside the class so that the
    1418             :      * ITABFeatureSymbol methods work properly for the class user.
    1419             :      *----------------------------------------------------------------*/
    1420             :     TABMAPObjFontPoint *poPointHdr =
    1421           2 :         cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
    1422             : 
    1423           2 :     poPointHdr->m_nX = nX;
    1424           2 :     poPointHdr->m_nY = nY;
    1425           2 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1426             : 
    1427           2 :     poPointHdr->m_nSymbolId =
    1428           2 :         static_cast<GByte>(m_sSymbolDef.nSymbolNo);  // shape
    1429           2 :     poPointHdr->m_nPointSize =
    1430           2 :         static_cast<GByte>(m_sSymbolDef.nPointSize);  // point size
    1431           2 :     poPointHdr->m_nFontStyle = m_nFontStyle;          // font style
    1432             : 
    1433           2 :     poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
    1434           2 :     poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
    1435           2 :     poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
    1436             : 
    1437             :     /*-------------------------------------------------------------
    1438             :      * Symbol Angle, in tenths of degree.
    1439             :      * Contrary to arc start/end angles, no conversion based on
    1440             :      * origin quadrant is required here.
    1441             :      *------------------------------------------------------------*/
    1442           2 :     poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
    1443             : 
    1444             :     // Write Font Def
    1445           2 :     m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    1446           2 :     poPointHdr->m_nFontId =
    1447           2 :         static_cast<GByte>(m_nFontDefIndex);  // Font name index
    1448             : 
    1449           2 :     if (CPLGetLastErrorType() == CE_Failure)
    1450           0 :         return -1;
    1451             : 
    1452           2 :     return 0;
    1453             : }
    1454             : 
    1455             : /**********************************************************************
    1456             :  *                   TABFontPoint::QueryFontStyle()
    1457             :  *
    1458             :  * Return TRUE if the specified font style attribute is turned ON,
    1459             :  * or FALSE otherwise.  See enum TABFontStyle for the list of styles
    1460             :  * that can be queried on.
    1461             :  **********************************************************************/
    1462           0 : GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
    1463             : {
    1464           0 :     return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
    1465             : }
    1466             : 
    1467           0 : void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
    1468             : {
    1469           0 :     if (bStyleOn)
    1470           0 :         m_nFontStyle |= static_cast<int>(eStyleToToggle);
    1471             :     else
    1472           0 :         m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
    1473           0 : }
    1474             : 
    1475             : /**********************************************************************
    1476             :  *                   TABFontPoint::GetFontStyleMIFValue()
    1477             :  *
    1478             :  * Return the Font Style value for this object using the style values
    1479             :  * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
    1480             :  *
    1481             :  * The reason why we have to differentiate between the TAB and the MIF font
    1482             :  * style values is that in TAB, TABFSBox is included in the style value
    1483             :  * as code 0x100, but in MIF it is not included, instead it is implied by
    1484             :  * the presence of the BG color in the FONT() clause (the BG color is
    1485             :  * present only when TABFSBox or TABFSHalo is set).
    1486             :  * This also has the effect of shifting all the other style values > 0x100
    1487             :  * by 1 byte.
    1488             :  *
    1489             :  * NOTE: Even if there is no BG color for font symbols, we inherit this
    1490             :  * problem because Font Point styles use the same codes as Text Font styles.
    1491             :  **********************************************************************/
    1492           0 : int TABFontPoint::GetFontStyleMIFValue()
    1493             : {
    1494             :     // The conversion is simply to remove bit 0x100 from the value and shift
    1495             :     // down all values past this bit.
    1496           0 :     return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
    1497             : }
    1498             : 
    1499         635 : void TABFontPoint::SetFontStyleMIFValue(int nStyle)
    1500             : {
    1501         635 :     m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
    1502         635 : }
    1503             : 
    1504             : /**********************************************************************
    1505             :  *                   TABFontPoint::SetSymbolAngle()
    1506             :  *
    1507             :  * Set the symbol angle value in degrees, making sure the value is
    1508             :  * always in the range [0..360]
    1509             :  **********************************************************************/
    1510         635 : void TABFontPoint::SetSymbolAngle(double dAngle)
    1511             : {
    1512         635 :     dAngle = fmod(dAngle, 360.0);
    1513         635 :     if (dAngle < 0.0)
    1514           0 :         dAngle += 360.0;
    1515             : 
    1516         635 :     m_dAngle = dAngle;
    1517         635 : }
    1518             : 
    1519             : /**********************************************************************
    1520             :  *                   TABFontPoint::GetSymbolStyleString()
    1521             :  *
    1522             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    1523             :  **********************************************************************/
    1524           7 : const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
    1525             : {
    1526             :     /* Get the SymbolStyleString, and add the outline Color
    1527             :        (halo/border in MapInfo Symbol terminology) */
    1528           7 :     const char *outlineColor = nullptr;
    1529           7 :     if (m_nFontStyle & 16)
    1530           0 :         outlineColor = ",o:#000000";
    1531           7 :     else if (m_nFontStyle & 512)
    1532           0 :         outlineColor = ",o:#ffffff";
    1533             :     else
    1534           7 :         outlineColor = "";
    1535             : 
    1536           7 :     int nAngle = static_cast<int>(dfAngle);
    1537             :     const char *pszStyle;
    1538             : 
    1539           7 :     pszStyle = CPLSPrintf(
    1540             :         "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
    1541           7 :         nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
    1542           7 :         m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
    1543           7 :     return pszStyle;
    1544             : }
    1545             : 
    1546             : /**********************************************************************
    1547             :  *                   TABFontPoint::GetStyleString() const
    1548             :  *
    1549             :  * Return style string for this feature.
    1550             :  *
    1551             :  * Style String is built only once during the first call to GetStyleString().
    1552             :  **********************************************************************/
    1553           9 : const char *TABFontPoint::GetStyleString() const
    1554             : {
    1555           9 :     if (m_pszStyleString == nullptr)
    1556             :     {
    1557           7 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
    1558             :     }
    1559             : 
    1560           9 :     return m_pszStyleString;
    1561             : }
    1562             : 
    1563             : /**********************************************************************
    1564             :  *                   TABFontPoint::SetSymbolFromStyle()
    1565             :  *
    1566             :  *  Set all Symbol var from a OGRStyleSymbol.
    1567             :  **********************************************************************/
    1568           2 : void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    1569             : {
    1570           2 :     ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
    1571             : 
    1572           2 :     GBool bIsNull = 0;
    1573             : 
    1574             :     // Try to set font glyph number
    1575           2 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    1576           2 :     if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
    1577             :     {
    1578           2 :         const int nSymbolId = atoi(pszSymbolId + 9);
    1579           2 :         SetSymbolNo(static_cast<GInt16>(nSymbolId));
    1580             :     }
    1581             : 
    1582           2 :     const char *pszFontName = poSymbolStyle->FontName(bIsNull);
    1583           2 :     if ((!bIsNull) && pszFontName)
    1584             :     {
    1585           2 :         SetFontName(pszFontName);
    1586             :     }
    1587           2 : }
    1588             : 
    1589             : /*=====================================================================
    1590             :  *                      class TABCustomPoint
    1591             :  *====================================================================*/
    1592             : 
    1593             : /**********************************************************************
    1594             :  *                   TABCustomPoint::TABCustomPoint()
    1595             :  *
    1596             :  * Constructor.
    1597             :  **********************************************************************/
    1598         686 : TABCustomPoint::TABCustomPoint(OGRFeatureDefn *poDefnIn)
    1599         686 :     : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
    1600             : {
    1601         686 : }
    1602             : 
    1603             : /**********************************************************************
    1604             :  *                   TABCustomPoint::~TABCustomPoint()
    1605             :  *
    1606             :  * Destructor.
    1607             :  **********************************************************************/
    1608        1372 : TABCustomPoint::~TABCustomPoint()
    1609             : {
    1610        1372 : }
    1611             : 
    1612             : /**********************************************************************
    1613             :  *                     TABCustomPoint::CloneTABFeature()
    1614             :  *
    1615             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1616             :  *
    1617             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1618             :  * then copies any members specific to its own type.
    1619             :  **********************************************************************/
    1620           0 : TABFeature *TABCustomPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    1621             : {
    1622             :     /*-----------------------------------------------------------------
    1623             :      * Alloc new feature and copy the base stuff
    1624             :      *----------------------------------------------------------------*/
    1625             :     TABCustomPoint *poNew =
    1626           0 :         new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
    1627             : 
    1628           0 :     CopyTABFeatureBase(poNew);
    1629             : 
    1630             :     /*-----------------------------------------------------------------
    1631             :      * And members specific to this class
    1632             :      *----------------------------------------------------------------*/
    1633             :     // ITABFeatureSymbol
    1634           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    1635             : 
    1636             :     // ITABFeatureFont
    1637           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    1638             : 
    1639           0 :     poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
    1640             : 
    1641           0 :     return poNew;
    1642             : }
    1643             : 
    1644             : /**********************************************************************
    1645             :  *                   TABCustomPoint::ReadGeometryFromMAPFile()
    1646             :  *
    1647             :  * Fill the geometry and representation (color, etc...) part of the
    1648             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    1649             :  *
    1650             :  * It is assumed that poMAPFile currently points to the beginning of
    1651             :  * a map object.
    1652             :  *
    1653             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1654             :  * been called.
    1655             :  **********************************************************************/
    1656           6 : int TABCustomPoint::ReadGeometryFromMAPFile(
    1657             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1658             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1659             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1660             : {
    1661             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1662           6 :     if (bCoordBlockDataOnly)
    1663           0 :         return 0;
    1664             : 
    1665             :     /*-----------------------------------------------------------------
    1666             :      * Fetch and validate geometry type
    1667             :      *----------------------------------------------------------------*/
    1668           6 :     m_nMapInfoType = poObjHdr->m_nType;
    1669             : 
    1670           6 :     if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
    1671           0 :         m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
    1672             :     {
    1673           0 :         CPLError(
    1674             :             CE_Failure, CPLE_AssertionFailed,
    1675             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1676           0 :             m_nMapInfoType, m_nMapInfoType);
    1677           0 :         return -1;
    1678             :     }
    1679             : 
    1680             :     /*-----------------------------------------------------------------
    1681             :      * Read object information
    1682             :      *----------------------------------------------------------------*/
    1683             :     TABMAPObjCustomPoint *poPointHdr =
    1684           6 :         cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
    1685             : 
    1686           6 :     m_nUnknown_ = poPointHdr->m_nUnknown_;        // ???
    1687           6 :     m_nCustomStyle = poPointHdr->m_nCustomStyle;  // 0x01=Show BG,
    1688             :                                                   // 0x02=Apply Color
    1689             : 
    1690           6 :     m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
    1691           6 :     poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    1692             : 
    1693           6 :     m_nFontDefIndex = poPointHdr->m_nFontId;  // Font index
    1694           6 :     poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    1695             : 
    1696             :     /*-----------------------------------------------------------------
    1697             :      * Create and fill geometry object
    1698             :      *----------------------------------------------------------------*/
    1699           6 :     double dX = 0.0;
    1700           6 :     double dY = 0.0;
    1701           6 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1702           6 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1703             : 
    1704           6 :     SetGeometryDirectly(poGeometry);
    1705             : 
    1706           6 :     SetMBR(dX, dY, dX, dY);
    1707           6 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1708             :               poObjHdr->m_nMaxY);
    1709             : 
    1710           6 :     return 0;
    1711             : }
    1712             : 
    1713             : /**********************************************************************
    1714             :  *                   TABCustomPoint::WriteGeometryToMAPFile()
    1715             :  *
    1716             :  * Write the geometry and representation (color, etc...) part of the
    1717             :  * feature to the .MAP object pointed to by poMAPFile.
    1718             :  *
    1719             :  * It is assumed that poMAPFile currently points to a valid map object.
    1720             :  *
    1721             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1722             :  * been called.
    1723             :  **********************************************************************/
    1724           2 : int TABCustomPoint::WriteGeometryToMAPFile(
    1725             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1726             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1727             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1728             : {
    1729             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1730           2 :     if (bCoordBlockDataOnly)
    1731           0 :         return 0;
    1732             : 
    1733             :     /*-----------------------------------------------------------------
    1734             :      * We assume that ValidateMapInfoType() was called already and that
    1735             :      * the type in poObjHdr->m_nType is valid.
    1736             :      *----------------------------------------------------------------*/
    1737           2 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1738             : 
    1739             :     /*-----------------------------------------------------------------
    1740             :      * Fetch and validate geometry
    1741             :      *----------------------------------------------------------------*/
    1742           2 :     OGRGeometry *poGeom = GetGeometryRef();
    1743           2 :     OGRPoint *poPoint = nullptr;
    1744           2 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1745           2 :         poPoint = poGeom->toPoint();
    1746             :     else
    1747             :     {
    1748           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1749             :                  "TABCustomPoint: Missing or Invalid Geometry!");
    1750           0 :         return -1;
    1751             :     }
    1752             : 
    1753           2 :     GInt32 nX = 0;
    1754           2 :     GInt32 nY = 0;
    1755           2 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1756             : 
    1757             :     /*-----------------------------------------------------------------
    1758             :      * Copy object information
    1759             :      *----------------------------------------------------------------*/
    1760             :     TABMAPObjCustomPoint *poPointHdr =
    1761           2 :         cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
    1762             : 
    1763           2 :     poPointHdr->m_nX = nX;
    1764           2 :     poPointHdr->m_nY = nY;
    1765           2 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1766           2 :     poPointHdr->m_nUnknown_ = m_nUnknown_;
    1767           2 :     poPointHdr->m_nCustomStyle = m_nCustomStyle;  // 0x01=Show BG,
    1768             :                                                   // 0x02=Apply Color
    1769             : 
    1770           2 :     m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    1771           2 :     poPointHdr->m_nSymbolId =
    1772           2 :         static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    1773             : 
    1774           2 :     m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    1775           2 :     poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex);  // Font index
    1776             : 
    1777           2 :     if (CPLGetLastErrorType() == CE_Failure)
    1778           0 :         return -1;
    1779             : 
    1780           2 :     return 0;
    1781             : }
    1782             : 
    1783             : /**********************************************************************
    1784             :  *                   TABCustomPoint::GetSymbolStyleString()
    1785             :  *
    1786             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    1787             :  **********************************************************************/
    1788           7 : const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
    1789             : {
    1790             :     /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
    1791             :      * "apply color". */
    1792           7 :     const char *color = nullptr;
    1793           7 :     if (m_nCustomStyle & 0x02)
    1794           7 :         color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
    1795             :     else
    1796           0 :         color = "";
    1797             : 
    1798           7 :     int nAngle = static_cast<int>(dfAngle);
    1799             :     const char *pszStyle;
    1800           7 :     const char *pszExt = CPLGetExtension(GetSymbolNameRef());
    1801           7 :     char szLowerExt[8] = "";
    1802           7 :     const char *pszPtr = pszExt;
    1803             :     int i;
    1804             : 
    1805           7 :     for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
    1806             :     {
    1807           0 :         szLowerExt[i] =
    1808           0 :             static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
    1809             :     }
    1810           7 :     szLowerExt[i] = '\0';
    1811             : 
    1812           7 :     pszStyle = CPLSPrintf(
    1813             :         "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
    1814           7 :         nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
    1815             :         GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
    1816           7 :     return pszStyle;
    1817             : }
    1818             : 
    1819             : /**********************************************************************
    1820             :  *                   TABCustomPoint::SetSymbolFromStyle()
    1821             :  *
    1822             :  *  Set all Symbol var from a OGRStyleSymbol.
    1823             :  **********************************************************************/
    1824           2 : void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    1825             : {
    1826           2 :     ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
    1827             : 
    1828           2 :     GBool bIsNull = 0;
    1829             : 
    1830             :     // Try to set font glyph number
    1831           2 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    1832           2 :     if ((!bIsNull) && pszSymbolId &&
    1833           2 :         STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
    1834             :     {
    1835           2 :         const int nSymbolStyle = atoi(pszSymbolId + 19);
    1836           2 :         SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
    1837             : 
    1838           2 :         const char *pszPtr = pszSymbolId + 19;
    1839           4 :         while (*pszPtr != '-')
    1840             :         {
    1841           2 :             pszPtr++;
    1842             :         }
    1843           2 :         pszPtr++;
    1844             : 
    1845           2 :         char szSymbolName[256] = "";
    1846             :         int i;
    1847           2 :         for (i = 0;
    1848           8 :              i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
    1849             :              i++, pszPtr++)
    1850             :         {
    1851           6 :             szSymbolName[i] = *pszPtr;
    1852             :         }
    1853           2 :         szSymbolName[i] = '\0';
    1854           2 :         SetSymbolName(szSymbolName);
    1855             :     }
    1856           2 : }
    1857             : 
    1858             : /**********************************************************************
    1859             :  *                   TABCustomPoint::GetStyleString() const
    1860             :  *
    1861             :  * Return style string for this feature.
    1862             :  *
    1863             :  * Style String is built only once during the first call to GetStyleString().
    1864             :  **********************************************************************/
    1865           9 : const char *TABCustomPoint::GetStyleString() const
    1866             : {
    1867           9 :     if (m_pszStyleString == nullptr)
    1868             :     {
    1869           7 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    1870             :     }
    1871             : 
    1872           9 :     return m_pszStyleString;
    1873             : }
    1874             : 
    1875             : /*=====================================================================
    1876             :  *                      class TABPolyline
    1877             :  *====================================================================*/
    1878             : 
    1879             : /**********************************************************************
    1880             :  *                   TABPolyline::TABPolyline()
    1881             :  *
    1882             :  * Constructor.
    1883             :  **********************************************************************/
    1884        4253 : TABPolyline::TABPolyline(OGRFeatureDefn *poDefnIn)
    1885             :     : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
    1886        4253 :       m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
    1887             : {
    1888        4253 : }
    1889             : 
    1890             : /**********************************************************************
    1891             :  *                   TABPolyline::~TABPolyline()
    1892             :  *
    1893             :  * Destructor.
    1894             :  **********************************************************************/
    1895        8506 : TABPolyline::~TABPolyline()
    1896             : {
    1897        8506 : }
    1898             : 
    1899             : /**********************************************************************
    1900             :  *                     TABPolyline::CloneTABFeature()
    1901             :  *
    1902             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1903             :  *
    1904             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1905             :  * then copies any members specific to its own type.
    1906             :  **********************************************************************/
    1907           0 : TABFeature *TABPolyline::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    1908             : {
    1909             :     /*-----------------------------------------------------------------
    1910             :      * Alloc new feature and copy the base stuff
    1911             :      *----------------------------------------------------------------*/
    1912           0 :     TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
    1913             : 
    1914           0 :     CopyTABFeatureBase(poNew);
    1915             : 
    1916             :     /*-----------------------------------------------------------------
    1917             :      * And members specific to this class
    1918             :      *----------------------------------------------------------------*/
    1919             :     // ITABFeaturePen
    1920           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    1921             : 
    1922           0 :     poNew->m_bSmooth = m_bSmooth;
    1923           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    1924           0 :     poNew->m_dCenterX = m_dCenterX;
    1925           0 :     poNew->m_dCenterY = m_dCenterY;
    1926             : 
    1927           0 :     return poNew;
    1928             : }
    1929             : 
    1930             : /**********************************************************************
    1931             :  *                   TABPolyline::GetNumParts()
    1932             :  *
    1933             :  * Return the total number of parts in this object.
    1934             :  *
    1935             :  * Returns 0 if the geometry contained in the object is invalid or missing.
    1936             :  **********************************************************************/
    1937           0 : int TABPolyline::GetNumParts()
    1938             : {
    1939           0 :     int numParts = 0;
    1940             : 
    1941           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1942           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    1943             :     {
    1944             :         /*-------------------------------------------------------------
    1945             :          * Simple polyline
    1946             :          *------------------------------------------------------------*/
    1947           0 :         numParts = 1;
    1948             :     }
    1949           0 :     else if (poGeom &&
    1950           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1951             :     {
    1952             :         /*-------------------------------------------------------------
    1953             :          * Multiple polyline
    1954             :          *------------------------------------------------------------*/
    1955           0 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    1956           0 :         numParts = poMultiLine->getNumGeometries();
    1957             :     }
    1958             : 
    1959           0 :     return numParts;
    1960             : }
    1961             : 
    1962             : /**********************************************************************
    1963             :  *                   TABPolyline::GetPartRef()
    1964             :  *
    1965             :  * Returns a reference to the specified OGRLineString number, hiding the
    1966             :  * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
    1967             :  *
    1968             :  * Returns NULL if the geometry contained in the object is invalid or
    1969             :  * missing or if the specified part index is invalid.
    1970             :  **********************************************************************/
    1971           0 : OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
    1972             : {
    1973           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1974           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    1975             :         nPartIndex == 0)
    1976             :     {
    1977             :         /*-------------------------------------------------------------
    1978             :          * Simple polyline
    1979             :          *------------------------------------------------------------*/
    1980           0 :         return poGeom->toLineString();
    1981             :     }
    1982           0 :     else if (poGeom &&
    1983           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1984             :     {
    1985             :         /*-------------------------------------------------------------
    1986             :          * Multiple polyline
    1987             :          *------------------------------------------------------------*/
    1988           0 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    1989           0 :         if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
    1990             :         {
    1991           0 :             return poMultiLine->getGeometryRef(nPartIndex);
    1992             :         }
    1993             :         else
    1994           0 :             return nullptr;
    1995             :     }
    1996             : 
    1997           0 :     return nullptr;
    1998             : }
    1999             : 
    2000             : /**********************************************************************
    2001             :  *                   TABPolyline::ValidateMapInfoType()
    2002             :  *
    2003             :  * Check the feature's geometry part and return the corresponding
    2004             :  * mapinfo object type code.  The m_nMapInfoType member will also
    2005             :  * be updated for further calls to GetMapInfoType();
    2006             :  *
    2007             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    2008             :  * is expected for this object class.
    2009             :  **********************************************************************/
    2010         238 : TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    2011             : {
    2012             :     /*-----------------------------------------------------------------
    2013             :      * Fetch and validate geometry
    2014             :      *----------------------------------------------------------------*/
    2015         238 :     OGRGeometry *poGeom = GetGeometryRef();
    2016         238 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2017             :     {
    2018             :         /*-------------------------------------------------------------
    2019             :          * Simple polyline
    2020             :          *------------------------------------------------------------*/
    2021         230 :         OGRLineString *poLine = poGeom->toLineString();
    2022         230 :         if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
    2023             :         {
    2024           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
    2025             :         }
    2026         230 :         else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
    2027             :         {
    2028           0 :             m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
    2029             :         }
    2030         230 :         else if (poLine->getNumPoints() > 2)
    2031             :         {
    2032         207 :             m_nMapInfoType = TAB_GEOM_PLINE;
    2033             :         }
    2034          46 :         else if ((poLine->getNumPoints() == 2) &&
    2035          23 :                  (m_bWriteTwoPointLineAsPolyline == TRUE))
    2036             :         {
    2037           0 :             m_nMapInfoType = TAB_GEOM_PLINE;
    2038             :         }
    2039          46 :         else if ((poLine->getNumPoints() == 2) &&
    2040          23 :                  (m_bWriteTwoPointLineAsPolyline == FALSE))
    2041             :         {
    2042          23 :             m_nMapInfoType = TAB_GEOM_LINE;
    2043             :         }
    2044             :         else
    2045             :         {
    2046           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    2047             :                      "TABPolyline: Geometry must contain at least 2 points.");
    2048           0 :             m_nMapInfoType = TAB_GEOM_NONE;
    2049             :         }
    2050             :     }
    2051          16 :     else if (poGeom &&
    2052           8 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2053             :     {
    2054             :         /*-------------------------------------------------------------
    2055             :          * Multiple polyline... validate all components
    2056             :          *------------------------------------------------------------*/
    2057           8 :         GInt32 numPointsTotal = 0;
    2058           8 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2059           8 :         int numLines = poMultiLine->getNumGeometries();
    2060             : 
    2061           8 :         m_nMapInfoType = TAB_GEOM_MULTIPLINE;
    2062             : 
    2063          18 :         for (int iLine = 0; iLine < numLines; iLine++)
    2064             :         {
    2065          10 :             poGeom = poMultiLine->getGeometryRef(iLine);
    2066          20 :             if (poGeom == nullptr ||
    2067          10 :                 wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    2068             :             {
    2069           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2070             :                          "TABPolyline: Object contains an invalid Geometry!");
    2071           0 :                 m_nMapInfoType = TAB_GEOM_NONE;
    2072           0 :                 numPointsTotal = 0;
    2073           0 :                 break;
    2074             :             }
    2075          10 :             OGRLineString *poLine = poGeom->toLineString();
    2076          10 :             numPointsTotal += poLine->getNumPoints();
    2077             :         }
    2078             : 
    2079           8 :         if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
    2080           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
    2081           8 :         else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
    2082           0 :             m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
    2083             :     }
    2084             :     else
    2085             :     {
    2086           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2087             :                  "TABPolyline: Missing or Invalid Geometry!");
    2088           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    2089             :     }
    2090             : 
    2091             :     /*-----------------------------------------------------------------
    2092             :      * Decide if coordinates should be compressed or not.
    2093             :      *
    2094             :      * __TODO__ We never write type LINE (2 points line) as compressed
    2095             :      * for the moment.  If we ever do it, then the decision to write
    2096             :      * a 2 point line in compressed coordinates or not should take into
    2097             :      * account the location of the object block MBR, so this would be
    2098             :      * better handled directly by TABMAPObjLine::WriteObject() since the
    2099             :      * object block center is not known until it is written to disk.
    2100             :      *----------------------------------------------------------------*/
    2101         238 :     if (m_nMapInfoType != TAB_GEOM_LINE)
    2102             :     {
    2103         215 :         ValidateCoordType(poMapFile);
    2104             :     }
    2105             :     else
    2106             :     {
    2107          23 :         UpdateMBR(poMapFile);
    2108             :     }
    2109             : 
    2110         238 :     return m_nMapInfoType;
    2111             : }
    2112             : 
    2113             : /**********************************************************************
    2114             :  *                   TABPolyline::ReadGeometryFromMAPFile()
    2115             :  *
    2116             :  * Fill the geometry and representation (color, etc...) part of the
    2117             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    2118             :  *
    2119             :  * It is assumed that poMAPFile currently points to the beginning of
    2120             :  * a map object.
    2121             :  *
    2122             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    2123             :  * been called.
    2124             :  **********************************************************************/
    2125        1582 : int TABPolyline::ReadGeometryFromMAPFile(
    2126             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    2127             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    2128             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    2129             : {
    2130        1582 :     GInt32 nX = 0;
    2131        1582 :     GInt32 nY = 0;
    2132        1582 :     double dX = 0.0;
    2133        1582 :     double dY = 0.0;
    2134        1582 :     double dXMin = 0.0;
    2135        1582 :     double dYMin = 0.0;
    2136        1582 :     double dXMax = 0.0;
    2137        1582 :     double dYMax = 0.0;
    2138        1582 :     OGRGeometry *poGeometry = nullptr;
    2139        1582 :     GBool bComprCoord = poObjHdr->IsCompressedType();
    2140        1582 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    2141             : 
    2142             :     /*-----------------------------------------------------------------
    2143             :      * Fetch and validate geometry type
    2144             :      *----------------------------------------------------------------*/
    2145        1582 :     m_nMapInfoType = poObjHdr->m_nType;
    2146             : 
    2147        1582 :     if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
    2148             :     {
    2149             :         /*=============================================================
    2150             :          * LINE (2 vertices)
    2151             :          *============================================================*/
    2152          21 :         TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
    2153             : 
    2154          21 :         m_bSmooth = FALSE;
    2155             : 
    2156          21 :         auto poLine = new OGRLineString();
    2157          21 :         poGeometry = poLine;
    2158          21 :         poLine->setNumPoints(2);
    2159             : 
    2160          21 :         poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
    2161             :                                 dYMin);
    2162          21 :         poLine->setPoint(0, dXMin, dYMin);
    2163             : 
    2164          21 :         poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
    2165             :                                 dYMax);
    2166          21 :         poLine->setPoint(1, dXMax, dYMax);
    2167             : 
    2168          21 :         if (!bCoordBlockDataOnly)
    2169             :         {
    2170          21 :             m_nPenDefIndex = poLineHdr->m_nPenId;  // Pen index
    2171          21 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2172          21 :         }
    2173             :     }
    2174        1561 :     else if (m_nMapInfoType == TAB_GEOM_PLINE ||
    2175        1556 :              m_nMapInfoType == TAB_GEOM_PLINE_C)
    2176             :     {
    2177             :         /*=============================================================
    2178             :          * PLINE ( > 2 vertices)
    2179             :          *============================================================*/
    2180             : 
    2181             :         /*-------------------------------------------------------------
    2182             :          * Copy data from poObjHdr
    2183             :          *------------------------------------------------------------*/
    2184        1551 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2185             : 
    2186        1551 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    2187        1551 :         const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
    2188        1551 :         if (nCoordDataSize > 1024 * 1024 &&
    2189           0 :             nCoordDataSize > poMapFile->GetFileSize())
    2190             :         {
    2191           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
    2192             :                      nCoordDataSize);
    2193           0 :             return -1;
    2194             :         }
    2195             :         // numLineSections = poPLineHdr->m_numLineSections; // Always 1
    2196        1551 :         m_bSmooth = poPLineHdr->m_bSmooth;
    2197             : 
    2198             :         // Centroid/label point
    2199        1551 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    2200             :                                 dX, dY);
    2201        1551 :         SetCenter(dX, dY);
    2202             : 
    2203             :         // Compressed coordinate origin (useful only in compressed case!)
    2204        1551 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    2205        1551 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    2206             : 
    2207             :         // MBR
    2208        1551 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    2209             :                                 dYMin);
    2210        1551 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    2211             :                                 dYMax);
    2212             : 
    2213        1551 :         if (!bCoordBlockDataOnly)
    2214             :         {
    2215        1383 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    2216        1383 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2217             :         }
    2218             : 
    2219             :         /*-------------------------------------------------------------
    2220             :          * Create Geometry and read coordinates
    2221             :          *------------------------------------------------------------*/
    2222        1551 :         const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
    2223             : 
    2224        1551 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2225         168 :             poCoordBlock = *ppoCoordBlock;
    2226             :         else
    2227        1383 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    2228        1551 :         if (poCoordBlock == nullptr)
    2229             :         {
    2230           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2231             :                      "Can't access coordinate block at offset %d",
    2232             :                      nCoordBlockPtr);
    2233           0 :             return -1;
    2234             :         }
    2235             : 
    2236        1551 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2237             : 
    2238        1551 :         auto poLine = new OGRLineString();
    2239        1551 :         poGeometry = poLine;
    2240        1551 :         poLine->setNumPoints(numPoints);
    2241             : 
    2242        1551 :         int nStatus = 0;
    2243       11906 :         for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2244             :         {
    2245       10355 :             nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
    2246       10355 :             if (nStatus != 0)
    2247           0 :                 break;
    2248       10355 :             poMapFile->Int2Coordsys(nX, nY, dX, dY);
    2249       10355 :             poLine->setPoint(i, dX, dY);
    2250             :         }
    2251             : 
    2252        1551 :         if (nStatus != 0)
    2253             :         {
    2254             :             // Failed ... error message has already been produced
    2255           0 :             delete poGeometry;
    2256           0 :             return nStatus;
    2257        1551 :         }
    2258             :     }
    2259          10 :     else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2260          10 :              m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2261           4 :              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2262           4 :              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2263           0 :              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2264           0 :              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
    2265             :     {
    2266             :         /*=============================================================
    2267             :          * PLINE MULTIPLE
    2268             :          *============================================================*/
    2269          10 :         const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    2270             : 
    2271             :         /*-------------------------------------------------------------
    2272             :          * Copy data from poObjHdr
    2273             :          *------------------------------------------------------------*/
    2274          10 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2275             : 
    2276          10 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    2277             :         /* GInt32 nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
    2278          10 :         GInt32 numLineSections = poPLineHdr->m_numLineSections;
    2279          10 :         m_bSmooth = poPLineHdr->m_bSmooth;
    2280             : 
    2281             :         // Centroid/label point
    2282          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    2283             :                                 dX, dY);
    2284          10 :         SetCenter(dX, dY);
    2285             : 
    2286             :         // Compressed coordinate origin (useful only in compressed case!)
    2287          10 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    2288          10 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    2289             : 
    2290             :         // MBR
    2291          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    2292             :                                 dYMin);
    2293          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    2294             :                                 dYMax);
    2295             : 
    2296          10 :         if (!bCoordBlockDataOnly)
    2297             :         {
    2298          10 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    2299          10 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2300             :         }
    2301             : 
    2302          10 :         const int nMinSizeOfSection = 24;
    2303          10 :         if (numLineSections > INT_MAX / nMinSizeOfSection)
    2304             :         {
    2305           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    2306           0 :             return -1;
    2307             :         }
    2308          10 :         const GUInt32 nMinimumBytesForSections =
    2309          10 :             nMinSizeOfSection * numLineSections;
    2310          10 :         if (nMinimumBytesForSections > 1024 * 1024 &&
    2311           0 :             nMinimumBytesForSections > poMapFile->GetFileSize())
    2312             :         {
    2313           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    2314           0 :             return -1;
    2315             :         }
    2316             : 
    2317             :         /*-------------------------------------------------------------
    2318             :          * Read data from the coord. block
    2319             :          *------------------------------------------------------------*/
    2320             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    2321          10 :             VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
    2322          10 :         if (pasSecHdrs == nullptr)
    2323           0 :             return -1;
    2324             : 
    2325          10 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2326           4 :             poCoordBlock = *ppoCoordBlock;
    2327             :         else
    2328           6 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    2329             : 
    2330          10 :         GInt32 numPointsTotal = 0;
    2331          20 :         if (poCoordBlock == nullptr ||
    2332          10 :             poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
    2333             :                                            numLineSections, pasSecHdrs,
    2334             :                                            numPointsTotal) != 0)
    2335             :         {
    2336           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2337             :                      "Failed reading coordinate data at offset %d",
    2338             :                      nCoordBlockPtr);
    2339           0 :             CPLFree(pasSecHdrs);
    2340           0 :             return -1;
    2341             :         }
    2342             : 
    2343          10 :         const GUInt32 nMinimumBytesForPoints =
    2344          10 :             (bComprCoord ? 4 : 8) * numPointsTotal;
    2345          10 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    2346           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    2347             :         {
    2348           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
    2349           0 :             CPLFree(pasSecHdrs);
    2350           0 :             return -1;
    2351             :         }
    2352             : 
    2353          10 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2354             : 
    2355             :         GInt32 *panXY = static_cast<GInt32 *>(
    2356          10 :             VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
    2357          10 :         if (panXY == nullptr)
    2358             :         {
    2359           0 :             CPLFree(pasSecHdrs);
    2360           0 :             return -1;
    2361             :         }
    2362             : 
    2363          10 :         if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
    2364             :             0)
    2365             :         {
    2366           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2367             :                      "Failed reading coordinate data at offset %d",
    2368             :                      nCoordBlockPtr);
    2369           0 :             CPLFree(pasSecHdrs);
    2370           0 :             CPLFree(panXY);
    2371           0 :             return -1;
    2372             :         }
    2373             : 
    2374             :         /*-------------------------------------------------------------
    2375             :          * Create a Geometry collection with one line geometry for
    2376             :          * each coordinates section
    2377             :          * If object contains only one section, then return a simple LineString
    2378             :          *------------------------------------------------------------*/
    2379          10 :         OGRMultiLineString *poMultiLine = nullptr;
    2380          10 :         if (numLineSections > 1)
    2381             :         {
    2382           6 :             poMultiLine = new OGRMultiLineString();
    2383           6 :             poGeometry = poMultiLine;
    2384             :         }
    2385             : 
    2386          26 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    2387             :         {
    2388          16 :             const int numSectionVertices = pasSecHdrs[iSection].numVertices;
    2389          16 :             GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
    2390             : 
    2391          16 :             auto poLine = new OGRLineString();
    2392          16 :             poLine->setNumPoints(numSectionVertices);
    2393             : 
    2394          48 :             for (int i = 0; i < numSectionVertices; i++)
    2395             :             {
    2396          32 :                 poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
    2397          32 :                 poLine->setPoint(i, dX, dY);
    2398          32 :                 pnXYPtr += 2;
    2399             :             }
    2400             : 
    2401          16 :             if (poGeometry == nullptr)
    2402           4 :                 poGeometry = poLine;
    2403          12 :             else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
    2404             :             {
    2405           0 :                 CPLAssert(false);  // Just in case lower-level lib is modified
    2406             :             }
    2407             :         }
    2408             : 
    2409          10 :         CPLFree(pasSecHdrs);
    2410          10 :         CPLFree(panXY);
    2411             :     }
    2412             :     else
    2413             :     {
    2414           0 :         CPLError(
    2415             :             CE_Failure, CPLE_AssertionFailed,
    2416             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    2417           0 :             m_nMapInfoType, m_nMapInfoType);
    2418           0 :         return -1;
    2419             :     }
    2420             : 
    2421        1582 :     SetGeometryDirectly(poGeometry);
    2422             : 
    2423        1582 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    2424        1582 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    2425             :               poObjHdr->m_nMaxY);
    2426             : 
    2427             :     /* Return a ref to coord block so that caller can continue reading
    2428             :      * after the end of this object (used by TABCollection and index splitting)
    2429             :      */
    2430        1582 :     if (ppoCoordBlock)
    2431         172 :         *ppoCoordBlock = poCoordBlock;
    2432             : 
    2433        1582 :     return 0;
    2434             : }
    2435             : 
    2436             : /**********************************************************************
    2437             :  *                   TABPolyline::WriteGeometryToMAPFile()
    2438             :  *
    2439             :  * Write the geometry and representation (color, etc...) part of the
    2440             :  * feature to the .MAP object pointed to by poMAPFile.
    2441             :  *
    2442             :  * It is assumed that poMAPFile currently points to a valid map object.
    2443             :  *
    2444             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    2445             :  * been called.
    2446             :  **********************************************************************/
    2447         406 : int TABPolyline::WriteGeometryToMAPFile(
    2448             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    2449             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    2450             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    2451             : {
    2452         406 :     GInt32 nX = 0;
    2453         406 :     GInt32 nY = 0;
    2454         406 :     OGRLineString *poLine = nullptr;
    2455         406 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    2456             : 
    2457             :     /*-----------------------------------------------------------------
    2458             :      * We assume that ValidateMapInfoType() was called already and that
    2459             :      * the type in poObjHdr->m_nType is valid.
    2460             :      *----------------------------------------------------------------*/
    2461         406 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    2462         406 :     CPLErrorReset();
    2463             : 
    2464             :     /*-----------------------------------------------------------------
    2465             :      * Fetch and validate geometry
    2466             :      *----------------------------------------------------------------*/
    2467         406 :     OGRGeometry *poGeom = GetGeometryRef();
    2468             : 
    2469        1195 :     if ((m_nMapInfoType == TAB_GEOM_LINE ||
    2470         406 :          m_nMapInfoType == TAB_GEOM_LINE_C) &&
    2471         835 :         poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    2472          23 :         (poLine = poGeom->toLineString())->getNumPoints() == 2)
    2473             :     {
    2474             :         /*=============================================================
    2475             :          * LINE (2 vertices)
    2476             :          *============================================================*/
    2477          23 :         TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
    2478             : 
    2479          23 :         poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
    2480          23 :                                 poLineHdr->m_nX1, poLineHdr->m_nY1);
    2481          23 :         poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
    2482          23 :                                 poLineHdr->m_nX2, poLineHdr->m_nY2);
    2483          23 :         poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
    2484             :                           poLineHdr->m_nY2);
    2485             : 
    2486          23 :         if (!bCoordBlockDataOnly)
    2487             :         {
    2488          23 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2489          23 :             poLineHdr->m_nPenId =
    2490          23 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2491             :         }
    2492             :     }
    2493        1146 :     else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
    2494         383 :               m_nMapInfoType == TAB_GEOM_PLINE_C) &&
    2495         766 :              poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2496             :     {
    2497             :         /*=============================================================
    2498             :          * PLINE ( > 2 vertices and less than 32767 vertices)
    2499             :          *============================================================*/
    2500         375 :         GBool bCompressed = poObjHdr->IsCompressedType();
    2501             : 
    2502             :         /*-------------------------------------------------------------
    2503             :          * Process geometry first...
    2504             :          *------------------------------------------------------------*/
    2505         375 :         poLine = poGeom->toLineString();
    2506         375 :         const int numPoints = poLine->getNumPoints();
    2507         375 :         CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
    2508             : 
    2509         375 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2510         168 :             poCoordBlock = *ppoCoordBlock;
    2511             :         else
    2512         207 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    2513         375 :         poCoordBlock->StartNewFeature();
    2514         375 :         const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    2515         375 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2516             : 
    2517         375 :         int nStatus = 0;
    2518        5207 :         for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2519             :         {
    2520        4832 :             poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
    2521        4832 :             if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
    2522             :                 0)
    2523             :             {
    2524             :                 // Failed ... error message has already been produced
    2525           0 :                 return nStatus;
    2526             :             }
    2527             :         }
    2528             : 
    2529         375 :         const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    2530             : 
    2531             :         /*-------------------------------------------------------------
    2532             :          * Copy info to poObjHdr
    2533             :          *------------------------------------------------------------*/
    2534         375 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2535             : 
    2536         375 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    2537         375 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    2538         375 :         poPLineHdr->m_numLineSections = 1;
    2539             : 
    2540         375 :         poPLineHdr->m_bSmooth = m_bSmooth;
    2541             : 
    2542             :         // MBR
    2543         375 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    2544             : 
    2545             :         // Polyline center/label point
    2546         375 :         double dX = 0.0;
    2547         375 :         double dY = 0.0;
    2548         375 :         if (GetCenter(dX, dY) != -1)
    2549             :         {
    2550         375 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    2551         375 :                                     poPLineHdr->m_nLabelY);
    2552             :         }
    2553             :         else
    2554             :         {
    2555           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    2556           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    2557             :         }
    2558             : 
    2559             :         // Compressed coordinate origin (useful only in compressed case!)
    2560         375 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    2561         375 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    2562             : 
    2563         375 :         if (!bCoordBlockDataOnly)
    2564             :         {
    2565         207 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2566         207 :             poPLineHdr->m_nPenId =
    2567         207 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2568             :         }
    2569             :     }
    2570          24 :     else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2571           8 :               m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2572           0 :               m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2573           0 :               m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2574           0 :               m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2575           8 :               m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
    2576          16 :              poGeom &&
    2577           8 :              (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
    2578           0 :               wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
    2579             :     {
    2580             :         /*=============================================================
    2581             :          * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
    2582             :          *============================================================*/
    2583             : 
    2584           8 :         CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2585             :                   m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2586             :                   m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2587             :                   m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2588             :                   m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2589             :                   m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
    2590             : 
    2591           8 :         int nStatus = 0;
    2592           8 :         OGREnvelope sEnvelope;
    2593           8 :         GBool bCompressed = poObjHdr->IsCompressedType();
    2594             : 
    2595             :         /*-------------------------------------------------------------
    2596             :          * Process geometry first...
    2597             :          *------------------------------------------------------------*/
    2598           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2599           0 :             poCoordBlock = *ppoCoordBlock;
    2600             :         else
    2601           8 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    2602           8 :         poCoordBlock->StartNewFeature();
    2603           8 :         const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    2604           8 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2605             : 
    2606           8 :         OGRMultiLineString *poMultiLine = nullptr;
    2607           8 :         GInt32 numLines = 1;
    2608           8 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2609             :         {
    2610           8 :             poMultiLine = poGeom->toMultiLineString();
    2611           8 :             numLines = poMultiLine->getNumGeometries();
    2612             :         }
    2613             :         // else
    2614             :         // {
    2615             :         //     poMultiLine = NULL;
    2616             :         //     numLines = 1;
    2617             :         // }
    2618             : 
    2619             :         /*-------------------------------------------------------------
    2620             :          * Build and write array of coord sections headers
    2621             :          *------------------------------------------------------------*/
    2622             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    2623           8 :             VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
    2624           8 :         if (pasSecHdrs == nullptr)
    2625             :         {
    2626           0 :             return -1;
    2627             :         }
    2628             : 
    2629             :         /*-------------------------------------------------------------
    2630             :          * In calculation of nDataOffset, we have to take into account that
    2631             :          * V450 header section uses int32 instead of int16 for numVertices
    2632             :          * and we add another 2 bytes to align with a 4 bytes boundary.
    2633             :          *------------------------------------------------------------*/
    2634           8 :         int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    2635             : 
    2636           8 :         const int nTotalHdrSizeUncompressed =
    2637           8 :             (nVersion >= 450 ? 28 : 24) * numLines;
    2638             : 
    2639           8 :         GInt32 numPointsTotal = 0;
    2640          18 :         for (int iLine = 0; iLine < numLines; iLine++)
    2641             :         {
    2642          10 :             if (poMultiLine)
    2643          10 :                 poGeom = poMultiLine->getGeometryRef(iLine);
    2644             : 
    2645          20 :             if (poGeom &&
    2646          10 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2647             :             {
    2648          10 :                 poLine = poGeom->toLineString();
    2649          10 :                 const GInt32 numPoints = poLine->getNumPoints();
    2650          10 :                 poLine->getEnvelope(&sEnvelope);
    2651             : 
    2652          10 :                 pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
    2653          10 :                 pasSecHdrs[iLine].numHoles = 0;  // It is a line!
    2654             : 
    2655          10 :                 poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
    2656          10 :                                         pasSecHdrs[iLine].nXMin,
    2657          10 :                                         pasSecHdrs[iLine].nYMin);
    2658          10 :                 poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
    2659          10 :                                         pasSecHdrs[iLine].nXMax,
    2660          10 :                                         pasSecHdrs[iLine].nYMax);
    2661          10 :                 pasSecHdrs[iLine].nDataOffset =
    2662          10 :                     nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
    2663          10 :                 pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
    2664             : 
    2665          10 :                 numPointsTotal += numPoints;
    2666             :             }
    2667             :             else
    2668             :             {
    2669           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2670             :                          "TABPolyline: Object contains an invalid Geometry!");
    2671           0 :                 nStatus = -1;
    2672             :             }
    2673             :         }
    2674             : 
    2675           8 :         if (nStatus == 0)
    2676           8 :             nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
    2677             :                                                       pasSecHdrs, bCompressed);
    2678             : 
    2679           8 :         CPLFree(pasSecHdrs);
    2680           8 :         pasSecHdrs = nullptr;
    2681             : 
    2682           8 :         if (nStatus != 0)
    2683           0 :             return nStatus;  // Error has already been reported.
    2684             : 
    2685             :         /*-------------------------------------------------------------
    2686             :          * Then write the coordinates themselves...
    2687             :          *------------------------------------------------------------*/
    2688          18 :         for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
    2689             :         {
    2690          10 :             if (poMultiLine)
    2691          10 :                 poGeom = poMultiLine->getGeometryRef(iLine);
    2692             : 
    2693          20 :             if (poGeom &&
    2694          10 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2695             :             {
    2696          10 :                 poLine = poGeom->toLineString();
    2697          10 :                 GInt32 numPoints = poLine->getNumPoints();
    2698             : 
    2699          30 :                 for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2700             :                 {
    2701          20 :                     poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
    2702             :                                             nX, nY);
    2703          20 :                     if ((nStatus = poCoordBlock->WriteIntCoord(
    2704          20 :                              nX, nY, bCompressed)) != 0)
    2705             :                     {
    2706             :                         // Failed ... error message has already been produced
    2707           0 :                         return nStatus;
    2708             :                     }
    2709             :                 }
    2710             :             }
    2711             :             else
    2712             :             {
    2713           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2714             :                          "TABPolyline: Object contains an invalid Geometry!");
    2715           0 :                 return -1;
    2716             :             }
    2717             :         }
    2718             : 
    2719           8 :         const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    2720             : 
    2721             :         /*-------------------------------------------------------------
    2722             :          * ... and finally copy info to poObjHdr
    2723             :          *------------------------------------------------------------*/
    2724           8 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2725             : 
    2726           8 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    2727           8 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    2728           8 :         poPLineHdr->m_numLineSections = numLines;
    2729             : 
    2730           8 :         poPLineHdr->m_bSmooth = m_bSmooth;
    2731             : 
    2732             :         // MBR
    2733           8 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    2734             : 
    2735             :         // Polyline center/label point
    2736           8 :         double dX = 0.0;
    2737           8 :         double dY = 0.0;
    2738           8 :         if (GetCenter(dX, dY) != -1)
    2739             :         {
    2740           8 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    2741           8 :                                     poPLineHdr->m_nLabelY);
    2742             :         }
    2743             :         else
    2744             :         {
    2745           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    2746           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    2747             :         }
    2748             : 
    2749             :         // Compressed coordinate origin (useful only in compressed case!)
    2750           8 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    2751           8 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    2752             : 
    2753           8 :         if (!bCoordBlockDataOnly)
    2754             :         {
    2755           8 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2756           8 :             poPLineHdr->m_nPenId =
    2757           8 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2758             :         }
    2759             :     }
    2760             :     else
    2761             :     {
    2762           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2763             :                  "TABPolyline: Object contains an invalid Geometry!");
    2764           0 :         return -1;
    2765             :     }
    2766             : 
    2767         406 :     if (CPLGetLastErrorType() == CE_Failure)
    2768           0 :         return -1;
    2769             : 
    2770             :     /* Return a ref to coord block so that caller can continue writing
    2771             :      * after the end of this object (used by index splitting)
    2772             :      */
    2773         406 :     if (ppoCoordBlock)
    2774         168 :         *ppoCoordBlock = poCoordBlock;
    2775             : 
    2776         406 :     return 0;
    2777             : }
    2778             : 
    2779             : /**********************************************************************
    2780             :  *                   TABPolyline::GetStyleString() const
    2781             :  *
    2782             :  * Return style string for this feature.
    2783             :  *
    2784             :  * Style String is built only once during the first call to GetStyleString().
    2785             :  **********************************************************************/
    2786          44 : const char *TABPolyline::GetStyleString() const
    2787             : {
    2788          44 :     if (m_pszStyleString == nullptr)
    2789             :     {
    2790          35 :         m_pszStyleString = CPLStrdup(GetPenStyleString());
    2791             :     }
    2792             : 
    2793          44 :     return m_pszStyleString;
    2794             : }
    2795             : 
    2796             : /**********************************************************************
    2797             :  *                   TABPolyline::DumpMIF()
    2798             :  *
    2799             :  * Dump feature geometry in a format similar to .MIF PLINEs.
    2800             :  **********************************************************************/
    2801           0 : void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
    2802             : {
    2803           0 :     OGRMultiLineString *poMultiLine = nullptr;
    2804           0 :     OGRLineString *poLine = nullptr;
    2805             :     int i, numPoints;
    2806             : 
    2807           0 :     if (fpOut == nullptr)
    2808           0 :         fpOut = stdout;
    2809             : 
    2810             :     /*-----------------------------------------------------------------
    2811             :      * Fetch and validate geometry
    2812             :      *----------------------------------------------------------------*/
    2813           0 :     OGRGeometry *poGeom = GetGeometryRef();
    2814           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2815             :     {
    2816             :         /*-------------------------------------------------------------
    2817             :          * Generate output for simple polyline
    2818             :          *------------------------------------------------------------*/
    2819           0 :         poLine = poGeom->toLineString();
    2820           0 :         numPoints = poLine->getNumPoints();
    2821           0 :         fprintf(fpOut, "PLINE %d\n", numPoints);
    2822           0 :         for (i = 0; i < numPoints; i++)
    2823           0 :             fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
    2824             :     }
    2825           0 :     else if (poGeom &&
    2826           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2827             :     {
    2828             :         /*-------------------------------------------------------------
    2829             :          * Generate output for multiple polyline
    2830             :          *------------------------------------------------------------*/
    2831             :         int iLine, numLines;
    2832           0 :         poMultiLine = poGeom->toMultiLineString();
    2833           0 :         numLines = poMultiLine->getNumGeometries();
    2834           0 :         fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
    2835           0 :         for (iLine = 0; iLine < numLines; iLine++)
    2836             :         {
    2837           0 :             poGeom = poMultiLine->getGeometryRef(iLine);
    2838           0 :             if (poGeom &&
    2839           0 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2840             :             {
    2841           0 :                 poLine = poGeom->toLineString();
    2842           0 :                 numPoints = poLine->getNumPoints();
    2843           0 :                 fprintf(fpOut, " %d\n", numPoints);
    2844           0 :                 for (i = 0; i < numPoints; i++)
    2845           0 :                     fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
    2846             :                             poLine->getY(i));
    2847             :             }
    2848             :             else
    2849             :             {
    2850           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2851             :                          "TABPolyline: Object contains an invalid Geometry!");
    2852           0 :                 return;
    2853             :             }
    2854             :         }
    2855             :     }
    2856             :     else
    2857             :     {
    2858           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2859             :                  "TABPolyline: Missing or Invalid Geometry!");
    2860           0 :         return;
    2861             :     }
    2862             : 
    2863           0 :     if (m_bCenterIsSet)
    2864           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    2865             : 
    2866             :     // Finish with PEN/BRUSH/etc. clauses
    2867           0 :     DumpPenDef();
    2868             : 
    2869           0 :     fflush(fpOut);
    2870             : }
    2871             : 
    2872             : /**********************************************************************
    2873             :  *                   TABPolyline::GetCenter()
    2874             :  *
    2875             :  * Returns the center point of the line.  Compute one if it was not
    2876             :  * explicitly set:
    2877             :  *
    2878             :  * In MapInfo, for a simple or multiple polyline (pline), the center point
    2879             :  * in the object definition is supposed to be either the center point of
    2880             :  * the pline or the first section of a multiple pline (if an odd number of
    2881             :  * points in the pline or first section), or the midway point between the
    2882             :  * two central points (if an even number of points involved).
    2883             :  *
    2884             :  * Returns 0 on success, -1 on error.
    2885             :  **********************************************************************/
    2886         383 : int TABPolyline::GetCenter(double &dX, double &dY)
    2887             : {
    2888         383 :     if (!m_bCenterIsSet)
    2889             :     {
    2890         215 :         OGRLineString *poLine = nullptr;
    2891             : 
    2892         215 :         OGRGeometry *poGeom = GetGeometryRef();
    2893         215 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2894             :         {
    2895         207 :             poLine = poGeom->toLineString();
    2896             :         }
    2897          16 :         else if (poGeom &&
    2898           8 :                  wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2899             :         {
    2900           8 :             OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2901           8 :             if (poMultiLine->getNumGeometries() > 0)
    2902           8 :                 poLine = poMultiLine->getGeometryRef(0);
    2903             :         }
    2904             : 
    2905         215 :         if (poLine && poLine->getNumPoints() > 0)
    2906             :         {
    2907         215 :             int i = poLine->getNumPoints() / 2;
    2908         215 :             if (poLine->getNumPoints() % 2 == 0)
    2909             :             {
    2910             :                 // Return the midway between the 2 center points
    2911          15 :                 m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
    2912          15 :                 m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
    2913             :             }
    2914             :             else
    2915             :             {
    2916             :                 // Return the center point
    2917         200 :                 m_dCenterX = poLine->getX(i);
    2918         200 :                 m_dCenterY = poLine->getY(i);
    2919             :             }
    2920         215 :             m_bCenterIsSet = TRUE;
    2921             :         }
    2922             :     }
    2923             : 
    2924         383 :     if (!m_bCenterIsSet)
    2925           0 :         return -1;
    2926             : 
    2927         383 :     dX = m_dCenterX;
    2928         383 :     dY = m_dCenterY;
    2929         383 :     return 0;
    2930             : }
    2931             : 
    2932             : /**********************************************************************
    2933             :  *                   TABPolyline::SetCenter()
    2934             :  *
    2935             :  * Set the X,Y coordinates to use as center point for the line.
    2936             :  **********************************************************************/
    2937        1561 : void TABPolyline::SetCenter(double dX, double dY)
    2938             : {
    2939        1561 :     m_dCenterX = dX;
    2940        1561 :     m_dCenterY = dY;
    2941        1561 :     m_bCenterIsSet = TRUE;
    2942        1561 : }
    2943             : 
    2944             : /**********************************************************************
    2945             :  *                   TABPolyline::TwoPointLineAsPolyline()
    2946             :  *
    2947             :  * Returns the value of m_bWriteTwoPointLineAsPolyline
    2948             :  **********************************************************************/
    2949           0 : GBool TABPolyline::TwoPointLineAsPolyline()
    2950             : {
    2951           0 :     return m_bWriteTwoPointLineAsPolyline;
    2952             : }
    2953             : 
    2954             : /**********************************************************************
    2955             :  *                   TABPolyline::TwoPointLineAsPolyline()
    2956             :  *
    2957             :  * Sets the value of m_bWriteTwoPointLineAsPolyline
    2958             :  **********************************************************************/
    2959           0 : void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
    2960             : {
    2961           0 :     m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
    2962           0 : }
    2963             : 
    2964             : /*=====================================================================
    2965             :  *                      class TABRegion
    2966             :  *====================================================================*/
    2967             : 
    2968             : /**********************************************************************
    2969             :  *                   TABRegion::TABRegion()
    2970             :  *
    2971             :  * Constructor.
    2972             :  **********************************************************************/
    2973        1168 : TABRegion::TABRegion(OGRFeatureDefn *poDefnIn)
    2974             :     : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
    2975        1168 :       m_dCenterX(0.0), m_dCenterY(0.0)
    2976             : {
    2977        1168 : }
    2978             : 
    2979             : /**********************************************************************
    2980             :  *                   TABRegion::~TABRegion()
    2981             :  *
    2982             :  * Destructor.
    2983             :  **********************************************************************/
    2984        2336 : TABRegion::~TABRegion()
    2985             : {
    2986        2336 : }
    2987             : 
    2988             : /**********************************************************************
    2989             :  *                     TABRegion::CloneTABFeature()
    2990             :  *
    2991             :  * Duplicate feature, including stuff specific to each TABFeature type.
    2992             :  *
    2993             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    2994             :  * then copies any members specific to its own type.
    2995             :  **********************************************************************/
    2996           0 : TABFeature *TABRegion::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    2997             : {
    2998             :     /*-----------------------------------------------------------------
    2999             :      * Alloc new feature and copy the base stuff
    3000             :      *----------------------------------------------------------------*/
    3001           0 :     TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
    3002             : 
    3003           0 :     CopyTABFeatureBase(poNew);
    3004             : 
    3005             :     /*-----------------------------------------------------------------
    3006             :      * And members specific to this class
    3007             :      *----------------------------------------------------------------*/
    3008             :     // ITABFeaturePen
    3009           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    3010             : 
    3011             :     // ITABFeatureBrush
    3012           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    3013             : 
    3014           0 :     poNew->m_bSmooth = m_bSmooth;
    3015           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    3016           0 :     poNew->m_dCenterX = m_dCenterX;
    3017           0 :     poNew->m_dCenterY = m_dCenterY;
    3018             : 
    3019           0 :     return poNew;
    3020             : }
    3021             : 
    3022             : /**********************************************************************
    3023             :  *                   TABRegion::ValidateMapInfoType()
    3024             :  *
    3025             :  * Check the feature's geometry part and return the corresponding
    3026             :  * mapinfo object type code.  The m_nMapInfoType member will also
    3027             :  * be updated for further calls to GetMapInfoType();
    3028             :  *
    3029             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    3030             :  * is expected for this object class.
    3031             :  **********************************************************************/
    3032          88 : TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    3033             : {
    3034             :     /*-----------------------------------------------------------------
    3035             :      * Fetch and validate geometry
    3036             :      *----------------------------------------------------------------*/
    3037          88 :     OGRGeometry *poGeom = GetGeometryRef();
    3038          94 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3039           6 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3040             :     {
    3041          88 :         GInt32 numPointsTotal = 0;
    3042          88 :         GInt32 numRings = GetNumRings();
    3043         176 :         for (int i = 0; i < numRings; i++)
    3044             :         {
    3045          88 :             OGRLinearRing *poRing = GetRingRef(i);
    3046          88 :             if (poRing)
    3047          88 :                 numPointsTotal += poRing->getNumPoints();
    3048             :         }
    3049          88 :         if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
    3050           0 :             m_nMapInfoType = TAB_GEOM_V800_REGION;
    3051          88 :         else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
    3052           0 :             m_nMapInfoType = TAB_GEOM_V450_REGION;
    3053             :         else
    3054          88 :             m_nMapInfoType = TAB_GEOM_REGION;
    3055             :     }
    3056             :     else
    3057             :     {
    3058           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3059             :                  "TABRegion: Missing or Invalid Geometry!");
    3060           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    3061             :     }
    3062             : 
    3063             :     /*-----------------------------------------------------------------
    3064             :      * Decide if coordinates should be compressed or not.
    3065             :      *----------------------------------------------------------------*/
    3066          88 :     ValidateCoordType(poMapFile);
    3067             : 
    3068          88 :     return m_nMapInfoType;
    3069             : }
    3070             : 
    3071             : /**********************************************************************
    3072             :  *                   TABRegion::ReadGeometryFromMAPFile()
    3073             :  *
    3074             :  * Fill the geometry and representation (color, etc...) part of the
    3075             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    3076             :  *
    3077             :  * It is assumed that poMAPFile currently points to the beginning of
    3078             :  * a map object.
    3079             :  *
    3080             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    3081             :  * been called.
    3082             :  **********************************************************************/
    3083         514 : int TABRegion::ReadGeometryFromMAPFile(
    3084             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    3085             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    3086             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    3087             : {
    3088         514 :     double dXMin = 0.0;
    3089         514 :     double dYMin = 0.0;
    3090         514 :     double dXMax = 0.0;
    3091         514 :     double dYMax = 0.0;
    3092         514 :     OGRGeometry *poGeometry = nullptr;
    3093         514 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    3094             : 
    3095             :     /*-----------------------------------------------------------------
    3096             :      * Fetch and validate geometry type
    3097             :      *----------------------------------------------------------------*/
    3098         514 :     m_nMapInfoType = poObjHdr->m_nType;
    3099             : 
    3100         514 :     if (m_nMapInfoType == TAB_GEOM_REGION ||
    3101         459 :         m_nMapInfoType == TAB_GEOM_REGION_C ||
    3102           4 :         m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3103           4 :         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3104           0 :         m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3105           0 :         m_nMapInfoType == TAB_GEOM_V800_REGION_C)
    3106             :     {
    3107             :         /*=============================================================
    3108             :          * REGION (Similar to PLINE MULTIPLE)
    3109             :          *============================================================*/
    3110             :         GInt32 /* nCoordDataSize, */ numPointsTotal;
    3111         514 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3112         514 :         OGRPolygon *poPolygon = nullptr;
    3113         514 :         GBool bComprCoord = poObjHdr->IsCompressedType();
    3114         514 :         int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    3115             : 
    3116             :         /*-------------------------------------------------------------
    3117             :          * Copy data from poObjHdr
    3118             :          *------------------------------------------------------------*/
    3119         514 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    3120             : 
    3121         514 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    3122             :         /* nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
    3123         514 :         GInt32 numLineSections = poPLineHdr->m_numLineSections;
    3124         514 :         m_bSmooth = poPLineHdr->m_bSmooth;
    3125             : 
    3126             :         // Centroid/label point
    3127         514 :         double dX = 0.0;
    3128         514 :         double dY = 0.0;
    3129         514 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    3130             :                                 dX, dY);
    3131         514 :         SetCenter(dX, dY);
    3132             : 
    3133             :         // Compressed coordinate origin (useful only in compressed case!)
    3134         514 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    3135         514 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    3136             : 
    3137             :         // MBR
    3138         514 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    3139             :                                 dYMin);
    3140         514 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    3141             :                                 dYMax);
    3142             : 
    3143         514 :         if (!bCoordBlockDataOnly)
    3144             :         {
    3145         514 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    3146         514 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    3147         514 :             m_nBrushDefIndex = poPLineHdr->m_nBrushId;  // Brush index
    3148         514 :             poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    3149             :         }
    3150             : 
    3151             :         /*-------------------------------------------------------------
    3152             :          * Read data from the coord. block
    3153             :          *------------------------------------------------------------*/
    3154             : 
    3155         514 :         const int nMinSizeOfSection = 24;
    3156         514 :         if (numLineSections > INT_MAX / nMinSizeOfSection)
    3157             :         {
    3158           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    3159           0 :             return -1;
    3160             :         }
    3161         514 :         const GUInt32 nMinimumBytesForSections =
    3162         514 :             nMinSizeOfSection * numLineSections;
    3163         514 :         if (nMinimumBytesForSections > 1024 * 1024 &&
    3164           0 :             nMinimumBytesForSections > poMapFile->GetFileSize())
    3165             :         {
    3166           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    3167           0 :             return -1;
    3168             :         }
    3169             : 
    3170             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    3171         514 :             VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
    3172         514 :         if (pasSecHdrs == nullptr)
    3173           0 :             return -1;
    3174             : 
    3175         514 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    3176           4 :             poCoordBlock = *ppoCoordBlock;
    3177             :         else
    3178         510 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    3179             : 
    3180         514 :         if (poCoordBlock)
    3181         514 :             poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    3182             : 
    3183        1028 :         if (poCoordBlock == nullptr ||
    3184         514 :             poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
    3185             :                                            numLineSections, pasSecHdrs,
    3186             :                                            numPointsTotal) != 0)
    3187             :         {
    3188           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3189             :                      "Failed reading coordinate data at offset %d",
    3190             :                      nCoordBlockPtr);
    3191           0 :             CPLFree(pasSecHdrs);
    3192           0 :             return -1;
    3193             :         }
    3194             : 
    3195         514 :         const GUInt32 nMinimumBytesForPoints =
    3196         514 :             (bComprCoord ? 4 : 8) * numPointsTotal;
    3197         514 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    3198           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    3199             :         {
    3200           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
    3201           0 :             CPLFree(pasSecHdrs);
    3202           0 :             return -1;
    3203             :         }
    3204             : 
    3205             :         GInt32 *panXY = static_cast<GInt32 *>(
    3206         514 :             VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
    3207         514 :         if (panXY == nullptr)
    3208             :         {
    3209           0 :             CPLFree(pasSecHdrs);
    3210           0 :             return -1;
    3211             :         }
    3212             : 
    3213         514 :         if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
    3214             :             0)
    3215             :         {
    3216           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3217             :                      "Failed reading coordinate data at offset %d",
    3218             :                      nCoordBlockPtr);
    3219           0 :             CPLFree(pasSecHdrs);
    3220           0 :             CPLFree(panXY);
    3221           0 :             return -1;
    3222             :         }
    3223             : 
    3224             :         /*-------------------------------------------------------------
    3225             :          * Decide if we should return an OGRPolygon or an OGRMultiPolygon
    3226             :          * depending on the number of outer rings found in CoordSecHdr blocks.
    3227             :          * The CoodSecHdr block for each outer ring in the region has a flag
    3228             :          * indicating the number of inner rings that follow.
    3229             :          * In older versions of the format, the count of inner rings was
    3230             :          * always zero, so in this case we would always return MultiPolygons.
    3231             :          *
    3232             :          * Note: The current implementation assumes that there cannot be
    3233             :          * holes inside holes (i.e. multiple levels of inner rings)... if
    3234             :          * that case was encountered then we would return an OGRMultiPolygon
    3235             :          * in which the topological relationship between the rings would
    3236             :          * be lost.
    3237             :          *------------------------------------------------------------*/
    3238         514 :         int numOuterRings = 0;
    3239        1028 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    3240             :         {
    3241             :             // Count this as an outer ring.
    3242         514 :             numOuterRings++;
    3243             :             // Skip inner rings... so loop continues on an outer ring.
    3244         514 :             iSection += pasSecHdrs[iSection].numHoles;
    3245             :         }
    3246             : 
    3247         514 :         if (numOuterRings > 1)
    3248             :         {
    3249           0 :             poMultiPolygon = new OGRMultiPolygon;
    3250           0 :             poGeometry = poMultiPolygon;
    3251             :         }
    3252             :         else
    3253             :         {
    3254         514 :             poGeometry = nullptr;  // Will be set later
    3255             :         }
    3256             : 
    3257             :         /*-------------------------------------------------------------
    3258             :          * OK, build the OGRGeometry object.
    3259             :          *------------------------------------------------------------*/
    3260         514 :         int numHolesToRead = 0;
    3261         514 :         poPolygon = nullptr;
    3262        1028 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    3263             :         {
    3264             : 
    3265         514 :             if (poPolygon == nullptr)
    3266         514 :                 poPolygon = new OGRPolygon();
    3267             : 
    3268         514 :             if (numHolesToRead < 1)
    3269         514 :                 numHolesToRead = pasSecHdrs[iSection].numHoles;
    3270             :             else
    3271           0 :                 numHolesToRead--;
    3272             : 
    3273         514 :             int numSectionVertices = pasSecHdrs[iSection].numVertices;
    3274         514 :             GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
    3275             : 
    3276         514 :             OGRLinearRing *poRing = new OGRLinearRing();
    3277         514 :             poRing->setNumPoints(numSectionVertices);
    3278             : 
    3279       13985 :             for (int i = 0; i < numSectionVertices; i++)
    3280             :             {
    3281       13471 :                 poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
    3282       13471 :                 poRing->setPoint(i, dX, dY);
    3283       13471 :                 pnXYPtr += 2;
    3284             :             }
    3285             : 
    3286         514 :             poPolygon->addRingDirectly(poRing);
    3287         514 :             poRing = nullptr;
    3288             : 
    3289         514 :             if (numHolesToRead < 1)
    3290             :             {
    3291         514 :                 if (numOuterRings > 1)
    3292             :                 {
    3293           0 :                     poMultiPolygon->addGeometryDirectly(poPolygon);
    3294             :                 }
    3295             :                 else
    3296             :                 {
    3297         514 :                     poGeometry = poPolygon;
    3298         514 :                     CPLAssert(iSection == numLineSections - 1);
    3299             :                 }
    3300             : 
    3301         514 :                 poPolygon = nullptr;  // We'll alloc a new polygon next loop.
    3302             :             }
    3303             :         }
    3304         514 :         delete poPolygon;  // should only trigger on corrupted files
    3305             : 
    3306         514 :         CPLFree(pasSecHdrs);
    3307         514 :         CPLFree(panXY);
    3308             :     }
    3309             :     else
    3310             :     {
    3311           0 :         CPLError(
    3312             :             CE_Failure, CPLE_AssertionFailed,
    3313             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    3314           0 :             m_nMapInfoType, m_nMapInfoType);
    3315           0 :         return -1;
    3316             :     }
    3317             : 
    3318         514 :     SetGeometryDirectly(poGeometry);
    3319             : 
    3320         514 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    3321         514 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    3322             :               poObjHdr->m_nMaxY);
    3323             : 
    3324             :     /* Return a ref to coord block so that caller can continue reading
    3325             :      * after the end of this object (used by TABCollection and index splitting)
    3326             :      */
    3327         514 :     if (ppoCoordBlock)
    3328           4 :         *ppoCoordBlock = poCoordBlock;
    3329             : 
    3330         514 :     return 0;
    3331             : }
    3332             : 
    3333             : /**********************************************************************
    3334             :  *                   TABRegion::WriteGeometryToMAPFile()
    3335             :  *
    3336             :  * Write the geometry and representation (color, etc...) part of the
    3337             :  * feature to the .MAP object pointed to by poMAPFile.
    3338             :  *
    3339             :  * It is assumed that poMAPFile currently points to a valid map object.
    3340             :  *
    3341             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    3342             :  * been called.
    3343             :  **********************************************************************/
    3344          88 : int TABRegion::WriteGeometryToMAPFile(
    3345             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    3346             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    3347             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    3348             : {
    3349             :     /*-----------------------------------------------------------------
    3350             :      * We assume that ValidateMapInfoType() was called already and that
    3351             :      * the type in poObjHdr->m_nType is valid.
    3352             :      *----------------------------------------------------------------*/
    3353          88 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    3354             : 
    3355             :     /*-----------------------------------------------------------------
    3356             :      * Fetch and validate geometry
    3357             :      *----------------------------------------------------------------*/
    3358          88 :     OGRGeometry *poGeom = GetGeometryRef();
    3359          88 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    3360             : 
    3361         258 :     if ((m_nMapInfoType == TAB_GEOM_REGION ||
    3362          82 :          m_nMapInfoType == TAB_GEOM_REGION_C ||
    3363           0 :          m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3364           0 :          m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3365           0 :          m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3366          88 :          m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
    3367         176 :         poGeom &&
    3368          88 :         (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3369           6 :          wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3370             :     {
    3371             :         /*=============================================================
    3372             :          * REGIONs are similar to PLINE MULTIPLE
    3373             :          *
    3374             :          * We accept both OGRPolygons (with one or multiple rings) and
    3375             :          * OGRMultiPolygons as input.
    3376             :          *============================================================*/
    3377          88 :         GBool bCompressed = poObjHdr->IsCompressedType();
    3378             : 
    3379             :         /*-------------------------------------------------------------
    3380             :          * Process geometry first...
    3381             :          *------------------------------------------------------------*/
    3382          88 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    3383           0 :             poCoordBlock = *ppoCoordBlock;
    3384             :         else
    3385          88 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    3386          88 :         poCoordBlock->StartNewFeature();
    3387          88 :         GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    3388          88 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    3389             : 
    3390             : #ifdef TABDUMP
    3391             :         printf(/*ok*/
    3392             :                "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
    3393             :                m_nComprOrgX, m_nComprOrgY);
    3394             : #endif
    3395             :         /*-------------------------------------------------------------
    3396             :          * Fetch total number of rings and build array of coord
    3397             :          * sections headers.
    3398             :          *------------------------------------------------------------*/
    3399          88 :         TABMAPCoordSecHdr *pasSecHdrs = nullptr;
    3400          88 :         int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
    3401          88 :         int nStatus = numRingsTotal == 0 ? -1 : 0;
    3402             : 
    3403             :         /*-------------------------------------------------------------
    3404             :          * Write the Coord. Section Header
    3405             :          *------------------------------------------------------------*/
    3406          88 :         const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    3407             : 
    3408          88 :         if (nStatus == 0)
    3409          88 :             nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
    3410             :                                                       pasSecHdrs, bCompressed);
    3411             : 
    3412          88 :         CPLFree(pasSecHdrs);
    3413          88 :         pasSecHdrs = nullptr;
    3414             : 
    3415          88 :         if (nStatus != 0)
    3416           0 :             return nStatus;  // Error has already been reported.
    3417             : 
    3418             :         /*-------------------------------------------------------------
    3419             :          * Go through all the rings in our OGRMultiPolygon or OGRPolygon
    3420             :          * to write the coordinates themselves...
    3421             :          *------------------------------------------------------------*/
    3422             : 
    3423          88 :         GInt32 nX = 0, nY = 0;
    3424         176 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3425             :         {
    3426          88 :             OGRLinearRing *poRing = GetRingRef(iRing);
    3427          88 :             if (poRing == nullptr)
    3428             :             {
    3429           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    3430             :                          "TABRegion: Object Geometry contains NULL rings!");
    3431           0 :                 return -1;
    3432             :             }
    3433             : 
    3434          88 :             int numPoints = poRing->getNumPoints();
    3435             : 
    3436        2515 :             for (int i = 0; nStatus == 0 && i < numPoints; i++)
    3437             :             {
    3438        2427 :                 poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
    3439             :                                         nY);
    3440        2427 :                 if ((nStatus =
    3441        2427 :                          poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
    3442             :                 {
    3443             :                     // Failed ... error message has already been produced
    3444           0 :                     return nStatus;
    3445             :                 }
    3446             :             }
    3447             :         } /* for iRing*/
    3448             : 
    3449          88 :         GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    3450             : 
    3451             :         /*-------------------------------------------------------------
    3452             :          * ... and finally copy info to poObjHdr
    3453             :          *------------------------------------------------------------*/
    3454          88 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    3455             : 
    3456          88 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    3457          88 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    3458          88 :         poPLineHdr->m_numLineSections = numRingsTotal;
    3459             : 
    3460          88 :         poPLineHdr->m_bSmooth = m_bSmooth;
    3461             : 
    3462             :         // MBR
    3463          88 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    3464             : 
    3465             :         // Region center/label point
    3466          88 :         double dX = 0.0;
    3467          88 :         double dY = 0.0;
    3468          88 :         if (GetCenter(dX, dY) != -1)
    3469             :         {
    3470          88 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    3471          88 :                                     poPLineHdr->m_nLabelY);
    3472             :         }
    3473             :         else
    3474             :         {
    3475           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    3476           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    3477             :         }
    3478             : 
    3479             :         // Compressed coordinate origin (useful only in compressed case!)
    3480          88 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    3481          88 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    3482             : 
    3483          88 :         if (!bCoordBlockDataOnly)
    3484             :         {
    3485          88 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    3486          88 :             poPLineHdr->m_nPenId =
    3487          88 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    3488             : 
    3489          88 :             m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    3490          88 :             poPLineHdr->m_nBrushId =
    3491          88 :                 static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    3492             :         }
    3493             :     }
    3494             :     else
    3495             :     {
    3496           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3497             :                  "TABRegion: Object contains an invalid Geometry!");
    3498           0 :         return -1;
    3499             :     }
    3500             : 
    3501          88 :     if (CPLGetLastErrorType() == CE_Failure)
    3502           0 :         return -1;
    3503             : 
    3504             :     /* Return a ref to coord block so that caller can continue writing
    3505             :      * after the end of this object (used by index splitting)
    3506             :      */
    3507          88 :     if (ppoCoordBlock)
    3508           0 :         *ppoCoordBlock = poCoordBlock;
    3509             : 
    3510          88 :     return 0;
    3511             : }
    3512             : 
    3513             : /**********************************************************************
    3514             :  *                   TABRegion::GetNumRings()
    3515             :  *
    3516             :  * Return the total number of rings in this object making it look like
    3517             :  * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
    3518             :  * of rings... hides the complexity of handling OGRMultiPolygons vs
    3519             :  * OGRPolygons, etc.
    3520             :  *
    3521             :  * Returns 0 if the geometry contained in the object is invalid or missing.
    3522             :  **********************************************************************/
    3523         109 : int TABRegion::GetNumRings()
    3524             : {
    3525         109 :     return ComputeNumRings(nullptr, nullptr);
    3526             : }
    3527             : 
    3528         197 : int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
    3529             :                                TABMAPFile *poMapFile)
    3530             : {
    3531         197 :     int numRingsTotal = 0;
    3532         197 :     int iLastSect = 0;
    3533             : 
    3534         197 :     if (ppasSecHdrs)
    3535          88 :         *ppasSecHdrs = nullptr;
    3536             : 
    3537         197 :     OGRGeometry *poGeom = GetGeometryRef();
    3538             : 
    3539         209 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3540          12 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3541             :     {
    3542             :         /*-------------------------------------------------------------
    3543             :          * Calculate total number of rings...
    3544             :          *------------------------------------------------------------*/
    3545         197 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3546             :         {
    3547          24 :             for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
    3548             :             {
    3549          12 :                 numRingsTotal += poPolygon->getNumInteriorRings() + 1;
    3550             : 
    3551          12 :                 if (ppasSecHdrs && poMapFile)
    3552             :                 {
    3553           6 :                     if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
    3554           6 :                                       iLastSect) != 0)
    3555           0 :                         return 0;  // An error happened, return count=0
    3556             :                 }
    3557             :             }  // for
    3558             :         }
    3559             :         else
    3560             :         {
    3561         185 :             OGRPolygon *poPolygon = poGeom->toPolygon();
    3562         185 :             numRingsTotal = poPolygon->getNumInteriorRings() + 1;
    3563             : 
    3564         185 :             if (ppasSecHdrs && poMapFile)
    3565             :             {
    3566          82 :                 if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
    3567          82 :                                   iLastSect) != 0)
    3568           0 :                     return 0;  // An error happened, return count=0
    3569             :             }
    3570             :         }
    3571             :     }
    3572             : 
    3573             :     /*-----------------------------------------------------------------
    3574             :      * If we're generating section header blocks, then init the
    3575             :      * coordinate offset values.
    3576             :      *
    3577             :      * In calculation of nDataOffset, we have to take into account that
    3578             :      * V450 header section uses int32 instead of int16 for numVertices
    3579             :      * and we add another 2 bytes to align with a 4 bytes boundary.
    3580             :      *------------------------------------------------------------*/
    3581         197 :     const int nTotalHdrSizeUncompressed =
    3582         394 :         (m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3583         197 :          m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3584         197 :          m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3585         197 :          m_nMapInfoType == TAB_GEOM_V800_REGION_C)
    3586         394 :             ? 28 * numRingsTotal
    3587             :             : 24 * numRingsTotal;
    3588             : 
    3589         197 :     if (ppasSecHdrs)
    3590             :     {
    3591          88 :         int numPointsTotal = 0;
    3592          88 :         CPLAssert(iLastSect == numRingsTotal);
    3593         176 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3594             :         {
    3595          88 :             (*ppasSecHdrs)[iRing].nDataOffset =
    3596          88 :                 nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
    3597          88 :             (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
    3598             : 
    3599          88 :             numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
    3600             :         }
    3601             :     }
    3602             : 
    3603         197 :     return numRingsTotal;
    3604             : }
    3605             : 
    3606             : /**********************************************************************
    3607             :  *                   TABRegion::AppendSecHdrs()
    3608             :  *
    3609             :  * (Private method)
    3610             :  *
    3611             :  * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
    3612             :  **********************************************************************/
    3613          88 : int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
    3614             :                              TABMAPCoordSecHdr *&pasSecHdrs,
    3615             :                              TABMAPFile *poMapFile, int &iLastRing)
    3616             : {
    3617             :     /*-------------------------------------------------------------
    3618             :      * Add a pasSecHdrs[] entry for each ring in this polygon.
    3619             :      * Note that the structs won't be fully initialized.
    3620             :      *------------------------------------------------------------*/
    3621          88 :     int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
    3622             : 
    3623          88 :     pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    3624          88 :         CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
    3625             :                                    sizeof(TABMAPCoordSecHdr)));
    3626             : 
    3627         176 :     for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
    3628             :     {
    3629          88 :         OGRLinearRing *poRing = nullptr;
    3630          88 :         OGREnvelope sEnvelope;
    3631             : 
    3632          88 :         if (iRing == 0)
    3633          88 :             poRing = poPolygon->getExteriorRing();
    3634             :         else
    3635           0 :             poRing = poPolygon->getInteriorRing(iRing - 1);
    3636             : 
    3637          88 :         if (poRing == nullptr)
    3638             :         {
    3639           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    3640             :                      "Assertion Failed: Encountered NULL ring in OGRPolygon");
    3641           0 :             return -1;
    3642             :         }
    3643             : 
    3644          88 :         poRing->getEnvelope(&sEnvelope);
    3645             : 
    3646          88 :         pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
    3647             : 
    3648          88 :         if (iRing == 0)
    3649          88 :             pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
    3650             :         else
    3651           0 :             pasSecHdrs[iLastRing].numHoles = 0;
    3652             : 
    3653          88 :         poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
    3654          88 :                                 pasSecHdrs[iLastRing].nXMin,
    3655          88 :                                 pasSecHdrs[iLastRing].nYMin);
    3656          88 :         poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
    3657          88 :                                 pasSecHdrs[iLastRing].nXMax,
    3658          88 :                                 pasSecHdrs[iLastRing].nYMax);
    3659             : 
    3660          88 :         iLastRing++;
    3661             :     } /* for iRing*/
    3662             : 
    3663          88 :     return 0;
    3664             : }
    3665             : 
    3666             : /**********************************************************************
    3667             :  *                   TABRegion::GetRingRef()
    3668             :  *
    3669             :  * Returns a reference to the specified ring number making it look like
    3670             :  * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
    3671             :  * of rings... hides the complexity of handling OGRMultiPolygons vs
    3672             :  * OGRPolygons, etc.
    3673             :  *
    3674             :  * Returns NULL if the geometry contained in the object is invalid or
    3675             :  * missing or if the specified ring index is invalid.
    3676             :  **********************************************************************/
    3677         197 : OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
    3678             : {
    3679         197 :     OGRLinearRing *poRing = nullptr;
    3680             : 
    3681         197 :     OGRGeometry *poGeom = GetGeometryRef();
    3682             : 
    3683         209 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3684          12 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3685             :     {
    3686             :         /*-------------------------------------------------------------
    3687             :          * Establish number of polygons based on geometry type
    3688             :          *------------------------------------------------------------*/
    3689         197 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3690         197 :         int iCurRing = 0;
    3691         197 :         int numOGRPolygons = 0;
    3692             : 
    3693         197 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3694             :         {
    3695          12 :             poMultiPolygon = poGeom->toMultiPolygon();
    3696          12 :             numOGRPolygons = poMultiPolygon->getNumGeometries();
    3697             :         }
    3698             :         else
    3699             :         {
    3700         185 :             numOGRPolygons = 1;
    3701             :         }
    3702             : 
    3703             :         /*-------------------------------------------------------------
    3704             :          * Loop through polygons until we find the requested ring.
    3705             :          *------------------------------------------------------------*/
    3706         197 :         iCurRing = 0;
    3707         394 :         for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
    3708             :              iPoly++)
    3709             :         {
    3710         197 :             OGRPolygon *poPolygon = nullptr;
    3711         197 :             if (poMultiPolygon)
    3712          12 :                 poPolygon = poMultiPolygon->getGeometryRef(iPoly);
    3713             :             else
    3714         185 :                 poPolygon = poGeom->toPolygon();
    3715             : 
    3716         197 :             int numIntRings = poPolygon->getNumInteriorRings();
    3717             : 
    3718         197 :             if (iCurRing == nRequestedRingIndex)
    3719             :             {
    3720         197 :                 poRing = poPolygon->getExteriorRing();
    3721             :             }
    3722           0 :             else if (nRequestedRingIndex > iCurRing &&
    3723           0 :                      nRequestedRingIndex - (iCurRing + 1) < numIntRings)
    3724             :             {
    3725           0 :                 poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
    3726           0 :                                                     (iCurRing + 1));
    3727             :             }
    3728         197 :             iCurRing += numIntRings + 1;
    3729             :         }
    3730             :     }
    3731             : 
    3732         197 :     return poRing;
    3733             : }
    3734             : 
    3735             : /**********************************************************************
    3736             :  *                   TABRegion::RingIsHole()
    3737             :  *
    3738             :  * Return false if the requested ring index is the first of a polygon
    3739             :  **********************************************************************/
    3740           0 : GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
    3741             : {
    3742           0 :     OGRGeometry *poGeom = GetGeometryRef();
    3743             : 
    3744           0 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3745           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3746             :     {
    3747             :         /*-------------------------------------------------------------
    3748             :          * Establish number of polygons based on geometry type
    3749             :          *------------------------------------------------------------*/
    3750           0 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3751           0 :         int iCurRing = 0;
    3752           0 :         int numOGRPolygons = 0;
    3753             : 
    3754           0 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3755             :         {
    3756           0 :             poMultiPolygon = poGeom->toMultiPolygon();
    3757           0 :             numOGRPolygons = poMultiPolygon->getNumGeometries();
    3758             :         }
    3759             :         else
    3760             :         {
    3761           0 :             numOGRPolygons = 1;
    3762             :         }
    3763             : 
    3764             :         /*-------------------------------------------------------------
    3765             :          * Loop through polygons until we find the requested ring.
    3766             :          *------------------------------------------------------------*/
    3767           0 :         iCurRing = 0;
    3768           0 :         for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
    3769             :         {
    3770           0 :             OGRPolygon *poPolygon = nullptr;
    3771           0 :             if (poMultiPolygon)
    3772           0 :                 poPolygon = poMultiPolygon->getGeometryRef(iPoly);
    3773             :             else
    3774           0 :                 poPolygon = poGeom->toPolygon();
    3775             : 
    3776           0 :             int numIntRings = poPolygon->getNumInteriorRings();
    3777             : 
    3778           0 :             if (iCurRing == nRequestedRingIndex)
    3779             :             {
    3780           0 :                 return FALSE;
    3781             :             }
    3782           0 :             else if (nRequestedRingIndex > iCurRing &&
    3783           0 :                      nRequestedRingIndex - (iCurRing + 1) < numIntRings)
    3784             :             {
    3785           0 :                 return TRUE;
    3786             :             }
    3787           0 :             iCurRing += numIntRings + 1;
    3788             :         }
    3789             :     }
    3790             : 
    3791           0 :     return FALSE;
    3792             : }
    3793             : 
    3794             : /**********************************************************************
    3795             :  *                   TABRegion::GetStyleString() const
    3796             :  *
    3797             :  * Return style string for this feature.
    3798             :  *
    3799             :  * Style String is built only once during the first call to GetStyleString().
    3800             :  **********************************************************************/
    3801          67 : const char *TABRegion::GetStyleString() const
    3802             : {
    3803          67 :     if (m_pszStyleString == nullptr)
    3804             :     {
    3805             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    3806             :         // to use temporary buffers
    3807          39 :         char *pszPen = CPLStrdup(GetPenStyleString());
    3808          39 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    3809             : 
    3810          39 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    3811             : 
    3812          39 :         CPLFree(pszPen);
    3813          39 :         CPLFree(pszBrush);
    3814             :     }
    3815             : 
    3816          67 :     return m_pszStyleString;
    3817             : }
    3818             : 
    3819             : /**********************************************************************
    3820             :  *                   TABRegion::DumpMIF()
    3821             :  *
    3822             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    3823             :  **********************************************************************/
    3824           0 : void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
    3825             : {
    3826           0 :     if (fpOut == nullptr)
    3827           0 :         fpOut = stdout;
    3828             : 
    3829             :     /*-----------------------------------------------------------------
    3830             :      * Fetch and validate geometry
    3831             :      *----------------------------------------------------------------*/
    3832           0 :     OGRGeometry *poGeom = GetGeometryRef();
    3833           0 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3834           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3835             :     {
    3836             :         /*-------------------------------------------------------------
    3837             :          * Generate output for region
    3838             :          *
    3839             :          * Note that we want to handle both OGRPolygons and OGRMultiPolygons
    3840             :          * that's why we use the GetNumRings()/GetRingRef() interface.
    3841             :          *------------------------------------------------------------*/
    3842           0 :         int numRingsTotal = GetNumRings();
    3843             : 
    3844           0 :         fprintf(fpOut, "REGION %d\n", numRingsTotal);
    3845             : 
    3846           0 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3847             :         {
    3848           0 :             OGRLinearRing *poRing = GetRingRef(iRing);
    3849             : 
    3850           0 :             if (poRing == nullptr)
    3851             :             {
    3852           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    3853             :                          "TABRegion: Object Geometry contains NULL rings!");
    3854           0 :                 return;
    3855             :             }
    3856             : 
    3857           0 :             const int numPoints = poRing->getNumPoints();
    3858           0 :             fprintf(fpOut, " %d\n", numPoints);
    3859           0 :             for (int i = 0; i < numPoints; i++)
    3860           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    3861             :                         poRing->getY(i));
    3862             :         }
    3863             :     }
    3864             :     else
    3865             :     {
    3866           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3867             :                  "TABRegion: Missing or Invalid Geometry!");
    3868           0 :         return;
    3869             :     }
    3870             : 
    3871           0 :     if (m_bCenterIsSet)
    3872           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    3873             : 
    3874             :     // Finish with PEN/BRUSH/etc. clauses
    3875           0 :     DumpPenDef();
    3876           0 :     DumpBrushDef();
    3877             : 
    3878           0 :     fflush(fpOut);
    3879             : }
    3880             : 
    3881             : /**********************************************************************
    3882             :  *                   TABRegion::GetCenter()
    3883             :  *
    3884             :  * Returns the center/label point of the region.
    3885             :  * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
    3886             :  * before.
    3887             :  *
    3888             :  * Returns 0 on success, -1 on error.
    3889             :  **********************************************************************/
    3890          88 : int TABRegion::GetCenter(double &dX, double &dY)
    3891             : {
    3892          88 :     if (!m_bCenterIsSet)
    3893             :     {
    3894             :         /*-------------------------------------------------------------
    3895             :          * Calculate label point.  If we have a multipolygon then we use
    3896             :          * the first OGRPolygon in the feature to calculate the point.
    3897             :          *------------------------------------------------------------*/
    3898          88 :         OGRGeometry *poGeom = GetGeometryRef();
    3899          88 :         if (poGeom == nullptr)
    3900           0 :             return -1;
    3901             : 
    3902          88 :         OGRPolygon *poPolygon = nullptr;
    3903             : 
    3904          88 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3905             :         {
    3906           6 :             OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
    3907           6 :             if (poMultiPolygon->getNumGeometries() > 0)
    3908           6 :                 poPolygon = poMultiPolygon->getGeometryRef(0);
    3909             :         }
    3910          82 :         else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    3911             :         {
    3912          82 :             poPolygon = poGeom->toPolygon();
    3913             :         }
    3914             : 
    3915          88 :         OGRPoint oLabelPoint;
    3916         176 :         if (poPolygon != nullptr &&
    3917          88 :             OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
    3918             :         {
    3919          88 :             m_dCenterX = oLabelPoint.getX();
    3920          88 :             m_dCenterY = oLabelPoint.getY();
    3921             :         }
    3922             :         else
    3923             :         {
    3924           0 :             OGREnvelope oEnv;
    3925           0 :             poGeom->getEnvelope(&oEnv);
    3926           0 :             m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
    3927           0 :             m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
    3928             :         }
    3929             : 
    3930          88 :         m_bCenterIsSet = TRUE;
    3931             :     }
    3932             : 
    3933          88 :     if (!m_bCenterIsSet)
    3934           0 :         return -1;
    3935             : 
    3936          88 :     dX = m_dCenterX;
    3937          88 :     dY = m_dCenterY;
    3938          88 :     return 0;
    3939             : }
    3940             : 
    3941             : /**********************************************************************
    3942             :  *                   TABRegion::SetCenter()
    3943             :  *
    3944             :  * Set the X,Y coordinates to use as center/label point for the region.
    3945             :  **********************************************************************/
    3946         516 : void TABRegion::SetCenter(double dX, double dY)
    3947             : {
    3948         516 :     m_dCenterX = dX;
    3949         516 :     m_dCenterY = dY;
    3950         516 :     m_bCenterIsSet = TRUE;
    3951         516 : }
    3952             : 
    3953             : /*=====================================================================
    3954             :  *                      class TABRectangle
    3955             :  *====================================================================*/
    3956             : 
    3957             : /**********************************************************************
    3958             :  *                   TABRectangle::TABRectangle()
    3959             :  *
    3960             :  * Constructor.
    3961             :  **********************************************************************/
    3962         949 : TABRectangle::TABRectangle(OGRFeatureDefn *poDefnIn)
    3963             :     : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
    3964         949 :       m_dRoundYRadius(0.0)
    3965             : {
    3966         949 : }
    3967             : 
    3968             : /**********************************************************************
    3969             :  *                   TABRectangle::~TABRectangle()
    3970             :  *
    3971             :  * Destructor.
    3972             :  **********************************************************************/
    3973        1898 : TABRectangle::~TABRectangle()
    3974             : {
    3975        1898 : }
    3976             : 
    3977             : /**********************************************************************
    3978             :  *                     TABRectangle::CloneTABFeature()
    3979             :  *
    3980             :  * Duplicate feature, including stuff specific to each TABFeature type.
    3981             :  *
    3982             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    3983             :  * then copies any members specific to its own type.
    3984             :  **********************************************************************/
    3985           0 : TABFeature *TABRectangle::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    3986             : {
    3987             :     /*-----------------------------------------------------------------
    3988             :      * Alloc new feature and copy the base stuff
    3989             :      *----------------------------------------------------------------*/
    3990             :     TABRectangle *poNew =
    3991           0 :         new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
    3992             : 
    3993           0 :     CopyTABFeatureBase(poNew);
    3994             : 
    3995             :     /*-----------------------------------------------------------------
    3996             :      * And members specific to this class
    3997             :      *----------------------------------------------------------------*/
    3998             :     // ITABFeaturePen
    3999           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4000             : 
    4001             :     // ITABFeatureBrush
    4002           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    4003             : 
    4004           0 :     poNew->m_bRoundCorners = m_bRoundCorners;
    4005           0 :     poNew->m_dRoundXRadius = m_dRoundXRadius;
    4006           0 :     poNew->m_dRoundYRadius = m_dRoundYRadius;
    4007             : 
    4008           0 :     return poNew;
    4009             : }
    4010             : 
    4011             : /**********************************************************************
    4012             :  *                   TABRectangle::ValidateMapInfoType()
    4013             :  *
    4014             :  * Check the feature's geometry part and return the corresponding
    4015             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4016             :  * be updated for further calls to GetMapInfoType();
    4017             :  *
    4018             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4019             :  * is expected for this object class.
    4020             :  **********************************************************************/
    4021           0 : TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4022             : {
    4023             :     /*-----------------------------------------------------------------
    4024             :      * Fetch and validate geometry
    4025             :      *----------------------------------------------------------------*/
    4026           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4027           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4028             :     {
    4029           0 :         if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    4030           0 :             m_nMapInfoType = TAB_GEOM_ROUNDRECT;
    4031             :         else
    4032           0 :             m_nMapInfoType = TAB_GEOM_RECT;
    4033             :     }
    4034             :     else
    4035             :     {
    4036           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4037             :                  "TABRectangle: Missing or Invalid Geometry!");
    4038           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4039             :     }
    4040             : 
    4041             :     /*-----------------------------------------------------------------
    4042             :      * Decide if coordinates should be compressed or not.
    4043             :      *----------------------------------------------------------------*/
    4044             :     // __TODO__ For now we always write uncompressed for this class...
    4045             :     // ValidateCoordType(poMapFile);
    4046           0 :     UpdateMBR(poMapFile);
    4047             : 
    4048           0 :     return m_nMapInfoType;
    4049             : }
    4050             : 
    4051             : /**********************************************************************
    4052             :  *                   TABRectangle::UpdateMBR()
    4053             :  *
    4054             :  * Update the feature MBR members using the geometry
    4055             :  *
    4056             :  * Returns 0 on success, or -1 if there is no geometry in object
    4057             :  **********************************************************************/
    4058           0 : int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4059             : {
    4060           0 :     OGREnvelope sEnvelope;
    4061             : 
    4062             :     /*-----------------------------------------------------------------
    4063             :      * Fetch and validate geometry
    4064             :      *----------------------------------------------------------------*/
    4065           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4066           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4067           0 :         poGeom->getEnvelope(&sEnvelope);
    4068             :     else
    4069             :     {
    4070           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4071             :                  "TABRectangle: Missing or Invalid Geometry!");
    4072           0 :         return -1;
    4073             :     }
    4074             : 
    4075             :     /*-----------------------------------------------------------------
    4076             :      * Note that we will simply use the rectangle's MBR and don't really
    4077             :      * read the polygon geometry... this should be OK unless the
    4078             :      * polygon geometry was not really a rectangle.
    4079             :      *----------------------------------------------------------------*/
    4080           0 :     m_dXMin = sEnvelope.MinX;
    4081           0 :     m_dYMin = sEnvelope.MinY;
    4082           0 :     m_dXMax = sEnvelope.MaxX;
    4083           0 :     m_dYMax = sEnvelope.MaxY;
    4084             : 
    4085           0 :     if (poMapFile)
    4086             :     {
    4087           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4088           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4089             :     }
    4090             : 
    4091           0 :     return 0;
    4092             : }
    4093             : 
    4094             : /**********************************************************************
    4095             :  *                   TABRectangle::ReadGeometryFromMAPFile()
    4096             :  *
    4097             :  * Fill the geometry and representation (color, etc...) part of the
    4098             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4099             :  *
    4100             :  * It is assumed that poMAPFile currently points to the beginning of
    4101             :  * a map object.
    4102             :  *
    4103             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4104             :  * been called.
    4105             :  **********************************************************************/
    4106           8 : int TABRectangle::ReadGeometryFromMAPFile(
    4107             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4108             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4109             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4110             : {
    4111             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4112           8 :     if (bCoordBlockDataOnly)
    4113           0 :         return 0;
    4114             : 
    4115             :     /*-----------------------------------------------------------------
    4116             :      * Fetch and validate geometry type
    4117             :      *----------------------------------------------------------------*/
    4118           8 :     m_nMapInfoType = poObjHdr->m_nType;
    4119             : 
    4120           8 :     if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
    4121           4 :         m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
    4122           0 :         m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
    4123             :     {
    4124           0 :         CPLError(
    4125             :             CE_Failure, CPLE_AssertionFailed,
    4126             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    4127           0 :             m_nMapInfoType, m_nMapInfoType);
    4128           0 :         return -1;
    4129             :     }
    4130             : 
    4131             :     /*-----------------------------------------------------------------
    4132             :      * Read object information
    4133             :      *----------------------------------------------------------------*/
    4134             :     TABMAPObjRectEllipse *poRectHdr =
    4135           8 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4136             : 
    4137             :     // Read the corners radius
    4138             : 
    4139           8 :     if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
    4140           4 :         m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
    4141             :     {
    4142             :         // Read the corner's diameters
    4143           4 :         poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
    4144           4 :                                     poRectHdr->m_nCornerHeight, m_dRoundXRadius,
    4145           4 :                                     m_dRoundYRadius);
    4146             : 
    4147             :         // Divide by 2 since we store the corner's radius
    4148           4 :         m_dRoundXRadius /= 2.0;
    4149           4 :         m_dRoundYRadius /= 2.0;
    4150             : 
    4151           4 :         m_bRoundCorners = TRUE;
    4152             :     }
    4153             :     else
    4154             :     {
    4155           4 :         m_bRoundCorners = FALSE;
    4156           4 :         m_dRoundXRadius = 0.0;
    4157           4 :         m_dRoundYRadius = 0.0;
    4158             :     }
    4159             : 
    4160             :     // A rectangle is defined by its MBR
    4161             : 
    4162           8 :     double dXMin = 0.0;
    4163           8 :     double dYMin = 0.0;
    4164           8 :     double dXMax = 0.0;
    4165           8 :     double dYMax = 0.0;
    4166           8 :     poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
    4167             :                             dYMin);
    4168           8 :     poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
    4169             :                             dYMax);
    4170             : 
    4171           8 :     m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
    4172           8 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    4173             : 
    4174           8 :     m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
    4175           8 :     poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    4176             : 
    4177             :     /*-----------------------------------------------------------------
    4178             :      * Call SetMBR() and GetMBR() now to make sure that min values are
    4179             :      * really smaller than max values.
    4180             :      *----------------------------------------------------------------*/
    4181           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    4182           8 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4183             : 
    4184             :     /* Copy int MBR to feature class members */
    4185           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    4186             :               poObjHdr->m_nMaxY);
    4187             : 
    4188             :     /*-----------------------------------------------------------------
    4189             :      * Create and fill geometry object
    4190             :      *----------------------------------------------------------------*/
    4191           8 :     OGRPolygon *poPolygon = new OGRPolygon;
    4192           8 :     OGRLinearRing *poRing = new OGRLinearRing();
    4193           8 :     if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    4194             :     {
    4195             :         /*-------------------------------------------------------------
    4196             :          * For rounded rectangles, we generate arcs with 45 line
    4197             :          * segments for each corner.  We start with lower-left corner
    4198             :          * and proceed counterclockwise
    4199             :          * We also have to make sure that rounding radius is not too
    4200             :          * large for the MBR in the generated polygon... however, we
    4201             :          * always return the true X/Y radius (not adjusted) since this
    4202             :          * is the way MapInfo seems to do it when a radius bigger than
    4203             :          * the MBR is passed from TBA to MIF.
    4204             :          *------------------------------------------------------------*/
    4205             :         const double dXRadius =
    4206           4 :             std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
    4207             :         const double dYRadius =
    4208           4 :             std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
    4209           4 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
    4210             :                        dYRadius, M_PI, 3.0 * M_PI / 2.0);
    4211           4 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
    4212             :                        dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
    4213           4 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
    4214             :                        dYRadius, 0.0, M_PI / 2.0);
    4215           4 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
    4216             :                        dYRadius, M_PI / 2.0, M_PI);
    4217             : 
    4218           4 :         TABCloseRing(poRing);
    4219             :     }
    4220             :     else
    4221             :     {
    4222           4 :         poRing->addPoint(dXMin, dYMin);
    4223           4 :         poRing->addPoint(dXMax, dYMin);
    4224           4 :         poRing->addPoint(dXMax, dYMax);
    4225           4 :         poRing->addPoint(dXMin, dYMax);
    4226           4 :         poRing->addPoint(dXMin, dYMin);
    4227             :     }
    4228             : 
    4229           8 :     poPolygon->addRingDirectly(poRing);
    4230           8 :     SetGeometryDirectly(poPolygon);
    4231             : 
    4232           8 :     return 0;
    4233             : }
    4234             : 
    4235             : /**********************************************************************
    4236             :  *                   TABRectangle::WriteGeometryToMAPFile()
    4237             :  *
    4238             :  * Write the geometry and representation (color, etc...) part of the
    4239             :  * feature to the .MAP object pointed to by poMAPFile.
    4240             :  *
    4241             :  * It is assumed that poMAPFile currently points to a valid map object.
    4242             :  *
    4243             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4244             :  * been called.
    4245             :  **********************************************************************/
    4246           0 : int TABRectangle::WriteGeometryToMAPFile(
    4247             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4248             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4249             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4250             : {
    4251             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4252           0 :     if (bCoordBlockDataOnly)
    4253           0 :         return 0;
    4254             : 
    4255             :     /*-----------------------------------------------------------------
    4256             :      * We assume that ValidateMapInfoType() was called already and that
    4257             :      * the type in poObjHdr->m_nType is valid.
    4258             :      *----------------------------------------------------------------*/
    4259           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    4260             : 
    4261             :     /*-----------------------------------------------------------------
    4262             :      * Fetch and validate geometry and update MBR
    4263             :      * Note that we will simply use the geometry's MBR and don't really
    4264             :      * read the polygon geometry... this should be OK unless the
    4265             :      * polygon geometry was not really a rectangle.
    4266             :      *----------------------------------------------------------------*/
    4267           0 :     if (UpdateMBR(poMapFile) != 0)
    4268           0 :         return -1; /* Error already reported */
    4269             : 
    4270             :     /*-----------------------------------------------------------------
    4271             :      * Copy object information
    4272             :      *----------------------------------------------------------------*/
    4273             :     TABMAPObjRectEllipse *poRectHdr =
    4274           0 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4275             : 
    4276           0 :     if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
    4277           0 :         m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
    4278             :     {
    4279           0 :         poMapFile->Coordsys2IntDist(
    4280           0 :             m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
    4281           0 :             poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
    4282             :     }
    4283             :     else
    4284             :     {
    4285           0 :         poRectHdr->m_nCornerWidth = 0;
    4286           0 :         poRectHdr->m_nCornerHeight = 0;
    4287             :     }
    4288             : 
    4289             :     // A rectangle is defined by its MBR (values were set in UpdateMBR())
    4290           0 :     poRectHdr->m_nMinX = m_nXMin;
    4291           0 :     poRectHdr->m_nMinY = m_nYMin;
    4292           0 :     poRectHdr->m_nMaxX = m_nXMax;
    4293           0 :     poRectHdr->m_nMaxY = m_nYMax;
    4294             : 
    4295           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    4296           0 :     poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    4297             : 
    4298           0 :     m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    4299           0 :     poRectHdr->m_nBrushId =
    4300           0 :         static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    4301             : 
    4302           0 :     if (CPLGetLastErrorType() == CE_Failure)
    4303           0 :         return -1;
    4304             : 
    4305           0 :     return 0;
    4306             : }
    4307             : 
    4308             : /**********************************************************************
    4309             :  *                   TABRectangle::GetStyleString() const
    4310             :  *
    4311             :  * Return style string for this feature.
    4312             :  *
    4313             :  * Style String is built only once during the first call to GetStyleString().
    4314             :  **********************************************************************/
    4315          18 : const char *TABRectangle::GetStyleString() const
    4316             : {
    4317          18 :     if (m_pszStyleString == nullptr)
    4318             :     {
    4319             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    4320             :         // to use temporary buffers
    4321          10 :         char *pszPen = CPLStrdup(GetPenStyleString());
    4322          10 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    4323             : 
    4324          10 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    4325             : 
    4326          10 :         CPLFree(pszPen);
    4327          10 :         CPLFree(pszBrush);
    4328             :     }
    4329             : 
    4330          18 :     return m_pszStyleString;
    4331             : }
    4332             : 
    4333             : /**********************************************************************
    4334             :  *                   TABRectangle::DumpMIF()
    4335             :  *
    4336             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    4337             :  **********************************************************************/
    4338           0 : void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
    4339             : {
    4340           0 :     if (fpOut == nullptr)
    4341           0 :         fpOut = stdout;
    4342             : 
    4343             :     /*-----------------------------------------------------------------
    4344             :      * Output RECT or ROUNDRECT parameters
    4345             :      *----------------------------------------------------------------*/
    4346           0 :     double dXMin = 0.0;
    4347           0 :     double dYMin = 0.0;
    4348           0 :     double dXMax = 0.0;
    4349           0 :     double dYMax = 0.0;
    4350           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4351             : 
    4352           0 :     if (m_bRoundCorners)
    4353           0 :         fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g    %.15g %.15g)\n",
    4354             :                 dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
    4355             :     else
    4356           0 :         fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
    4357             :                 dYMax);
    4358             : 
    4359             :     /*-----------------------------------------------------------------
    4360             :      * Fetch and validate geometry
    4361             :      *----------------------------------------------------------------*/
    4362           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4363           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4364             :     {
    4365             :         /*-------------------------------------------------------------
    4366             :          * Generate rectangle output as a region
    4367             :          * We could also output as a RECT or ROUNDRECT in a real MIF generator
    4368             :          *------------------------------------------------------------*/
    4369           0 :         OGRPolygon *poPolygon = poGeom->toPolygon();
    4370           0 :         int numIntRings = poPolygon->getNumInteriorRings();
    4371           0 :         fprintf(fpOut, "REGION %d\n", numIntRings + 1);
    4372             :         // In this loop, iRing=-1 for the outer ring.
    4373           0 :         for (int iRing = -1; iRing < numIntRings; iRing++)
    4374             :         {
    4375           0 :             OGRLinearRing *poRing = nullptr;
    4376             : 
    4377           0 :             if (iRing == -1)
    4378           0 :                 poRing = poPolygon->getExteriorRing();
    4379             :             else
    4380           0 :                 poRing = poPolygon->getInteriorRing(iRing);
    4381             : 
    4382           0 :             if (poRing == nullptr)
    4383             :             {
    4384           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    4385             :                          "TABRectangle: Object Geometry contains NULL rings!");
    4386           0 :                 return;
    4387             :             }
    4388             : 
    4389           0 :             const int numPoints = poRing->getNumPoints();
    4390           0 :             fprintf(fpOut, " %d\n", numPoints);
    4391           0 :             for (int i = 0; i < numPoints; i++)
    4392           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    4393             :                         poRing->getY(i));
    4394             :         }
    4395             :     }
    4396             :     else
    4397             :     {
    4398           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4399             :                  "TABRectangle: Missing or Invalid Geometry!");
    4400           0 :         return;
    4401             :     }
    4402             : 
    4403             :     // Finish with PEN/BRUSH/etc. clauses
    4404           0 :     DumpPenDef();
    4405           0 :     DumpBrushDef();
    4406             : 
    4407           0 :     fflush(fpOut);
    4408             : }
    4409             : 
    4410             : /*=====================================================================
    4411             :  *                      class TABEllipse
    4412             :  *====================================================================*/
    4413             : 
    4414             : /**********************************************************************
    4415             :  *                   TABEllipse::TABEllipse()
    4416             :  *
    4417             :  * Constructor.
    4418             :  **********************************************************************/
    4419         401 : TABEllipse::TABEllipse(OGRFeatureDefn *poDefnIn)
    4420             :     : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
    4421         401 :       m_dYRadius(0.0)
    4422             : {
    4423         401 : }
    4424             : 
    4425             : /**********************************************************************
    4426             :  *                   TABEllipse::~TABEllipse()
    4427             :  *
    4428             :  * Destructor.
    4429             :  **********************************************************************/
    4430         802 : TABEllipse::~TABEllipse()
    4431             : {
    4432         802 : }
    4433             : 
    4434             : /**********************************************************************
    4435             :  *                     TABEllipse::CloneTABFeature()
    4436             :  *
    4437             :  * Duplicate feature, including stuff specific to each TABFeature type.
    4438             :  *
    4439             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    4440             :  * then copies any members specific to its own type.
    4441             :  **********************************************************************/
    4442           0 : TABFeature *TABEllipse::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    4443             : {
    4444             :     /*-----------------------------------------------------------------
    4445             :      * Alloc new feature and copy the base stuff
    4446             :      *----------------------------------------------------------------*/
    4447           0 :     TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
    4448             : 
    4449           0 :     CopyTABFeatureBase(poNew);
    4450             : 
    4451             :     /*-----------------------------------------------------------------
    4452             :      * And members specific to this class
    4453             :      *----------------------------------------------------------------*/
    4454             :     // ITABFeaturePen
    4455           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4456             : 
    4457             :     // ITABFeatureBrush
    4458           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    4459             : 
    4460           0 :     poNew->m_dCenterX = m_dCenterX;
    4461           0 :     poNew->m_dCenterY = m_dCenterY;
    4462           0 :     poNew->m_dXRadius = m_dXRadius;
    4463           0 :     poNew->m_dYRadius = m_dYRadius;
    4464             : 
    4465           0 :     return poNew;
    4466             : }
    4467             : 
    4468             : /**********************************************************************
    4469             :  *                   TABEllipse::ValidateMapInfoType()
    4470             :  *
    4471             :  * Check the feature's geometry part and return the corresponding
    4472             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4473             :  * be updated for further calls to GetMapInfoType();
    4474             :  *
    4475             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4476             :  * is expected for this object class.
    4477             :  **********************************************************************/
    4478           0 : TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4479             : {
    4480             :     /*-----------------------------------------------------------------
    4481             :      * Fetch and validate geometry
    4482             :      *----------------------------------------------------------------*/
    4483           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4484           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    4485           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4486             :     {
    4487           0 :         m_nMapInfoType = TAB_GEOM_ELLIPSE;
    4488             :     }
    4489             :     else
    4490             :     {
    4491           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4492             :                  "TABEllipse: Missing or Invalid Geometry!");
    4493           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4494             :     }
    4495             : 
    4496             :     /*-----------------------------------------------------------------
    4497             :      * Decide if coordinates should be compressed or not.
    4498             :      *----------------------------------------------------------------*/
    4499             :     // __TODO__ For now we always write uncompressed for this class...
    4500             :     // ValidateCoordType(poMapFile);
    4501           0 :     UpdateMBR(poMapFile);
    4502             : 
    4503           0 :     return m_nMapInfoType;
    4504             : }
    4505             : 
    4506             : /**********************************************************************
    4507             :  *                   TABEllipse::UpdateMBR()
    4508             :  *
    4509             :  * Update the feature MBR members using the geometry
    4510             :  *
    4511             :  * Returns 0 on success, or -1 if there is no geometry in object
    4512             :  **********************************************************************/
    4513           0 : int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4514             : {
    4515           0 :     OGREnvelope sEnvelope;
    4516             : 
    4517             :     /*-----------------------------------------------------------------
    4518             :      * Fetch and validate geometry... Polygon and point are accepted.
    4519             :      * Note that we will simply use the ellipse's MBR and don't really
    4520             :      * read the polygon geometry... this should be OK unless the
    4521             :      * polygon geometry was not really an ellipse.
    4522             :      *----------------------------------------------------------------*/
    4523           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4524           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    4525           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4526           0 :         poGeom->getEnvelope(&sEnvelope);
    4527             :     else
    4528             :     {
    4529           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4530             :                  "TABEllipse: Missing or Invalid Geometry!");
    4531           0 :         return -1;
    4532             :     }
    4533             : 
    4534             :     /*-----------------------------------------------------------------
    4535             :      * We use the center of the MBR as the ellipse center, and the
    4536             :      * X/Y radius to define the MBR size.  If X/Y radius are null then
    4537             :      * we'll try to use the MBR to recompute them.
    4538             :      *----------------------------------------------------------------*/
    4539           0 :     const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
    4540           0 :     const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
    4541           0 :     if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
    4542             :     {
    4543           0 :         m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
    4544           0 :         m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
    4545             :     }
    4546             : 
    4547           0 :     m_dXMin = dXCenter - m_dXRadius;
    4548           0 :     m_dYMin = dYCenter - m_dYRadius;
    4549           0 :     m_dXMax = dXCenter + m_dXRadius;
    4550           0 :     m_dYMax = dYCenter + m_dYRadius;
    4551             : 
    4552           0 :     if (poMapFile)
    4553             :     {
    4554           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4555           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4556             :     }
    4557             : 
    4558           0 :     return 0;
    4559             : }
    4560             : 
    4561             : /**********************************************************************
    4562             :  *                   TABEllipse::ReadGeometryFromMAPFile()
    4563             :  *
    4564             :  * Fill the geometry and representation (color, etc...) part of the
    4565             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4566             :  *
    4567             :  * It is assumed that poMAPFile currently points to the beginning of
    4568             :  * a map object.
    4569             :  *
    4570             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4571             :  * been called.
    4572             :  **********************************************************************/
    4573           4 : int TABEllipse::ReadGeometryFromMAPFile(
    4574             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4575             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4576             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4577             : {
    4578             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4579           4 :     if (bCoordBlockDataOnly)
    4580           0 :         return 0;
    4581             : 
    4582             :     /*-----------------------------------------------------------------
    4583             :      * Fetch and validate geometry type
    4584             :      *----------------------------------------------------------------*/
    4585           4 :     m_nMapInfoType = poObjHdr->m_nType;
    4586             : 
    4587           4 :     if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
    4588           0 :         m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
    4589             :     {
    4590           0 :         CPLError(
    4591             :             CE_Failure, CPLE_AssertionFailed,
    4592             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    4593           0 :             m_nMapInfoType, m_nMapInfoType);
    4594           0 :         return -1;
    4595             :     }
    4596             : 
    4597             :     /*-----------------------------------------------------------------
    4598             :      * Read object information
    4599             :      *----------------------------------------------------------------*/
    4600             :     TABMAPObjRectEllipse *poRectHdr =
    4601           4 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4602             : 
    4603             :     // An ellipse is defined by its MBR
    4604             : 
    4605           4 :     double dXMin = 0.0;
    4606           4 :     double dYMin = 0.0;
    4607           4 :     double dXMax = 0.0;
    4608           4 :     double dYMax = 0.0;
    4609           4 :     poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
    4610             :                             dYMin);
    4611           4 :     poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
    4612             :                             dYMax);
    4613             : 
    4614           4 :     m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
    4615           4 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    4616             : 
    4617           4 :     m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
    4618           4 :     poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    4619             : 
    4620             :     /*-----------------------------------------------------------------
    4621             :      * Save info about the ellipse def. inside class members
    4622             :      *----------------------------------------------------------------*/
    4623           4 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    4624           4 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    4625           4 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    4626           4 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    4627             : 
    4628           4 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    4629             : 
    4630           4 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    4631             :               poObjHdr->m_nMaxY);
    4632             : 
    4633             :     /*-----------------------------------------------------------------
    4634             :      * Create and fill geometry object
    4635             :      *----------------------------------------------------------------*/
    4636           4 :     OGRPolygon *poPolygon = new OGRPolygon;
    4637           4 :     OGRLinearRing *poRing = new OGRLinearRing();
    4638             : 
    4639             :     /*-----------------------------------------------------------------
    4640             :      * For the OGR geometry, we generate an ellipse with 2 degrees line
    4641             :      * segments.
    4642             :      *----------------------------------------------------------------*/
    4643           4 :     TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
    4644             :                    0.0, 2.0 * M_PI);
    4645           4 :     TABCloseRing(poRing);
    4646             : 
    4647           4 :     poPolygon->addRingDirectly(poRing);
    4648           4 :     SetGeometryDirectly(poPolygon);
    4649             : 
    4650           4 :     return 0;
    4651             : }
    4652             : 
    4653             : /**********************************************************************
    4654             :  *                   TABEllipse::WriteGeometryToMAPFile()
    4655             :  *
    4656             :  * Write the geometry and representation (color, etc...) part of the
    4657             :  * feature to the .MAP object pointed to by poMAPFile.
    4658             :  *
    4659             :  * It is assumed that poMAPFile currently points to a valid map object.
    4660             :  *
    4661             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4662             :  * been called.
    4663             :  **********************************************************************/
    4664           0 : int TABEllipse::WriteGeometryToMAPFile(
    4665             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4666             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4667             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4668             : {
    4669             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4670           0 :     if (bCoordBlockDataOnly)
    4671           0 :         return 0;
    4672             : 
    4673             :     /*-----------------------------------------------------------------
    4674             :      * We assume that ValidateMapInfoType() was called already and that
    4675             :      * the type in poObjHdr->m_nType is valid.
    4676             :      *----------------------------------------------------------------*/
    4677           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    4678             : 
    4679             :     /*-----------------------------------------------------------------
    4680             :      * Fetch and validate geometry... Polygon and point are accepted.
    4681             :      * Note that we will simply use the ellipse's MBR and don't really
    4682             :      * read the polygon geometry... this should be OK unless the
    4683             :      * polygon geometry was not really an ellipse.
    4684             :      *
    4685             :      * We use the center of the MBR as the ellipse center, and the
    4686             :      * X/Y radius to define the MBR size.  If X/Y radius are null then
    4687             :      * we'll try to use the MBR to recompute them.
    4688             :      *----------------------------------------------------------------*/
    4689           0 :     if (UpdateMBR(poMapFile) != 0)
    4690           0 :         return -1; /* Error already reported */
    4691             : 
    4692             :     /*-----------------------------------------------------------------
    4693             :      * Copy object information
    4694             :      *----------------------------------------------------------------*/
    4695             :     TABMAPObjRectEllipse *poRectHdr =
    4696           0 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4697             : 
    4698             :     // Reset RoundRect Corner members... just in case (unused for ellipse)
    4699           0 :     poRectHdr->m_nCornerWidth = 0;
    4700           0 :     poRectHdr->m_nCornerHeight = 0;
    4701             : 
    4702             :     // An ellipse is defined by its MBR (values were set in UpdateMBR())
    4703           0 :     poRectHdr->m_nMinX = m_nXMin;
    4704           0 :     poRectHdr->m_nMinY = m_nYMin;
    4705           0 :     poRectHdr->m_nMaxX = m_nXMax;
    4706           0 :     poRectHdr->m_nMaxY = m_nYMax;
    4707             : 
    4708           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    4709           0 :     poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    4710             : 
    4711           0 :     m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    4712           0 :     poRectHdr->m_nBrushId =
    4713           0 :         static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    4714             : 
    4715           0 :     if (CPLGetLastErrorType() == CE_Failure)
    4716           0 :         return -1;
    4717             : 
    4718           0 :     return 0;
    4719             : }
    4720             : 
    4721             : /**********************************************************************
    4722             :  *                   TABEllipse::GetStyleString() const
    4723             :  *
    4724             :  * Return style string for this feature.
    4725             :  *
    4726             :  * Style String is built only once during the first call to GetStyleString().
    4727             :  **********************************************************************/
    4728           9 : const char *TABEllipse::GetStyleString() const
    4729             : {
    4730           9 :     if (m_pszStyleString == nullptr)
    4731             :     {
    4732             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    4733             :         // to use temporary buffers
    4734           5 :         char *pszPen = CPLStrdup(GetPenStyleString());
    4735           5 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    4736             : 
    4737           5 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    4738             : 
    4739           5 :         CPLFree(pszPen);
    4740           5 :         CPLFree(pszBrush);
    4741             :     }
    4742             : 
    4743           9 :     return m_pszStyleString;
    4744             : }
    4745             : 
    4746             : /**********************************************************************
    4747             :  *                   TABEllipse::DumpMIF()
    4748             :  *
    4749             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    4750             :  **********************************************************************/
    4751           0 : void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
    4752             : {
    4753           0 :     if (fpOut == nullptr)
    4754           0 :         fpOut = stdout;
    4755             : 
    4756             :     /*-----------------------------------------------------------------
    4757             :      * Output ELLIPSE parameters
    4758             :      *----------------------------------------------------------------*/
    4759           0 :     double dXMin = 0.0;
    4760           0 :     double dYMin = 0.0;
    4761           0 :     double dXMax = 0.0;
    4762           0 :     double dYMax = 0.0;
    4763           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4764           0 :     fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
    4765             :             dYMax);
    4766             : 
    4767             :     /*-----------------------------------------------------------------
    4768             :      * Fetch and validate geometry
    4769             :      *----------------------------------------------------------------*/
    4770           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4771           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4772             :     {
    4773             :         /*-------------------------------------------------------------
    4774             :          * Generate ellipse output as a region
    4775             :          * We could also output as an ELLIPSE in a real MIF generator
    4776             :          *------------------------------------------------------------*/
    4777           0 :         OGRPolygon *poPolygon = poGeom->toPolygon();
    4778           0 :         int numIntRings = poPolygon->getNumInteriorRings();
    4779           0 :         fprintf(fpOut, "REGION %d\n", numIntRings + 1);
    4780             :         // In this loop, iRing=-1 for the outer ring.
    4781           0 :         for (int iRing = -1; iRing < numIntRings; iRing++)
    4782             :         {
    4783           0 :             OGRLinearRing *poRing = nullptr;
    4784             : 
    4785           0 :             if (iRing == -1)
    4786           0 :                 poRing = poPolygon->getExteriorRing();
    4787             :             else
    4788           0 :                 poRing = poPolygon->getInteriorRing(iRing);
    4789             : 
    4790           0 :             if (poRing == nullptr)
    4791             :             {
    4792           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    4793             :                          "TABEllipse: Object Geometry contains NULL rings!");
    4794           0 :                 return;
    4795             :             }
    4796             : 
    4797           0 :             int numPoints = poRing->getNumPoints();
    4798           0 :             fprintf(fpOut, " %d\n", numPoints);
    4799           0 :             for (int i = 0; i < numPoints; i++)
    4800           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    4801             :                         poRing->getY(i));
    4802             :         }
    4803             :     }
    4804             :     else
    4805             :     {
    4806           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4807             :                  "TABEllipse: Missing or Invalid Geometry!");
    4808           0 :         return;
    4809             :     }
    4810             : 
    4811             :     // Finish with PEN/BRUSH/etc. clauses
    4812           0 :     DumpPenDef();
    4813           0 :     DumpBrushDef();
    4814             : 
    4815           0 :     fflush(fpOut);
    4816             : }
    4817             : 
    4818             : /*=====================================================================
    4819             :  *                      class TABArc
    4820             :  *====================================================================*/
    4821             : 
    4822             : /**********************************************************************
    4823             :  *                   TABArc::TABArc()
    4824             :  *
    4825             :  * Constructor.
    4826             :  **********************************************************************/
    4827         680 : TABArc::TABArc(OGRFeatureDefn *poDefnIn)
    4828             :     : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
    4829         680 :       m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
    4830             : {
    4831         680 : }
    4832             : 
    4833             : /**********************************************************************
    4834             :  *                   TABArc::~TABArc()
    4835             :  *
    4836             :  * Destructor.
    4837             :  **********************************************************************/
    4838        1360 : TABArc::~TABArc()
    4839             : {
    4840        1360 : }
    4841             : 
    4842             : /**********************************************************************
    4843             :  *                     TABArc::CloneTABFeature()
    4844             :  *
    4845             :  * Duplicate feature, including stuff specific to each TABFeature type.
    4846             :  *
    4847             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    4848             :  * then copies any members specific to its own type.
    4849             :  **********************************************************************/
    4850           0 : TABFeature *TABArc::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    4851             : {
    4852             :     /*-----------------------------------------------------------------
    4853             :      * Alloc new feature and copy the base stuff
    4854             :      *----------------------------------------------------------------*/
    4855           0 :     TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
    4856             : 
    4857           0 :     CopyTABFeatureBase(poNew);
    4858             : 
    4859             :     /*-----------------------------------------------------------------
    4860             :      * And members specific to this class
    4861             :      *----------------------------------------------------------------*/
    4862             :     // ITABFeaturePen
    4863           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4864             : 
    4865           0 :     poNew->SetStartAngle(GetStartAngle());
    4866           0 :     poNew->SetEndAngle(GetEndAngle());
    4867             : 
    4868           0 :     poNew->m_dCenterX = m_dCenterX;
    4869           0 :     poNew->m_dCenterY = m_dCenterY;
    4870           0 :     poNew->m_dXRadius = m_dXRadius;
    4871           0 :     poNew->m_dYRadius = m_dYRadius;
    4872             : 
    4873           0 :     return poNew;
    4874             : }
    4875             : 
    4876             : /**********************************************************************
    4877             :  *                   TABArc::ValidateMapInfoType()
    4878             :  *
    4879             :  * Check the feature's geometry part and return the corresponding
    4880             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4881             :  * be updated for further calls to GetMapInfoType();
    4882             :  *
    4883             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4884             :  * is expected for this object class.
    4885             :  **********************************************************************/
    4886           0 : TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4887             : {
    4888             :     /*-----------------------------------------------------------------
    4889             :      * Fetch and validate geometry
    4890             :      *----------------------------------------------------------------*/
    4891           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4892           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
    4893           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4894             :     {
    4895           0 :         m_nMapInfoType = TAB_GEOM_ARC;
    4896             :     }
    4897             :     else
    4898             :     {
    4899           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4900             :                  "TABArc: Missing or Invalid Geometry!");
    4901           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4902             :     }
    4903             : 
    4904             :     /*-----------------------------------------------------------------
    4905             :      * Decide if coordinates should be compressed or not.
    4906             :      *----------------------------------------------------------------*/
    4907             :     // __TODO__ For now we always write uncompressed for this class...
    4908             :     // ValidateCoordType(poMapFile);
    4909           0 :     UpdateMBR(poMapFile);
    4910             : 
    4911           0 :     return m_nMapInfoType;
    4912             : }
    4913             : 
    4914             : /**********************************************************************
    4915             :  *                   TABArc::UpdateMBR()
    4916             :  *
    4917             :  * Update the feature MBR members using the geometry
    4918             :  *
    4919             :  * Returns 0 on success, or -1 if there is no geometry in object
    4920             :  **********************************************************************/
    4921           0 : int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4922             : {
    4923           0 :     OGREnvelope sEnvelope;
    4924             : 
    4925           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4926           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
    4927             :     {
    4928             :         /*-------------------------------------------------------------
    4929             :          * POLYGON geometry:
    4930             :          * Note that we will simply use the ellipse's MBR and don't really
    4931             :          * read the polygon geometry... this should be OK unless the
    4932             :          * polygon geometry was not really an ellipse.
    4933             :          * In the case of a polygon geometry. the m_dCenterX/Y values MUST
    4934             :          * have been set by the caller.
    4935             :          *------------------------------------------------------------*/
    4936           0 :         poGeom->getEnvelope(&sEnvelope);
    4937             :     }
    4938           0 :     else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4939             :     {
    4940             :         /*-------------------------------------------------------------
    4941             :          * In the case of a POINT GEOMETRY, we will make sure the
    4942             :          * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
    4943             :          *
    4944             :          * In this case we have to reconstruct the arc inside a temporary
    4945             :          * geometry object in order to find its real MBR.
    4946             :          *------------------------------------------------------------*/
    4947           0 :         OGRPoint *poPoint = poGeom->toPoint();
    4948           0 :         m_dCenterX = poPoint->getX();
    4949           0 :         m_dCenterY = poPoint->getY();
    4950             : 
    4951           0 :         OGRLineString oTmpLine;
    4952           0 :         int numPts = 0;
    4953           0 :         if (m_dEndAngle < m_dStartAngle)
    4954           0 :             numPts = static_cast<int>(
    4955           0 :                 std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
    4956             :         else
    4957           0 :             numPts = static_cast<int>(
    4958           0 :                 std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
    4959           0 :         numPts = std::max(2, numPts);
    4960             : 
    4961           0 :         TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    4962           0 :                        m_dYRadius, m_dStartAngle * M_PI / 180.0,
    4963           0 :                        m_dEndAngle * M_PI / 180.0);
    4964             : 
    4965           0 :         oTmpLine.getEnvelope(&sEnvelope);
    4966             :     }
    4967             :     else
    4968             :     {
    4969           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4970             :                  "TABArc: Missing or Invalid Geometry!");
    4971           0 :         return -1;
    4972             :     }
    4973             : 
    4974             :     // Update the Arc's MBR
    4975           0 :     m_dXMin = sEnvelope.MinX;
    4976           0 :     m_dYMin = sEnvelope.MinY;
    4977           0 :     m_dXMax = sEnvelope.MaxX;
    4978           0 :     m_dYMax = sEnvelope.MaxY;
    4979             : 
    4980           0 :     if (poMapFile)
    4981             :     {
    4982           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4983           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4984             :     }
    4985             : 
    4986           0 :     return 0;
    4987             : }
    4988             : 
    4989             : /**********************************************************************
    4990             :  *                   TABArc::ReadGeometryFromMAPFile()
    4991             :  *
    4992             :  * Fill the geometry and representation (color, etc...) part of the
    4993             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4994             :  *
    4995             :  * It is assumed that poMAPFile currently points to the beginning of
    4996             :  * a map object.
    4997             :  *
    4998             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4999             :  * been called.
    5000             :  **********************************************************************/
    5001           8 : int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
    5002             :                                     TABMAPObjHdr *poObjHdr,
    5003             :                                     GBool bCoordBlockDataOnly /*=FALSE*/,
    5004             :                                     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    5005             : {
    5006             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    5007           8 :     if (bCoordBlockDataOnly)
    5008           0 :         return 0;
    5009             : 
    5010             :     /*-----------------------------------------------------------------
    5011             :      * Fetch and validate geometry type
    5012             :      *----------------------------------------------------------------*/
    5013           8 :     m_nMapInfoType = poObjHdr->m_nType;
    5014             : 
    5015           8 :     if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
    5016             :     {
    5017           0 :         CPLError(
    5018             :             CE_Failure, CPLE_AssertionFailed,
    5019             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    5020           0 :             m_nMapInfoType, m_nMapInfoType);
    5021           0 :         return -1;
    5022             :     }
    5023             : 
    5024             :     /*-----------------------------------------------------------------
    5025             :      * Read object information
    5026             :      *----------------------------------------------------------------*/
    5027           8 :     TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
    5028             : 
    5029             :     /*-------------------------------------------------------------
    5030             :      * Start/End angles
    5031             :      * Since the angles are specified for integer coordinates, and
    5032             :      * that these coordinates can have the X axis reversed, we have to
    5033             :      * adjust the angle values for the change in the X axis
    5034             :      * direction.
    5035             :      *
    5036             :      * This should be necessary only when X axis is flipped.
    5037             :      * __TODO__ Why is order of start/end values reversed as well???
    5038             :      *------------------------------------------------------------*/
    5039             : 
    5040             :     /*-------------------------------------------------------------
    5041             :      * OK, Arc angles again!!!!!!!!!!!!
    5042             :      * After some tests in 1999-11, it appeared that the angle values
    5043             :      * ALWAYS had to be flipped (read order= end angle followed by
    5044             :      * start angle), no matter which quadrant the file is in.
    5045             :      * This does not make any sense, so I suspect that there is something
    5046             :      * that we are missing here!
    5047             :      *
    5048             :      * 2000-01-14.... Again!!!  Based on some sample data files:
    5049             :      *  File         Ver Quadr  ReflXAxis  Read_Order   Adjust_Angle
    5050             :      * test_symb.tab 300    2        1      end,start    X=yes Y=no
    5051             :      * alltypes.tab: 300    1        0      start,end    X=no  Y=no
    5052             :      * arcs.tab:     300    2        0      end,start    X=yes Y=no
    5053             :      *
    5054             :      * Until we prove it wrong, the rule would be:
    5055             :      *  -> Quadrant 1 and 3, angles order = start, end
    5056             :      *  -> Quadrant 2 and 4, angles order = end, start
    5057             :      * + Always adjust angles for x and y axis based on quadrant.
    5058             :      *
    5059             :      * This was confirmed using some more files in which the quadrant was
    5060             :      * manually changed, but whether these are valid results is
    5061             :      * disputable.
    5062             :      *
    5063             :      * The ReflectXAxis flag seems to have no effect here...
    5064             :      *------------------------------------------------------------*/
    5065             : 
    5066             :     /*-------------------------------------------------------------
    5067             :      * In version 100 .tab files (version 400 .map), it is possible
    5068             :      * to have a quadrant value of 0 and it should be treated the
    5069             :      * same way as quadrant 3
    5070             :      *------------------------------------------------------------*/
    5071           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
    5072           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5073           0 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5074             :     {
    5075             :         // Quadrants 1 and 3 ... read order = start, end
    5076           8 :         m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
    5077           8 :         m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
    5078             :     }
    5079             :     else
    5080             :     {
    5081             :         // Quadrants 2 and 4 ... read order = end, start
    5082           0 :         m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
    5083           0 :         m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
    5084             :     }
    5085             : 
    5086           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
    5087          16 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5088           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5089             :     {
    5090             :         // X axis direction is flipped... adjust angle
    5091           0 :         m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
    5092           0 :                                                  : (540.0 - m_dStartAngle);
    5093           0 :         m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
    5094           0 :                                              : (540.0 - m_dEndAngle);
    5095             :     }
    5096             : 
    5097           8 :     if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
    5098             :     {
    5099           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5100             :                  "Wrong start and end angles: %f %f", m_dStartAngle,
    5101             :                  m_dEndAngle);
    5102           0 :         return -1;
    5103             :     }
    5104             : 
    5105           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5106          16 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
    5107           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5108             :     {
    5109             :         // Y axis direction is flipped... this reverses angle direction
    5110             :         // Unfortunately we never found any file that contains this case,
    5111             :         // but this should be the behavior to expect!!!
    5112             :         //
    5113             :         // 2000-01-14: some files in which quadrant was set to 3 and 4
    5114             :         // manually seemed to confirm that this is the right thing to do.
    5115           0 :         m_dStartAngle = 360.0 - m_dStartAngle;
    5116           0 :         m_dEndAngle = 360.0 - m_dEndAngle;
    5117             :     }
    5118             : 
    5119             :     // An arc is defined by its defining ellipse's MBR:
    5120             : 
    5121           8 :     double dXMin = 0.0;
    5122           8 :     double dYMin = 0.0;
    5123           8 :     double dXMax = 0.0;
    5124           8 :     double dYMax = 0.0;
    5125             : 
    5126           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
    5127             :                             poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
    5128           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
    5129             :                             poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
    5130             : 
    5131           8 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    5132           8 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    5133           8 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    5134           8 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    5135             : 
    5136             :     // Read the Arc's MBR and use that as this feature's MBR
    5137           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
    5138           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
    5139           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    5140             : 
    5141           8 :     m_nPenDefIndex = poArcHdr->m_nPenId;  // Pen index
    5142           8 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    5143             : 
    5144             :     /*-----------------------------------------------------------------
    5145             :      * Create and fill geometry object
    5146             :      * For the OGR geometry, we generate an arc with 2 degrees line
    5147             :      * segments.
    5148             :      *----------------------------------------------------------------*/
    5149           8 :     OGRLineString *poLine = new OGRLineString;
    5150             : 
    5151             :     const int numPts = std::max(
    5152          16 :         2,
    5153           8 :         (m_dEndAngle < m_dStartAngle
    5154           8 :              ? static_cast<int>(
    5155           0 :                    std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
    5156           8 :              : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
    5157           8 :                                 1)));
    5158             : 
    5159           8 :     TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    5160           8 :                    m_dYRadius, m_dStartAngle * M_PI / 180.0,
    5161           8 :                    m_dEndAngle * M_PI / 180.0);
    5162             : 
    5163           8 :     SetGeometryDirectly(poLine);
    5164             : 
    5165           8 :     return 0;
    5166             : }
    5167             : 
    5168             : /**********************************************************************
    5169             :  *                   TABArc::WriteGeometryToMAPFile()
    5170             :  *
    5171             :  * Write the geometry and representation (color, etc...) part of the
    5172             :  * feature to the .MAP object pointed to by poMAPFile.
    5173             :  *
    5174             :  * It is assumed that poMAPFile currently points to a valid map object.
    5175             :  *
    5176             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5177             :  * been called.
    5178             :  **********************************************************************/
    5179           0 : int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    5180             :                                    TABMAPObjHdr *poObjHdr,
    5181             :                                    GBool bCoordBlockDataOnly /*=FALSE*/,
    5182             :                                    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    5183             : {
    5184             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    5185           0 :     if (bCoordBlockDataOnly)
    5186           0 :         return 0;
    5187             : 
    5188             :     /*-----------------------------------------------------------------
    5189             :      * We assume that ValidateMapInfoType() was called already and that
    5190             :      * the type in poObjHdr->m_nType is valid.
    5191             :      *----------------------------------------------------------------*/
    5192           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    5193             : 
    5194             :     /*-----------------------------------------------------------------
    5195             :      * Fetch and validate geometry
    5196             :      * In the case of ARCs, this is all done inside UpdateMBR()
    5197             :      *----------------------------------------------------------------*/
    5198           0 :     if (UpdateMBR(poMapFile) != 0)
    5199           0 :         return -1; /* Error already reported */
    5200             : 
    5201             :     /*-----------------------------------------------------------------
    5202             :      * Copy object information
    5203             :      *----------------------------------------------------------------*/
    5204           0 :     TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
    5205             : 
    5206             :     /*-------------------------------------------------------------
    5207             :      * Start/End angles
    5208             :      * Since we ALWAYS produce files in quadrant 1 then we can
    5209             :      * ignore the special angle conversion required by flipped axis.
    5210             :      *
    5211             :      * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
    5212             :      *------------------------------------------------------------*/
    5213           0 :     CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
    5214             : 
    5215           0 :     poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
    5216           0 :     poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
    5217             : 
    5218             :     // An arc is defined by its defining ellipse's MBR:
    5219           0 :     poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
    5220           0 :                             poArcHdr->m_nArcEllipseMinX,
    5221           0 :                             poArcHdr->m_nArcEllipseMinY);
    5222           0 :     poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
    5223           0 :                             poArcHdr->m_nArcEllipseMaxX,
    5224           0 :                             poArcHdr->m_nArcEllipseMaxY);
    5225             : 
    5226             :     // Pass the Arc's actual MBR (values were set in UpdateMBR())
    5227           0 :     poArcHdr->m_nMinX = m_nXMin;
    5228           0 :     poArcHdr->m_nMinY = m_nYMin;
    5229           0 :     poArcHdr->m_nMaxX = m_nXMax;
    5230           0 :     poArcHdr->m_nMaxY = m_nYMax;
    5231             : 
    5232           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    5233           0 :     poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    5234             : 
    5235           0 :     if (CPLGetLastErrorType() == CE_Failure)
    5236           0 :         return -1;
    5237             : 
    5238           0 :     return 0;
    5239             : }
    5240             : 
    5241             : /**********************************************************************
    5242             :  *                   TABArc::SetStart/EndAngle()
    5243             :  *
    5244             :  * Set the start/end angle values in degrees, making sure the values are
    5245             :  * always in the range [0..360]
    5246             :  **********************************************************************/
    5247           0 : void TABArc::SetStartAngle(double dAngle)
    5248             : {
    5249           0 :     dAngle = fmod(dAngle, 360.0);
    5250           0 :     if (dAngle < 0.0)
    5251           0 :         dAngle += 360.0;
    5252             : 
    5253           0 :     m_dStartAngle = dAngle;
    5254           0 : }
    5255             : 
    5256           0 : void TABArc::SetEndAngle(double dAngle)
    5257             : {
    5258           0 :     dAngle = fmod(dAngle, 360.0);
    5259           0 :     if (dAngle < 0.0)
    5260           0 :         dAngle += 360.0;
    5261             : 
    5262           0 :     m_dEndAngle = dAngle;
    5263           0 : }
    5264             : 
    5265             : /**********************************************************************
    5266             :  *                   TABArc::GetStyleString() const
    5267             :  *
    5268             :  * Return style string for this feature.
    5269             :  *
    5270             :  * Style String is built only once during the first call to GetStyleString().
    5271             :  **********************************************************************/
    5272          14 : const char *TABArc::GetStyleString() const
    5273             : {
    5274          14 :     if (m_pszStyleString == nullptr)
    5275             :     {
    5276          10 :         m_pszStyleString = CPLStrdup(GetPenStyleString());
    5277             :     }
    5278             : 
    5279          14 :     return m_pszStyleString;
    5280             : }
    5281             : 
    5282             : /**********************************************************************
    5283             :  *                   TABArc::DumpMIF()
    5284             :  *
    5285             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    5286             :  **********************************************************************/
    5287           0 : void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
    5288             : {
    5289           0 :     if (fpOut == nullptr)
    5290           0 :         fpOut = stdout;
    5291             : 
    5292             :     /*-----------------------------------------------------------------
    5293             :      * Output ARC parameters
    5294             :      *----------------------------------------------------------------*/
    5295           0 :     fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g   %d %d)\n",
    5296           0 :             m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
    5297           0 :             m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
    5298           0 :             static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
    5299             : 
    5300             :     /*-----------------------------------------------------------------
    5301             :      * Fetch and validate geometry
    5302             :      *----------------------------------------------------------------*/
    5303           0 :     OGRGeometry *poGeom = GetGeometryRef();
    5304           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    5305             :     {
    5306             :         /*-------------------------------------------------------------
    5307             :          * Generate arc output as a simple polyline
    5308             :          * We could also output as an ELLIPSE in a real MIF generator
    5309             :          *------------------------------------------------------------*/
    5310           0 :         OGRLineString *poLine = poGeom->toLineString();
    5311           0 :         const int numPoints = poLine->getNumPoints();
    5312           0 :         fprintf(fpOut, "PLINE %d\n", numPoints);
    5313           0 :         for (int i = 0; i < numPoints; i++)
    5314           0 :             fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
    5315             :     }
    5316             :     else
    5317             :     {
    5318           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5319             :                  "TABArc: Missing or Invalid Geometry!");
    5320           0 :         return;
    5321             :     }
    5322             : 
    5323             :     // Finish with PEN/BRUSH/etc. clauses
    5324           0 :     DumpPenDef();
    5325             : 
    5326           0 :     fflush(fpOut);
    5327             : }
    5328             : 
    5329             : /*=====================================================================
    5330             :  *                      class TABText
    5331             :  *====================================================================*/
    5332             : 
    5333             : /**********************************************************************
    5334             :  *                   TABText::TABText()
    5335             :  *
    5336             :  * Constructor.
    5337             :  **********************************************************************/
    5338         309 : TABText::TABText(OGRFeatureDefn *poDefnIn)
    5339             :     : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
    5340             :       m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
    5341             :       m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
    5342             :       m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
    5343         309 :       m_nFontStyle(0)
    5344             : {
    5345         309 : }
    5346             : 
    5347             : /**********************************************************************
    5348             :  *                   TABText::~TABText()
    5349             :  *
    5350             :  * Destructor.
    5351             :  **********************************************************************/
    5352         618 : TABText::~TABText()
    5353             : {
    5354         309 :     CPLFree(m_pszString);
    5355         618 : }
    5356             : 
    5357             : /**********************************************************************
    5358             :  *                     TABText::CloneTABFeature()
    5359             :  *
    5360             :  * Duplicate feature, including stuff specific to each TABFeature type.
    5361             :  *
    5362             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    5363             :  * then copies any members specific to its own type.
    5364             :  **********************************************************************/
    5365           0 : TABFeature *TABText::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    5366             : {
    5367             :     /*-----------------------------------------------------------------
    5368             :      * Alloc new feature and copy the base stuff
    5369             :      *----------------------------------------------------------------*/
    5370           0 :     TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
    5371             : 
    5372           0 :     CopyTABFeatureBase(poNew);
    5373             : 
    5374             :     /*-----------------------------------------------------------------
    5375             :      * And members specific to this class
    5376             :      *----------------------------------------------------------------*/
    5377             :     // ITABFeaturePen
    5378           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    5379             : 
    5380             :     // ITABFeatureFont
    5381           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    5382             : 
    5383           0 :     poNew->SetTextString(GetTextString());
    5384           0 :     poNew->SetTextAngle(GetTextAngle());
    5385           0 :     poNew->SetTextBoxHeight(GetTextBoxHeight());
    5386           0 :     poNew->SetTextBoxWidth(GetTextBoxWidth());
    5387           0 :     poNew->SetFontStyleTABValue(GetFontStyleTABValue());
    5388           0 :     poNew->SetFontBGColor(GetFontBGColor());
    5389           0 :     poNew->SetFontFGColor(GetFontFGColor());
    5390           0 :     poNew->SetFontOColor(GetFontOColor());
    5391           0 :     poNew->SetFontSColor(GetFontSColor());
    5392             : 
    5393           0 :     poNew->SetTextJustification(GetTextJustification());
    5394           0 :     poNew->SetTextSpacing(GetTextSpacing());
    5395             :     // Note: Text arrow/line coordinates are not transported... but
    5396             :     //       we ignore them most of the time anyways.
    5397           0 :     poNew->SetTextLineType(TABTLNoLine);
    5398             : 
    5399           0 :     return poNew;
    5400             : }
    5401             : 
    5402             : /**********************************************************************
    5403             :  *                   TABText::ValidateMapInfoType()
    5404             :  *
    5405             :  * Check the feature's geometry part and return the corresponding
    5406             :  * mapinfo object type code.  The m_nMapInfoType member will also
    5407             :  * be updated for further calls to GetMapInfoType();
    5408             :  *
    5409             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    5410             :  * is expected for this object class.
    5411             :  **********************************************************************/
    5412           4 : TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    5413             : {
    5414             :     /*-----------------------------------------------------------------
    5415             :      * Fetch and validate geometry
    5416             :      *----------------------------------------------------------------*/
    5417           4 :     OGRGeometry *poGeom = GetGeometryRef();
    5418           4 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5419             :     {
    5420           4 :         m_nMapInfoType = TAB_GEOM_TEXT;
    5421             :     }
    5422             :     else
    5423             :     {
    5424           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5425             :                  "TABText: Missing or Invalid Geometry!");
    5426           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    5427             :     }
    5428             : 
    5429             :     /*-----------------------------------------------------------------
    5430             :      * Decide if coordinates should be compressed or not.
    5431             :      *----------------------------------------------------------------*/
    5432             :     // __TODO__ For now we always write uncompressed for this class...
    5433             :     // ValidateCoordType(poMapFile);
    5434           4 :     UpdateMBR(poMapFile);
    5435             : 
    5436           4 :     return m_nMapInfoType;
    5437             : }
    5438             : 
    5439             : /**********************************************************************
    5440             :  *                   TABText::ReadGeometryFromMAPFile()
    5441             :  *
    5442             :  * Fill the geometry and representation (color, etc...) part of the
    5443             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    5444             :  *
    5445             :  * It is assumed that poMAPFile currently points to the beginning of
    5446             :  * a map object.
    5447             :  *
    5448             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5449             :  * been called.
    5450             :  **********************************************************************/
    5451           8 : int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
    5452             :                                      TABMAPObjHdr *poObjHdr,
    5453             :                                      GBool bCoordBlockDataOnly /*=FALSE*/,
    5454             :                                      TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    5455             : {
    5456             :     /*-----------------------------------------------------------------
    5457             :      * Fetch and validate geometry type
    5458             :      *----------------------------------------------------------------*/
    5459           8 :     m_nMapInfoType = poObjHdr->m_nType;
    5460             : 
    5461           8 :     if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
    5462             :     {
    5463           0 :         CPLError(
    5464             :             CE_Failure, CPLE_AssertionFailed,
    5465             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    5466           0 :             m_nMapInfoType, m_nMapInfoType);
    5467           0 :         return -1;
    5468             :     }
    5469             : 
    5470             :     /*=============================================================
    5471             :      * TEXT
    5472             :      *============================================================*/
    5473             : 
    5474             :     /*-----------------------------------------------------------------
    5475             :      * Read object information
    5476             :      *----------------------------------------------------------------*/
    5477           8 :     TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
    5478             : 
    5479           8 :     const GInt32 nCoordBlockPtr =
    5480             :         poTextHdr->m_nCoordBlockPtr;                     // String position
    5481           8 :     const int nStringLen = poTextHdr->m_nCoordDataSize;  // String length
    5482           8 :     m_nTextAlignment = poTextHdr->m_nTextAlignment;      // just./spacing/arrow
    5483             : 
    5484             :     /*-------------------------------------------------------------
    5485             :      * Text Angle, in tenths of degree.
    5486             :      * Contrary to arc start/end angles, no conversion based on
    5487             :      * origin quadrant is required here.
    5488             :      *------------------------------------------------------------*/
    5489           8 :     m_dAngle = poTextHdr->m_nAngle / 10.0;
    5490             : 
    5491           8 :     m_nFontStyle = poTextHdr->m_nFontStyle;  // Font style
    5492             : 
    5493           8 :     m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
    5494           8 :                        poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
    5495           8 :     m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
    5496           8 :                        poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
    5497           8 :     m_rgbOutline = m_rgbBackground;
    5498             :     // In MapInfo, the shadow color is always gray (128,128,128)
    5499           8 :     m_rgbShadow = 0x808080;
    5500             : 
    5501             :     // arrow endpoint
    5502           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
    5503           8 :                             m_dfLineEndX, m_dfLineEndY);
    5504           8 :     m_bLineEndSet = TRUE;
    5505             : 
    5506             :     // Text Height
    5507           8 :     double dJunk = 0.0;
    5508           8 :     poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
    5509             : 
    5510           8 :     if (!bCoordBlockDataOnly)
    5511             :     {
    5512           8 :         m_nFontDefIndex = poTextHdr->m_nFontId;  // Font name index
    5513           8 :         poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    5514             :     }
    5515             : 
    5516             :     // MBR after rotation
    5517           8 :     double dXMin = 0.0;
    5518           8 :     double dYMin = 0.0;
    5519           8 :     double dXMax = 0.0;
    5520           8 :     double dYMax = 0.0;
    5521           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
    5522             :                             dYMin);
    5523           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
    5524             :                             dYMax);
    5525             : 
    5526           8 :     if (!bCoordBlockDataOnly)
    5527             :     {
    5528           8 :         m_nPenDefIndex = poTextHdr->m_nPenId;  // Pen index for line
    5529           8 :         poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    5530             :     }
    5531             : 
    5532             :     /*-------------------------------------------------------------
    5533             :      * Read text string from the coord. block
    5534             :      * Note that the string may contain binary '\n' and '\\' chars
    5535             :      * that we keep to an unescaped form internally. This is to
    5536             :      * be like OGR drivers. See bug 1107 for details.
    5537             :      *------------------------------------------------------------*/
    5538             :     char *pszTmpString =
    5539           8 :         static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
    5540             : 
    5541           8 :     if (nStringLen > 0)
    5542             :     {
    5543           8 :         TABMAPCoordBlock *poCoordBlock = nullptr;
    5544             : 
    5545           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    5546           0 :             poCoordBlock = *ppoCoordBlock;
    5547             :         else
    5548           8 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    5549          16 :         if (poCoordBlock == nullptr ||
    5550           8 :             poCoordBlock->ReadBytes(
    5551             :                 nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
    5552             :         {
    5553           0 :             CPLError(CE_Failure, CPLE_FileIO,
    5554             :                      "Failed reading text string at offset %d", nCoordBlockPtr);
    5555           0 :             CPLFree(pszTmpString);
    5556           0 :             return -1;
    5557             :         }
    5558             : 
    5559             :         /* Return a ref to coord block so that caller can continue reading
    5560             :          * after the end of this object (used by index splitting)
    5561             :          */
    5562           8 :         if (ppoCoordBlock)
    5563           0 :             *ppoCoordBlock = poCoordBlock;
    5564             :     }
    5565             : 
    5566           8 :     pszTmpString[nStringLen] = '\0';
    5567             : 
    5568           8 :     if (!poMapFile->GetEncoding().empty())
    5569             :     {
    5570             :         char *pszUtf8String =
    5571           1 :             CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
    5572           1 :         CPLFree(pszTmpString);
    5573           1 :         pszTmpString = pszUtf8String;
    5574             :     }
    5575             : 
    5576           8 :     CPLFree(m_pszString);
    5577           8 :     m_pszString = pszTmpString;  // This string was Escaped before 20050714
    5578             : 
    5579             :     /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
    5580             :      */
    5581           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    5582           8 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    5583             : 
    5584             :     /* Copy int MBR to feature class members */
    5585           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    5586             :               poObjHdr->m_nMaxY);
    5587             : 
    5588             :     /*-----------------------------------------------------------------
    5589             :      * Create an OGRPoint Geometry...
    5590             :      * The point X,Y values will be the coords of the lower-left corner before
    5591             :      * rotation is applied.  (Note that the rotation in MapInfo is done around
    5592             :      * the upper-left corner)
    5593             :      * We need to calculate the true lower left corner of the text based
    5594             :      * on the MBR after rotation, the text height and the rotation angle.
    5595             :      *----------------------------------------------------------------*/
    5596           8 :     double dSin = sin(m_dAngle * M_PI / 180.0);
    5597           8 :     double dCos = cos(m_dAngle * M_PI / 180.0);
    5598           8 :     double dX = 0.0;
    5599           8 :     double dY = 0.0;
    5600           8 :     if (dSin > 0.0 && dCos > 0.0)
    5601             :     {
    5602           7 :         dX = dXMin + m_dHeight * dSin;
    5603           7 :         dY = dYMin;
    5604             :     }
    5605           1 :     else if (dSin > 0.0 && dCos < 0.0)
    5606             :     {
    5607           0 :         dX = dXMax;
    5608           0 :         dY = dYMin - m_dHeight * dCos;
    5609             :     }
    5610           1 :     else if (dSin < 0.0 && dCos < 0.0)
    5611             :     {
    5612           0 :         dX = dXMax + m_dHeight * dSin;
    5613           0 :         dY = dYMax;
    5614             :     }
    5615             :     else  // dSin < 0 && dCos > 0
    5616             :     {
    5617           1 :         dX = dXMin;
    5618           1 :         dY = dYMax - m_dHeight * dCos;
    5619             :     }
    5620             : 
    5621           8 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    5622             : 
    5623           8 :     SetGeometryDirectly(poGeometry);
    5624             : 
    5625             :     /*-----------------------------------------------------------------
    5626             :      * Compute Text Width: the width of the Text MBR before rotation
    5627             :      * in ground units... unfortunately this value is not stored in the
    5628             :      * file, so we have to compute it with the MBR after rotation and
    5629             :      * the height of the MBR before rotation:
    5630             :      * With  W = Width of MBR before rotation
    5631             :      *       H = Height of MBR before rotation
    5632             :      *       dX = Width of MBR after rotation
    5633             :      *       dY = Height of MBR after rotation
    5634             :      *       teta = rotation angle
    5635             :      *
    5636             :      *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
    5637             :      *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
    5638             :      *
    5639             :      * and for other teta values, use:
    5640             :      *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
    5641             :      *----------------------------------------------------------------*/
    5642           8 :     dSin = std::abs(dSin);
    5643           8 :     dCos = std::abs(dCos);
    5644           8 :     if (m_dHeight == 0.0)
    5645           0 :         m_dWidth = 0.0;
    5646           8 :     else if (dCos > dSin)
    5647           8 :         m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
    5648           8 :                    (m_dHeight * dCos);
    5649             :     else
    5650           0 :         m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
    5651           0 :                    (m_dHeight * dSin);
    5652           8 :     m_dWidth = std::abs(m_dWidth);
    5653             : 
    5654           8 :     return 0;
    5655             : }
    5656             : 
    5657             : /**********************************************************************
    5658             :  *                   TABText::WriteGeometryToMAPFile()
    5659             :  *
    5660             :  * Write the geometry and representation (color, etc...) part of the
    5661             :  * feature to the .MAP object pointed to by poMAPFile.
    5662             :  *
    5663             :  * It is assumed that poMAPFile currently points to a valid map object.
    5664             :  *
    5665             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5666             :  * been called.
    5667             :  **********************************************************************/
    5668           4 : int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    5669             :                                     TABMAPObjHdr *poObjHdr,
    5670             :                                     GBool bCoordBlockDataOnly /*=FALSE*/,
    5671             :                                     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    5672             : {
    5673             :     GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
    5674             : 
    5675             :     /*-----------------------------------------------------------------
    5676             :      * We assume that ValidateMapInfoType() was called already and that
    5677             :      * the type in poObjHdr->m_nType is valid.
    5678             :      *----------------------------------------------------------------*/
    5679           4 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    5680             : 
    5681             :     /*-----------------------------------------------------------------
    5682             :      * Fetch and validate geometry
    5683             :      *----------------------------------------------------------------*/
    5684           4 :     OGRGeometry *poGeom = GetGeometryRef();
    5685           4 :     OGRPoint *poPoint = nullptr;
    5686           4 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5687           4 :         poPoint = poGeom->toPoint();
    5688             :     else
    5689             :     {
    5690           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5691             :                  "TABText: Missing or Invalid Geometry!");
    5692           0 :         return -1;
    5693             :     }
    5694             : 
    5695           4 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    5696             : 
    5697             :     /*-----------------------------------------------------------------
    5698             :      * Write string to a coord block first...
    5699             :      * Note that the string may contain unescaped '\n' and '\\'
    5700             :      * that we have to keep like that for the MAP file.
    5701             :      * See MapTools bug 1107 for more details.
    5702             :      *----------------------------------------------------------------*/
    5703           4 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    5704           4 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    5705           0 :         poCoordBlock = *ppoCoordBlock;
    5706             :     else
    5707           4 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    5708           4 :     poCoordBlock->StartNewFeature();
    5709           4 :     GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    5710             : 
    5711             :     // This string was escaped before 20050714
    5712           8 :     CPLString oTmpString(m_pszString ? m_pszString : "");
    5713           4 :     if (!poMapFile->GetEncoding().empty())
    5714             :     {
    5715           0 :         oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
    5716             :     }
    5717             : 
    5718           4 :     int nStringLen = static_cast<int>(oTmpString.length());
    5719             : 
    5720           4 :     if (nStringLen > 0)
    5721             :     {
    5722           3 :         poCoordBlock->WriteBytes(
    5723           3 :             nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
    5724             :     }
    5725             :     else
    5726             :     {
    5727           1 :         nCoordBlockPtr = 0;
    5728             :     }
    5729             : 
    5730             :     /*-----------------------------------------------------------------
    5731             :      * Copy object information
    5732             :      *----------------------------------------------------------------*/
    5733           4 :     TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
    5734             : 
    5735           4 :     poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr;    // String position
    5736           4 :     poTextHdr->m_nCoordDataSize = nStringLen;        // String length
    5737           4 :     poTextHdr->m_nTextAlignment = m_nTextAlignment;  // just./spacing/arrow
    5738             : 
    5739             :     /*-----------------------------------------------------------------
    5740             :      * Text Angle, (written in tenths of degrees)
    5741             :      * Contrary to arc start/end angles, no conversion based on
    5742             :      * origin quadrant is required here.
    5743             :      *----------------------------------------------------------------*/
    5744           4 :     poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
    5745             : 
    5746           4 :     poTextHdr->m_nFontStyle = m_nFontStyle;  // Font style/effect
    5747             : 
    5748           4 :     poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
    5749           4 :     poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
    5750           4 :     poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
    5751             : 
    5752           4 :     poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
    5753           4 :     poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
    5754           4 :     poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
    5755             : 
    5756             :     /*-----------------------------------------------------------------
    5757             :      * The OGRPoint's X,Y values were the coords of the lower-left corner
    5758             :      * before rotation was applied.  (Note that the rotation in MapInfo is
    5759             :      * done around the upper-left corner)
    5760             :      * The Feature's MBR is the MBR of the text after rotation... that's
    5761             :      * what MapInfo uses to define the text location.
    5762             :      *----------------------------------------------------------------*/
    5763           4 :     double dXMin = 0.0;
    5764           4 :     double dYMin = 0.0;
    5765           4 :     double dXMax = 0.0;
    5766           4 :     double dYMax = 0.0;
    5767             :     // Make sure Feature MBR is in sync with other params
    5768             : 
    5769           4 :     UpdateMBR();
    5770           4 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    5771             : 
    5772           4 :     poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
    5773           4 :     poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
    5774             : 
    5775             :     // Label line end point
    5776           4 :     double dX = 0.0;
    5777           4 :     double dY = 0.0;
    5778           4 :     GetTextLineEndPoint(dX, dY);  // Make sure a default line end point is set
    5779           4 :     poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
    5780           4 :                             poTextHdr->m_nLineEndY);
    5781             : 
    5782             :     // Text Height
    5783           4 :     poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
    5784           4 :     poTextHdr->m_nHeight = nY;
    5785             : 
    5786           4 :     if (!bCoordBlockDataOnly)
    5787             :     {
    5788             :         // Font name
    5789           4 :         m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    5790           4 :         poTextHdr->m_nFontId =
    5791           4 :             static_cast<GByte>(m_nFontDefIndex);  // Font name index
    5792             :     }
    5793             : 
    5794             :     // MBR after rotation
    5795           4 :     poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
    5796             : 
    5797           4 :     if (!bCoordBlockDataOnly)
    5798             :     {
    5799           4 :         m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    5800           4 :         poTextHdr->m_nPenId =
    5801           4 :             static_cast<GByte>(m_nPenDefIndex);  // Pen index for line/arrow
    5802             :     }
    5803             : 
    5804           4 :     if (CPLGetLastErrorType() == CE_Failure)
    5805           0 :         return -1;
    5806             : 
    5807             :     /* Return a ref to coord block so that caller can continue writing
    5808             :      * after the end of this object (used by index splitting)
    5809             :      */
    5810           4 :     if (ppoCoordBlock)
    5811           0 :         *ppoCoordBlock = poCoordBlock;
    5812             : 
    5813           4 :     return 0;
    5814             : }
    5815             : 
    5816             : /**********************************************************************
    5817             :  *                   TABText::GetTextString()
    5818             :  *
    5819             :  * Return ref to text string value.
    5820             :  *
    5821             :  * Returned string is a reference to the internal string buffer and should
    5822             :  * not be modified or freed by the caller.
    5823             :  **********************************************************************/
    5824         300 : const char *TABText::GetTextString() const
    5825             : {
    5826         300 :     if (m_pszString == nullptr)
    5827           0 :         return "";
    5828             : 
    5829         300 :     return m_pszString;
    5830             : }
    5831             : 
    5832             : /**********************************************************************
    5833             :  *                   TABText::SetTextString()
    5834             :  *
    5835             :  * Set new text string value.
    5836             :  *
    5837             :  * Note: The text string may contain "\n" chars or "\\" chars
    5838             :  * and we expect to receive them in a 2 chars escaped form as
    5839             :  * described in the MIF format specs.
    5840             :  **********************************************************************/
    5841           3 : void TABText::SetTextString(const char *pszNewStr)
    5842             : {
    5843           3 :     CPLFree(m_pszString);
    5844           3 :     m_pszString = CPLStrdup(pszNewStr);
    5845           3 : }
    5846             : 
    5847             : /**********************************************************************
    5848             :  *                   TABText::GetTextAngle()
    5849             :  *
    5850             :  * Return text angle in degrees.
    5851             :  **********************************************************************/
    5852          10 : double TABText::GetTextAngle() const
    5853             : {
    5854          10 :     return m_dAngle;
    5855             : }
    5856             : 
    5857         211 : void TABText::SetTextAngle(double dAngle)
    5858             : {
    5859             :     // Make sure angle is in the range [0..360]
    5860         211 :     dAngle = fmod(dAngle, 360.0);
    5861         211 :     if (dAngle < 0.0)
    5862           0 :         dAngle += 360.0;
    5863         211 :     m_dAngle = dAngle;
    5864         211 :     UpdateMBR();
    5865         211 : }
    5866             : 
    5867             : /**********************************************************************
    5868             :  *                   TABText::GetTextBoxHeight()
    5869             :  *
    5870             :  * Return text height in Y axis coord. units of the text box before rotation.
    5871             :  **********************************************************************/
    5872          10 : double TABText::GetTextBoxHeight() const
    5873             : {
    5874          10 :     return m_dHeight;
    5875             : }
    5876             : 
    5877           3 : void TABText::SetTextBoxHeight(double dHeight)
    5878             : {
    5879           3 :     m_dHeight = dHeight;
    5880           3 :     UpdateMBR();
    5881           3 : }
    5882             : 
    5883             : /**********************************************************************
    5884             :  *                   TABText::GetTextBoxWidth()
    5885             :  *
    5886             :  * Return text width in X axis coord. units. of the text box before rotation.
    5887             :  *
    5888             :  * If value has not been set, then we force a default value that assumes
    5889             :  * that one char's box width is 60% of its height... and we ignore
    5890             :  * the multiline case.  This should not matter when the user PROPERLY sets
    5891             :  * the value.
    5892             :  **********************************************************************/
    5893          12 : double TABText::GetTextBoxWidth() const
    5894             : {
    5895          12 :     if (m_dWidth == 0.0 && m_pszString)
    5896             :     {
    5897           3 :         m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
    5898             :     }
    5899          12 :     return m_dWidth;
    5900             : }
    5901             : 
    5902           0 : void TABText::SetTextBoxWidth(double dWidth)
    5903             : {
    5904           0 :     m_dWidth = dWidth;
    5905           0 :     UpdateMBR();
    5906           0 : }
    5907             : 
    5908             : /**********************************************************************
    5909             :  *                   TABText::GetTextLineEndPoint()
    5910             :  *
    5911             :  * Return X,Y coordinates of the text label line end point.
    5912             :  * Default is the center of the text MBR.
    5913             :  **********************************************************************/
    5914           4 : void TABText::GetTextLineEndPoint(double &dX, double &dY)
    5915             : {
    5916           4 :     if (!m_bLineEndSet)
    5917             :     {
    5918             :         // Set default location at center of text MBR
    5919           4 :         double dXMin = 0.0;
    5920           4 :         double dYMin = 0.0;
    5921           4 :         double dXMax = 0.0;
    5922           4 :         double dYMax = 0.0;
    5923           4 :         UpdateMBR();
    5924           4 :         GetMBR(dXMin, dYMin, dXMax, dYMax);
    5925           4 :         m_dfLineEndX = (dXMin + dXMax) / 2.0;
    5926           4 :         m_dfLineEndY = (dYMin + dYMax) / 2.0;
    5927           4 :         m_bLineEndSet = TRUE;
    5928             :     }
    5929             : 
    5930             :     // Return values
    5931           4 :     dX = m_dfLineEndX;
    5932           4 :     dY = m_dfLineEndY;
    5933           4 : }
    5934             : 
    5935         178 : void TABText::SetTextLineEndPoint(double dX, double dY)
    5936             : {
    5937         178 :     m_dfLineEndX = dX;
    5938         178 :     m_dfLineEndY = dY;
    5939         178 :     m_bLineEndSet = TRUE;
    5940         178 : }
    5941             : 
    5942             : /**********************************************************************
    5943             :  *                   TABText::UpdateMBR()
    5944             :  *
    5945             :  * Update the feature MBR using the text origin (OGRPoint geometry), the
    5946             :  * rotation angle, and the Width/height before rotation.
    5947             :  *
    5948             :  * This function cannot perform properly unless all the above have been set.
    5949             :  *
    5950             :  * Returns 0 on success, or -1 if there is no geometry in object
    5951             :  **********************************************************************/
    5952         226 : int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    5953             : {
    5954         226 :     OGRGeometry *poGeom = GetGeometryRef();
    5955         226 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5956             :     {
    5957          12 :         OGRPoint *poPoint = poGeom->toPoint();
    5958             : 
    5959          12 :         const double dX0 = poPoint->getX();
    5960          12 :         const double dY0 = poPoint->getY();
    5961             : 
    5962          12 :         const double dSin = sin(m_dAngle * M_PI / 180.0);
    5963          12 :         const double dCos = cos(m_dAngle * M_PI / 180.0);
    5964             : 
    5965          12 :         GetTextBoxWidth();  // Force default width value if necessary.
    5966             : 
    5967          12 :         const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
    5968          12 :         const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
    5969             : 
    5970          12 :         SetMBR(dX0, dY0, dX0, dY0);
    5971          60 :         for (int i = 0; i < 4; i++)
    5972             :         {
    5973             :             // Rotate one of the box corners
    5974          48 :             const double dX1 =
    5975          48 :                 dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
    5976          48 :             const double dY1 =
    5977          48 :                 dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
    5978             : 
    5979             :             // And update feature MBR with rotated coordinate
    5980          48 :             if (dX1 < m_dXMin)
    5981           9 :                 m_dXMin = dX1;
    5982          48 :             if (dX1 > m_dXMax)
    5983           9 :                 m_dXMax = dX1;
    5984          48 :             if (dY1 < m_dYMin)
    5985           0 :                 m_dYMin = dY1;
    5986          48 :             if (dY1 > m_dYMax)
    5987          18 :                 m_dYMax = dY1;
    5988             :         }
    5989             : 
    5990          12 :         if (poMapFile)
    5991             :         {
    5992           4 :             poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    5993           4 :             poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    5994             :         }
    5995             : 
    5996          12 :         return 0;
    5997             :     }
    5998             : 
    5999         214 :     return -1;
    6000             : }
    6001             : 
    6002             : /**********************************************************************
    6003             :  *                   TABText::GetFontBGColor()
    6004             :  *
    6005             :  * Return background color.
    6006             :  **********************************************************************/
    6007           8 : GInt32 TABText::GetFontBGColor() const
    6008             : {
    6009           8 :     return m_rgbBackground;
    6010             : }
    6011             : 
    6012         258 : void TABText::SetFontBGColor(GInt32 rgbColor)
    6013             : {
    6014         258 :     m_rgbBackground = rgbColor;
    6015         258 : }
    6016             : 
    6017             : /**********************************************************************
    6018             :  *                   TABText::GetFontOColor()
    6019             :  *
    6020             :  * Return outline color.
    6021             :  **********************************************************************/
    6022           1 : GInt32 TABText::GetFontOColor() const
    6023             : {
    6024           1 :     return m_rgbOutline;
    6025             : }
    6026             : 
    6027           1 : void TABText::SetFontOColor(GInt32 rgbColor)
    6028             : {
    6029           1 :     m_rgbOutline = rgbColor;
    6030           1 : }
    6031             : 
    6032             : /**********************************************************************
    6033             :  *                   TABText::GetFontSColor()
    6034             :  *
    6035             :  * Return shadow color.
    6036             :  **********************************************************************/
    6037           0 : GInt32 TABText::GetFontSColor() const
    6038             : {
    6039           0 :     return m_rgbShadow;
    6040             : }
    6041             : 
    6042           0 : void TABText::SetFontSColor(GInt32 rgbColor)
    6043             : {
    6044           0 :     m_rgbShadow = rgbColor;
    6045           0 : }
    6046             : 
    6047             : /**********************************************************************
    6048             :  *                   TABText::GetFontFGColor()
    6049             :  *
    6050             :  * Return foreground color.
    6051             :  **********************************************************************/
    6052          10 : GInt32 TABText::GetFontFGColor() const
    6053             : {
    6054          10 :     return m_rgbForeground;
    6055             : }
    6056             : 
    6057         265 : void TABText::SetFontFGColor(GInt32 rgbColor)
    6058             : {
    6059         265 :     m_rgbForeground = rgbColor;
    6060         265 : }
    6061             : 
    6062             : /**********************************************************************
    6063             :  *                   TABText::GetTextJustification()
    6064             :  *
    6065             :  * Return text justification.  Default is TABTJLeft
    6066             :  **********************************************************************/
    6067          10 : TABTextJust TABText::GetTextJustification() const
    6068             : {
    6069          10 :     TABTextJust eJust = TABTJLeft;
    6070             : 
    6071          10 :     if (m_nTextAlignment & 0x0200)
    6072           9 :         eJust = TABTJCenter;
    6073           1 :     else if (m_nTextAlignment & 0x0400)
    6074           0 :         eJust = TABTJRight;
    6075             : 
    6076          10 :     return eJust;
    6077             : }
    6078             : 
    6079         222 : void TABText::SetTextJustification(TABTextJust eJustification)
    6080             : {
    6081             :     // Flush current value... default is TABTJLeft
    6082         222 :     m_nTextAlignment &= ~0x0600;
    6083             :     // ... and set new one.
    6084         222 :     if (eJustification == TABTJCenter)
    6085         222 :         m_nTextAlignment |= 0x0200;
    6086           0 :     else if (eJustification == TABTJRight)
    6087           0 :         m_nTextAlignment |= 0x0400;
    6088         222 : }
    6089             : 
    6090             : /**********************************************************************
    6091             :  *                   TABText::GetTextSpacing()
    6092             :  *
    6093             :  * Return text vertical spacing factor.  Default is TABTSSingle
    6094             :  **********************************************************************/
    6095           0 : TABTextSpacing TABText::GetTextSpacing() const
    6096             : {
    6097           0 :     TABTextSpacing eSpacing = TABTSSingle;
    6098             : 
    6099           0 :     if (m_nTextAlignment & 0x0800)
    6100           0 :         eSpacing = TABTS1_5;
    6101           0 :     else if (m_nTextAlignment & 0x1000)
    6102           0 :         eSpacing = TABTSDouble;
    6103             : 
    6104           0 :     return eSpacing;
    6105             : }
    6106             : 
    6107         237 : void TABText::SetTextSpacing(TABTextSpacing eSpacing)
    6108             : {
    6109             :     // Flush current value... default is TABTSSingle
    6110         237 :     m_nTextAlignment &= ~0x1800;
    6111             :     // ... and set new one.
    6112         237 :     if (eSpacing == TABTS1_5)
    6113           0 :         m_nTextAlignment |= 0x0800;
    6114         237 :     else if (eSpacing == TABTSDouble)
    6115         237 :         m_nTextAlignment |= 0x1000;
    6116         237 : }
    6117             : 
    6118             : /**********************************************************************
    6119             :  *                   TABText::GetTextLineType()
    6120             :  *
    6121             :  * Return text line (arrow) type.  Default is TABTLNoLine
    6122             :  **********************************************************************/
    6123           0 : TABTextLineType TABText::GetTextLineType() const
    6124             : {
    6125           0 :     TABTextLineType eLine = TABTLNoLine;
    6126             : 
    6127           0 :     if (m_nTextAlignment & 0x2000)
    6128           0 :         eLine = TABTLSimple;
    6129           0 :     else if (m_nTextAlignment & 0x4000)
    6130           0 :         eLine = TABTLArrow;
    6131             : 
    6132           0 :     return eLine;
    6133             : }
    6134             : 
    6135         178 : void TABText::SetTextLineType(TABTextLineType eLineType)
    6136             : {
    6137             :     // Flush current value... default is TABTLNoLine
    6138         178 :     m_nTextAlignment &= ~0x6000;
    6139             :     // ... and set new one.
    6140         178 :     if (eLineType == TABTLSimple)
    6141         178 :         m_nTextAlignment |= 0x2000;
    6142           0 :     else if (eLineType == TABTLArrow)
    6143           0 :         m_nTextAlignment |= 0x4000;
    6144         178 : }
    6145             : 
    6146             : /**********************************************************************
    6147             :  *                   TABText::QueryFontStyle()
    6148             :  *
    6149             :  * Return TRUE if the specified font style attribute is turned ON,
    6150             :  * or FALSE otherwise.  See enum TABFontStyle for the list of styles
    6151             :  * that can be queried on.
    6152             :  **********************************************************************/
    6153         385 : GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
    6154             : {
    6155         385 :     return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
    6156             : }
    6157             : 
    6158         264 : void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
    6159             : {
    6160         264 :     if (bStyleOn)
    6161         264 :         m_nFontStyle |= static_cast<int>(eStyleToToggle);
    6162             :     else
    6163           0 :         m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
    6164         264 : }
    6165             : 
    6166             : /**********************************************************************
    6167             :  *                   TABText::GetFontStyleMIFValue()
    6168             :  *
    6169             :  * Return the Font Style value for this object using the style values
    6170             :  * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
    6171             :  *
    6172             :  * The reason why we have to differentiate between the TAB and the MIF font
    6173             :  * style values is that in TAB, TABFSBox is included in the style value
    6174             :  * as code 0x100, but in MIF it is not included, instead it is implied by
    6175             :  * the presence of the BG color in the FONT() clause (the BG color is
    6176             :  * present only when TABFSBox or TABFSHalo is set).
    6177             :  * This also has the effect of shifting all the other style values > 0x100
    6178             :  * by 1 byte.
    6179             :  **********************************************************************/
    6180           0 : int TABText::GetFontStyleMIFValue() const
    6181             : {
    6182             :     // The conversion is simply to remove bit 0x100 from the value and shift
    6183             :     // down all values past this bit.
    6184           0 :     return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
    6185             : }
    6186             : 
    6187         261 : void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
    6188             : {
    6189         261 :     m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
    6190             :     // When BG color is set, then either BOX or HALO should be set.
    6191         261 :     if (bBGColorSet && !QueryFontStyle(TABFSHalo))
    6192         254 :         ToggleFontStyle(TABFSBox, TRUE);
    6193         261 : }
    6194             : 
    6195          10 : int TABText::IsFontBGColorUsed() const
    6196             : {
    6197             :     // Font BG color is used only when BOX is set.
    6198          10 :     return QueryFontStyle(TABFSBox);
    6199             : }
    6200             : 
    6201          10 : int TABText::IsFontOColorUsed() const
    6202             : {
    6203             :     // Font outline color is used only when HALO is set.
    6204          10 :     return QueryFontStyle(TABFSHalo);
    6205             : }
    6206             : 
    6207          10 : int TABText::IsFontSColorUsed() const
    6208             : {
    6209             :     // Font shadow color is used only when Shadow is set.
    6210          10 :     return QueryFontStyle(TABFSShadow);
    6211             : }
    6212             : 
    6213          10 : int TABText::IsFontBold() const
    6214             : {
    6215             :     // Font bold is used only when Bold is set.
    6216          10 :     return QueryFontStyle(TABFSBold);
    6217             : }
    6218             : 
    6219          10 : int TABText::IsFontItalic() const
    6220             : {
    6221             :     // Font italic is used only when Italic is set.
    6222          10 :     return QueryFontStyle(TABFSItalic);
    6223             : }
    6224             : 
    6225          10 : int TABText::IsFontUnderline() const
    6226             : {
    6227             :     // Font underline is used only when Underline is set.
    6228          10 :     return QueryFontStyle(TABFSUnderline);
    6229             : }
    6230             : 
    6231             : /**********************************************************************
    6232             :  *                   TABText::GetLabelStyleString()
    6233             :  *
    6234             :  * This is not the correct location, it should be in ITABFeatureFont,
    6235             :  * but it is really more easy to put it here.  This fct return a complete
    6236             :  * string for the representation with the string to display
    6237             :  **********************************************************************/
    6238          10 : const char *TABText::GetLabelStyleString() const
    6239             : {
    6240          10 :     const char *pszStyle = nullptr;
    6241          10 :     int nStringLen = static_cast<int>(strlen(GetTextString()));
    6242             :     // ALL Caps, Extpanded need to modify the string value
    6243             :     char *pszTextString =
    6244          10 :         static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
    6245             :     /* char szPattern[20]; */
    6246          10 :     int nJustification = 1;
    6247             : 
    6248          10 :     strcpy(pszTextString, GetTextString());
    6249             :     /* szPattern[0] = '\0'; */
    6250             : 
    6251          10 :     switch (GetTextJustification())
    6252             :     {
    6253           9 :         case TABTJCenter:
    6254           9 :             nJustification = 2;
    6255           9 :             break;
    6256           0 :         case TABTJRight:
    6257           0 :             nJustification = 3;
    6258           0 :             break;
    6259           1 :         case TABTJLeft:
    6260             :         default:
    6261           1 :             nJustification = 1;
    6262           1 :             break;
    6263             :     }
    6264             : 
    6265             :     // Compute real font size, taking number of lines ("\\n", "\n") and line
    6266             :     // spacing into account.
    6267          10 :     int numLines = 1;
    6268          61 :     for (int i = 0; pszTextString[i];
    6269          51 :          numLines +=
    6270         102 :          ((pszTextString[i] == '\n' ||
    6271          51 :            (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
    6272           0 :           pszTextString[i + 1] != '\0'),
    6273             :              ++i)
    6274             :         ;
    6275             : 
    6276          10 :     double dHeight = GetTextBoxHeight() / numLines;
    6277             : 
    6278             :     // In all cases, take out 20% of font height to account for line spacing
    6279          10 :     if (numLines > 1)
    6280             :     {
    6281           0 :         switch (GetTextSpacing())
    6282             :         {
    6283           0 :             case TABTS1_5:
    6284           0 :                 dHeight *= (0.80 * 0.69);
    6285           0 :                 break;
    6286           0 :             case TABTSDouble:
    6287           0 :                 dHeight *= (0.66 * 0.69);
    6288           0 :                 break;
    6289           0 :             default:
    6290           0 :                 dHeight *= 0.69;
    6291             :         }
    6292             :     }
    6293             :     else
    6294             :     {
    6295          10 :         dHeight *= 0.69;
    6296             :     }
    6297             : 
    6298          10 :     if (QueryFontStyle(TABFSAllCaps))
    6299           0 :         for (int i = 0; pszTextString[i]; ++i)
    6300           0 :             if (isalpha(static_cast<unsigned char>(pszTextString[i])))
    6301           0 :                 pszTextString[i] = static_cast<char>(
    6302           0 :                     CPLToupper(static_cast<unsigned char>(pszTextString[i])));
    6303             : 
    6304             :     /* Escape the double quote chars and expand the text */
    6305          10 :     char *pszTmpTextString = nullptr;
    6306             : 
    6307          10 :     if (QueryFontStyle(TABFSExpanded))
    6308             :         pszTmpTextString = static_cast<char *>(
    6309           0 :             CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
    6310             :     else
    6311             :         pszTmpTextString = static_cast<char *>(
    6312          10 :             CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
    6313             : 
    6314          10 :     int j = 0;
    6315          61 :     for (int i = 0; i < nStringLen; ++i, ++j)
    6316             :     {
    6317          51 :         if (pszTextString[i] == '"')
    6318             :         {
    6319           0 :             pszTmpTextString[j] = '\\';
    6320           0 :             pszTmpTextString[j + 1] = pszTextString[i];
    6321           0 :             ++j;
    6322             :         }
    6323             :         else
    6324          51 :             pszTmpTextString[j] = pszTextString[i];
    6325             : 
    6326          51 :         if (QueryFontStyle(TABFSExpanded))
    6327             :         {
    6328           0 :             pszTmpTextString[j + 1] = ' ';
    6329           0 :             ++j;
    6330             :         }
    6331             :     }
    6332             : 
    6333          10 :     pszTmpTextString[j] = '\0';
    6334          10 :     CPLFree(pszTextString);
    6335             :     pszTextString = static_cast<char *>(
    6336          10 :         CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
    6337          10 :     strcpy(pszTextString, pszTmpTextString);
    6338          10 :     CPLFree(pszTmpTextString);
    6339             : 
    6340             :     const char *pszBGColor =
    6341          10 :         IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
    6342             :     const char *pszOColor =
    6343          10 :         IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
    6344             :     const char *pszSColor =
    6345          10 :         IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
    6346          10 :     const char *pszBold = IsFontBold() ? ",bo:1" : "";
    6347          10 :     const char *pszItalic = IsFontItalic() ? ",it:1" : "";
    6348          10 :     const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
    6349             : 
    6350          10 :     pszStyle = CPLSPrintf(
    6351             :         "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
    6352             :         pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
    6353             :         pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
    6354             :         GetFontNameRef());
    6355             : 
    6356          10 :     CPLFree(pszTextString);
    6357          10 :     return pszStyle;
    6358             : }
    6359             : 
    6360             : /**********************************************************************
    6361             :  *                   TABText::GetStyleString() const
    6362             :  *
    6363             :  * Return style string for this feature.
    6364             :  *
    6365             :  * Style String is built only once during the first call to GetStyleString().
    6366             :  **********************************************************************/
    6367          10 : const char *TABText::GetStyleString() const
    6368             : {
    6369          10 :     if (m_pszStyleString == nullptr)
    6370             :     {
    6371          10 :         m_pszStyleString = CPLStrdup(GetLabelStyleString());
    6372             :     }
    6373             : 
    6374          10 :     return m_pszStyleString;
    6375             : }
    6376             : 
    6377           4 : void TABText::SetLabelFromStyleString(const char *pszStyleString)
    6378             : {
    6379             :     // Use the Style Manager to retrieve all the information we need.
    6380           4 :     auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
    6381           0 :     std::unique_ptr<OGRStyleTool> poStylePart;
    6382             : 
    6383             :     // Init the StyleMgr with the StyleString.
    6384           4 :     poStyleMgr->InitStyleString(pszStyleString);
    6385             : 
    6386             :     // Retrieve the Symbol info.
    6387           4 :     const int numParts = poStyleMgr->GetPartCount();
    6388           4 :     for (int i = 0; i < numParts; i++)
    6389             :     {
    6390           4 :         poStylePart.reset(poStyleMgr->GetPart(i));
    6391           4 :         if (poStylePart == nullptr)
    6392             :         {
    6393           0 :             continue;
    6394             :         }
    6395             : 
    6396           4 :         if (poStylePart->GetType() == OGRSTCLabel)
    6397             :         {
    6398           4 :             break;
    6399             :         }
    6400             :         else
    6401             :         {
    6402           0 :             poStylePart.reset();
    6403             :         }
    6404             :     }
    6405             : 
    6406             :     // If the no Symbol found, do nothing.
    6407           4 :     if (poStylePart == nullptr)
    6408             :     {
    6409           0 :         return;
    6410             :     }
    6411             : 
    6412           4 :     auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
    6413             : 
    6414           4 :     GBool bIsNull = 0;
    6415           4 :     const char *pszText = poLabelStyle->TextString(bIsNull);
    6416           4 :     if (!bIsNull && pszText)
    6417             :     {
    6418           3 :         SetTextString(pszText);
    6419             : 
    6420           3 :         poLabelStyle->SetUnit(OGRSTUMM);
    6421           3 :         double dfSize = poLabelStyle->Size(bIsNull);
    6422           3 :         if (!bIsNull)
    6423             :         {
    6424           3 :             dfSize /= 1000;
    6425             : 
    6426             :             // Compute text box height, taking number of lines ("\\n", "\n") and
    6427             :             // line spacing into account.
    6428           3 :             int numLines = 1;
    6429          18 :             for (int i = 0; pszText[i];
    6430          45 :                  numLines += ((pszText[i] == '\n' ||
    6431          15 :                                (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
    6432           0 :                               pszText[i + 1] != '\0'),
    6433             :                      ++i)
    6434             :                 ;
    6435             : 
    6436             :             // Cf GetLabelStyleString() for 0.69. We should likely also take
    6437             :             // into account line spacing if we knew how to compute it.
    6438           3 :             SetTextBoxHeight(dfSize / 0.69 * numLines);
    6439             :         }
    6440             :     }
    6441             : 
    6442           4 :     if (poLabelStyle->Bold(bIsNull))
    6443           3 :         ToggleFontStyle(TABFSBold, true);
    6444             : 
    6445           4 :     if (poLabelStyle->Italic(bIsNull))
    6446           1 :         ToggleFontStyle(TABFSItalic, true);
    6447             : 
    6448           4 :     if (poLabelStyle->Underline(bIsNull))
    6449           1 :         ToggleFontStyle(TABFSUnderline, true);
    6450             : 
    6451           4 :     const char *pszFontName = poLabelStyle->FontName(bIsNull);
    6452           4 :     if (!bIsNull && pszFontName)
    6453           4 :         SetFontName(pszFontName);
    6454             : 
    6455             :     // Set the ForeColor
    6456           4 :     const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
    6457           4 :     if (bIsNull)
    6458           0 :         pszForeColor = nullptr;
    6459           4 :     if (pszForeColor)
    6460             :     {
    6461           4 :         if (pszForeColor[0] == '#')
    6462           4 :             pszForeColor++;
    6463           8 :         CPLString osForeColor(pszForeColor);
    6464           4 :         if (strlen(pszForeColor) > 6)
    6465           1 :             osForeColor.resize(6);
    6466           4 :         const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
    6467           4 :         SetFontFGColor(static_cast<GInt32>(nColor));
    6468             :     }
    6469             : 
    6470             :     // Set the BackgroundColor
    6471           4 :     const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
    6472           4 :     if (bIsNull)
    6473           0 :         pszBackColor = nullptr;
    6474           4 :     if (pszBackColor)
    6475             :     {
    6476           4 :         if (pszBackColor[0] == '#')
    6477           4 :             pszBackColor++;
    6478           8 :         CPLString osBackColor(pszBackColor);
    6479           4 :         if (strlen(pszBackColor) > 6)
    6480           1 :             osBackColor.resize(6);
    6481           4 :         const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
    6482           4 :         ToggleFontStyle(TABFSBox, true);
    6483           4 :         SetFontBGColor(static_cast<GInt32>(nColor));
    6484             :     }
    6485             : 
    6486             :     // Set the OutlineColor
    6487           4 :     const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
    6488           4 :     if (bIsNull)
    6489           3 :         pszOutlineColor = nullptr;
    6490           4 :     if (pszOutlineColor)
    6491             :     {
    6492           1 :         if (pszOutlineColor[0] == '#')
    6493           1 :             pszOutlineColor++;
    6494           2 :         CPLString osOutlineColor(pszOutlineColor);
    6495           1 :         if (strlen(pszOutlineColor) > 6)
    6496           0 :             osOutlineColor.resize(6);
    6497             :         const int nColor =
    6498           1 :             static_cast<int>(strtol(osOutlineColor, nullptr, 16));
    6499           1 :         ToggleFontStyle(TABFSHalo, true);
    6500           1 :         SetFontOColor(static_cast<GInt32>(nColor));
    6501             :     }
    6502             : 
    6503             : #if 0
    6504             :     // Commented out since it is hardcoded to 0x808080.
    6505             :     // Set the ShadowColor
    6506             :     const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
    6507             :     if(bIsNull) pszShadowColor = nullptr;
    6508             :     if(pszShadowColor)
    6509             :     {
    6510             :         if(pszShadowColor[0] == '#')
    6511             :             pszShadowColor++;
    6512             :         CPLString osShadowColor(pszShadowColor);
    6513             :         if( strlen(pszShadowColor) > 6 )
    6514             :             osShadowColor.resize(6);
    6515             :         const int nColor =
    6516             :             static_cast<int>(strtol(osShadowColor, nullptr, 16));
    6517             :         ToggleFontStyle(TABFSShadow, true);
    6518             :         SetFontSColor(static_cast<GInt32>(nColor));
    6519             :     }
    6520             : #endif
    6521             : 
    6522           4 :     const double dfAngle = poLabelStyle->Angle(bIsNull);
    6523           4 :     if (!bIsNull)
    6524           3 :         SetTextAngle(dfAngle);
    6525             : 
    6526           4 :     const int nAnchor = poLabelStyle->Anchor(bIsNull);
    6527           4 :     if (!bIsNull)
    6528             :     {
    6529           3 :         switch ((nAnchor - 1) % 3)
    6530             :         {
    6531           0 :             case 0:
    6532           0 :                 SetTextJustification(TABTJLeft);
    6533           0 :                 break;
    6534           3 :             case 1:
    6535           3 :                 SetTextJustification(TABTJCenter);
    6536           3 :                 break;
    6537           0 :             default /* 2 */:
    6538           0 :                 SetTextJustification(TABTJRight);
    6539           0 :                 break;
    6540             :         }
    6541             :     }
    6542             : }
    6543             : 
    6544             : /**********************************************************************
    6545             :  *                   TABText::DumpMIF()
    6546             :  *
    6547             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    6548             :  **********************************************************************/
    6549           0 : void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
    6550             : {
    6551           0 :     if (fpOut == nullptr)
    6552           0 :         fpOut = stdout;
    6553             : 
    6554             :     /*-----------------------------------------------------------------
    6555             :      * Fetch and validate geometry
    6556             :      *----------------------------------------------------------------*/
    6557           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6558           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6559             :     {
    6560             :         /*-------------------------------------------------------------
    6561             :          * Generate output for text object
    6562             :          *------------------------------------------------------------*/
    6563           0 :         OGRPoint *poPoint = poGeom->toPoint();
    6564             : 
    6565           0 :         fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
    6566           0 :                 m_pszString ? m_pszString : "", poPoint->getX(),
    6567             :                 poPoint->getY());
    6568             : 
    6569           0 :         fprintf(fpOut, "  m_pszString = '%s'\n", m_pszString);
    6570           0 :         fprintf(fpOut, "  m_dAngle    = %.15g\n", m_dAngle);
    6571           0 :         fprintf(fpOut, "  m_dHeight   = %.15g\n", m_dHeight);
    6572           0 :         fprintf(fpOut, "  m_rgbForeground  = 0x%6.6x (%d)\n", m_rgbForeground,
    6573             :                 m_rgbForeground);
    6574           0 :         fprintf(fpOut, "  m_rgbBackground  = 0x%6.6x (%d)\n", m_rgbBackground,
    6575             :                 m_rgbBackground);
    6576           0 :         fprintf(fpOut, "  m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
    6577           0 :         fprintf(fpOut, "  m_nFontStyle     = 0x%4.4x\n", m_nFontStyle);
    6578             :     }
    6579             :     else
    6580             :     {
    6581           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6582             :                  "TABText: Missing or Invalid Geometry!");
    6583           0 :         return;
    6584             :     }
    6585             : 
    6586             :     // Finish with PEN/BRUSH/etc. clauses
    6587           0 :     DumpPenDef();
    6588           0 :     DumpFontDef();
    6589             : 
    6590           0 :     fflush(fpOut);
    6591             : }
    6592             : 
    6593             : /*=====================================================================
    6594             :  *                      class TABMultiPoint
    6595             :  *====================================================================*/
    6596             : 
    6597             : /**********************************************************************
    6598             :  *                   TABMultiPoint::TABMultiPoint()
    6599             :  *
    6600             :  * Constructor.
    6601             :  **********************************************************************/
    6602         191 : TABMultiPoint::TABMultiPoint(OGRFeatureDefn *poDefnIn)
    6603             :     : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
    6604         191 :       m_dCenterY(0.0)
    6605             : {
    6606         191 : }
    6607             : 
    6608             : /**********************************************************************
    6609             :  *                   TABMultiPoint::~TABMultiPoint()
    6610             :  *
    6611             :  * Destructor.
    6612             :  **********************************************************************/
    6613         382 : TABMultiPoint::~TABMultiPoint()
    6614             : {
    6615         382 : }
    6616             : 
    6617             : /**********************************************************************
    6618             :  *                     TABMultiPoint::CloneTABFeature()
    6619             :  *
    6620             :  * Duplicate feature, including stuff specific to each TABFeature type.
    6621             :  *
    6622             :  * This method calls the generic TABFeature::CloneTABFeature() and
    6623             :  * then copies any members specific to its own type.
    6624             :  **********************************************************************/
    6625           0 : TABFeature *TABMultiPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    6626             : {
    6627             :     /*-----------------------------------------------------------------
    6628             :      * Alloc new feature and copy the base stuff
    6629             :      *----------------------------------------------------------------*/
    6630             :     TABMultiPoint *poNew =
    6631           0 :         new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
    6632             : 
    6633           0 :     CopyTABFeatureBase(poNew);
    6634             : 
    6635             :     /*-----------------------------------------------------------------
    6636             :      * And members specific to this class
    6637             :      *----------------------------------------------------------------*/
    6638             :     // ITABFeatureSymbol
    6639           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    6640             : 
    6641           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    6642           0 :     poNew->m_dCenterX = m_dCenterX;
    6643           0 :     poNew->m_dCenterY = m_dCenterY;
    6644             : 
    6645           0 :     return poNew;
    6646             : }
    6647             : 
    6648             : /**********************************************************************
    6649             :  *                   TABMultiPoint::ValidateMapInfoType()
    6650             :  *
    6651             :  * Check the feature's geometry part and return the corresponding
    6652             :  * mapinfo object type code.  The m_nMapInfoType member will also
    6653             :  * be updated for further calls to GetMapInfoType();
    6654             :  *
    6655             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    6656             :  * is expected for this object class.
    6657             :  **********************************************************************/
    6658           0 : TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    6659             : {
    6660             :     /*-----------------------------------------------------------------
    6661             :      * Fetch and validate geometry
    6662             :      *----------------------------------------------------------------*/
    6663           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6664           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6665             :     {
    6666           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    6667             : 
    6668           0 :         if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
    6669           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
    6670             :         else
    6671           0 :             m_nMapInfoType = TAB_GEOM_MULTIPOINT;
    6672             :     }
    6673             :     else
    6674             :     {
    6675           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6676             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6677           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    6678             :     }
    6679             : 
    6680             :     /*-----------------------------------------------------------------
    6681             :      * Decide if coordinates should be compressed or not.
    6682             :      *----------------------------------------------------------------*/
    6683           0 :     ValidateCoordType(poMapFile);
    6684             : 
    6685           0 :     return m_nMapInfoType;
    6686             : }
    6687             : 
    6688             : /**********************************************************************
    6689             :  *                   TABMultiPoint::ReadGeometryFromMAPFile()
    6690             :  *
    6691             :  * Fill the geometry and representation (color, etc...) part of the
    6692             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    6693             :  *
    6694             :  * It is assumed that poMAPFile currently points to the beginning of
    6695             :  * a map object.
    6696             :  *
    6697             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    6698             :  * been called.
    6699             :  **********************************************************************/
    6700           8 : int TABMultiPoint::ReadGeometryFromMAPFile(
    6701             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    6702             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    6703             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    6704             : {
    6705           8 :     double dXMin = 0.0;
    6706           8 :     double dYMin = 0.0;
    6707           8 :     double dXMax = 0.0;
    6708           8 :     double dYMax = 0.0;
    6709           8 :     OGRGeometry *poGeometry = nullptr;
    6710           8 :     GBool bComprCoord = poObjHdr->IsCompressedType();
    6711           8 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    6712             : 
    6713             :     /*-----------------------------------------------------------------
    6714             :      * Fetch and validate geometry type
    6715             :      *----------------------------------------------------------------*/
    6716           8 :     m_nMapInfoType = poObjHdr->m_nType;
    6717             : 
    6718             :     /*-----------------------------------------------------------------
    6719             :      * Read object information
    6720             :      *----------------------------------------------------------------*/
    6721           8 :     if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
    6722           8 :         m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
    6723           0 :         m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
    6724           0 :         m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
    6725             :     {
    6726             :         /*-------------------------------------------------------------
    6727             :          * Copy data from poObjHdr
    6728             :          *------------------------------------------------------------*/
    6729             :         TABMAPObjMultiPoint *poMPointHdr =
    6730           8 :             cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
    6731             : 
    6732           8 :         const GUInt32 nMinimumBytesForPoints =
    6733           8 :             (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
    6734           8 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    6735           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    6736             :         {
    6737           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
    6738           0 :             return -1;
    6739             :         }
    6740             : 
    6741             :         // MBR
    6742           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
    6743             :                                 dXMin, dYMin);
    6744           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
    6745             :                                 dXMax, dYMax);
    6746             : 
    6747           8 :         if (!bCoordBlockDataOnly)
    6748             :         {
    6749           8 :             m_nSymbolDefIndex = poMPointHdr->m_nSymbolId;  // Symbol index
    6750           8 :             poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    6751             :         }
    6752             : 
    6753           8 :         double dX = 0.0;
    6754           8 :         double dY = 0.0;
    6755             :         // Centroid/label point
    6756           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
    6757             :                                 dX, dY);
    6758           8 :         SetCenter(dX, dY);
    6759             : 
    6760             :         // Compressed coordinate origin (useful only in compressed case!)
    6761           8 :         m_nComprOrgX = poMPointHdr->m_nComprOrgX;
    6762           8 :         m_nComprOrgY = poMPointHdr->m_nComprOrgY;
    6763             : 
    6764             :         /*-------------------------------------------------------------
    6765             :          * Read Point Coordinates
    6766             :          *------------------------------------------------------------*/
    6767           8 :         OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
    6768           8 :         poGeometry = poMultiPoint;
    6769             : 
    6770           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    6771           4 :             poCoordBlock = *ppoCoordBlock;
    6772             :         else
    6773             :             poCoordBlock =
    6774           4 :                 poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
    6775           8 :         if (poCoordBlock == nullptr)
    6776             :         {
    6777           0 :             delete poGeometry;
    6778           0 :             return -1;
    6779             :         }
    6780           8 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    6781             : 
    6782          24 :         for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
    6783             :         {
    6784          16 :             GInt32 nX = 0;
    6785          16 :             GInt32 nY = 0;
    6786          16 :             if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
    6787             :             {
    6788           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    6789             :                          "Failed reading coordinate data at offset %d",
    6790             :                          poMPointHdr->m_nCoordBlockPtr);
    6791           0 :                 delete poGeometry;
    6792           0 :                 return -1;
    6793             :             }
    6794             : 
    6795          16 :             poMapFile->Int2Coordsys(nX, nY, dX, dY);
    6796          16 :             OGRPoint *poPoint = new OGRPoint(dX, dY);
    6797             : 
    6798          16 :             if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
    6799             :             {
    6800           0 :                 CPLAssert(false);  // Just in case lower-level lib is modified
    6801             :             }
    6802           8 :         }
    6803             :     }
    6804             :     else
    6805             :     {
    6806           0 :         CPLError(
    6807             :             CE_Failure, CPLE_AssertionFailed,
    6808             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    6809           0 :             m_nMapInfoType, m_nMapInfoType);
    6810           0 :         return -1;
    6811             :     }
    6812             : 
    6813           8 :     SetGeometryDirectly(poGeometry);
    6814             : 
    6815           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    6816             : 
    6817             :     /* Copy int MBR to feature class members */
    6818           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    6819             :               poObjHdr->m_nMaxY);
    6820             : 
    6821             :     /* Return a ref to coord block so that caller can continue reading
    6822             :      * after the end of this object (used by TABCollection and index splitting)
    6823             :      */
    6824           8 :     if (ppoCoordBlock)
    6825           4 :         *ppoCoordBlock = poCoordBlock;
    6826             : 
    6827           8 :     return 0;
    6828             : }
    6829             : 
    6830             : /**********************************************************************
    6831             :  *                   TABMultiPoint::WriteGeometryToMAPFile()
    6832             :  *
    6833             :  * Write the geometry and representation (color, etc...) part of the
    6834             :  * feature to the .MAP object pointed to by poMAPFile.
    6835             :  *
    6836             :  * It is assumed that poMAPFile currently points to a valid map object.
    6837             :  *
    6838             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    6839             :  * been called.
    6840             :  **********************************************************************/
    6841           0 : int TABMultiPoint::WriteGeometryToMAPFile(
    6842             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    6843             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    6844             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    6845             : {
    6846             :     GInt32 nX, nY;
    6847             : 
    6848             :     /*-----------------------------------------------------------------
    6849             :      * We assume that ValidateMapInfoType() was called already and that
    6850             :      * the type in poObjHdr->m_nType is valid.
    6851             :      *----------------------------------------------------------------*/
    6852           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    6853             : 
    6854             :     TABMAPObjMultiPoint *poMPointHdr =
    6855           0 :         cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
    6856             : 
    6857             :     /*-----------------------------------------------------------------
    6858             :      * Fetch and validate geometry
    6859             :      *----------------------------------------------------------------*/
    6860           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6861           0 :     OGRMultiPoint *poMPoint = nullptr;
    6862           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6863           0 :         poMPoint = poGeom->toMultiPoint();
    6864             :     else
    6865             :     {
    6866           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6867             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6868           0 :         return -1;
    6869             :     }
    6870             : 
    6871           0 :     poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
    6872             : 
    6873             :     /*-----------------------------------------------------------------
    6874             :      * Write data to coordinate block
    6875             :      *----------------------------------------------------------------*/
    6876           0 :     const GBool bCompressed = poObjHdr->IsCompressedType();
    6877             : 
    6878           0 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    6879           0 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    6880           0 :         poCoordBlock = *ppoCoordBlock;
    6881             :     else
    6882           0 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    6883           0 :     poCoordBlock->StartNewFeature();
    6884           0 :     poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    6885           0 :     poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    6886             : 
    6887           0 :     for (int iPoint = 0, nStatus = 0;
    6888           0 :          nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
    6889             :     {
    6890           0 :         poGeom = poMPoint->getGeometryRef(iPoint);
    6891             : 
    6892           0 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6893             :         {
    6894           0 :             OGRPoint *poPoint = poGeom->toPoint();
    6895             : 
    6896           0 :             poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    6897           0 :             if (iPoint == 0)
    6898             :             {
    6899             :                 // Default to the first point, we may use explicit value below
    6900           0 :                 poMPointHdr->m_nLabelX = nX;
    6901           0 :                 poMPointHdr->m_nLabelY = nY;
    6902             :             }
    6903             : 
    6904           0 :             if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
    6905             :                 0)
    6906             :             {
    6907             :                 // Failed ... error message has already been produced
    6908           0 :                 return nStatus;
    6909             :             }
    6910             :         }
    6911             :         else
    6912             :         {
    6913           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    6914             :                      "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
    6915           0 :             return -1;
    6916             :         }
    6917             :     }
    6918             : 
    6919             :     /*-----------------------------------------------------------------
    6920             :      * Copy object information
    6921             :      *----------------------------------------------------------------*/
    6922             : 
    6923             :     // Compressed coordinate origin (useful only in compressed case!)
    6924           0 :     poMPointHdr->m_nComprOrgX = m_nComprOrgX;
    6925           0 :     poMPointHdr->m_nComprOrgY = m_nComprOrgY;
    6926             : 
    6927           0 :     poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    6928           0 :     poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    6929             : 
    6930             :     // Center/label point (default value already set above)
    6931           0 :     double dX = 0.0;
    6932           0 :     double dY = 0.0;
    6933           0 :     if (GetCenter(dX, dY) != -1)
    6934             :     {
    6935           0 :         poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
    6936           0 :                                 poMPointHdr->m_nLabelY);
    6937             :     }
    6938             : 
    6939           0 :     if (!bCoordBlockDataOnly)
    6940             :     {
    6941           0 :         m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    6942           0 :         poMPointHdr->m_nSymbolId =
    6943           0 :             static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    6944             :     }
    6945             : 
    6946           0 :     if (CPLGetLastErrorType() == CE_Failure)
    6947           0 :         return -1;
    6948             : 
    6949             :     /* Return a ref to coord block so that caller can continue writing
    6950             :      * after the end of this object (used by index splitting)
    6951             :      */
    6952           0 :     if (ppoCoordBlock)
    6953           0 :         *ppoCoordBlock = poCoordBlock;
    6954             : 
    6955           0 :     return 0;
    6956             : }
    6957             : 
    6958             : /**********************************************************************
    6959             :  *                   TABMultiPoint::GetXY()
    6960             :  *
    6961             :  * Return this point's X,Y coordinates.
    6962             :  **********************************************************************/
    6963           0 : int TABMultiPoint::GetXY(int i, double &dX, double &dY)
    6964             : {
    6965             :     /*-----------------------------------------------------------------
    6966             :      * Fetch and validate geometry
    6967             :      *----------------------------------------------------------------*/
    6968           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6969           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6970             :     {
    6971           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    6972             : 
    6973           0 :         if (i >= 0 && i < poMPoint->getNumGeometries() &&
    6974           0 :             (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
    6975           0 :             wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6976             :         {
    6977           0 :             OGRPoint *poPoint = poGeom->toPoint();
    6978             : 
    6979           0 :             dX = poPoint->getX();
    6980           0 :             dY = poPoint->getY();
    6981             :         }
    6982             :     }
    6983             :     else
    6984             :     {
    6985           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6986             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6987           0 :         dX = 0.0;
    6988           0 :         dY = 0.0;
    6989           0 :         return -1;
    6990             :     }
    6991             : 
    6992           0 :     return 0;
    6993             : }
    6994             : 
    6995             : /**********************************************************************
    6996             :  *                   TABMultiPoint::GetNumPoints()
    6997             :  *
    6998             :  * Return the number of points in this multipoint object
    6999             :  **********************************************************************/
    7000           0 : int TABMultiPoint::GetNumPoints()
    7001             : {
    7002             :     /*-----------------------------------------------------------------
    7003             :      * Fetch and validate geometry
    7004             :      *----------------------------------------------------------------*/
    7005           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7006           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    7007             :     {
    7008           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    7009             : 
    7010           0 :         return poMPoint->getNumGeometries();
    7011             :     }
    7012             :     else
    7013             :     {
    7014           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7015             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7016           0 :         return 0;
    7017             :     }
    7018             : }
    7019             : 
    7020             : /**********************************************************************
    7021             :  *                   TABMultiPoint::GetStyleString() const
    7022             :  *
    7023             :  * Return style string for this feature.
    7024             :  *
    7025             :  * Style String is built only once during the first call to GetStyleString().
    7026             :  **********************************************************************/
    7027           3 : const char *TABMultiPoint::GetStyleString() const
    7028             : {
    7029           3 :     if (m_pszStyleString == nullptr)
    7030             :     {
    7031           3 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    7032             :     }
    7033             : 
    7034           3 :     return m_pszStyleString;
    7035             : }
    7036             : 
    7037             : /**********************************************************************
    7038             :  *                   TABMultiPoint::GetCenter()
    7039             :  *
    7040             :  * Returns the center point (or label point?) of the object.  Compute one
    7041             :  * if it was not explicitly set:
    7042             :  *
    7043             :  * The default seems to be to use the first point in the collection as
    7044             :  * the center.. so we'll use that.
    7045             :  *
    7046             :  * Returns 0 on success, -1 on error.
    7047             :  **********************************************************************/
    7048           0 : int TABMultiPoint::GetCenter(double &dX, double &dY)
    7049             : {
    7050           0 :     if (!m_bCenterIsSet && GetNumPoints() > 0)
    7051             :     {
    7052             :         // The default seems to be to use the first point in the collection
    7053             :         // as the center... so we'll use that.
    7054           0 :         if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
    7055           0 :             m_bCenterIsSet = TRUE;
    7056             :     }
    7057             : 
    7058           0 :     if (!m_bCenterIsSet)
    7059           0 :         return -1;
    7060             : 
    7061           0 :     dX = m_dCenterX;
    7062           0 :     dY = m_dCenterY;
    7063           0 :     return 0;
    7064             : }
    7065             : 
    7066             : /**********************************************************************
    7067             :  *                   TABMultiPoint::SetCenter()
    7068             :  *
    7069             :  * Set the X,Y coordinates to use as center point (or label point?)
    7070             :  **********************************************************************/
    7071         173 : void TABMultiPoint::SetCenter(double dX, double dY)
    7072             : {
    7073         173 :     m_dCenterX = dX;
    7074         173 :     m_dCenterY = dY;
    7075         173 :     m_bCenterIsSet = TRUE;
    7076         173 : }
    7077             : 
    7078             : /**********************************************************************
    7079             :  *                   TABMultiPoint::DumpMIF()
    7080             :  *
    7081             :  * Dump feature geometry in a format similar to .MIF POINTs.
    7082             :  **********************************************************************/
    7083           0 : void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
    7084             : {
    7085           0 :     if (fpOut == nullptr)
    7086           0 :         fpOut = stdout;
    7087             : 
    7088             :     /*-----------------------------------------------------------------
    7089             :      * Fetch and validate geometry
    7090             :      *----------------------------------------------------------------*/
    7091           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7092           0 :     OGRMultiPoint *poMPoint = nullptr;
    7093           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    7094           0 :         poMPoint = poGeom->toMultiPoint();
    7095             :     else
    7096             :     {
    7097           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7098             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7099           0 :         return;
    7100             :     }
    7101             : 
    7102             :     /*-----------------------------------------------------------------
    7103             :      * Generate output
    7104             :      *----------------------------------------------------------------*/
    7105           0 :     fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
    7106             : 
    7107           0 :     for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
    7108             :     {
    7109           0 :         poGeom = poMPoint->getGeometryRef(iPoint);
    7110             : 
    7111           0 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    7112             :         {
    7113           0 :             OGRPoint *poPoint = poGeom->toPoint();
    7114           0 :             fprintf(fpOut, "  %.15g %.15g\n", poPoint->getX(), poPoint->getY());
    7115             :         }
    7116             :         else
    7117             :         {
    7118           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    7119             :                      "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
    7120           0 :             return;
    7121             :         }
    7122             :     }
    7123             : 
    7124           0 :     DumpSymbolDef(fpOut);
    7125             : 
    7126           0 :     if (m_bCenterIsSet)
    7127           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    7128             : 
    7129           0 :     fflush(fpOut);
    7130             : }
    7131             : 
    7132             : /*=====================================================================
    7133             :  *                      class TABCollection
    7134             :  *====================================================================*/
    7135             : 
    7136             : /**********************************************************************
    7137             :  *                   TABCollection::TABCollection()
    7138             :  *
    7139             :  * Constructor.
    7140             :  **********************************************************************/
    7141         103 : TABCollection::TABCollection(OGRFeatureDefn *poDefnIn)
    7142             :     : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
    7143         103 :       m_poMpoint(nullptr)
    7144             : {
    7145         103 : }
    7146             : 
    7147             : /**********************************************************************
    7148             :  *                   TABCollection::~TABCollection()
    7149             :  *
    7150             :  * Destructor.
    7151             :  **********************************************************************/
    7152         206 : TABCollection::~TABCollection()
    7153             : {
    7154         103 :     EmptyCollection();
    7155         206 : }
    7156             : 
    7157             : /**********************************************************************
    7158             :  *                   TABCollection::EmptyCollection()
    7159             :  *
    7160             :  * Delete/free all collection components.
    7161             :  **********************************************************************/
    7162         204 : void TABCollection::EmptyCollection()
    7163             : {
    7164             : 
    7165         204 :     if (m_poRegion)
    7166             :     {
    7167          54 :         delete m_poRegion;
    7168          54 :         m_poRegion = nullptr;
    7169             :     }
    7170             : 
    7171         204 :     if (m_poPline)
    7172             :     {
    7173          37 :         delete m_poPline;
    7174          37 :         m_poPline = nullptr;
    7175             :     }
    7176             : 
    7177         204 :     if (m_poMpoint)
    7178             :     {
    7179           6 :         delete m_poMpoint;
    7180           6 :         m_poMpoint = nullptr;
    7181             :     }
    7182             : 
    7183             :     // Empty OGR Geometry Collection as well
    7184         204 :     SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
    7185         204 : }
    7186             : 
    7187             : /**********************************************************************
    7188             :  *                     TABCollection::CloneTABFeature()
    7189             :  *
    7190             :  * Duplicate feature, including stuff specific to each TABFeature type.
    7191             :  *
    7192             :  * This method calls the generic TABFeature::CloneTABFeature() and
    7193             :  * then copies any members specific to its own type.
    7194             :  **********************************************************************/
    7195           0 : TABFeature *TABCollection::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
    7196             : {
    7197             :     /*-----------------------------------------------------------------
    7198             :      * Alloc new feature and copy the base stuff
    7199             :      *----------------------------------------------------------------*/
    7200             :     TABCollection *poNew =
    7201           0 :         new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
    7202             : 
    7203           0 :     CopyTABFeatureBase(poNew);
    7204             : 
    7205             :     /*-----------------------------------------------------------------
    7206             :      * And members specific to this class
    7207             :      *----------------------------------------------------------------*/
    7208             : 
    7209           0 :     if (m_poRegion)
    7210           0 :         poNew->SetRegionDirectly(
    7211           0 :             cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
    7212             : 
    7213           0 :     if (m_poPline)
    7214           0 :         poNew->SetPolylineDirectly(
    7215           0 :             cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
    7216             : 
    7217           0 :     if (m_poMpoint)
    7218           0 :         poNew->SetMultiPointDirectly(
    7219           0 :             cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
    7220             : 
    7221           0 :     return poNew;
    7222             : }
    7223             : 
    7224             : /**********************************************************************
    7225             :  *                   TABCollection::ValidateMapInfoType()
    7226             :  *
    7227             :  * Check the feature's geometry part and return the corresponding
    7228             :  * mapinfo object type code.  The m_nMapInfoType member will also
    7229             :  * be updated for further calls to GetMapInfoType();
    7230             :  *
    7231             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    7232             :  * is expected for this object class.
    7233             :  **********************************************************************/
    7234           0 : TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    7235             : {
    7236           0 :     int nRegionType = TAB_GEOM_NONE;
    7237           0 :     int nPLineType = TAB_GEOM_NONE;
    7238           0 :     int nMPointType = TAB_GEOM_NONE;
    7239           0 :     int nVersion = 650;
    7240             : 
    7241             :     /*-----------------------------------------------------------------
    7242             :      * Fetch and validate geometry
    7243             :      *----------------------------------------------------------------*/
    7244           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7245           0 :     if (poGeom &&
    7246           0 :         wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
    7247             :     {
    7248           0 :         m_nMapInfoType = TAB_GEOM_COLLECTION;
    7249             :     }
    7250             :     else
    7251             :     {
    7252           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7253             :                  "TABCollection: Missing or Invalid Geometry!");
    7254           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    7255             :     }
    7256             : 
    7257             :     /*-----------------------------------------------------------------
    7258             :      * Decide if coordinates should be compressed or not.
    7259             :      *----------------------------------------------------------------*/
    7260           0 :     GBool bComprCoord = ValidateCoordType(poMapFile);
    7261             : 
    7262             :     /*-----------------------------------------------------------------
    7263             :      * Since all members of the collection share the same compressed coord
    7264             :      * origin, we should force the compressed origin in all components
    7265             :      * to be the same.
    7266             :      * This also implies that ValidateMapInfoType() should *NOT* be called
    7267             :      * again until the collection components are written by WriteGeom...()
    7268             :      *----------------------------------------------------------------*/
    7269             : 
    7270             :     // First pass to figure collection type...
    7271           0 :     if (m_poRegion)
    7272             :     {
    7273           0 :         m_poRegion->ValidateCoordType(poMapFile);
    7274           0 :         nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
    7275           0 :         if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
    7276           0 :             nVersion = TAB_GEOM_GET_VERSION(nRegionType);
    7277             :     }
    7278             : 
    7279           0 :     if (m_poPline)
    7280             :     {
    7281           0 :         m_poPline->ValidateCoordType(poMapFile);
    7282           0 :         nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
    7283           0 :         if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
    7284           0 :             nVersion = TAB_GEOM_GET_VERSION(nPLineType);
    7285             :     }
    7286             : 
    7287           0 :     if (m_poMpoint)
    7288             :     {
    7289           0 :         m_poMpoint->ValidateCoordType(poMapFile);
    7290           0 :         nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
    7291           0 :         if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
    7292           0 :             nVersion = TAB_GEOM_GET_VERSION(nMPointType);
    7293             :     }
    7294             : 
    7295             :     // Need to upgrade native type of collection?
    7296           0 :     if (nVersion == 800)
    7297             :     {
    7298           0 :         m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
    7299             :     }
    7300             : 
    7301             :     // Make another pass updating native type and coordinates type and origin
    7302             :     // of each component
    7303           0 :     if (m_poRegion && nRegionType != TAB_GEOM_NONE)
    7304             :     {
    7305           0 :         GInt32 nXMin = 0;
    7306           0 :         GInt32 nYMin = 0;
    7307           0 :         GInt32 nXMax = 0;
    7308           0 :         GInt32 nYMax = 0;
    7309           0 :         m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7310           0 :         m_poRegion->ForceCoordTypeAndOrigin(
    7311             :             (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
    7312             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7313             :             nYMax);
    7314             :     }
    7315             : 
    7316           0 :     if (m_poPline && nPLineType != TAB_GEOM_NONE)
    7317             :     {
    7318             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    7319           0 :         m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7320           0 :         m_poPline->ForceCoordTypeAndOrigin(
    7321             :             (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
    7322             :                              : TAB_GEOM_V450_MULTIPLINE),
    7323             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7324             :             nYMax);
    7325             :     }
    7326             : 
    7327           0 :     if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
    7328             :     {
    7329             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    7330           0 :         m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7331           0 :         m_poMpoint->ForceCoordTypeAndOrigin(
    7332             :             (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
    7333             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7334             :             nYMax);
    7335             :     }
    7336             : 
    7337           0 :     return m_nMapInfoType;
    7338             : }
    7339             : 
    7340             : /**********************************************************************
    7341             :  *                   TABCollection::ReadLabelAndMBR()
    7342             :  *
    7343             :  * Reads the label and MBR elements of the header of a collection component
    7344             :  *
    7345             :  * Returns 0 on success, -1 on failure.
    7346             :  **********************************************************************/
    7347          12 : int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
    7348             :                                    GBool bComprCoord, GInt32 nComprOrgX,
    7349             :                                    GInt32 nComprOrgY, GInt32 &pnMinX,
    7350             :                                    GInt32 &pnMinY, GInt32 &pnMaxX,
    7351             :                                    GInt32 &pnMaxY, GInt32 &pnLabelX,
    7352             :                                    GInt32 &pnLabelY)
    7353             : {
    7354             :     //
    7355             :     // The sections in the collection's coord blocks start with center/label
    7356             :     // point + MBR that are normally found in the object data blocks
    7357             :     // of regular region/pline/mulitpoint objects.
    7358             :     //
    7359             : 
    7360          12 :     if (bComprCoord)
    7361             :     {
    7362             :         // Region center/label point, relative to compr. coord. origin
    7363             :         // No it is not relative to the Object block center
    7364          12 :         pnLabelX = poCoordBlock->ReadInt16();
    7365          12 :         pnLabelY = poCoordBlock->ReadInt16();
    7366             : 
    7367          12 :         TABSaturatedAdd(pnLabelX, nComprOrgX);
    7368          12 :         TABSaturatedAdd(pnLabelY, nComprOrgY);
    7369             : 
    7370          12 :         pnMinX = poCoordBlock->ReadInt16();  // Read MBR
    7371          12 :         pnMinY = poCoordBlock->ReadInt16();
    7372          12 :         pnMaxX = poCoordBlock->ReadInt16();
    7373          12 :         pnMaxY = poCoordBlock->ReadInt16();
    7374          12 :         TABSaturatedAdd(pnMinX, nComprOrgX);
    7375          12 :         TABSaturatedAdd(pnMinY, nComprOrgY);
    7376          12 :         TABSaturatedAdd(pnMaxX, nComprOrgX);
    7377          12 :         TABSaturatedAdd(pnMaxY, nComprOrgY);
    7378             :     }
    7379             :     else
    7380             :     {
    7381             :         // Region center/label point, relative to compr. coord. origin
    7382             :         // No it is not relative to the Object block center
    7383           0 :         pnLabelX = poCoordBlock->ReadInt32();
    7384           0 :         pnLabelY = poCoordBlock->ReadInt32();
    7385             : 
    7386           0 :         pnMinX = poCoordBlock->ReadInt32();  // Read MBR
    7387           0 :         pnMinY = poCoordBlock->ReadInt32();
    7388           0 :         pnMaxX = poCoordBlock->ReadInt32();
    7389           0 :         pnMaxY = poCoordBlock->ReadInt32();
    7390             :     }
    7391             : 
    7392          12 :     return 0;
    7393             : }
    7394             : 
    7395             : /**********************************************************************
    7396             :  *                   TABCollection::WriteLabelAndMBR()
    7397             :  *
    7398             :  * Writes the label and MBR elements of the header of a collection component
    7399             :  *
    7400             :  * Returns 0 on success, -1 on failure.
    7401             :  **********************************************************************/
    7402           0 : int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
    7403             :                                     GBool bComprCoord, GInt32 nMinX,
    7404             :                                     GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
    7405             :                                     GInt32 nLabelX, GInt32 nLabelY)
    7406             : {
    7407             :     //
    7408             :     // The sections in the collection's coord blocks start with center/label
    7409             :     // point + MBR that are normally found in the object data blocks
    7410             :     // of regular region/pline/mulitpoint objects.
    7411             :     //
    7412             : 
    7413           0 :     int nStatus = 0;
    7414           0 :     if ((nStatus =
    7415           0 :              poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
    7416           0 :         (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
    7417           0 :             0 ||
    7418           0 :         (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
    7419             :     {
    7420             :         // Failed ... error message has already been produced
    7421           0 :         return nStatus;
    7422             :     }
    7423             : 
    7424           0 :     return 0;
    7425             : }
    7426             : 
    7427             : /**********************************************************************
    7428             :  *                   TABCollection::ReadGeometryFromMAPFile()
    7429             :  *
    7430             :  * Fill the geometry and representation (color, etc...) part of the
    7431             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    7432             :  *
    7433             :  * It is assumed that poMAPFile currently points to the beginning of
    7434             :  * a map object.
    7435             :  *
    7436             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    7437             :  * been called.
    7438             :  **********************************************************************/
    7439           4 : int TABCollection::ReadGeometryFromMAPFile(
    7440             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    7441             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    7442             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    7443             : {
    7444           4 :     const GBool bComprCoord = poObjHdr->IsCompressedType();
    7445             : 
    7446             :     /*-----------------------------------------------------------------
    7447             :      * Fetch and validate geometry type
    7448             :      *----------------------------------------------------------------*/
    7449           4 :     m_nMapInfoType = poObjHdr->m_nType;
    7450             : 
    7451           4 :     if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
    7452           4 :         m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
    7453           0 :         m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
    7454           0 :         m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
    7455             :     {
    7456           0 :         CPLError(
    7457             :             CE_Failure, CPLE_AssertionFailed,
    7458             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    7459           0 :             m_nMapInfoType, m_nMapInfoType);
    7460           0 :         return -1;
    7461             :     }
    7462             : 
    7463           4 :     int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    7464             : 
    7465             :     // Make sure collection is empty
    7466           4 :     EmptyCollection();
    7467             : 
    7468             :     /*-------------------------------------------------------------
    7469             :      * Copy data from poObjHdr
    7470             :      *------------------------------------------------------------*/
    7471             :     TABMAPObjCollection *poCollHdr =
    7472           4 :         cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
    7473             : 
    7474             :     // MBR
    7475           4 :     double dXMin = 0.0;
    7476           4 :     double dYMin = 0.0;
    7477           4 :     double dXMax = 0.0;
    7478           4 :     double dYMax = 0.0;
    7479           4 :     poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
    7480             :                             dYMin);
    7481           4 :     poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
    7482             :                             dYMax);
    7483             : 
    7484           4 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    7485             : 
    7486           4 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    7487             :               poObjHdr->m_nMaxY);
    7488             : 
    7489           4 :     int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
    7490           4 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    7491           4 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    7492           0 :         poCoordBlock = *ppoCoordBlock;
    7493             :     else
    7494           4 :         poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
    7495             : 
    7496             :     // Compressed coordinate origin (useful only in compressed case!)
    7497           4 :     m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7498           4 :     m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7499             : 
    7500             :     /*-----------------------------------------------------------------
    7501             :      * Region Component
    7502             :      *----------------------------------------------------------------*/
    7503           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
    7504             :     {
    7505             :         //
    7506             :         // Build fake coord section header to pass to TABRegion::ReadGeom...()
    7507             :         //
    7508           4 :         TABMAPObjPLine oRegionHdr;
    7509             : 
    7510           4 :         oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7511           4 :         oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7512             : 
    7513             :         //
    7514             :         // The region section in the coord block starts with center/label
    7515             :         // point + MBR that are normally found in the object data blocks
    7516             :         // of regular region objects.
    7517             :         //
    7518             : 
    7519             :         // In V800 the mini-header starts with a copy of num_parts
    7520           4 :         if (nVersion >= 800)
    7521             :         {
    7522             :             // int numParts = poCoordBlock->ReadInt32();
    7523           0 :             CPLAssert(poCoordBlock->ReadInt32() ==
    7524             :                       poCollHdr->m_nNumRegSections);
    7525             :         }
    7526             : 
    7527           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
    7528             :                         oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
    7529             :                         oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
    7530             :                         oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
    7531             :                         oRegionHdr.m_nLabelY);
    7532             : 
    7533             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7534           4 :         oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7535             : 
    7536           4 :         if (bComprCoord)
    7537           4 :             oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
    7538             :         else
    7539           0 :             oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
    7540           4 :         if (nVersion == 800)
    7541           0 :             oRegionHdr.m_nType = static_cast<TABGeomType>(
    7542           0 :                 oRegionHdr.m_nType +
    7543             :                 (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
    7544             : 
    7545           4 :         oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
    7546           4 :         oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
    7547           4 :         oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
    7548           4 :         oRegionHdr.m_bSmooth = 0;  // TODO
    7549             : 
    7550             :         //
    7551             :         // Use a TABRegion to read/store the Region coord data
    7552             :         //
    7553           4 :         m_poRegion = new TABRegion(GetDefnRef());
    7554           4 :         if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
    7555             :                                                 bCoordBlockDataOnly,
    7556           4 :                                                 &poCoordBlock) != 0)
    7557           0 :             return -1;
    7558             : 
    7559             :         // Set new coord block ptr for next object
    7560             :         /*if (poCoordBlock)
    7561             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7562             :     }
    7563             : 
    7564             :     /*-----------------------------------------------------------------
    7565             :      * PLine Component
    7566             :      *----------------------------------------------------------------*/
    7567           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
    7568             :     {
    7569             :         //
    7570             :         // Build fake coord section header to pass to TABPolyline::ReadGeom..()
    7571             :         //
    7572           4 :         TABMAPObjPLine oPLineHdr;
    7573             : 
    7574           4 :         oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7575           4 :         oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7576             : 
    7577             :         //
    7578             :         // The pline section in the coord block starts with center/label
    7579             :         // point + MBR that are normally found in the object data blocks
    7580             :         // of regular pline objects.
    7581             :         //
    7582             : 
    7583             :         // In V800 the mini-header starts with a copy of num_parts
    7584           4 :         if (nVersion >= 800)
    7585             :         {
    7586             :             // int numParts = poCoordBlock->ReadInt32();
    7587           0 :             CPLAssert(poCoordBlock->ReadInt32() ==
    7588             :                       poCollHdr->m_nNumPLineSections);
    7589             :         }
    7590             : 
    7591           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
    7592             :                         oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
    7593             :                         oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
    7594             :                         oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
    7595             : 
    7596             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7597           4 :         oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7598             : 
    7599           4 :         if (bComprCoord)
    7600           4 :             oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
    7601             :         else
    7602           0 :             oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
    7603           4 :         if (nVersion == 800)
    7604           0 :             oPLineHdr.m_nType = static_cast<TABGeomType>(
    7605           0 :                 oPLineHdr.m_nType +
    7606             :                 (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
    7607             : 
    7608           4 :         oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
    7609           4 :         oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
    7610           4 :         oPLineHdr.m_bSmooth = 0;  // TODO
    7611             : 
    7612             :         //
    7613             :         // Use a TABPolyline to read/store the Polyline coord data
    7614             :         //
    7615           4 :         m_poPline = new TABPolyline(GetDefnRef());
    7616           4 :         if (m_poPline->ReadGeometryFromMAPFile(
    7617           4 :                 poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
    7618           0 :             return -1;
    7619             : 
    7620             :         // Set new coord block ptr for next object
    7621             :         /*if (poCoordBlock)
    7622             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7623             :     }
    7624             : 
    7625             :     /*-----------------------------------------------------------------
    7626             :      * MultiPoint Component
    7627             :      *----------------------------------------------------------------*/
    7628           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
    7629             :     {
    7630             :         //
    7631             :         // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
    7632             :         //
    7633           4 :         TABMAPObjMultiPoint oMPointHdr;
    7634             : 
    7635           4 :         oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7636           4 :         oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7637             : 
    7638             :         //
    7639             :         // The pline section in the coord block starts with center/label
    7640             :         // point + MBR that are normally found in the object data blocks
    7641             :         // of regular pline objects.
    7642             :         //
    7643           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
    7644             :                         oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
    7645             :                         oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
    7646             :                         oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
    7647             :                         oMPointHdr.m_nLabelY);
    7648             : 
    7649             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7650           4 :         oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7651             : 
    7652           4 :         if (bComprCoord)
    7653           4 :             oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
    7654             :         else
    7655           0 :             oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
    7656           4 :         if (nVersion == 800)
    7657           0 :             oMPointHdr.m_nType = static_cast<TABGeomType>(
    7658           0 :                 oMPointHdr.m_nType +
    7659             :                 (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
    7660             : 
    7661           4 :         oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
    7662           4 :         oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
    7663             : 
    7664             :         //
    7665             :         // Use a TABMultiPoint to read/store the coord data
    7666             :         //
    7667           4 :         m_poMpoint = new TABMultiPoint(GetDefnRef());
    7668           4 :         if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
    7669             :                                                 bCoordBlockDataOnly,
    7670           4 :                                                 &poCoordBlock) != 0)
    7671           0 :             return -1;
    7672             : 
    7673             :         // Set new coord block ptr for next object (not really useful here)
    7674             :         /*if (poCoordBlock)
    7675             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7676             :     }
    7677             : 
    7678             :     /*-----------------------------------------------------------------
    7679             :      * Set the main OGRFeature Geometry
    7680             :      * (this is actually duplicating geometries from each member)
    7681             :      *----------------------------------------------------------------*/
    7682           4 :     if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
    7683           0 :         return -1;
    7684             : 
    7685             :     /* Return a ref to coord block so that caller can continue reading
    7686             :      * after the end of this object (used by index splitting)
    7687             :      */
    7688           4 :     if (ppoCoordBlock)
    7689           0 :         *ppoCoordBlock = poCoordBlock;
    7690             : 
    7691           4 :     return 0;
    7692             : }
    7693             : 
    7694             : /**********************************************************************
    7695             :  *                   TABCollection::WriteGeometryToMAPFile()
    7696             :  *
    7697             :  * Write the geometry and representation (color, etc...) part of the
    7698             :  * feature to the .MAP object pointed to by poMAPFile.
    7699             :  *
    7700             :  * It is assumed that poMAPFile currently points to a valid map object.
    7701             :  *
    7702             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    7703             :  * been called.
    7704             :  **********************************************************************/
    7705           0 : int TABCollection::WriteGeometryToMAPFile(
    7706             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    7707             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    7708             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    7709             : {
    7710             :     /*-----------------------------------------------------------------
    7711             :      * Note that the current implementation does not allow setting the
    7712             :      * Geometry via OGRFeature::SetGeometry(). The geometries must be set
    7713             :      * via the SetRegion/Pline/MpointDirectly() methods which will take
    7714             :      * care of keeping the OGRFeature's geometry in sync.
    7715             :      *
    7716             :      * TODO: If we ever want to support sync'ing changes from the OGRFeature's
    7717             :      * geometry to the m_poRegion/Pline/Mpoint then a call should be added
    7718             :      * here, or perhaps in ValidateMapInfoType(), or even better in
    7719             :      * custom TABCollection::SetGeometry*()... but then this last option
    7720             :      * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
    7721             :      *----------------------------------------------------------------*/
    7722             : 
    7723             :     /*-----------------------------------------------------------------
    7724             :      * We assume that ValidateMapInfoType() was called already and that
    7725             :      * the type in poObjHdr->m_nType is valid.
    7726             :      *----------------------------------------------------------------*/
    7727           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    7728             : 
    7729             :     TABMAPObjCollection *poCollHdr =
    7730           0 :         cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
    7731             : 
    7732             :     /*-----------------------------------------------------------------
    7733             :      * Write data to coordinate block for each component...
    7734             :      *
    7735             :      * Note that at this point, the caller (TABFile) has called
    7736             :      * TABCollection::ValidateMapInfoType() which in turn has called
    7737             :      * each component's respective ValidateMapInfoType() and
    7738             :      * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
    7739             :      * their respective WriteGeometryToMapFile() called.
    7740             :      *----------------------------------------------------------------*/
    7741           0 :     const GBool bCompressed = poObjHdr->IsCompressedType();
    7742             :     // TODO: ??? Do we need to track overall collection coord data size???
    7743           0 :     int nTotalFeatureDataSize = 0;
    7744             : 
    7745           0 :     const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    7746             : 
    7747           0 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    7748           0 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    7749           0 :         poCoordBlock = *ppoCoordBlock;
    7750             :     else
    7751           0 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    7752           0 :     poCoordBlock->StartNewFeature();
    7753           0 :     poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7754           0 :     poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    7755             : 
    7756             :     /*-----------------------------------------------------------------
    7757             :      * Region component
    7758             :      *----------------------------------------------------------------*/
    7759           0 :     if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
    7760             :     {
    7761           0 :         CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
    7762             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
    7763             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
    7764             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
    7765             : 
    7766           0 :         TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
    7767           0 :             TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
    7768             : 
    7769             :         // Update count of objects by type in header
    7770           0 :         if (!bCoordBlockDataOnly)
    7771           0 :             poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
    7772             : 
    7773             :         // Write a placeholder for centroid/label point and MBR mini-header
    7774             :         // and we'll come back later to write the real values.
    7775             :         //
    7776             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7777             :         // StartNewFeature() as well, so we need to track the current
    7778             :         // value before calling it
    7779             : 
    7780           0 :         poCoordBlock->StartNewFeature();
    7781           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7782             : 
    7783             :         // In V800 the mini-header starts with a copy of num_parts
    7784           0 :         if (nVersion >= 800)
    7785             :         {
    7786           0 :             poCoordBlock->WriteInt32(0);
    7787             :         }
    7788           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7789           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7790             : 
    7791           0 :         if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
    7792             :                                                bCoordBlockDataOnly,
    7793           0 :                                                &poCoordBlock) != 0)
    7794             :         {
    7795           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7796             :                      "Failed writing Region part in collection.");
    7797           0 :             delete poRegionHdr;
    7798           0 :             return -1;
    7799             :         }
    7800             : 
    7801           0 :         nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
    7802             : 
    7803             :         // Come back to write the real values in the mini-header
    7804           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7805           0 :         poCoordBlock->StartNewFeature();
    7806             : 
    7807           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7808             :         {
    7809           0 :             delete poRegionHdr;
    7810           0 :             return -1;
    7811             :         }
    7812             : 
    7813             :         // In V800 the mini-header starts with a copy of num_parts
    7814           0 :         if (nVersion >= 800)
    7815             :         {
    7816           0 :             poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
    7817             :         }
    7818           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
    7819             :                          poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
    7820             :                          poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
    7821             :                          poRegionHdr->m_nLabelY);
    7822             : 
    7823             :         // And finally move the pointer back to the end of this component
    7824           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    7825             :         {
    7826           0 :             delete poRegionHdr;
    7827           0 :             return -1;
    7828             :         }
    7829             : 
    7830             :         // Copy other header members to the main collection header
    7831             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    7832             :         //       mini-header???
    7833           0 :         poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
    7834           0 :         poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
    7835             : 
    7836           0 :         if (!bCoordBlockDataOnly)
    7837             :         {
    7838           0 :             poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
    7839           0 :             poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
    7840             :             // TODO: Smooth flag         = poRegionHdr->m_bSmooth;
    7841             :         }
    7842             : 
    7843           0 :         delete poRegionHdr;
    7844             :     }
    7845             :     else
    7846             :     {
    7847             :         // No Region component. Set corresponding header fields to 0
    7848             : 
    7849           0 :         poCollHdr->m_nRegionDataSize = 0;
    7850           0 :         poCollHdr->m_nNumRegSections = 0;
    7851           0 :         poCollHdr->m_nRegionPenId = 0;
    7852           0 :         poCollHdr->m_nRegionBrushId = 0;
    7853             :     }
    7854             : 
    7855             :     /*-----------------------------------------------------------------
    7856             :      * PLine component
    7857             :      *----------------------------------------------------------------*/
    7858           0 :     if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
    7859             :     {
    7860           0 :         CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
    7861             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
    7862             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
    7863             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
    7864             : 
    7865           0 :         TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
    7866           0 :             TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
    7867             : 
    7868             :         // Update count of objects by type in header
    7869           0 :         if (!bCoordBlockDataOnly)
    7870           0 :             poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
    7871             : 
    7872             :         // Write a placeholder for centroid/label point and MBR mini-header
    7873             :         // and we'll come back later to write the real values.
    7874             :         //
    7875             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7876             :         // StartNewFeature() as well, so we need to track the current
    7877             :         // value before calling it
    7878             : 
    7879           0 :         poCoordBlock->StartNewFeature();
    7880           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7881             : 
    7882             :         // In V800 the mini-header starts with a copy of num_parts
    7883           0 :         if (nVersion >= 800)
    7884             :         {
    7885           0 :             poCoordBlock->WriteInt32(0);
    7886             :         }
    7887           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7888           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7889             : 
    7890           0 :         if (m_poPline->WriteGeometryToMAPFile(
    7891           0 :                 poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
    7892             :         {
    7893           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7894             :                      "Failed writing Region part in collection.");
    7895           0 :             delete poPlineHdr;
    7896           0 :             return -1;
    7897             :         }
    7898             : 
    7899           0 :         nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
    7900             : 
    7901             :         // Come back to write the real values in the mini-header
    7902           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7903           0 :         poCoordBlock->StartNewFeature();
    7904             : 
    7905           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7906             :         {
    7907           0 :             delete poPlineHdr;
    7908           0 :             return -1;
    7909             :         }
    7910             : 
    7911             :         // In V800 the mini-header starts with a copy of num_parts
    7912           0 :         if (nVersion >= 800)
    7913             :         {
    7914           0 :             poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
    7915             :         }
    7916           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
    7917             :                          poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
    7918             :                          poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
    7919             :                          poPlineHdr->m_nLabelY);
    7920             : 
    7921             :         // And finally move the pointer back to the end of this component
    7922           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    7923             :         {
    7924           0 :             delete poPlineHdr;
    7925           0 :             return -1;
    7926             :         }
    7927             : 
    7928             :         // Copy other header members to the main collection header
    7929             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    7930             :         //       mini-header???
    7931           0 :         poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
    7932           0 :         poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
    7933           0 :         if (!bCoordBlockDataOnly)
    7934             :         {
    7935           0 :             poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
    7936             :             // TODO: Smooth flag           = poPlineHdr->m_bSmooth;
    7937             :         }
    7938             : 
    7939           0 :         delete poPlineHdr;
    7940             :     }
    7941             :     else
    7942             :     {
    7943             :         // No Polyline component. Set corresponding header fields to 0
    7944             : 
    7945           0 :         poCollHdr->m_nPolylineDataSize = 0;
    7946           0 :         poCollHdr->m_nNumPLineSections = 0;
    7947           0 :         poCollHdr->m_nPolylinePenId = 0;
    7948             :     }
    7949             : 
    7950             :     /*-----------------------------------------------------------------
    7951             :      * MultiPoint component
    7952             :      *----------------------------------------------------------------*/
    7953           0 :     if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
    7954             :     {
    7955           0 :         CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
    7956             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
    7957             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
    7958             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
    7959             : 
    7960             :         TABMAPObjMultiPoint *poMpointHdr =
    7961           0 :             cpl::down_cast<TABMAPObjMultiPoint *>(
    7962           0 :                 TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
    7963             : 
    7964             :         // Update count of objects by type in header
    7965           0 :         if (!bCoordBlockDataOnly)
    7966           0 :             poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
    7967             : 
    7968             :         // Write a placeholder for centroid/label point and MBR mini-header
    7969             :         // and we'll come back later to write the real values.
    7970             :         //
    7971             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7972             :         // StartNewFeature() as well, so we need to track the current
    7973             :         // value before calling it
    7974             : 
    7975           0 :         poCoordBlock->StartNewFeature();
    7976           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7977             : 
    7978           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7979           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7980             : 
    7981           0 :         if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
    7982             :                                                bCoordBlockDataOnly,
    7983           0 :                                                &poCoordBlock) != 0)
    7984             :         {
    7985           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7986             :                      "Failed writing Region part in collection.");
    7987           0 :             delete poMpointHdr;
    7988           0 :             return -1;
    7989             :         }
    7990             : 
    7991           0 :         nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
    7992             : 
    7993             :         // Come back to write the real values in the mini-header
    7994           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7995           0 :         poCoordBlock->StartNewFeature();
    7996             : 
    7997           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7998             :         {
    7999           0 :             delete poMpointHdr;
    8000           0 :             return -1;
    8001             :         }
    8002             : 
    8003           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
    8004             :                          poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
    8005             :                          poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
    8006             :                          poMpointHdr->m_nLabelY);
    8007             : 
    8008             :         // And finally move the pointer back to the end of this component
    8009           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    8010             :         {
    8011           0 :             delete poMpointHdr;
    8012           0 :             return -1;
    8013             :         }
    8014             : 
    8015             :         // Copy other header members to the main collection header
    8016             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    8017             :         //       mini-header???
    8018           0 :         poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
    8019           0 :         poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
    8020           0 :         if (!bCoordBlockDataOnly)
    8021             :         {
    8022           0 :             poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
    8023             :         }
    8024             : 
    8025           0 :         delete poMpointHdr;
    8026             :     }
    8027             :     else
    8028             :     {
    8029             :         // No Multipoint component. Set corresponding header fields to 0
    8030             : 
    8031           0 :         poCollHdr->m_nMPointDataSize = 0;
    8032           0 :         poCollHdr->m_nNumMultiPoints = 0;
    8033           0 :         poCollHdr->m_nMultiPointSymbolId = 0;
    8034             :     }
    8035             : 
    8036             :     /*-----------------------------------------------------------------
    8037             :      * Copy object information
    8038             :      *----------------------------------------------------------------*/
    8039             : 
    8040             :     // Compressed coordinate origin (useful only in compressed case!)
    8041           0 :     poCollHdr->m_nComprOrgX = m_nComprOrgX;
    8042           0 :     poCollHdr->m_nComprOrgY = m_nComprOrgY;
    8043             : 
    8044           0 :     poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
    8045             : 
    8046           0 :     poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    8047             : 
    8048           0 :     if (CPLGetLastErrorType() == CE_Failure)
    8049           0 :         return -1;
    8050             : 
    8051             :     /* Return a ref to coord block so that caller can continue writing
    8052             :      * after the end of this object (used by index splitting)
    8053             :      */
    8054           0 :     if (ppoCoordBlock)
    8055           0 :         *ppoCoordBlock = poCoordBlock;
    8056             : 
    8057           0 :     return 0;
    8058             : }
    8059             : 
    8060             : /**********************************************************************
    8061             :  *                   TABCollection::SyncOGRGeometryCollection()
    8062             :  *
    8063             :  * Copy the region/pline/multipoint's geometries to the OGRFeature's
    8064             :  * geometry.
    8065             :  **********************************************************************/
    8066         208 : int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
    8067             :                                              GBool bSyncPline,
    8068             :                                              GBool bSyncMpoint)
    8069             : {
    8070         208 :     OGRGeometry *poThisGeom = GetGeometryRef();
    8071         208 :     OGRGeometryCollection *poGeomColl = nullptr;
    8072             : 
    8073             :     // poGeometry is defined in the OGRFeature class
    8074         208 :     if (poThisGeom == nullptr)
    8075             :     {
    8076         103 :         poGeomColl = new OGRGeometryCollection();
    8077             :     }
    8078         105 :     else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
    8079             :     {
    8080         105 :         poGeomColl = poThisGeom->toGeometryCollection();
    8081             :     }
    8082             :     else
    8083             :     {
    8084           0 :         CPLError(
    8085             :             CE_Failure, CPLE_AssertionFailed,
    8086             :             "TABCollection: Invalid Geometry. Type must be OGRCollection.");
    8087           0 :         return -1;
    8088             :     }
    8089             : 
    8090             :     /*-----------------------------------------------------------------
    8091             :      * Start by removing geometries that need to be replaced
    8092             :      * In theory there should be a single geometry of each type, but
    8093             :      * just in case, we'll loop over the whole collection and delete all
    8094             :      * instances of each type if there are some.
    8095             :      *----------------------------------------------------------------*/
    8096         208 :     int numGeometries = poGeomColl->getNumGeometries();
    8097         220 :     for (int i = 0; i < numGeometries; i++)
    8098             :     {
    8099          12 :         OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
    8100          12 :         if (!poGeom)
    8101           0 :             continue;
    8102             : 
    8103          24 :         if ((bSyncRegion &&
    8104          12 :              (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    8105           6 :               wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
    8106           6 :             (bSyncPline &&
    8107           6 :              (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
    8108          30 :               wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
    8109           6 :             (bSyncMpoint &&
    8110           6 :              (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
    8111             :         {
    8112             :             // Remove this geometry
    8113          12 :             poGeomColl->removeGeometry(i);
    8114             : 
    8115             :             // Unless this was the last geometry, we need to restart
    8116             :             // scanning the collection since we modified it
    8117          12 :             if (i != numGeometries - 1)
    8118             :             {
    8119           6 :                 i = 0;
    8120           6 :                 numGeometries = poGeomColl->getNumGeometries();
    8121             :             }
    8122             :         }
    8123             :     }
    8124             : 
    8125             :     /*-----------------------------------------------------------------
    8126             :      * Copy TAB Feature geometries to OGRGeometryCollection
    8127             :      *----------------------------------------------------------------*/
    8128         208 :     if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
    8129           4 :         poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
    8130             : 
    8131         208 :     if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
    8132           4 :         poGeomColl->addGeometry(m_poPline->GetGeometryRef());
    8133             : 
    8134         208 :     if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
    8135           4 :         poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
    8136             : 
    8137         208 :     if (poThisGeom == nullptr)
    8138         103 :         SetGeometryDirectly(poGeomColl);
    8139             : 
    8140         208 :     return 0;
    8141             : }
    8142             : 
    8143             : /**********************************************************************
    8144             :  *                   TABCollection::SetRegionDirectly()
    8145             :  *
    8146             :  * Set the region component of the collection, deleting the current
    8147             :  * region component if there is one. The object is then owned by the
    8148             :  * TABCollection object. Passing NULL just deletes it.
    8149             :  *
    8150             :  * Note that an intentional side-effect is that calling this method
    8151             :  * with the same poRegion pointer that is already owned by this object
    8152             :  * will force resync'ing the OGR Geometry member.
    8153             :  **********************************************************************/
    8154           0 : int TABCollection::SetRegionDirectly(TABRegion *poRegion)
    8155             : {
    8156           0 :     if (m_poRegion && m_poRegion != poRegion)
    8157           0 :         delete m_poRegion;
    8158           0 :     m_poRegion = poRegion;
    8159             : 
    8160             :     // Update OGRGeometryCollection component as well
    8161           0 :     return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
    8162             : }
    8163             : 
    8164             : /**********************************************************************
    8165             :  *                   TABCollection::SetPolylineDirectly()
    8166             :  *
    8167             :  * Set the polyline component of the collection, deleting the current
    8168             :  * polyline component if there is one. The object is then owned by the
    8169             :  * TABCollection object. Passing NULL just deletes it.
    8170             :  *
    8171             :  * Note that an intentional side-effect is that calling this method
    8172             :  * with the same poPline pointer that is already owned by this object
    8173             :  * will force resync'ing the OGR Geometry member.
    8174             :  **********************************************************************/
    8175           0 : int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
    8176             : {
    8177           0 :     if (m_poPline && m_poPline != poPline)
    8178           0 :         delete m_poPline;
    8179           0 :     m_poPline = poPline;
    8180             : 
    8181             :     // Update OGRGeometryCollection component as well
    8182           0 :     return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
    8183             : }
    8184             : 
    8185             : /**********************************************************************
    8186             :  *                   TABCollection::SetMultiPointDirectly()
    8187             :  *
    8188             :  * Set the multipoint component of the collection, deleting the current
    8189             :  * multipoint component if there is one. The object is then owned by the
    8190             :  * TABCollection object. Passing NULL just deletes it.
    8191             :  *
    8192             :  * Note that an intentional side-effect is that calling this method
    8193             :  * with the same poMpoint pointer that is already owned by this object
    8194             :  * will force resync'ing the OGR Geometry member.
    8195             :  **********************************************************************/
    8196           0 : int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
    8197             : {
    8198           0 :     if (m_poMpoint && m_poMpoint != poMpoint)
    8199           0 :         delete m_poMpoint;
    8200           0 :     m_poMpoint = poMpoint;
    8201             : 
    8202             :     // Update OGRGeometryCollection component as well
    8203           0 :     return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
    8204             : }
    8205             : 
    8206             : /**********************************************************************
    8207             :  *                   TABCollection::GetStyleString() const
    8208             :  *
    8209             :  * Return style string for this feature.
    8210             :  *
    8211             :  * Style String is built only once during the first call to GetStyleString().
    8212             :  **********************************************************************/
    8213           3 : const char *TABCollection::GetStyleString() const
    8214             : {
    8215           3 :     if (m_pszStyleString == nullptr)
    8216             :     {
    8217           3 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    8218             :     }
    8219             : 
    8220           3 :     return m_pszStyleString;
    8221             : }
    8222             : 
    8223             : /**********************************************************************
    8224             :  *                   TABCollection::DumpMIF()
    8225             :  *
    8226             :  * Dump feature geometry
    8227             :  **********************************************************************/
    8228           0 : void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
    8229             : {
    8230           0 :     if (fpOut == nullptr)
    8231           0 :         fpOut = stdout;
    8232             : 
    8233             :     /*-----------------------------------------------------------------
    8234             :      * Generate output
    8235             :      *----------------------------------------------------------------*/
    8236           0 :     int numParts = 0;
    8237           0 :     if (m_poRegion)
    8238           0 :         numParts++;
    8239           0 :     if (m_poPline)
    8240           0 :         numParts++;
    8241           0 :     if (m_poMpoint)
    8242           0 :         numParts++;
    8243             : 
    8244           0 :     fprintf(fpOut, "COLLECTION %d\n", numParts);
    8245             : 
    8246           0 :     if (m_poRegion)
    8247           0 :         m_poRegion->DumpMIF(fpOut);
    8248             : 
    8249           0 :     if (m_poPline)
    8250           0 :         m_poPline->DumpMIF(fpOut);
    8251             : 
    8252           0 :     if (m_poMpoint)
    8253           0 :         m_poMpoint->DumpMIF(fpOut);
    8254             : 
    8255           0 :     DumpSymbolDef(fpOut);
    8256             : 
    8257           0 :     fflush(fpOut);
    8258           0 : }
    8259             : 
    8260             : /*=====================================================================
    8261             :  *                      class TABDebugFeature
    8262             :  *====================================================================*/
    8263             : 
    8264             : /**********************************************************************
    8265             :  *                   TABDebugFeature::TABDebugFeature()
    8266             :  *
    8267             :  * Constructor.
    8268             :  **********************************************************************/
    8269           0 : TABDebugFeature::TABDebugFeature(OGRFeatureDefn *poDefnIn)
    8270           0 :     : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
    8271             : {
    8272           0 :     memset(m_abyBuf, 0, sizeof(m_abyBuf));
    8273           0 : }
    8274             : 
    8275             : /**********************************************************************
    8276             :  *                   TABDebugFeature::~TABDebugFeature()
    8277             :  *
    8278             :  * Destructor.
    8279             :  **********************************************************************/
    8280           0 : TABDebugFeature::~TABDebugFeature()
    8281             : {
    8282           0 : }
    8283             : 
    8284             : /**********************************************************************
    8285             :  *                   TABDebugFeature::ReadGeometryFromMAPFile()
    8286             :  *
    8287             :  * Fill the geometry and representation (color, etc...) part of the
    8288             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    8289             :  *
    8290             :  * It is assumed that poMAPFile currently points to the beginning of
    8291             :  * a map object.
    8292             :  *
    8293             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    8294             :  * been called.
    8295             :  **********************************************************************/
    8296           0 : int TABDebugFeature::ReadGeometryFromMAPFile(
    8297             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    8298             :     GBool /*bCoordBlockDataOnly=FALSE*/,
    8299             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    8300             : {
    8301             :     /*-----------------------------------------------------------------
    8302             :      * Fetch geometry type
    8303             :      *----------------------------------------------------------------*/
    8304           0 :     m_nMapInfoType = poObjHdr->m_nType;
    8305             : 
    8306           0 :     TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
    8307           0 :     TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
    8308             : 
    8309             :     /*-----------------------------------------------------------------
    8310             :      * If object type has coords in a type 3 block, then its position
    8311             :      * follows
    8312             :      *----------------------------------------------------------------*/
    8313           0 :     if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
    8314             :     {
    8315           0 :         m_nCoordDataPtr = poObjBlock->ReadInt32();
    8316           0 :         m_nCoordDataSize = poObjBlock->ReadInt32();
    8317             :     }
    8318             :     else
    8319             :     {
    8320           0 :         m_nCoordDataPtr = -1;
    8321           0 :         m_nCoordDataSize = 0;
    8322             :     }
    8323             : 
    8324           0 :     m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
    8325           0 :     if (m_nSize > 0)
    8326             :     {
    8327           0 :         poObjBlock->GotoByteRel(-5);  // Go back to beginning of header
    8328           0 :         poObjBlock->ReadBytes(
    8329           0 :             std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
    8330             :     }
    8331             : 
    8332           0 :     return 0;
    8333             : }
    8334             : 
    8335             : /**********************************************************************
    8336             :  *                   TABDebugFeature::WriteGeometryToMAPFile()
    8337             :  *
    8338             :  * Write the geometry and representation (color, etc...) part of the
    8339             :  * feature to the .MAP object pointed to by poMAPFile.
    8340             :  *
    8341             :  * It is assumed that poMAPFile currently points to a valid map object.
    8342             :  *
    8343             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    8344             :  * been called.
    8345             :  **********************************************************************/
    8346           0 : int TABDebugFeature::WriteGeometryToMAPFile(
    8347             :     TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
    8348             :     GBool /*bCoordBlockDataOnly=FALSE*/,
    8349             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    8350             : {
    8351             :     // Nothing to do here!
    8352             : 
    8353           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    8354             :              "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
    8355             : 
    8356           0 :     return -1;
    8357             : }
    8358             : 
    8359             : /**********************************************************************
    8360             :  *                   TABDebugFeature::DumpMIF()
    8361             :  *
    8362             :  * Dump feature contents... available only in DEBUG mode.
    8363             :  **********************************************************************/
    8364           0 : void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
    8365             : {
    8366           0 :     if (fpOut == nullptr)
    8367           0 :         fpOut = stdout;
    8368             : 
    8369           0 :     fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
    8370           0 :             GetMapInfoType());
    8371           0 :     fprintf(fpOut, "  Object size: %d bytes\n", m_nSize);
    8372           0 :     fprintf(fpOut, "  m_nCoordDataPtr  = %d\n", m_nCoordDataPtr);
    8373           0 :     fprintf(fpOut, "  m_nCoordDataSize = %d\n", m_nCoordDataSize);
    8374           0 :     fprintf(fpOut, "  ");
    8375             : 
    8376           0 :     for (int i = 0; i < m_nSize; i++)
    8377           0 :         fprintf(fpOut, " %2.2x", m_abyBuf[i]);
    8378             : 
    8379           0 :     fprintf(fpOut, "  \n");
    8380             : 
    8381           0 :     fflush(fpOut);
    8382           0 : }
    8383             : 
    8384             : /*=====================================================================
    8385             :  *                      class ITABFeaturePen
    8386             :  *====================================================================*/
    8387             : 
    8388             : /**********************************************************************
    8389             :  *                   ITABFeaturePen::ITABFeaturePen()
    8390             :  **********************************************************************/
    8391             : 
    8392             : // MI default is PEN(1, 2, 0)
    8393             : static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
    8394             : 
    8395        7760 : ITABFeaturePen::ITABFeaturePen()
    8396        7760 :     : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
    8397             : {
    8398        7760 : }
    8399             : 
    8400             : /**********************************************************************
    8401             :  *                   ITABFeaturePen::GetPenWidthPixel()
    8402             :  *                   ITABFeaturePen::SetPenWidthPixel()
    8403             :  *                   ITABFeaturePen::GetPenWidthPoint()
    8404             :  *                   ITABFeaturePen::SetPenWidthPoint()
    8405             :  *
    8406             :  * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
    8407             :  * in points (value from 0.1 to 203.7 points). The default pen width
    8408             :  * in MapInfo is 1 pixel.  Pen width in points exist only in file version 450.
    8409             :  *
    8410             :  * The following methods hide the way the pen width is stored in the files.
    8411             :  *
    8412             :  * In order to establish if a given pen def had its width specified in
    8413             :  * pixels or in points, one should first call GetPenWidthPoint(), and if
    8414             :  * it returns 0 then the Pixel width should be used instead:
    8415             :  *    if (GetPenWidthPoint() == 0)
    8416             :  *       ... use pen width in points ...
    8417             :  *    else
    8418             :  *       ... use Pixel width from GetPenWidthPixel()
    8419             :  *
    8420             :  * Note that the reverse is not true: the default pixel width is always 1,
    8421             :  * even when the pen width was actually set in points.
    8422             :  **********************************************************************/
    8423             : 
    8424          99 : GByte ITABFeaturePen::GetPenWidthPixel() const
    8425             : {
    8426          99 :     return m_sPenDef.nPixelWidth;
    8427             : }
    8428             : 
    8429          29 : void ITABFeaturePen::SetPenWidthPixel(GByte val)
    8430             : {
    8431          29 :     const GByte nPixelWidthMin = 1;
    8432          29 :     const GByte nPixelWidthMax = 7;
    8433          29 :     m_sPenDef.nPixelWidth =
    8434          29 :         std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
    8435          29 :     m_sPenDef.nPointWidth = 0;
    8436          29 : }
    8437             : 
    8438           0 : double ITABFeaturePen::GetPenWidthPoint() const
    8439             : {
    8440             :     // We store point width internally as tenths of points
    8441           0 :     return m_sPenDef.nPointWidth / 10.0;
    8442             : }
    8443             : 
    8444           0 : void ITABFeaturePen::SetPenWidthPoint(double val)
    8445             : {
    8446           0 :     m_sPenDef.nPointWidth =
    8447           0 :         std::min(std::max(static_cast<int>(val * 10), 1), 2037);
    8448           0 :     m_sPenDef.nPixelWidth = 1;
    8449           0 : }
    8450             : 
    8451             : /**********************************************************************
    8452             :  *                   ITABFeaturePen::GetPenWidthMIF()
    8453             :  *                   ITABFeaturePen::SetPenWidthMIF()
    8454             :  *
    8455             :  * The MIF representation for pen width is either a value from 1 to 7
    8456             :  * for a pen width in pixels, or a value from 11 to 2047 for a pen
    8457             :  * width in points = 10 + (point_width*10)
    8458             :  **********************************************************************/
    8459          21 : int ITABFeaturePen::GetPenWidthMIF() const
    8460             : {
    8461          21 :     return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
    8462          21 :                                       : m_sPenDef.nPixelWidth);
    8463             : }
    8464             : 
    8465        2423 : void ITABFeaturePen::SetPenWidthMIF(int val)
    8466             : {
    8467        2423 :     if (val > 10)
    8468             :     {
    8469           0 :         m_sPenDef.nPointWidth = std::min((val - 10), 2037);
    8470           0 :         m_sPenDef.nPixelWidth = 0;
    8471             :     }
    8472             :     else
    8473             :     {
    8474        2423 :         m_sPenDef.nPixelWidth =
    8475        2423 :             static_cast<GByte>(std::min(std::max(val, 1), 7));
    8476        2423 :         m_sPenDef.nPointWidth = 0;
    8477             :     }
    8478        2423 : }
    8479             : 
    8480             : /**********************************************************************
    8481             :  *                   ITABFeaturePen::GetPenStyleString()
    8482             :  *
    8483             :  *  Return a PEN() string. All representations info for the pen are here.
    8484             :  **********************************************************************/
    8485          99 : const char *ITABFeaturePen::GetPenStyleString() const
    8486             : {
    8487          99 :     const char *pszStyle = nullptr;
    8488          99 :     int nOGRStyle = 0;
    8489             :     char szPattern[20];
    8490             : 
    8491          99 :     szPattern[0] = '\0';
    8492             : 
    8493             :     // For now, I only add the 25 first styles
    8494          99 :     switch (GetPenPattern())
    8495             :     {
    8496           0 :         case 1:
    8497           0 :             nOGRStyle = 1;
    8498           0 :             break;
    8499          99 :         case 2:
    8500          99 :             nOGRStyle = 0;
    8501          99 :             break;
    8502           0 :         case 3:
    8503           0 :             nOGRStyle = 3;
    8504           0 :             strcpy(szPattern, "1 1");
    8505           0 :             break;
    8506           0 :         case 4:
    8507           0 :             nOGRStyle = 3;
    8508           0 :             strcpy(szPattern, "2 1");
    8509           0 :             break;
    8510           0 :         case 5:
    8511           0 :             nOGRStyle = 3;
    8512           0 :             strcpy(szPattern, "3 1");
    8513           0 :             break;
    8514           0 :         case 6:
    8515           0 :             nOGRStyle = 3;
    8516           0 :             strcpy(szPattern, "6 1");
    8517           0 :             break;
    8518           0 :         case 7:
    8519           0 :             nOGRStyle = 4;
    8520           0 :             strcpy(szPattern, "12 2");
    8521           0 :             break;
    8522           0 :         case 8:
    8523           0 :             nOGRStyle = 4;
    8524           0 :             strcpy(szPattern, "24 4");
    8525           0 :             break;
    8526           0 :         case 9:
    8527           0 :             nOGRStyle = 3;
    8528           0 :             strcpy(szPattern, "4 3");
    8529           0 :             break;
    8530           0 :         case 10:
    8531           0 :             nOGRStyle = 5;
    8532           0 :             strcpy(szPattern, "1 4");
    8533           0 :             break;
    8534           0 :         case 11:
    8535           0 :             nOGRStyle = 3;
    8536           0 :             strcpy(szPattern, "4 6");
    8537           0 :             break;
    8538           0 :         case 12:
    8539           0 :             nOGRStyle = 3;
    8540           0 :             strcpy(szPattern, "6 4");
    8541           0 :             break;
    8542           0 :         case 13:
    8543           0 :             nOGRStyle = 4;
    8544           0 :             strcpy(szPattern, "12 12");
    8545           0 :             break;
    8546           0 :         case 14:
    8547           0 :             nOGRStyle = 6;
    8548           0 :             strcpy(szPattern, "8 2 1 2");
    8549           0 :             break;
    8550           0 :         case 15:
    8551           0 :             nOGRStyle = 6;
    8552           0 :             strcpy(szPattern, "12 1 1 1");
    8553           0 :             break;
    8554           0 :         case 16:
    8555           0 :             nOGRStyle = 6;
    8556           0 :             strcpy(szPattern, "12 1 3 1");
    8557           0 :             break;
    8558           0 :         case 17:
    8559           0 :             nOGRStyle = 6;
    8560           0 :             strcpy(szPattern, "24 6 4 6");
    8561           0 :             break;
    8562           0 :         case 18:
    8563           0 :             nOGRStyle = 7;
    8564           0 :             strcpy(szPattern, "24 3 3 3 3 3");
    8565           0 :             break;
    8566           0 :         case 19:
    8567           0 :             nOGRStyle = 7;
    8568           0 :             strcpy(szPattern, "24 3 3 3 3 3 3 3");
    8569           0 :             break;
    8570           0 :         case 20:
    8571           0 :             nOGRStyle = 7;
    8572           0 :             strcpy(szPattern, "6 3 1 3 1 3");
    8573           0 :             break;
    8574           0 :         case 21:
    8575           0 :             nOGRStyle = 7;
    8576           0 :             strcpy(szPattern, "12 2 1 2 1 2");
    8577           0 :             break;
    8578           0 :         case 22:
    8579           0 :             nOGRStyle = 7;
    8580           0 :             strcpy(szPattern, "12 2 1 2 1 2 1 2");
    8581           0 :             break;
    8582           0 :         case 23:
    8583           0 :             nOGRStyle = 6;
    8584           0 :             strcpy(szPattern, "4 1 1 1");
    8585           0 :             break;
    8586           0 :         case 24:
    8587           0 :             nOGRStyle = 7;
    8588           0 :             strcpy(szPattern, "4 1 1 1 1");
    8589           0 :             break;
    8590           0 :         case 25:
    8591           0 :             nOGRStyle = 6;
    8592           0 :             strcpy(szPattern, "4 1 1 1 2 1 1 1");
    8593           0 :             break;
    8594             : 
    8595           0 :         default:
    8596           0 :             nOGRStyle = 0;
    8597           0 :             break;
    8598             :     }
    8599             : 
    8600             :     // note - MapInfo renders all lines using a round pen cap and round pen join
    8601             :     // which are not the default values for OGR pen cap/join styles. So we need
    8602             :     // to explicitly include the cap/j parameters in these strings
    8603          99 :     if (strlen(szPattern) != 0)
    8604             :     {
    8605           0 :         if (m_sPenDef.nPointWidth > 0)
    8606           0 :             pszStyle = CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
    8607             :                                   "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
    8608           0 :                                   static_cast<int>(GetPenWidthPoint()),
    8609           0 :                                   m_sPenDef.rgbColor, GetPenPattern(),
    8610             :                                   nOGRStyle, szPattern);
    8611             :         else
    8612           0 :             pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
    8613             :                                   "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
    8614           0 :                                   GetPenWidthPixel(), m_sPenDef.rgbColor,
    8615           0 :                                   GetPenPattern(), nOGRStyle, szPattern);
    8616             :     }
    8617             :     else
    8618             :     {
    8619          99 :         if (m_sPenDef.nPointWidth > 0)
    8620             :             pszStyle =
    8621           0 :                 CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\""
    8622             :                            "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
    8623           0 :                            static_cast<int>(GetPenWidthPoint()),
    8624           0 :                            m_sPenDef.rgbColor, GetPenPattern(), nOGRStyle);
    8625             :         else
    8626          99 :             pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
    8627             :                                   "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
    8628          99 :                                   GetPenWidthPixel(), m_sPenDef.rgbColor,
    8629          99 :                                   GetPenPattern(), nOGRStyle);
    8630             :     }
    8631             : 
    8632          99 :     return pszStyle;
    8633             : }
    8634             : 
    8635             : /**********************************************************************
    8636             :  *                   ITABFeaturePen::SetPenFromStyleString()
    8637             :  *
    8638             :  *  Init the Pen properties from a style string.
    8639             :  **********************************************************************/
    8640          32 : void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
    8641             : {
    8642          32 :     GBool bIsNull = 0;
    8643             : 
    8644             :     // Use the Style Manager to retrieve all the information we need.
    8645          32 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    8646          32 :     OGRStyleTool *poStylePart = nullptr;
    8647             : 
    8648             :     // Init the StyleMgr with the StyleString.
    8649          32 :     poStyleMgr->InitStyleString(pszStyleString);
    8650             : 
    8651             :     // Retrieve the Pen info.
    8652          32 :     const int numParts = poStyleMgr->GetPartCount();
    8653          51 :     for (int i = 0; i < numParts; i++)
    8654             :     {
    8655          49 :         poStylePart = poStyleMgr->GetPart(i);
    8656          49 :         if (poStylePart == nullptr)
    8657           0 :             continue;
    8658             : 
    8659          49 :         if (poStylePart->GetType() == OGRSTCPen)
    8660             :         {
    8661          30 :             break;
    8662             :         }
    8663             :         else
    8664             :         {
    8665          19 :             delete poStylePart;
    8666          19 :             poStylePart = nullptr;
    8667             :         }
    8668             :     }
    8669             : 
    8670             :     // If the no Pen found, do nothing.
    8671          32 :     if (poStylePart == nullptr)
    8672             :     {
    8673           2 :         delete poStyleMgr;
    8674           2 :         return;
    8675             :     }
    8676             : 
    8677          30 :     OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
    8678             : 
    8679             :     // With Pen, we always want to output points or pixels (which are the same,
    8680             :     // so just use points).
    8681             :     //
    8682             :     // It's very important to set the output unit of the feature.
    8683             :     // The default value is meter. If we don't do it all numerical values
    8684             :     // will be assumed to be converted from the input unit to meter when we
    8685             :     // will get them via GetParam...() functions.
    8686             :     // See OGRStyleTool::Parse() for more details.
    8687          30 :     poPenStyle->SetUnit(OGRSTUPoints, 1);
    8688             : 
    8689             :     // Get the Pen Id or pattern
    8690          30 :     const char *pszPenName = poPenStyle->Id(bIsNull);
    8691          30 :     if (bIsNull)
    8692           1 :         pszPenName = nullptr;
    8693             : 
    8694             :     // Set the width
    8695          30 :     if (poPenStyle->Width(bIsNull) != 0.0)
    8696             :     {
    8697          29 :         const double nPenWidth = poPenStyle->Width(bIsNull);
    8698             :         // Width < 10 is a pixel
    8699          29 :         if (nPenWidth > 10)
    8700           0 :             SetPenWidthPoint(nPenWidth);
    8701             :         else
    8702          29 :             SetPenWidthPixel(static_cast<GByte>(nPenWidth));
    8703             :     }
    8704             : 
    8705             :     // Set the color
    8706          30 :     const char *pszPenColor = poPenStyle->Color(bIsNull);
    8707          30 :     if (pszPenColor != nullptr)
    8708             :     {
    8709          30 :         if (pszPenColor[0] == '#')
    8710          30 :             pszPenColor++;
    8711             :         // The Pen color is an Hexa string that need to be convert in a int
    8712             :         const GInt32 nPenColor =
    8713          30 :             static_cast<int>(strtol(pszPenColor, nullptr, 16));
    8714          30 :         SetPenColor(nPenColor);
    8715             :     }
    8716             : 
    8717          30 :     const char *pszPenPattern = nullptr;
    8718             : 
    8719             :     // Set the Id of the Pen, use Pattern if necessary.
    8720          30 :     if (pszPenName &&
    8721          29 :         (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
    8722             :     {
    8723          29 :         const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
    8724          29 :         if (pszPenId != nullptr)
    8725             :         {
    8726          29 :             const int nPenId = atoi(pszPenId + 12);
    8727          29 :             SetPenPattern(static_cast<GByte>(nPenId));
    8728             :         }
    8729             :         else
    8730             :         {
    8731           0 :             pszPenId = strstr(pszPenName, "ogr-pen-");
    8732           0 :             if (pszPenId != nullptr)
    8733             :             {
    8734           0 :                 int nPenId = atoi(pszPenId + 8);
    8735           0 :                 if (nPenId == 0)
    8736           0 :                     nPenId = 2;
    8737           0 :                 SetPenPattern(static_cast<GByte>(nPenId));
    8738             :             }
    8739          29 :         }
    8740             :     }
    8741             :     else
    8742             :     {
    8743             :         // If no Pen Id, use the Pen Pattern to retrieve the Id.
    8744           1 :         pszPenPattern = poPenStyle->Pattern(bIsNull);
    8745           1 :         if (bIsNull)
    8746           1 :             pszPenPattern = nullptr;
    8747             :         else
    8748             :         {
    8749           0 :             if (strcmp(pszPenPattern, "1 1") == 0)
    8750           0 :                 SetPenPattern(3);
    8751           0 :             else if (strcmp(pszPenPattern, "2 1") == 0)
    8752           0 :                 SetPenPattern(4);
    8753           0 :             else if (strcmp(pszPenPattern, "3 1") == 0)
    8754           0 :                 SetPenPattern(5);
    8755           0 :             else if (strcmp(pszPenPattern, "6 1") == 0)
    8756           0 :                 SetPenPattern(6);
    8757           0 :             else if (strcmp(pszPenPattern, "12 2") == 0)
    8758           0 :                 SetPenPattern(7);
    8759           0 :             else if (strcmp(pszPenPattern, "24 4") == 0)
    8760           0 :                 SetPenPattern(8);
    8761           0 :             else if (strcmp(pszPenPattern, "4 3") == 0)
    8762           0 :                 SetPenPattern(9);
    8763           0 :             else if (strcmp(pszPenPattern, "1 4") == 0)
    8764           0 :                 SetPenPattern(10);
    8765           0 :             else if (strcmp(pszPenPattern, "4 6") == 0)
    8766           0 :                 SetPenPattern(11);
    8767           0 :             else if (strcmp(pszPenPattern, "6 4") == 0)
    8768           0 :                 SetPenPattern(12);
    8769           0 :             else if (strcmp(pszPenPattern, "12 12") == 0)
    8770           0 :                 SetPenPattern(13);
    8771           0 :             else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
    8772           0 :                 SetPenPattern(14);
    8773           0 :             else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
    8774           0 :                 SetPenPattern(15);
    8775           0 :             else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
    8776           0 :                 SetPenPattern(16);
    8777           0 :             else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
    8778           0 :                 SetPenPattern(17);
    8779           0 :             else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
    8780           0 :                 SetPenPattern(18);
    8781           0 :             else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
    8782           0 :                 SetPenPattern(19);
    8783           0 :             else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
    8784           0 :                 SetPenPattern(20);
    8785           0 :             else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
    8786           0 :                 SetPenPattern(21);
    8787           0 :             else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
    8788           0 :                 SetPenPattern(22);
    8789           0 :             else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
    8790           0 :                 SetPenPattern(23);
    8791           0 :             else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
    8792           0 :                 SetPenPattern(24);
    8793           0 :             else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
    8794           0 :                 SetPenPattern(25);
    8795             :         }
    8796             :     }
    8797             : 
    8798          30 :     delete poStyleMgr;
    8799          30 :     delete poStylePart;
    8800             : 
    8801          30 :     return;
    8802             : }
    8803             : 
    8804             : /**********************************************************************
    8805             :  *                   ITABFeaturePen::DumpPenDef()
    8806             :  *
    8807             :  * Dump pen definition information.
    8808             :  **********************************************************************/
    8809           0 : void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
    8810             : {
    8811           0 :     if (fpOut == nullptr)
    8812           0 :         fpOut = stdout;
    8813             : 
    8814           0 :     fprintf(fpOut, "  m_nPenDefIndex         = %d\n", m_nPenDefIndex);
    8815           0 :     fprintf(fpOut, "  m_sPenDef.nRefCount    = %d\n", m_sPenDef.nRefCount);
    8816           0 :     fprintf(fpOut, "  m_sPenDef.nPixelWidth  = %u\n", m_sPenDef.nPixelWidth);
    8817           0 :     fprintf(fpOut, "  m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
    8818           0 :     fprintf(fpOut, "  m_sPenDef.nPointWidth  = %d\n", m_sPenDef.nPointWidth);
    8819           0 :     fprintf(fpOut, "  m_sPenDef.rgbColor     = 0x%6.6x (%d)\n",
    8820             :             m_sPenDef.rgbColor, m_sPenDef.rgbColor);
    8821             : 
    8822           0 :     fflush(fpOut);
    8823           0 : }
    8824             : 
    8825             : /*=====================================================================
    8826             :  *                      class ITABFeatureBrush
    8827             :  *====================================================================*/
    8828             : 
    8829             : /**********************************************************************
    8830             :  *                   ITABFeatureBrush::ITABFeatureBrush()
    8831             :  **********************************************************************/
    8832             : 
    8833             : // MI default is BRUSH(2, 16777215, 16777215)
    8834             : static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
    8835             : 
    8836        2518 : ITABFeatureBrush::ITABFeatureBrush()
    8837        2518 :     : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
    8838             : {
    8839        2518 : }
    8840             : 
    8841             : /**********************************************************************
    8842             :  *                   ITABFeatureBrush::GetBrushStyleString()
    8843             :  *
    8844             :  *  Return a Brush() string. All representations info for the Brush are here.
    8845             :  **********************************************************************/
    8846          54 : const char *ITABFeatureBrush::GetBrushStyleString() const
    8847             : {
    8848          54 :     const char *pszStyle = nullptr;
    8849          54 :     int nOGRStyle = 0;
    8850             :     /* char szPattern[20]; */
    8851             :     //* szPattern[0] = '\0'; */
    8852             : 
    8853          54 :     if (m_sBrushDef.nFillPattern == 1)
    8854          53 :         nOGRStyle = 1;
    8855           1 :     else if (m_sBrushDef.nFillPattern == 3)
    8856           0 :         nOGRStyle = 2;
    8857           1 :     else if (m_sBrushDef.nFillPattern == 4)
    8858           0 :         nOGRStyle = 3;
    8859           1 :     else if (m_sBrushDef.nFillPattern == 5)
    8860           0 :         nOGRStyle = 5;
    8861           1 :     else if (m_sBrushDef.nFillPattern == 6)
    8862           0 :         nOGRStyle = 4;
    8863           1 :     else if (m_sBrushDef.nFillPattern == 7)
    8864           0 :         nOGRStyle = 6;
    8865           1 :     else if (m_sBrushDef.nFillPattern == 8)
    8866           0 :         nOGRStyle = 7;
    8867             : 
    8868          54 :     if (GetBrushTransparent())
    8869             :     {
    8870             :         /* Omit BG Color for transparent brushes */
    8871           2 :         pszStyle = CPLSPrintf(
    8872             :             "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
    8873           2 :             m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
    8874             :     }
    8875             :     else
    8876             :     {
    8877          52 :         pszStyle = CPLSPrintf(
    8878             :             "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
    8879          52 :             m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
    8880          52 :             m_sBrushDef.nFillPattern, nOGRStyle);
    8881             :     }
    8882             : 
    8883          54 :     return pszStyle;
    8884             : }
    8885             : 
    8886             : /**********************************************************************
    8887             :  *                   ITABFeatureBrush::SetBrushFromStyleString()
    8888             :  *
    8889             :  *  Set all Brush elements from a StyleString.
    8890             :  *  Use StyleMgr to do so.
    8891             :  **********************************************************************/
    8892          19 : void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
    8893             : {
    8894          19 :     GBool bIsNull = 0;
    8895             : 
    8896             :     // Use the Style Manager to retrieve all the information we need.
    8897          19 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    8898          19 :     OGRStyleTool *poStylePart = nullptr;
    8899             : 
    8900             :     // Init the StyleMgr with the StyleString.
    8901          19 :     poStyleMgr->InitStyleString(pszStyleString);
    8902             : 
    8903             :     // Retrieve the Brush info.
    8904          19 :     const int numParts = poStyleMgr->GetPartCount();
    8905          19 :     for (int i = 0; i < numParts; i++)
    8906             :     {
    8907          19 :         poStylePart = poStyleMgr->GetPart(i);
    8908          19 :         if (poStylePart == nullptr)
    8909           0 :             continue;
    8910             : 
    8911          19 :         if (poStylePart->GetType() == OGRSTCBrush)
    8912             :         {
    8913          19 :             break;
    8914             :         }
    8915             :         else
    8916             :         {
    8917           0 :             delete poStylePart;
    8918           0 :             poStylePart = nullptr;
    8919             :         }
    8920             :     }
    8921             : 
    8922             :     // If the no Brush found, do nothing.
    8923          19 :     if (poStylePart == nullptr)
    8924             :     {
    8925           0 :         delete poStyleMgr;
    8926           0 :         return;
    8927             :     }
    8928             : 
    8929          19 :     OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
    8930             : 
    8931             :     // Set the Brush Id (FillPattern)
    8932          19 :     const char *pszBrushId = poBrushStyle->Id(bIsNull);
    8933          19 :     if (bIsNull)
    8934           2 :         pszBrushId = nullptr;
    8935          19 :     bool bHasBrushId = false;
    8936             : 
    8937          19 :     if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
    8938           0 :                        strstr(pszBrushId, "ogr-brush-")))
    8939             :     {
    8940          17 :         if (strstr(pszBrushId, "mapinfo-brush-"))
    8941             :         {
    8942          17 :             const int nBrushId = atoi(pszBrushId + 14);
    8943          17 :             SetBrushPattern(static_cast<GByte>(nBrushId));
    8944          17 :             bHasBrushId = true;
    8945             :         }
    8946           0 :         else if (strstr(pszBrushId, "ogr-brush-"))
    8947             :         {
    8948           0 :             int nBrushId = atoi(pszBrushId + 10);
    8949           0 :             if (nBrushId > 1)
    8950           0 :                 nBrushId++;
    8951           0 :             SetBrushPattern(static_cast<GByte>(nBrushId));
    8952           0 :             bHasBrushId = true;
    8953             :         }
    8954             :     }
    8955             : 
    8956             :     // Set the BackColor, if not set, then it is transparent
    8957          19 :     const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
    8958          19 :     if (bIsNull)
    8959           1 :         pszBrushColor = nullptr;
    8960             : 
    8961          19 :     if (pszBrushColor)
    8962             :     {
    8963          18 :         if (pszBrushColor[0] == '#')
    8964          18 :             pszBrushColor++;
    8965          18 :         if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
    8966           1 :             pszBrushColor[7] == '0')
    8967             :         {
    8968           1 :             SetBrushTransparent(1);
    8969             :         }
    8970             :         else
    8971             :         {
    8972          34 :             CPLString osBrushColor(pszBrushColor);
    8973          17 :             if (strlen(pszBrushColor) > 6)
    8974           0 :                 osBrushColor.resize(6);
    8975             :             const int nBrushColor =
    8976          17 :                 static_cast<int>(strtol(osBrushColor, nullptr, 16));
    8977          17 :             SetBrushBGColor(static_cast<GInt32>(nBrushColor));
    8978             :         }
    8979             :     }
    8980             :     else
    8981             :     {
    8982           1 :         SetBrushTransparent(1);
    8983             :     }
    8984             : 
    8985             :     // Set the ForeColor
    8986          19 :     pszBrushColor = poBrushStyle->ForeColor(bIsNull);
    8987          19 :     if (bIsNull)
    8988           0 :         pszBrushColor = nullptr;
    8989             : 
    8990          19 :     if (pszBrushColor)
    8991             :     {
    8992          19 :         if (pszBrushColor[0] == '#')
    8993          19 :             pszBrushColor++;
    8994          19 :         if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
    8995           1 :             pszBrushColor[7] == '0')
    8996             :         {
    8997           1 :             if (!bHasBrushId)
    8998           1 :                 SetBrushPattern(static_cast<GByte>(1));  // No-fill
    8999             :         }
    9000             :         else
    9001             :         {
    9002          18 :             if (!bHasBrushId)
    9003           1 :                 SetBrushPattern(static_cast<GByte>(2));  // Solid-fill
    9004             :         }
    9005             : 
    9006          38 :         CPLString osBrushColor(pszBrushColor);
    9007          19 :         if (strlen(pszBrushColor) > 6)
    9008           1 :             osBrushColor.resize(6);
    9009             :         const int nBrushColor =
    9010          19 :             static_cast<int>(strtol(osBrushColor, nullptr, 16));
    9011          19 :         SetBrushFGColor(static_cast<GInt32>(nBrushColor));
    9012             :     }
    9013             : 
    9014          19 :     delete poStyleMgr;
    9015          19 :     delete poStylePart;
    9016             : 
    9017          19 :     return;
    9018             : }
    9019             : 
    9020             : /**********************************************************************
    9021             :  *                   ITABFeatureBrush::DumpBrushDef()
    9022             :  *
    9023             :  * Dump Brush definition information.
    9024             :  **********************************************************************/
    9025           0 : void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
    9026             : {
    9027           0 :     if (fpOut == nullptr)
    9028           0 :         fpOut = stdout;
    9029             : 
    9030           0 :     fprintf(fpOut, "  m_nBrushDefIndex         = %d\n", m_nBrushDefIndex);
    9031           0 :     fprintf(fpOut, "  m_sBrushDef.nRefCount    = %d\n", m_sBrushDef.nRefCount);
    9032           0 :     fprintf(fpOut, "  m_sBrushDef.nFillPattern = %d\n",
    9033           0 :             static_cast<int>(m_sBrushDef.nFillPattern));
    9034           0 :     fprintf(fpOut, "  m_sBrushDef.bTransparentFill = %d\n",
    9035           0 :             static_cast<int>(m_sBrushDef.bTransparentFill));
    9036           0 :     fprintf(fpOut, "  m_sBrushDef.rgbFGColor   = 0x%6.6x (%d)\n",
    9037             :             m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
    9038           0 :     fprintf(fpOut, "  m_sBrushDef.rgbBGColor   = 0x%6.6x (%d)\n",
    9039             :             m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
    9040             : 
    9041           0 :     fflush(fpOut);
    9042           0 : }
    9043             : 
    9044             : /*=====================================================================
    9045             :  *                      class ITABFeatureFont
    9046             :  *====================================================================*/
    9047             : 
    9048             : /**********************************************************************
    9049             :  *                   ITABFeatureFont::ITABFeatureFont()
    9050             :  **********************************************************************/
    9051             : 
    9052             : // MI default is Font("Arial", 0, 0, 0)
    9053             : static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
    9054             : 
    9055        1638 : ITABFeatureFont::ITABFeatureFont()
    9056        1638 :     : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
    9057             : {
    9058        1638 : }
    9059             : 
    9060             : /**********************************************************************
    9061             :  *                   ITABFeatureFont::SetFontName()
    9062             :  **********************************************************************/
    9063        1582 : void ITABFeatureFont::SetFontName(const char *pszName)
    9064             : {
    9065        1582 :     strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
    9066        1582 :     m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
    9067        1582 : }
    9068             : 
    9069             : /**********************************************************************
    9070             :  *                   ITABFeatureFont::DumpFontDef()
    9071             :  *
    9072             :  * Dump Font definition information.
    9073             :  **********************************************************************/
    9074           0 : void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
    9075             : {
    9076           0 :     if (fpOut == nullptr)
    9077           0 :         fpOut = stdout;
    9078             : 
    9079           0 :     fprintf(fpOut, "  m_nFontDefIndex       = %d\n", m_nFontDefIndex);
    9080           0 :     fprintf(fpOut, "  m_sFontDef.nRefCount  = %d\n", m_sFontDef.nRefCount);
    9081           0 :     fprintf(fpOut, "  m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
    9082             : 
    9083           0 :     fflush(fpOut);
    9084           0 : }
    9085             : 
    9086             : /*=====================================================================
    9087             :  *                      class ITABFeatureSymbol
    9088             :  *====================================================================*/
    9089             : 
    9090             : /**********************************************************************
    9091             :  *                   ITABFeatureSymbol::ITABFeatureSymbol()
    9092             :  **********************************************************************/
    9093             : 
    9094             : // MI default is Symbol(35, 0, 12)
    9095             : static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
    9096             : 
    9097      544458 : ITABFeatureSymbol::ITABFeatureSymbol()
    9098      544458 :     : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
    9099             : {
    9100      544458 : }
    9101             : 
    9102             : /**********************************************************************
    9103             :  *                   ITABFeatureSymbol::GetSymbolStyleString()
    9104             :  *
    9105             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    9106             :  **********************************************************************/
    9107          33 : const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
    9108             : {
    9109          33 :     const char *pszStyle = nullptr;
    9110          33 :     int nOGRStyle = 0;
    9111             :     /* char szPattern[20]; */
    9112          33 :     int nAngle = 0;
    9113             :     /* szPattern[0] = '\0'; */
    9114             : 
    9115          33 :     switch (m_sSymbolDef.nSymbolNo)
    9116             :     {
    9117           0 :         case 31:
    9118             :             // this is actually a "null" symbol in MapInfo!
    9119           0 :             nOGRStyle = 0;
    9120           0 :             break;
    9121           0 :         case 32:  // filled square
    9122           0 :             nOGRStyle = 5;
    9123           0 :             break;
    9124           0 :         case 33:  // filled diamond
    9125           0 :             nAngle = 45;
    9126           0 :             nOGRStyle = 5;
    9127           0 :             break;
    9128           0 :         case 34:  // filled circle
    9129           0 :             nOGRStyle = 3;
    9130           0 :             break;
    9131          33 :         case 35:  // filled star
    9132          33 :             nOGRStyle = 9;
    9133          33 :             break;
    9134           0 :         case 36:  // filled upward pointing triangle
    9135           0 :             nOGRStyle = 7;
    9136           0 :             break;
    9137           0 :         case 37:  // filled downward pointing triangle
    9138           0 :             nAngle = 180;
    9139           0 :             nOGRStyle = 7;
    9140           0 :             break;
    9141           0 :         case 38:  // hollow square
    9142           0 :             nOGRStyle = 4;
    9143           0 :             break;
    9144           0 :         case 39:  // hollow diamond
    9145           0 :             nAngle = 45;
    9146           0 :             nOGRStyle = 4;
    9147           0 :             break;
    9148           0 :         case 40:  // hollow circle
    9149           0 :             nOGRStyle = 2;
    9150           0 :             break;
    9151           0 :         case 41:  // hollow star
    9152           0 :             nOGRStyle = 8;
    9153           0 :             break;
    9154           0 :         case 42:  // hollow upward pointing triangle
    9155           0 :             nOGRStyle = 6;
    9156           0 :             break;
    9157           0 :         case 43:  // hollow downward pointing triangle
    9158           0 :             nAngle = 180;
    9159           0 :             nOGRStyle = 6;
    9160           0 :             break;
    9161           0 :         case 44:  // filled square (with shadow)
    9162           0 :             nOGRStyle = 5;
    9163           0 :             break;
    9164           0 :         case 45:  // filled upward triangle (with shadow)
    9165           0 :             nOGRStyle = 7;
    9166           0 :             break;
    9167           0 :         case 46:  // filled circle (with shadow)
    9168           0 :             nOGRStyle = 3;
    9169           0 :             break;
    9170           0 :         case 49:  // crossed lines
    9171           0 :             nOGRStyle = 0;
    9172           0 :             break;
    9173           0 :         case 50:  // X crossed lines
    9174           0 :             nOGRStyle = 1;
    9175           0 :             break;
    9176             :     }
    9177             : 
    9178          33 :     nAngle += static_cast<int>(dfAngle);
    9179             : 
    9180          66 :     pszStyle = CPLSPrintf(
    9181             :         "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
    9182          33 :         m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
    9183             :         nOGRStyle);
    9184             : 
    9185          33 :     return pszStyle;
    9186             : }
    9187             : 
    9188             : /**********************************************************************
    9189             :  *                   ITABFeatureSymbol::SetSymbolFromStyleString()
    9190             :  *
    9191             :  *  Set all Symbol var from a OGRStyleSymbol.
    9192             :  **********************************************************************/
    9193         109 : void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    9194             : {
    9195         109 :     GBool bIsNull = 0;
    9196             : 
    9197             :     // Set the Symbol Id (SymbolNo)
    9198         109 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    9199         109 :     if (bIsNull)
    9200           0 :         pszSymbolId = nullptr;
    9201             : 
    9202         109 :     if (pszSymbolId)
    9203             :     {
    9204         109 :         if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
    9205             :         {
    9206         105 :             const int nSymbolId = atoi(pszSymbolId + 12);
    9207         105 :             SetSymbolNo(static_cast<GByte>(nSymbolId));
    9208             :         }
    9209           4 :         else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
    9210             :         {
    9211           0 :             const int nSymbolId = atoi(pszSymbolId + 8);
    9212             : 
    9213             :             // The OGR symbol is not the MapInfo one
    9214             :             // Here's some mapping
    9215           0 :             switch (nSymbolId)
    9216             :             {
    9217           0 :                 case 0:
    9218           0 :                     SetSymbolNo(49);
    9219           0 :                     break;
    9220           0 :                 case 1:
    9221           0 :                     SetSymbolNo(50);
    9222           0 :                     break;
    9223           0 :                 case 2:
    9224           0 :                     SetSymbolNo(40);
    9225           0 :                     break;
    9226           0 :                 case 3:
    9227           0 :                     SetSymbolNo(34);
    9228           0 :                     break;
    9229           0 :                 case 4:
    9230           0 :                     SetSymbolNo(38);
    9231           0 :                     break;
    9232           0 :                 case 5:
    9233           0 :                     SetSymbolNo(32);
    9234           0 :                     break;
    9235           0 :                 case 6:
    9236           0 :                     SetSymbolNo(42);
    9237           0 :                     break;
    9238           0 :                 case 7:
    9239           0 :                     SetSymbolNo(36);
    9240           0 :                     break;
    9241           0 :                 case 8:
    9242           0 :                     SetSymbolNo(41);
    9243           0 :                     break;
    9244           0 :                 case 9:
    9245           0 :                     SetSymbolNo(35);
    9246           0 :                     break;
    9247           0 :                 case 10:  // vertical bar -- no mapinfo equivalent, so use
    9248             :                           // crosshairs as closest match
    9249           0 :                     SetSymbolNo(49);
    9250           0 :                     break;
    9251             :             }
    9252             :         }
    9253             :     }
    9254             : 
    9255             :     // Set SymbolSize
    9256         109 :     const double dSymbolSize = poSymbolStyle->Size(bIsNull);
    9257         109 :     if (dSymbolSize != 0.0)
    9258             :     {
    9259         109 :         SetSymbolSize(static_cast<GInt16>(dSymbolSize));
    9260             :     }
    9261             : 
    9262             :     // Set Symbol Color
    9263         109 :     const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
    9264         109 :     if (pszSymbolColor)
    9265             :     {
    9266         109 :         if (pszSymbolColor[0] == '#')
    9267         109 :             pszSymbolColor++;
    9268             :         int nSymbolColor =
    9269         109 :             static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
    9270         109 :         SetSymbolColor(static_cast<GInt32>(nSymbolColor));
    9271             :     }
    9272         109 : }
    9273             : 
    9274             : /**********************************************************************
    9275             :  *                   ITABFeatureSymbol::SetSymbolFromStyleString()
    9276             :  *
    9277             :  *  Set all Symbol var from a StyleString. Use StyleMgr to do so.
    9278             :  **********************************************************************/
    9279         109 : void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
    9280             : {
    9281             :     // Use the Style Manager to retrieve all the information we need.
    9282         109 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    9283         109 :     OGRStyleTool *poStylePart = nullptr;
    9284             : 
    9285             :     // Init the StyleMgr with the StyleString.
    9286         109 :     poStyleMgr->InitStyleString(pszStyleString);
    9287             : 
    9288             :     // Retrieve the Symbol info.
    9289         109 :     const int numParts = poStyleMgr->GetPartCount();
    9290         109 :     for (int i = 0; i < numParts; i++)
    9291             :     {
    9292         109 :         poStylePart = poStyleMgr->GetPart(i);
    9293         109 :         if (poStylePart == nullptr)
    9294           0 :             continue;
    9295             : 
    9296         109 :         if (poStylePart->GetType() == OGRSTCSymbol)
    9297             :         {
    9298         109 :             break;
    9299             :         }
    9300             :         else
    9301             :         {
    9302           0 :             delete poStylePart;
    9303           0 :             poStylePart = nullptr;
    9304             :         }
    9305             :     }
    9306             : 
    9307             :     // If the no Symbol found, do nothing.
    9308         109 :     if (poStylePart == nullptr)
    9309             :     {
    9310           0 :         delete poStyleMgr;
    9311           0 :         return;
    9312             :     }
    9313             : 
    9314             :     OGRStyleSymbol *poSymbolStyle =
    9315         109 :         cpl::down_cast<OGRStyleSymbol *>(poStylePart);
    9316             : 
    9317             :     // With Symbol, we always want to output points
    9318             :     //
    9319             :     // It's very important to set the output unit of the feature.
    9320             :     // The default value is meter. If we don't do it all numerical values
    9321             :     // will be assumed to be converted from the input unit to meter when we
    9322             :     // will get them via GetParam...() functions.
    9323             :     // See OGRStyleTool::Parse() for more details.
    9324         109 :     poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
    9325             : 
    9326         109 :     SetSymbolFromStyle(poSymbolStyle);
    9327             : 
    9328         109 :     delete poStyleMgr;
    9329         109 :     delete poStylePart;
    9330             : 
    9331         109 :     return;
    9332             : }
    9333             : 
    9334             : /**********************************************************************
    9335             :  *                   ITABFeatureSymbol::GetSymbolFeatureClass()
    9336             :  *
    9337             :  *  Return the feature class needed to represent the style string.
    9338             :  **********************************************************************/
    9339             : TABFeatureClass
    9340         109 : ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
    9341             : {
    9342             :     // Use the Style Manager to retrieve all the information we need.
    9343         109 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    9344         109 :     OGRStyleTool *poStylePart = nullptr;
    9345             : 
    9346             :     // Init the StyleMgr with the StyleString.
    9347         109 :     poStyleMgr->InitStyleString(pszStyleString);
    9348             : 
    9349             :     // Retrieve the Symbol info.
    9350         109 :     const int numParts = poStyleMgr->GetPartCount();
    9351         109 :     for (int i = 0; i < numParts; i++)
    9352             :     {
    9353         109 :         poStylePart = poStyleMgr->GetPart(i);
    9354         109 :         if (poStylePart == nullptr)
    9355             :         {
    9356           0 :             continue;
    9357             :         }
    9358             : 
    9359         109 :         if (poStylePart->GetType() == OGRSTCSymbol)
    9360             :         {
    9361         109 :             break;
    9362             :         }
    9363             :         else
    9364             :         {
    9365           0 :             delete poStylePart;
    9366           0 :             poStylePart = nullptr;
    9367             :         }
    9368             :     }
    9369             : 
    9370         109 :     TABFeatureClass result = TABFCPoint;
    9371             : 
    9372             :     // If the no Symbol found, do nothing.
    9373         109 :     if (poStylePart == nullptr)
    9374             :     {
    9375           0 :         delete poStyleMgr;
    9376           0 :         return result;
    9377             :     }
    9378             : 
    9379             :     OGRStyleSymbol *poSymbolStyle =
    9380         109 :         cpl::down_cast<OGRStyleSymbol *>(poStylePart);
    9381             : 
    9382         109 :     GBool bIsNull = 0;
    9383             : 
    9384             :     // Set the Symbol Id (SymbolNo)
    9385         109 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    9386         109 :     if (bIsNull)
    9387           0 :         pszSymbolId = nullptr;
    9388             : 
    9389         109 :     if (pszSymbolId)
    9390             :     {
    9391         109 :         if (STARTS_WITH(pszSymbolId, "font-sym-"))
    9392             :         {
    9393           2 :             result = TABFCFontPoint;
    9394             :         }
    9395         107 :         else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
    9396             :         {
    9397           2 :             result = TABFCCustomPoint;
    9398             :         }
    9399             :     }
    9400             : 
    9401         109 :     delete poStyleMgr;
    9402         109 :     delete poStylePart;
    9403             : 
    9404         109 :     return result;
    9405             : }
    9406             : 
    9407             : /**********************************************************************
    9408             :  *                   ITABFeatureSymbol::DumpSymbolDef()
    9409             :  *
    9410             :  * Dump Symbol definition information.
    9411             :  **********************************************************************/
    9412           0 : void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
    9413             : {
    9414           0 :     if (fpOut == nullptr)
    9415           0 :         fpOut = stdout;
    9416             : 
    9417           0 :     fprintf(fpOut, "  m_nSymbolDefIndex       = %d\n", m_nSymbolDefIndex);
    9418           0 :     fprintf(fpOut, "  m_sSymbolDef.nRefCount  = %d\n", m_sSymbolDef.nRefCount);
    9419           0 :     fprintf(fpOut, "  m_sSymbolDef.nSymbolNo  = %d\n", m_sSymbolDef.nSymbolNo);
    9420           0 :     fprintf(fpOut, "  m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
    9421           0 :     fprintf(fpOut, "  m_sSymbolDef._unknown_  = %d\n",
    9422           0 :             static_cast<int>(m_sSymbolDef._nUnknownValue_));
    9423           0 :     fprintf(fpOut, "  m_sSymbolDef.rgbColor   = 0x%6.6x (%d)\n",
    9424             :             m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
    9425             : 
    9426           0 :     fflush(fpOut);
    9427           0 : }

Generated by: LCOV version 1.14