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: 2025-09-10 17:48:50 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      574275 : TABFeature::TABFeature(const 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      574275 :       m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
      55             : {
      56      574275 : }
      57             : 
      58             : /**********************************************************************
      59             :  *                   TABFeature::~TABFeature()
      60             :  *
      61             :  * Destructor.
      62             :  **********************************************************************/
      63      574540 : TABFeature::~TABFeature()
      64             : {
      65      574540 : }
      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      551382 : TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
      75             :                                               OGRFeatureDefn *poDefn)
      76             : {
      77      551382 :     TABFeature *poFeature = nullptr;
      78             : 
      79             :     /*-----------------------------------------------------------------
      80             :      * Create new feature object of the right type
      81             :      *----------------------------------------------------------------*/
      82      551382 :     switch (nMapInfoType)
      83             :     {
      84         155 :         case TAB_GEOM_NONE:
      85         155 :             poFeature = new TABFeature(poDefn);
      86         155 :             break;
      87      549085 :         case TAB_GEOM_SYMBOL_C:
      88             :         case TAB_GEOM_SYMBOL:
      89      549085 :             poFeature = new TABPoint(poDefn);
      90      549085 :             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        1573 :         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        1573 :             poFeature = new TABPolyline(poDefn);
     110        1573 :             break;
     111           8 :         case TAB_GEOM_ARC_C:
     112             :         case TAB_GEOM_ARC:
     113           8 :             poFeature = new TABArc(poDefn);
     114           8 :             break;
     115             : 
     116         521 :         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         521 :             poFeature = new TABRegion(poDefn);
     123         521 :             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      551382 :     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 :     const 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             : TABFeature *
     237           0 : TABFeature::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
     238             : {
     239             :     /*-----------------------------------------------------------------
     240             :      * Alloc new feature and copy the base stuff
     241             :      *----------------------------------------------------------------*/
     242           0 :     TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
     243             : 
     244           0 :     CopyTABFeatureBase(poNew);
     245             : 
     246             :     /*-----------------------------------------------------------------
     247             :      * And members specific to this class
     248             :      *----------------------------------------------------------------*/
     249             :     // Nothing to do for this class
     250             : 
     251           0 :     return poNew;
     252             : }
     253             : 
     254             : /**********************************************************************
     255             :  *                   TABFeature::SetMBR()
     256             :  *
     257             :  * Set the values for the MBR corners for this feature.
     258             :  **********************************************************************/
     259      558529 : void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
     260             : {
     261      558529 :     m_dXMin = std::min(dXMin, dXMax);
     262      558529 :     m_dYMin = std::min(dYMin, dYMax);
     263      558529 :     m_dXMax = std::max(dXMin, dXMax);
     264      558529 :     m_dYMax = std::max(dYMin, dYMax);
     265      558529 : }
     266             : 
     267             : /**********************************************************************
     268             :  *                   TABFeature::GetMBR()
     269             :  *
     270             :  * Return the values for the MBR corners for this feature.
     271             :  **********************************************************************/
     272        1227 : void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
     273             :                         double &dYMax)
     274             : {
     275        1227 :     dXMin = m_dXMin;
     276        1227 :     dYMin = m_dYMin;
     277        1227 :     dXMax = m_dXMax;
     278        1227 :     dYMax = m_dYMax;
     279        1227 : }
     280             : 
     281             : /**********************************************************************
     282             :  *                   TABFeature::SetIntMBR()
     283             :  *
     284             :  * Set the integer coordinates values of the MBR of this feature.
     285             :  **********************************************************************/
     286      551233 : void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
     287             :                            GInt32 nYMax)
     288             : {
     289      551233 :     m_nXMin = nXMin;
     290      551233 :     m_nYMin = nYMin;
     291      551233 :     m_nXMax = nXMax;
     292      551233 :     m_nYMax = nYMax;
     293      551233 : }
     294             : 
     295             : /**********************************************************************
     296             :  *                   TABFeature::GetIntMBR()
     297             :  *
     298             :  * Return the integer coordinates values of the MBR of this feature.
     299             :  **********************************************************************/
     300       15023 : void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
     301             :                            GInt32 &nYMax)
     302             : {
     303       15023 :     nXMin = m_nXMin;
     304       15023 :     nYMin = m_nYMin;
     305       15023 :     nXMax = m_nXMax;
     306       15023 :     nYMax = m_nYMax;
     307       15023 : }
     308             : 
     309             : /**********************************************************************
     310             :  *                   TABFeature::ReadRecordFromDATFile()
     311             :  *
     312             :  * Fill the fields part of the feature from the contents of the
     313             :  * table record pointed to by poDATFile.
     314             :  *
     315             :  * It is assumed that poDATFile currently points to the beginning of
     316             :  * the table record and that this feature's OGRFeatureDefn has been
     317             :  * properly initialized for this table.
     318             :  **********************************************************************/
     319      551214 : int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
     320             : {
     321      551214 :     CPLAssert(poDATFile);
     322             : 
     323      551214 :     const int numFields = poDATFile->GetNumFields();
     324             : 
     325     1104240 :     for (int iField = 0; iField < numFields; iField++)
     326             :     {
     327      553026 :         switch (poDATFile->GetFieldType(iField))
     328             :         {
     329        1320 :             case TABFChar:
     330             :             {
     331        1320 :                 int iWidth(poDATFile->GetFieldWidth(iField));
     332        2640 :                 CPLString osValue(poDATFile->ReadCharField(iWidth));
     333             : 
     334        1320 :                 if (!poDATFile->GetEncoding().empty())
     335             :                 {
     336         110 :                     osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
     337             :                 }
     338        1320 :                 SetField(iField, osValue);
     339        1320 :                 break;
     340             :             }
     341          28 :             case TABFDecimal:
     342             :             {
     343          28 :                 const double dValue = poDATFile->ReadDecimalField(
     344             :                     poDATFile->GetFieldWidth(iField));
     345          28 :                 SetField(iField, dValue);
     346          28 :                 break;
     347             :             }
     348      550994 :             case TABFInteger:
     349             :             {
     350      550994 :                 const int nValue = poDATFile->ReadIntegerField(
     351             :                     poDATFile->GetFieldWidth(iField));
     352      550994 :                 SetField(iField, nValue);
     353      550994 :                 break;
     354             :             }
     355           4 :             case TABFSmallInt:
     356             :             {
     357           4 :                 const int nValue = poDATFile->ReadSmallIntField(
     358           4 :                     poDATFile->GetFieldWidth(iField));
     359           4 :                 SetField(iField, nValue);
     360           4 :                 break;
     361             :             }
     362           6 :             case TABFLargeInt:
     363             :             {
     364           6 :                 const GInt64 nValue = poDATFile->ReadLargeIntField(
     365             :                     poDATFile->GetFieldWidth(iField));
     366           6 :                 SetField(iField, nValue);
     367           6 :                 break;
     368             :             }
     369         609 :             case TABFFloat:
     370             :             {
     371             :                 const double dValue =
     372         609 :                     poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
     373         609 :                 SetField(iField, dValue);
     374         609 :                 break;
     375             :             }
     376           6 :             case TABFLogical:
     377             :             {
     378           6 :                 const bool bValue = poDATFile->ReadLogicalField(
     379             :                     poDATFile->GetFieldWidth(iField));
     380           6 :                 SetField(iField, bValue ? 1 : 0);
     381           6 :                 break;
     382             :             }
     383          29 :             case TABFDate:
     384             :             {
     385             : #ifdef MITAB_USE_OFTDATETIME
     386          29 :                 int nYear = 0;
     387          29 :                 int nMonth = 0;
     388          29 :                 int nDay = 0;
     389          29 :                 const int status = poDATFile->ReadDateField(
     390             :                     poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
     391          29 :                 if (status == 0)
     392             :                 {
     393          12 :                     SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
     394             :                 }
     395             : #else
     396             :                 const char *pszValue =
     397             :                     poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
     398             :                 SetField(iField, pszValue);
     399             : #endif
     400          29 :                 break;
     401             :             }
     402           7 :             case TABFTime:
     403             :             {
     404             : #ifdef MITAB_USE_OFTDATETIME
     405           7 :                 int nHour = 0;
     406           7 :                 int nMin = 0;
     407           7 :                 int nMS = 0;
     408           7 :                 int nSec = 0;
     409             :                 const int status =
     410           7 :                     poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
     411             :                                              &nHour, &nMin, &nSec, &nMS);
     412           7 :                 if (status == 0)
     413             :                 {
     414           6 :                     int nYear = 0;
     415           6 :                     int nMonth = 0;
     416           6 :                     int nDay = 0;
     417           6 :                     SetField(iField, nYear, nMonth, nDay, nHour, nMin,
     418           6 :                              nSec + nMS / 1000.0f, 0);
     419             :                 }
     420             : #else
     421             :                 const char *pszValue =
     422             :                     poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
     423             :                 SetField(iField, pszValue);
     424             : #endif
     425           7 :                 break;
     426             :             }
     427          23 :             case TABFDateTime:
     428             :             {
     429             : #ifdef MITAB_USE_OFTDATETIME
     430          23 :                 int nYear = 0;
     431          23 :                 int nMonth = 0;
     432          23 :                 int nDay = 0;
     433          23 :                 int nHour = 0;
     434          23 :                 int nMin = 0;
     435          23 :                 int nMS = 0;
     436          23 :                 int nSec = 0;
     437          23 :                 const int status = poDATFile->ReadDateTimeField(
     438             :                     poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
     439             :                     &nHour, &nMin, &nSec, &nMS);
     440          23 :                 if (status == 0)
     441             :                 {
     442           6 :                     SetField(iField, nYear, nMonth, nDay, nHour, nMin,
     443           6 :                              nSec + nMS / 1000.0f, 0);
     444             :                 }
     445             : #else
     446             :                 const char *pszValue = poDATFile->ReadDateTimeField(
     447             :                     poDATFile->GetFieldWidth(iField));
     448             :                 SetField(iField, pszValue);
     449             : #endif
     450          23 :                 break;
     451             :             }
     452           0 :             default:
     453             :                 // Other type???  Impossible!
     454           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     455             :                          "Unsupported field type for field %d!", iField);
     456             :         }
     457             :     }
     458             : 
     459      551214 :     return 0;
     460             : }
     461             : 
     462             : /**********************************************************************
     463             :  *                   TABFeature::WriteRecordToDATFile()
     464             :  *
     465             :  * Write the attribute part of the feature to the .DAT file.
     466             :  *
     467             :  * It is assumed that poDATFile currently points to the beginning of
     468             :  * the table record and that this feature's OGRFeatureDefn has been
     469             :  * properly initialized for this table.
     470             :  *
     471             :  * Returns 0 on success, -1 on error.
     472             :  **********************************************************************/
     473       15087 : int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
     474             :                                      TABINDFile *poINDFile, int *panIndexNo)
     475             : {
     476             : #ifdef MITAB_USE_OFTDATETIME
     477       15087 :     int nYear = 0;
     478       15087 :     int nMon = 0;
     479       15087 :     int nDay = 0;
     480       15087 :     int nHour = 0;
     481       15087 :     int nMin = 0;
     482       15087 :     int nTZFlag = 0;
     483       15087 :     float fSec = 0.0f;
     484             : #endif
     485             : 
     486       15087 :     CPLAssert(poDATFile);
     487             : 
     488       15087 :     const int numFields = poDATFile->GetNumFields();
     489             : 
     490       15087 :     poDATFile->MarkRecordAsExisting();
     491             : 
     492       15087 :     int nStatus = 0;
     493       30858 :     for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
     494             :     {
     495             :         // Hack for "extra" introduced field.
     496       15771 :         if (iField >= GetDefnRef()->GetFieldCount())
     497             :         {
     498           1 :             CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
     499             :                       iField == 0);
     500           1 :             nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
     501             :                                                    poINDFile, 0);
     502           1 :             continue;
     503             :         }
     504       15770 :         CPLAssert(panIndexNo != nullptr);
     505             : 
     506       15770 :         switch (poDATFile->GetFieldType(iField))
     507             :         {
     508         377 :             case TABFChar:
     509             :             {
     510         377 :                 CPLString osValue(GetFieldAsString(iField));
     511         377 :                 if (!poDATFile->GetEncoding().empty())
     512             :                 {
     513          30 :                     osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
     514             :                 }
     515         377 :                 nStatus = poDATFile->WriteCharField(
     516             :                     osValue, poDATFile->GetFieldWidth(iField), poINDFile,
     517         377 :                     panIndexNo[iField]);
     518             :             }
     519         377 :             break;
     520           5 :             case TABFDecimal:
     521           5 :                 nStatus = poDATFile->WriteDecimalField(
     522             :                     GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
     523             :                     poDATFile->GetFieldPrecision(iField), poINDFile,
     524           5 :                     panIndexNo[iField]);
     525           5 :                 break;
     526       14993 :             case TABFInteger:
     527       14993 :                 nStatus = poDATFile->WriteIntegerField(
     528       14993 :                     GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
     529       14993 :                 break;
     530           0 :             case TABFSmallInt:
     531           0 :                 nStatus = poDATFile->WriteSmallIntField(
     532           0 :                     static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
     533           0 :                     panIndexNo[iField]);
     534           0 :                 break;
     535           2 :             case TABFLargeInt:
     536           6 :                 nStatus = poDATFile->WriteLargeIntField(
     537           2 :                     static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
     538           2 :                     panIndexNo[iField]);
     539           2 :                 break;
     540         237 :             case TABFFloat:
     541         237 :                 nStatus = poDATFile->WriteFloatField(
     542         237 :                     GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
     543         237 :                 break;
     544           2 :             case TABFLogical:
     545             :                 nStatus =
     546           2 :                     poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
     547           2 :                                                  poINDFile, panIndexNo[iField]);
     548           2 :                 break;
     549          76 :             case TABFDate:
     550             : #ifdef MITAB_USE_OFTDATETIME
     551          76 :                 if (IsFieldSetAndNotNull(iField))
     552             :                 {
     553          59 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     554             :                                        &nMin, &fSec, &nTZFlag);
     555             :                 }
     556             :                 else
     557             :                 {
     558          17 :                     nYear = 0;
     559          17 :                     nMon = 0;
     560          17 :                     nDay = 0;
     561             :                 }
     562             : 
     563         152 :                 nStatus = poDATFile->WriteDateField(
     564          76 :                     nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
     565             : #else
     566             :                 nStatus = poDATFile->WriteDateField(
     567             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     568             : #endif
     569          76 :                 break;
     570           3 :             case TABFTime:
     571             : #ifdef MITAB_USE_OFTDATETIME
     572           3 :                 if (IsFieldSetAndNotNull(iField))
     573             :                 {
     574           2 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     575             :                                        &nMin, &fSec, &nTZFlag);
     576             :                 }
     577             :                 else
     578             :                 {
     579             :                     // Put negative values, so that WriteTimeField() forges
     580             :                     // a negative value, and ultimately write -1 in the binary
     581             :                     // field
     582           1 :                     nHour = -1;
     583           1 :                     nMin = -1;
     584           1 :                     fSec = -1;
     585             :                 }
     586           3 :                 nStatus = poDATFile->WriteTimeField(
     587             :                     nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
     588           3 :                     poINDFile, panIndexNo[iField]);
     589             : 
     590             : #else
     591             :                 nStatus = poDATFile->WriteTimeField(
     592             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     593             : #endif
     594           3 :                 break;
     595          75 :             case TABFDateTime:
     596             : #ifdef MITAB_USE_OFTDATETIME
     597          75 :                 if (IsFieldSetAndNotNull(iField))
     598             :                 {
     599          58 :                     GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
     600             :                                        &nMin, &fSec, &nTZFlag);
     601             :                 }
     602             :                 else
     603             :                 {
     604          17 :                     nYear = 0;
     605          17 :                     nMon = 0;
     606          17 :                     nDay = 0;
     607          17 :                     nHour = 0;
     608          17 :                     nMin = 0;
     609          17 :                     fSec = 0;
     610             :                 }
     611             : 
     612          75 :                 nStatus = poDATFile->WriteDateTimeField(
     613             :                     nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
     614          75 :                     OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
     615             : #else
     616             :                 nStatus = poDATFile->WriteDateTimeField(
     617             :                     GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
     618             : #endif
     619          75 :                 break;
     620           0 :             default:
     621             :                 // Other type???  Impossible!
     622           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     623             :                          "Unsupported field type!");
     624             :         }
     625             :     }
     626             : 
     627       15087 :     if (nStatus != 0)
     628           1 :         return nStatus;
     629             : 
     630       15086 :     if (poDATFile->CommitRecordToFile() != 0)
     631           0 :         return -1;
     632             : 
     633       15086 :     return 0;
     634             : }
     635             : 
     636             : /**********************************************************************
     637             :  *                   TABFeature::ReadGeometryFromMAPFile()
     638             :  *
     639             :  * In derived classes, this method should be reimplemented to
     640             :  * fill the geometry and representation (color, etc...) part of the
     641             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     642             :  *
     643             :  * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
     644             :  * currently points to the beginning of a map object.
     645             :  *
     646             :  * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
     647             :  * the CoordBlock data during splitting of object blocks. In this case we
     648             :  * need to process only the information related to the CoordBlock. One
     649             :  * important thing to avoid is reading/writing pen/brush/symbol definitions
     650             :  * as that would screw up their ref counters.
     651             :  *
     652             :  * ppoCoordBlock is used by TABCollection and by index splitting code
     653             :  * to provide a CoordBlock to use instead of the one from the poMAPFile and
     654             :  * return the current pointer at the end of the call.
     655             :  *
     656             :  * The current implementation does nothing since instances of TABFeature
     657             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     658             :  *
     659             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     660             :  * been called.
     661             :  **********************************************************************/
     662         155 : int TABFeature::ReadGeometryFromMAPFile(
     663             :     TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
     664             :     GBool /*bCoordBlockDataOnly=FALSE*/,
     665             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     666             : {
     667             :     // Nothing to do. Instances of TABFeature objects contain no geometry.
     668         155 :     return 0;
     669             : }
     670             : 
     671             : /**********************************************************************
     672             :  *                   TABFeature::UpdateMBR()
     673             :  *
     674             :  * Fetch envelope of poGeom and update MBR.
     675             :  * Integer coord MBR is updated only if poMapFile is not NULL.
     676             :  *
     677             :  * Returns 0 on success, or -1 if there is no geometry in object
     678             :  **********************************************************************/
     679       15017 : int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
     680             : {
     681       15017 :     OGRGeometry *poGeom = GetGeometryRef();
     682             : 
     683       15017 :     if (poGeom)
     684             :     {
     685       15017 :         OGREnvelope oEnv;
     686       15017 :         poGeom->getEnvelope(&oEnv);
     687             : 
     688       15017 :         m_dXMin = oEnv.MinX;
     689       15017 :         m_dYMin = oEnv.MinY;
     690       15017 :         m_dXMax = oEnv.MaxX;
     691       15017 :         m_dYMax = oEnv.MaxY;
     692             : 
     693       15017 :         if (poMapFile)
     694             :         {
     695       15017 :             poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
     696       15017 :             poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
     697             :             // Coordsy2Int can transform a min value to a max one and vice
     698             :             // versa.
     699       15017 :             if (m_nXMin > m_nXMax)
     700             :             {
     701           0 :                 std::swap(m_nXMin, m_nXMax);
     702             :             }
     703       15017 :             if (m_nYMin > m_nYMax)
     704             :             {
     705           0 :                 std::swap(m_nYMin, m_nYMax);
     706             :             }
     707             :         }
     708             : 
     709       15017 :         return 0;
     710             :     }
     711             : 
     712           0 :     return -1;
     713             : }
     714             : 
     715             : /**********************************************************************
     716             :  *                   TABFeature::ValidateCoordType()
     717             :  *
     718             :  * Checks the feature envelope to establish if the feature should be
     719             :  * written using Compressed coordinates or not and adjust m_nMapInfoType
     720             :  * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
     721             :  * m_nXMax, m_nYMax
     722             :  *
     723             :  * This function should be used only by the ValidateMapInfoType()
     724             :  * implementations.
     725             :  *
     726             :  * Returns TRUE if coord. should be compressed, FALSE otherwise
     727             :  **********************************************************************/
     728         303 : GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
     729             : {
     730         303 :     GBool bCompr = FALSE;
     731             : 
     732             :     /*-------------------------------------------------------------
     733             :      * Decide if coordinates should be compressed or not.
     734             :      *------------------------------------------------------------*/
     735         303 :     if (UpdateMBR(poMapFile) == 0)
     736             :     {
     737             :         /* Test for max range < 65535 here instead of < 65536 to avoid
     738             :          * compressed coordinate overflows in some boundary situations
     739             :          */
     740         303 :         if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
     741         294 :             (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
     742             :         {
     743         294 :             bCompr = TRUE;
     744             :         }
     745         303 :         m_nComprOrgX =
     746         303 :             static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
     747         303 :         m_nComprOrgY =
     748         303 :             static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
     749             :     }
     750             : 
     751             :     /*-------------------------------------------------------------
     752             :      * Adjust native type
     753             :      *------------------------------------------------------------*/
     754         303 :     if (bCompr && ((m_nMapInfoType % 3) == 2))
     755         294 :         m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
     756             :                                                   1);  // compr = 1, 4, 7, ...
     757           9 :     else if (!bCompr && ((m_nMapInfoType % 3) == 1))
     758           0 :         m_nMapInfoType = static_cast<TABGeomType>(
     759           0 :             m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
     760             : 
     761         303 :     return bCompr;
     762             : }
     763             : 
     764             : /**********************************************************************
     765             :  *                   TABFeature::ForceCoordTypeAndOrigin()
     766             :  *
     767             :  * This function is used by TABCollection::ValidateMapInfoType() to force
     768             :  * the coord type and compressed origin of all members of a collection
     769             :  * to be the same. (A replacement for ValidateCoordType() for this
     770             :  * specific case)
     771             :  **********************************************************************/
     772           0 : void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
     773             :                                          GInt32 nComprOrgX, GInt32 nComprOrgY,
     774             :                                          GInt32 nXMin, GInt32 nYMin,
     775             :                                          GInt32 nXMax, GInt32 nYMax)
     776             : {
     777             :     /*-------------------------------------------------------------
     778             :      * Set Compressed Origin and adjust native type
     779             :      *------------------------------------------------------------*/
     780           0 :     m_nComprOrgX = nComprOrgX;
     781           0 :     m_nComprOrgY = nComprOrgY;
     782             : 
     783           0 :     m_nMapInfoType = nMapInfoType;
     784             : 
     785           0 :     if (bCompr && ((m_nMapInfoType % 3) == 2))
     786           0 :         m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
     787             :                                                   1);  // compr = 1, 4, 7, ...
     788           0 :     else if (!bCompr && ((m_nMapInfoType % 3) == 1))
     789           0 :         m_nMapInfoType = static_cast<TABGeomType>(
     790           0 :             m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
     791             : 
     792           0 :     m_nXMin = nXMin;
     793           0 :     m_nYMin = nYMin;
     794           0 :     m_nXMax = nXMax;
     795           0 :     m_nYMax = nYMax;
     796           0 : }
     797             : 
     798             : /**********************************************************************
     799             :  *                   TABFeature::WriteGeometryToMAPFile()
     800             :  *
     801             :  *
     802             :  * In derived classes, this method should be reimplemented to
     803             :  * write the geometry and representation (color, etc...) part of the
     804             :  * feature to the .MAP object pointed to by poMAPFile.
     805             :  *
     806             :  * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
     807             :  * currently points to a valid map object.
     808             :  *
     809             :  * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
     810             :  * the CoordBlock data during splitting of object blocks. In this case we
     811             :  * need to process only the information related to the CoordBlock. One
     812             :  * important thing to avoid is reading/writing pen/brush/symbol definitions
     813             :  * as that would screw up their ref counters.
     814             :  *
     815             :  * ppoCoordBlock is used by TABCollection and by index splitting code
     816             :  * to provide a CoordBlock to use instead of the one from the poMAPFile and
     817             :  * return the current pointer at the end of the call.
     818             :  *
     819             :  * The current implementation does nothing since instances of TABFeature
     820             :  * objects contain no geometry (i.e. TAB_GEOM_NONE).
     821             :  *
     822             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     823             :  * been called.
     824             :  **********************************************************************/
     825          60 : int TABFeature::WriteGeometryToMAPFile(
     826             :     TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
     827             :     GBool /*bCoordBlockDataOnly=FALSE*/,
     828             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     829             : {
     830             :     /*-----------------------------------------------------------------
     831             :      * Nothing to do... instances of TABFeature objects contain no geometry.
     832             :      *----------------------------------------------------------------*/
     833             : 
     834          60 :     return 0;
     835             : }
     836             : 
     837             : /**********************************************************************
     838             :  *                   TABFeature::DumpMID()
     839             :  *
     840             :  * Dump feature attributes in a format similar to .MID data records.
     841             :  **********************************************************************/
     842           0 : void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
     843             : {
     844           0 :     const OGRFeatureDefn *l_poDefn = GetDefnRef();
     845             : 
     846           0 :     if (fpOut == nullptr)
     847           0 :         fpOut = stdout;
     848             : 
     849           0 :     for (int iField = 0; iField < l_poDefn->GetFieldCount(); iField++)
     850             :     {
     851           0 :         const OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
     852             : 
     853           0 :         fprintf(fpOut, "  %s (%s) = %s\n", poFDefn->GetNameRef(),
     854             :                 OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
     855             :                 GetFieldAsString(iField));
     856             :     }
     857             : 
     858           0 :     fflush(fpOut);
     859           0 : }
     860             : 
     861             : /**********************************************************************
     862             :  *                   TABFeature::DumpMIF()
     863             :  *
     864             :  * Dump feature geometry in a format similar to .MIF files.
     865             :  **********************************************************************/
     866           0 : void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
     867             : {
     868           0 :     if (fpOut == nullptr)
     869           0 :         fpOut = stdout;
     870             : 
     871             :     /*-----------------------------------------------------------------
     872             :      * Generate output... not much to do, feature contains no geometry.
     873             :      *----------------------------------------------------------------*/
     874           0 :     fprintf(fpOut, "NONE\n");
     875             : 
     876           0 :     fflush(fpOut);
     877           0 : }
     878             : 
     879             : /*=====================================================================
     880             :  *                      class TABPoint
     881             :  *====================================================================*/
     882             : 
     883             : /**********************************************************************
     884             :  *                   TABPoint::TABPoint()
     885             :  *
     886             :  * Constructor.
     887             :  **********************************************************************/
     888      565939 : TABPoint::TABPoint(const OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
     889             : {
     890      565939 : }
     891             : 
     892             : /**********************************************************************
     893             :  *                   TABPoint::~TABPoint()
     894             :  *
     895             :  * Destructor.
     896             :  **********************************************************************/
     897     1130549 : TABPoint::~TABPoint()
     898             : {
     899     1130549 : }
     900             : 
     901             : /**********************************************************************
     902             :  *                     TABPoint::CloneTABFeature()
     903             :  *
     904             :  * Duplicate feature, including stuff specific to each TABFeature type.
     905             :  *
     906             :  * This method calls the generic TABFeature::CloneTABFeature() and
     907             :  * then copies any members specific to its own type.
     908             :  **********************************************************************/
     909           2 : TABFeature *TABPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
     910             : {
     911             :     /*-----------------------------------------------------------------
     912             :      * Alloc new feature and copy the base stuff
     913             :      *----------------------------------------------------------------*/
     914           2 :     TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
     915             : 
     916           2 :     CopyTABFeatureBase(poNew);
     917             : 
     918             :     /*-----------------------------------------------------------------
     919             :      * And members specific to this class
     920             :      *----------------------------------------------------------------*/
     921             :     // ITABFeatureSymbol
     922           2 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
     923             : 
     924           2 :     return poNew;
     925             : }
     926             : 
     927             : /**********************************************************************
     928             :  *                   TABPoint::ValidateMapInfoType()
     929             :  *
     930             :  * Check the feature's geometry part and return the corresponding
     931             :  * mapinfo object type code.  The m_nMapInfoType member will also
     932             :  * be updated for further calls to GetMapInfoType();
     933             :  *
     934             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
     935             :  * is expected for this object class.
     936             :  **********************************************************************/
     937       14691 : TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
     938             : {
     939             :     /*-----------------------------------------------------------------
     940             :      * Fetch and validate geometry
     941             :      * __TODO__ For now we always write in uncompressed format (until we
     942             :      * find that this is not correct... note that at this point the
     943             :      * decision to use compressed/uncompressed will likely be based on
     944             :      * the distance between the point and the object block center in
     945             :      * integer coordinates being > 32767 or not... remains to be verified)
     946             :      *----------------------------------------------------------------*/
     947       14691 :     OGRGeometry *poGeom = GetGeometryRef();
     948       14691 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     949             :     {
     950       14691 :         switch (GetFeatureClass())
     951             :         {
     952           2 :             case TABFCFontPoint:
     953           2 :                 m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
     954           2 :                 break;
     955           2 :             case TABFCCustomPoint:
     956           2 :                 m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
     957           2 :                 break;
     958       14687 :             case TABFCPoint:
     959             :             default:
     960       14687 :                 m_nMapInfoType = TAB_GEOM_SYMBOL;
     961       14687 :                 break;
     962             :         }
     963             :     }
     964             :     else
     965             :     {
     966           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     967             :                  "TABPoint: Missing or Invalid Geometry!");
     968           0 :         m_nMapInfoType = TAB_GEOM_NONE;
     969             :     }
     970             : 
     971       14691 :     UpdateMBR(poMapFile);
     972             : 
     973       14691 :     return m_nMapInfoType;
     974             : }
     975             : 
     976             : /**********************************************************************
     977             :  *                   TABPoint::ReadGeometryFromMAPFile()
     978             :  *
     979             :  * Fill the geometry and representation (color, etc...) part of the
     980             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
     981             :  *
     982             :  * It is assumed that poMAPFile currently points to the beginning of
     983             :  * a map object.
     984             :  *
     985             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
     986             :  * been called.
     987             :  **********************************************************************/
     988      549085 : int TABPoint::ReadGeometryFromMAPFile(
     989             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
     990             :     GBool bCoordBlockDataOnly /*=FALSE*/,
     991             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
     992             : {
     993             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
     994      549085 :     if (bCoordBlockDataOnly)
     995           0 :         return 0;
     996             : 
     997             :     /*-----------------------------------------------------------------
     998             :      * Fetch and validate geometry type
     999             :      *----------------------------------------------------------------*/
    1000      549085 :     m_nMapInfoType = poObjHdr->m_nType;
    1001             : 
    1002      549085 :     if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
    1003       13872 :         m_nMapInfoType != TAB_GEOM_SYMBOL_C)
    1004             :     {
    1005           0 :         CPLError(
    1006             :             CE_Failure, CPLE_AssertionFailed,
    1007             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1008           0 :             m_nMapInfoType, m_nMapInfoType);
    1009           0 :         return -1;
    1010             :     }
    1011             : 
    1012             :     /*-----------------------------------------------------------------
    1013             :      * Read object information
    1014             :      *----------------------------------------------------------------*/
    1015      549085 :     TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
    1016             : 
    1017      549085 :     m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
    1018             : 
    1019      549085 :     poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    1020             : 
    1021             :     /*-----------------------------------------------------------------
    1022             :      * Create and fill geometry object
    1023             :      *----------------------------------------------------------------*/
    1024      549085 :     double dX = 0.0;
    1025      549085 :     double dY = 0.0;
    1026             : 
    1027      549085 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1028      549085 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1029             : 
    1030      549085 :     SetGeometryDirectly(poGeometry);
    1031             : 
    1032      549085 :     SetMBR(dX, dY, dX, dY);
    1033      549085 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1034             :               poObjHdr->m_nMaxY);
    1035             : 
    1036      549085 :     return 0;
    1037             : }
    1038             : 
    1039             : /**********************************************************************
    1040             :  *                   TABPoint::WriteGeometryToMAPFile()
    1041             :  *
    1042             :  * Write the geometry and representation (color, etc...) part of the
    1043             :  * feature to the .MAP object pointed to by poMAPFile.
    1044             :  *
    1045             :  * It is assumed that poMAPFile currently points to a valid map object.
    1046             :  *
    1047             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1048             :  * been called.
    1049             :  **********************************************************************/
    1050       14687 : int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    1051             :                                      TABMAPObjHdr *poObjHdr,
    1052             :                                      GBool bCoordBlockDataOnly /*=FALSE*/,
    1053             :                                      TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1054             : {
    1055             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1056       14687 :     if (bCoordBlockDataOnly)
    1057           0 :         return 0;
    1058             : 
    1059             :     /*-----------------------------------------------------------------
    1060             :      * We assume that ValidateMapInfoType() was called already and that
    1061             :      * the type in poObjHdr->m_nType is valid.
    1062             :      *----------------------------------------------------------------*/
    1063       14687 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1064             : 
    1065             :     /*-----------------------------------------------------------------
    1066             :      * Fetch and validate geometry
    1067             :      *----------------------------------------------------------------*/
    1068       14687 :     OGRGeometry *poGeom = GetGeometryRef();
    1069       14687 :     OGRPoint *poPoint = nullptr;
    1070       14687 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1071       14687 :         poPoint = poGeom->toPoint();
    1072             :     else
    1073             :     {
    1074           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1075             :                  "TABPoint: Missing or Invalid Geometry!");
    1076           0 :         return -1;
    1077             :     }
    1078             : 
    1079       14687 :     GInt32 nX = 0;
    1080       14687 :     GInt32 nY = 0;
    1081       14687 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1082             : 
    1083             :     /*-----------------------------------------------------------------
    1084             :      * Copy object information
    1085             :      *----------------------------------------------------------------*/
    1086       14687 :     TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
    1087             : 
    1088       14687 :     poPointHdr->m_nX = nX;
    1089       14687 :     poPointHdr->m_nY = nY;
    1090       14687 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1091             : 
    1092       14687 :     m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    1093       14687 :     poPointHdr->m_nSymbolId =
    1094       14687 :         static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    1095             : 
    1096       14687 :     if (CPLGetLastErrorType() == CE_Failure)
    1097           0 :         return -1;
    1098             : 
    1099       14687 :     return 0;
    1100             : }
    1101             : 
    1102             : /**********************************************************************
    1103             :  *                   TABPoint::GetX()
    1104             :  *
    1105             :  * Return this point's X coordinate.
    1106             :  **********************************************************************/
    1107           0 : double TABPoint::GetX()
    1108             : {
    1109             : 
    1110             :     /*-----------------------------------------------------------------
    1111             :      * Fetch and validate geometry
    1112             :      *----------------------------------------------------------------*/
    1113           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1114           0 :     OGRPoint *poPoint = nullptr;
    1115           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1116           0 :         poPoint = poGeom->toPoint();
    1117             :     else
    1118             :     {
    1119           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1120             :                  "TABPoint: Missing or Invalid Geometry!");
    1121           0 :         return 0.0;
    1122             :     }
    1123             : 
    1124           0 :     return poPoint->getX();
    1125             : }
    1126             : 
    1127             : /**********************************************************************
    1128             :  *                   TABPoint::GetY()
    1129             :  *
    1130             :  * Return this point's Y coordinate.
    1131             :  **********************************************************************/
    1132           0 : double TABPoint::GetY()
    1133             : {
    1134             :     /*-----------------------------------------------------------------
    1135             :      * Fetch and validate geometry
    1136             :      *----------------------------------------------------------------*/
    1137           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1138           0 :     OGRPoint *poPoint = nullptr;
    1139           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1140           0 :         poPoint = poGeom->toPoint();
    1141             :     else
    1142             :     {
    1143           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1144             :                  "TABPoint: Missing or Invalid Geometry!");
    1145           0 :         return 0.0;
    1146             :     }
    1147             : 
    1148           0 :     return poPoint->getY();
    1149             : }
    1150             : 
    1151             : /**********************************************************************
    1152             :  *                   TABPoint::GetStyleString() const
    1153             :  *
    1154             :  * Return style string for this feature.
    1155             :  *
    1156             :  * Style String is built only once during the first call to GetStyleString().
    1157             :  **********************************************************************/
    1158         239 : const char *TABPoint::GetStyleString() const
    1159             : {
    1160         239 :     if (m_pszStyleString == nullptr)
    1161             :     {
    1162          27 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    1163             :     }
    1164             : 
    1165         239 :     return m_pszStyleString;
    1166             : }
    1167             : 
    1168             : /**********************************************************************
    1169             :  *                   TABPoint::DumpMIF()
    1170             :  *
    1171             :  * Dump feature geometry in a format similar to .MIF POINTs.
    1172             :  **********************************************************************/
    1173           0 : void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
    1174             : {
    1175           0 :     if (fpOut == nullptr)
    1176           0 :         fpOut = stdout;
    1177             : 
    1178             :     /*-----------------------------------------------------------------
    1179             :      * Fetch and validate geometry
    1180             :      *----------------------------------------------------------------*/
    1181           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1182           0 :     OGRPoint *poPoint = nullptr;
    1183           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1184           0 :         poPoint = poGeom->toPoint();
    1185             :     else
    1186             :     {
    1187           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1188             :                  "TABPoint: Missing or Invalid Geometry!");
    1189           0 :         return;
    1190             :     }
    1191             : 
    1192             :     /*-----------------------------------------------------------------
    1193             :      * Generate output
    1194             :      *----------------------------------------------------------------*/
    1195           0 :     fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
    1196             : 
    1197           0 :     DumpSymbolDef(fpOut);
    1198             : 
    1199             :     /*-----------------------------------------------------------------
    1200             :      * Handle stuff specific to derived classes
    1201             :      *----------------------------------------------------------------*/
    1202             :     // cppcheck-suppress knownConditionTrueFalse
    1203           0 :     if (GetFeatureClass() == TABFCFontPoint)
    1204             :     {
    1205           0 :         TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
    1206           0 :         fprintf(fpOut, "  m_nFontStyle     = 0x%2.2x (%d)\n",
    1207             :                 poFeature->GetFontStyleTABValue(),
    1208             :                 poFeature->GetFontStyleTABValue());
    1209             : 
    1210           0 :         poFeature->DumpFontDef(fpOut);
    1211             :     }
    1212             :     // cppcheck-suppress knownConditionTrueFalse
    1213           0 :     if (GetFeatureClass() == TABFCCustomPoint)
    1214             :     {
    1215           0 :         TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
    1216             : 
    1217           0 :         fprintf(fpOut, "  m_nUnknown_      = 0x%2.2x (%d)\n",
    1218           0 :                 poFeature->m_nUnknown_, poFeature->m_nUnknown_);
    1219           0 :         fprintf(fpOut, "  m_nCustomStyle   = 0x%2.2x (%d)\n",
    1220           0 :                 poFeature->GetCustomSymbolStyle(),
    1221           0 :                 poFeature->GetCustomSymbolStyle());
    1222             : 
    1223           0 :         poFeature->DumpFontDef(fpOut);
    1224             :     }
    1225             : 
    1226           0 :     fflush(fpOut);
    1227             : }
    1228             : 
    1229             : /*=====================================================================
    1230             :  *                      class TABFontPoint
    1231             :  *====================================================================*/
    1232             : 
    1233             : /**********************************************************************
    1234             :  *                   TABFontPoint::TABFontPoint()
    1235             :  *
    1236             :  * Constructor.
    1237             :  **********************************************************************/
    1238         643 : TABFontPoint::TABFontPoint(const OGRFeatureDefn *poDefnIn)
    1239         643 :     : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
    1240             : {
    1241         643 : }
    1242             : 
    1243             : /**********************************************************************
    1244             :  *                   TABFontPoint::~TABFontPoint()
    1245             :  *
    1246             :  * Destructor.
    1247             :  **********************************************************************/
    1248        1286 : TABFontPoint::~TABFontPoint()
    1249             : {
    1250        1286 : }
    1251             : 
    1252             : /**********************************************************************
    1253             :  *                     TABFontPoint::CloneTABFeature()
    1254             :  *
    1255             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1256             :  *
    1257             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1258             :  * then copies any members specific to its own type.
    1259             :  **********************************************************************/
    1260             : TABFeature *
    1261           0 : TABFontPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    1262             : {
    1263             :     /*-----------------------------------------------------------------
    1264             :      * Alloc new feature and copy the base stuff
    1265             :      *----------------------------------------------------------------*/
    1266             :     TABFontPoint *poNew =
    1267           0 :         new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
    1268             : 
    1269           0 :     CopyTABFeatureBase(poNew);
    1270             : 
    1271             :     /*-----------------------------------------------------------------
    1272             :      * And members specific to this class
    1273             :      *----------------------------------------------------------------*/
    1274             :     // ITABFeatureSymbol
    1275           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    1276             : 
    1277             :     // ITABFeatureFont
    1278           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    1279             : 
    1280           0 :     poNew->SetSymbolAngle(GetSymbolAngle());
    1281           0 :     poNew->SetFontStyleTABValue(GetFontStyleTABValue());
    1282             : 
    1283           0 :     return poNew;
    1284             : }
    1285             : 
    1286             : /**********************************************************************
    1287             :  *                   TABFontPoint::ReadGeometryFromMAPFile()
    1288             :  *
    1289             :  * Fill the geometry and representation (color, etc...) part of the
    1290             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    1291             :  *
    1292             :  * It is assumed that poMAPFile currently points to the beginning of
    1293             :  * a map object.
    1294             :  *
    1295             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1296             :  * been called.
    1297             :  **********************************************************************/
    1298           6 : int TABFontPoint::ReadGeometryFromMAPFile(
    1299             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1300             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1301             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1302             : {
    1303             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1304           6 :     if (bCoordBlockDataOnly)
    1305           0 :         return 0;
    1306             : 
    1307             :     /*-----------------------------------------------------------------
    1308             :      * Fetch and validate geometry type
    1309             :      *----------------------------------------------------------------*/
    1310           6 :     m_nMapInfoType = poObjHdr->m_nType;
    1311             : 
    1312           6 :     if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
    1313           0 :         m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
    1314             :     {
    1315           0 :         CPLError(
    1316             :             CE_Failure, CPLE_AssertionFailed,
    1317             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1318           0 :             m_nMapInfoType, m_nMapInfoType);
    1319           0 :         return -1;
    1320             :     }
    1321             : 
    1322             :     /*-----------------------------------------------------------------
    1323             :      * Read object information
    1324             :      * NOTE: This symbol type does not contain a reference to a
    1325             :      * SymbolDef block in the file, but we still use the m_sSymbolDef
    1326             :      * structure to store the information inside the class so that the
    1327             :      * ITABFeatureSymbol methods work properly for the class user.
    1328             :      *----------------------------------------------------------------*/
    1329             :     TABMAPObjFontPoint *poPointHdr =
    1330           6 :         cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
    1331             : 
    1332           6 :     m_nSymbolDefIndex = -1;
    1333           6 :     m_sSymbolDef.nRefCount = 0;
    1334             : 
    1335           6 :     m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId;    // shape
    1336           6 :     m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize;  // point size
    1337             : 
    1338           6 :     m_nFontStyle = poPointHdr->m_nFontStyle;  // font style
    1339             : 
    1340           6 :     m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
    1341           6 :                             poPointHdr->m_nG * 256 + poPointHdr->m_nB;
    1342             : 
    1343             :     /*-------------------------------------------------------------
    1344             :      * Symbol Angle, in tenths of degree.
    1345             :      * Contrary to arc start/end angles, no conversion based on
    1346             :      * origin quadrant is required here.
    1347             :      *------------------------------------------------------------*/
    1348           6 :     m_dAngle = poPointHdr->m_nAngle / 10.0;
    1349             : 
    1350           6 :     m_nFontDefIndex = poPointHdr->m_nFontId;  // Font name index
    1351             : 
    1352           6 :     poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    1353             : 
    1354             :     /*-----------------------------------------------------------------
    1355             :      * Create and fill geometry object
    1356             :      *----------------------------------------------------------------*/
    1357           6 :     double dX = 0.0;
    1358           6 :     double dY = 0.0;
    1359           6 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1360           6 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1361             : 
    1362           6 :     SetGeometryDirectly(poGeometry);
    1363             : 
    1364           6 :     SetMBR(dX, dY, dX, dY);
    1365           6 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1366             :               poObjHdr->m_nMaxY);
    1367             : 
    1368           6 :     return 0;
    1369             : }
    1370             : 
    1371             : /**********************************************************************
    1372             :  *                   TABFontPoint::WriteGeometryToMAPFile()
    1373             :  *
    1374             :  * Write the geometry and representation (color, etc...) part of the
    1375             :  * feature to the .MAP object pointed to by poMAPFile.
    1376             :  *
    1377             :  * It is assumed that poMAPFile currently points to a valid map object.
    1378             :  *
    1379             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1380             :  * been called.
    1381             :  **********************************************************************/
    1382           2 : int TABFontPoint::WriteGeometryToMAPFile(
    1383             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1384             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1385             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1386             : {
    1387             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1388           2 :     if (bCoordBlockDataOnly)
    1389           0 :         return 0;
    1390             : 
    1391             :     /*-----------------------------------------------------------------
    1392             :      * We assume that ValidateMapInfoType() was called already and that
    1393             :      * the type in poObjHdr->m_nType is valid.
    1394             :      *----------------------------------------------------------------*/
    1395           2 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1396             : 
    1397             :     /*-----------------------------------------------------------------
    1398             :      * Fetch and validate geometry
    1399             :      *----------------------------------------------------------------*/
    1400           2 :     OGRGeometry *poGeom = GetGeometryRef();
    1401           2 :     OGRPoint *poPoint = nullptr;
    1402           2 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1403           2 :         poPoint = poGeom->toPoint();
    1404             :     else
    1405             :     {
    1406           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1407             :                  "TABFontPoint: Missing or Invalid Geometry!");
    1408           0 :         return -1;
    1409             :     }
    1410             : 
    1411           2 :     GInt32 nX = 0;
    1412           2 :     GInt32 nY = 0;
    1413           2 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1414             : 
    1415             :     /*-----------------------------------------------------------------
    1416             :      * Copy object information
    1417             :      * NOTE: This symbol type does not contain a reference to a
    1418             :      * SymbolDef block in the file, but we still use the m_sSymbolDef
    1419             :      * structure to store the information inside the class so that the
    1420             :      * ITABFeatureSymbol methods work properly for the class user.
    1421             :      *----------------------------------------------------------------*/
    1422             :     TABMAPObjFontPoint *poPointHdr =
    1423           2 :         cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
    1424             : 
    1425           2 :     poPointHdr->m_nX = nX;
    1426           2 :     poPointHdr->m_nY = nY;
    1427           2 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1428             : 
    1429           2 :     poPointHdr->m_nSymbolId =
    1430           2 :         static_cast<GByte>(m_sSymbolDef.nSymbolNo);  // shape
    1431           2 :     poPointHdr->m_nPointSize =
    1432           2 :         static_cast<GByte>(m_sSymbolDef.nPointSize);  // point size
    1433           2 :     poPointHdr->m_nFontStyle = m_nFontStyle;          // font style
    1434             : 
    1435           2 :     poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
    1436           2 :     poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
    1437           2 :     poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
    1438             : 
    1439             :     /*-------------------------------------------------------------
    1440             :      * Symbol Angle, in tenths of degree.
    1441             :      * Contrary to arc start/end angles, no conversion based on
    1442             :      * origin quadrant is required here.
    1443             :      *------------------------------------------------------------*/
    1444           2 :     poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
    1445             : 
    1446             :     // Write Font Def
    1447           2 :     m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    1448           2 :     poPointHdr->m_nFontId =
    1449           2 :         static_cast<GByte>(m_nFontDefIndex);  // Font name index
    1450             : 
    1451           2 :     if (CPLGetLastErrorType() == CE_Failure)
    1452           0 :         return -1;
    1453             : 
    1454           2 :     return 0;
    1455             : }
    1456             : 
    1457             : /**********************************************************************
    1458             :  *                   TABFontPoint::QueryFontStyle()
    1459             :  *
    1460             :  * Return TRUE if the specified font style attribute is turned ON,
    1461             :  * or FALSE otherwise.  See enum TABFontStyle for the list of styles
    1462             :  * that can be queried on.
    1463             :  **********************************************************************/
    1464           0 : GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
    1465             : {
    1466           0 :     return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
    1467             : }
    1468             : 
    1469           0 : void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
    1470             : {
    1471           0 :     if (bStyleOn)
    1472           0 :         m_nFontStyle |= static_cast<int>(eStyleToToggle);
    1473             :     else
    1474           0 :         m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
    1475           0 : }
    1476             : 
    1477             : /**********************************************************************
    1478             :  *                   TABFontPoint::GetFontStyleMIFValue()
    1479             :  *
    1480             :  * Return the Font Style value for this object using the style values
    1481             :  * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
    1482             :  *
    1483             :  * The reason why we have to differentiate between the TAB and the MIF font
    1484             :  * style values is that in TAB, TABFSBox is included in the style value
    1485             :  * as code 0x100, but in MIF it is not included, instead it is implied by
    1486             :  * the presence of the BG color in the FONT() clause (the BG color is
    1487             :  * present only when TABFSBox or TABFSHalo is set).
    1488             :  * This also has the effect of shifting all the other style values > 0x100
    1489             :  * by 1 byte.
    1490             :  *
    1491             :  * NOTE: Even if there is no BG color for font symbols, we inherit this
    1492             :  * problem because Font Point styles use the same codes as Text Font styles.
    1493             :  **********************************************************************/
    1494           0 : int TABFontPoint::GetFontStyleMIFValue()
    1495             : {
    1496             :     // The conversion is simply to remove bit 0x100 from the value and shift
    1497             :     // down all values past this bit.
    1498           0 :     return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
    1499             : }
    1500             : 
    1501         635 : void TABFontPoint::SetFontStyleMIFValue(int nStyle)
    1502             : {
    1503         635 :     m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
    1504         635 : }
    1505             : 
    1506             : /**********************************************************************
    1507             :  *                   TABFontPoint::SetSymbolAngle()
    1508             :  *
    1509             :  * Set the symbol angle value in degrees, making sure the value is
    1510             :  * always in the range [0..360]
    1511             :  **********************************************************************/
    1512         635 : void TABFontPoint::SetSymbolAngle(double dAngle)
    1513             : {
    1514         635 :     dAngle = fmod(dAngle, 360.0);
    1515         635 :     if (dAngle < 0.0)
    1516           0 :         dAngle += 360.0;
    1517             : 
    1518         635 :     m_dAngle = dAngle;
    1519         635 : }
    1520             : 
    1521             : /**********************************************************************
    1522             :  *                   TABFontPoint::GetSymbolStyleString()
    1523             :  *
    1524             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    1525             :  **********************************************************************/
    1526           7 : const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
    1527             : {
    1528             :     /* Get the SymbolStyleString, and add the outline Color
    1529             :        (halo/border in MapInfo Symbol terminology) */
    1530           7 :     const char *outlineColor = nullptr;
    1531           7 :     if (m_nFontStyle & 16)
    1532           0 :         outlineColor = ",o:#000000";
    1533           7 :     else if (m_nFontStyle & 512)
    1534           0 :         outlineColor = ",o:#ffffff";
    1535             :     else
    1536           7 :         outlineColor = "";
    1537             : 
    1538           7 :     int nAngle = static_cast<int>(dfAngle);
    1539             :     const char *pszStyle;
    1540             : 
    1541           7 :     pszStyle = CPLSPrintf(
    1542             :         "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
    1543           7 :         nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
    1544           7 :         m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
    1545           7 :     return pszStyle;
    1546             : }
    1547             : 
    1548             : /**********************************************************************
    1549             :  *                   TABFontPoint::GetStyleString() const
    1550             :  *
    1551             :  * Return style string for this feature.
    1552             :  *
    1553             :  * Style String is built only once during the first call to GetStyleString().
    1554             :  **********************************************************************/
    1555           9 : const char *TABFontPoint::GetStyleString() const
    1556             : {
    1557           9 :     if (m_pszStyleString == nullptr)
    1558             :     {
    1559           7 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
    1560             :     }
    1561             : 
    1562           9 :     return m_pszStyleString;
    1563             : }
    1564             : 
    1565             : /**********************************************************************
    1566             :  *                   TABFontPoint::SetSymbolFromStyle()
    1567             :  *
    1568             :  *  Set all Symbol var from a OGRStyleSymbol.
    1569             :  **********************************************************************/
    1570           2 : void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    1571             : {
    1572           2 :     ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
    1573             : 
    1574           2 :     GBool bIsNull = 0;
    1575             : 
    1576             :     // Try to set font glyph number
    1577           2 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    1578           2 :     if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
    1579             :     {
    1580           2 :         const int nSymbolId = atoi(pszSymbolId + 9);
    1581           2 :         SetSymbolNo(static_cast<GInt16>(nSymbolId));
    1582             :     }
    1583             : 
    1584           2 :     const char *pszFontName = poSymbolStyle->FontName(bIsNull);
    1585           2 :     if ((!bIsNull) && pszFontName)
    1586             :     {
    1587           2 :         SetFontName(pszFontName);
    1588             :     }
    1589           2 : }
    1590             : 
    1591             : /*=====================================================================
    1592             :  *                      class TABCustomPoint
    1593             :  *====================================================================*/
    1594             : 
    1595             : /**********************************************************************
    1596             :  *                   TABCustomPoint::TABCustomPoint()
    1597             :  *
    1598             :  * Constructor.
    1599             :  **********************************************************************/
    1600         686 : TABCustomPoint::TABCustomPoint(const OGRFeatureDefn *poDefnIn)
    1601         686 :     : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
    1602             : {
    1603         686 : }
    1604             : 
    1605             : /**********************************************************************
    1606             :  *                   TABCustomPoint::~TABCustomPoint()
    1607             :  *
    1608             :  * Destructor.
    1609             :  **********************************************************************/
    1610        1372 : TABCustomPoint::~TABCustomPoint()
    1611             : {
    1612        1372 : }
    1613             : 
    1614             : /**********************************************************************
    1615             :  *                     TABCustomPoint::CloneTABFeature()
    1616             :  *
    1617             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1618             :  *
    1619             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1620             :  * then copies any members specific to its own type.
    1621             :  **********************************************************************/
    1622             : TABFeature *
    1623           0 : TABCustomPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    1624             : {
    1625             :     /*-----------------------------------------------------------------
    1626             :      * Alloc new feature and copy the base stuff
    1627             :      *----------------------------------------------------------------*/
    1628             :     TABCustomPoint *poNew =
    1629           0 :         new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
    1630             : 
    1631           0 :     CopyTABFeatureBase(poNew);
    1632             : 
    1633             :     /*-----------------------------------------------------------------
    1634             :      * And members specific to this class
    1635             :      *----------------------------------------------------------------*/
    1636             :     // ITABFeatureSymbol
    1637           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    1638             : 
    1639             :     // ITABFeatureFont
    1640           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    1641             : 
    1642           0 :     poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
    1643             : 
    1644           0 :     return poNew;
    1645             : }
    1646             : 
    1647             : /**********************************************************************
    1648             :  *                   TABCustomPoint::ReadGeometryFromMAPFile()
    1649             :  *
    1650             :  * Fill the geometry and representation (color, etc...) part of the
    1651             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    1652             :  *
    1653             :  * It is assumed that poMAPFile currently points to the beginning of
    1654             :  * a map object.
    1655             :  *
    1656             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1657             :  * been called.
    1658             :  **********************************************************************/
    1659           6 : int TABCustomPoint::ReadGeometryFromMAPFile(
    1660             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1661             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1662             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1663             : {
    1664             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1665           6 :     if (bCoordBlockDataOnly)
    1666           0 :         return 0;
    1667             : 
    1668             :     /*-----------------------------------------------------------------
    1669             :      * Fetch and validate geometry type
    1670             :      *----------------------------------------------------------------*/
    1671           6 :     m_nMapInfoType = poObjHdr->m_nType;
    1672             : 
    1673           6 :     if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
    1674           0 :         m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
    1675             :     {
    1676           0 :         CPLError(
    1677             :             CE_Failure, CPLE_AssertionFailed,
    1678             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    1679           0 :             m_nMapInfoType, m_nMapInfoType);
    1680           0 :         return -1;
    1681             :     }
    1682             : 
    1683             :     /*-----------------------------------------------------------------
    1684             :      * Read object information
    1685             :      *----------------------------------------------------------------*/
    1686             :     TABMAPObjCustomPoint *poPointHdr =
    1687           6 :         cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
    1688             : 
    1689           6 :     m_nUnknown_ = poPointHdr->m_nUnknown_;        // ???
    1690           6 :     m_nCustomStyle = poPointHdr->m_nCustomStyle;  // 0x01=Show BG,
    1691             :                                                   // 0x02=Apply Color
    1692             : 
    1693           6 :     m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
    1694           6 :     poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    1695             : 
    1696           6 :     m_nFontDefIndex = poPointHdr->m_nFontId;  // Font index
    1697           6 :     poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    1698             : 
    1699             :     /*-----------------------------------------------------------------
    1700             :      * Create and fill geometry object
    1701             :      *----------------------------------------------------------------*/
    1702           6 :     double dX = 0.0;
    1703           6 :     double dY = 0.0;
    1704           6 :     poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
    1705           6 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    1706             : 
    1707           6 :     SetGeometryDirectly(poGeometry);
    1708             : 
    1709           6 :     SetMBR(dX, dY, dX, dY);
    1710           6 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1711             :               poObjHdr->m_nMaxY);
    1712             : 
    1713           6 :     return 0;
    1714             : }
    1715             : 
    1716             : /**********************************************************************
    1717             :  *                   TABCustomPoint::WriteGeometryToMAPFile()
    1718             :  *
    1719             :  * Write the geometry and representation (color, etc...) part of the
    1720             :  * feature to the .MAP object pointed to by poMAPFile.
    1721             :  *
    1722             :  * It is assumed that poMAPFile currently points to a valid map object.
    1723             :  *
    1724             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    1725             :  * been called.
    1726             :  **********************************************************************/
    1727           2 : int TABCustomPoint::WriteGeometryToMAPFile(
    1728             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    1729             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    1730             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    1731             : {
    1732             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    1733           2 :     if (bCoordBlockDataOnly)
    1734           0 :         return 0;
    1735             : 
    1736             :     /*-----------------------------------------------------------------
    1737             :      * We assume that ValidateMapInfoType() was called already and that
    1738             :      * the type in poObjHdr->m_nType is valid.
    1739             :      *----------------------------------------------------------------*/
    1740           2 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    1741             : 
    1742             :     /*-----------------------------------------------------------------
    1743             :      * Fetch and validate geometry
    1744             :      *----------------------------------------------------------------*/
    1745           2 :     OGRGeometry *poGeom = GetGeometryRef();
    1746           2 :     OGRPoint *poPoint = nullptr;
    1747           2 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    1748           2 :         poPoint = poGeom->toPoint();
    1749             :     else
    1750             :     {
    1751           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1752             :                  "TABCustomPoint: Missing or Invalid Geometry!");
    1753           0 :         return -1;
    1754             :     }
    1755             : 
    1756           2 :     GInt32 nX = 0;
    1757           2 :     GInt32 nY = 0;
    1758           2 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    1759             : 
    1760             :     /*-----------------------------------------------------------------
    1761             :      * Copy object information
    1762             :      *----------------------------------------------------------------*/
    1763             :     TABMAPObjCustomPoint *poPointHdr =
    1764           2 :         cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
    1765             : 
    1766           2 :     poPointHdr->m_nX = nX;
    1767           2 :     poPointHdr->m_nY = nY;
    1768           2 :     poPointHdr->SetMBR(nX, nY, nX, nY);
    1769           2 :     poPointHdr->m_nUnknown_ = m_nUnknown_;
    1770           2 :     poPointHdr->m_nCustomStyle = m_nCustomStyle;  // 0x01=Show BG,
    1771             :                                                   // 0x02=Apply Color
    1772             : 
    1773           2 :     m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    1774           2 :     poPointHdr->m_nSymbolId =
    1775           2 :         static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    1776             : 
    1777           2 :     m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    1778           2 :     poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex);  // Font index
    1779             : 
    1780           2 :     if (CPLGetLastErrorType() == CE_Failure)
    1781           0 :         return -1;
    1782             : 
    1783           2 :     return 0;
    1784             : }
    1785             : 
    1786             : /**********************************************************************
    1787             :  *                   TABCustomPoint::GetSymbolStyleString()
    1788             :  *
    1789             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    1790             :  **********************************************************************/
    1791           7 : const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
    1792             : {
    1793             :     /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
    1794             :      * "apply color". */
    1795           7 :     const char *color = nullptr;
    1796           7 :     if (m_nCustomStyle & 0x02)
    1797           7 :         color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
    1798             :     else
    1799           0 :         color = "";
    1800             : 
    1801           7 :     int nAngle = static_cast<int>(dfAngle);
    1802             :     const char *pszStyle;
    1803           7 :     const std::string osExt = CPLGetExtensionSafe(GetSymbolNameRef());
    1804           7 :     char szLowerExt[8] = "";
    1805           7 :     const char *pszPtr = osExt.c_str();
    1806             :     int i;
    1807             : 
    1808           7 :     for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
    1809             :     {
    1810           0 :         szLowerExt[i] =
    1811           0 :             static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
    1812             :     }
    1813           7 :     szLowerExt[i] = '\0';
    1814             : 
    1815           7 :     pszStyle = CPLSPrintf(
    1816             :         "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
    1817           7 :         nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
    1818             :         GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
    1819          14 :     return pszStyle;
    1820             : }
    1821             : 
    1822             : /**********************************************************************
    1823             :  *                   TABCustomPoint::SetSymbolFromStyle()
    1824             :  *
    1825             :  *  Set all Symbol var from a OGRStyleSymbol.
    1826             :  **********************************************************************/
    1827           2 : void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    1828             : {
    1829           2 :     ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
    1830             : 
    1831           2 :     GBool bIsNull = 0;
    1832             : 
    1833             :     // Try to set font glyph number
    1834           2 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    1835           2 :     if ((!bIsNull) && pszSymbolId &&
    1836           2 :         STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
    1837             :     {
    1838           2 :         const int nSymbolStyle = atoi(pszSymbolId + 19);
    1839           2 :         SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
    1840             : 
    1841           2 :         const char *pszPtr = pszSymbolId + 19;
    1842           4 :         while (*pszPtr != '-')
    1843             :         {
    1844           2 :             pszPtr++;
    1845             :         }
    1846           2 :         pszPtr++;
    1847             : 
    1848           2 :         char szSymbolName[256] = "";
    1849             :         int i;
    1850           2 :         for (i = 0;
    1851           8 :              i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
    1852             :              i++, pszPtr++)
    1853             :         {
    1854           6 :             szSymbolName[i] = *pszPtr;
    1855             :         }
    1856           2 :         szSymbolName[i] = '\0';
    1857           2 :         SetSymbolName(szSymbolName);
    1858             :     }
    1859           2 : }
    1860             : 
    1861             : /**********************************************************************
    1862             :  *                   TABCustomPoint::GetStyleString() const
    1863             :  *
    1864             :  * Return style string for this feature.
    1865             :  *
    1866             :  * Style String is built only once during the first call to GetStyleString().
    1867             :  **********************************************************************/
    1868           9 : const char *TABCustomPoint::GetStyleString() const
    1869             : {
    1870           9 :     if (m_pszStyleString == nullptr)
    1871             :     {
    1872           7 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    1873             :     }
    1874             : 
    1875           9 :     return m_pszStyleString;
    1876             : }
    1877             : 
    1878             : /*=====================================================================
    1879             :  *                      class TABPolyline
    1880             :  *====================================================================*/
    1881             : 
    1882             : /**********************************************************************
    1883             :  *                   TABPolyline::TABPolyline()
    1884             :  *
    1885             :  * Constructor.
    1886             :  **********************************************************************/
    1887        4248 : TABPolyline::TABPolyline(const OGRFeatureDefn *poDefnIn)
    1888             :     : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
    1889        4248 :       m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
    1890             : {
    1891        4248 : }
    1892             : 
    1893             : /**********************************************************************
    1894             :  *                   TABPolyline::~TABPolyline()
    1895             :  *
    1896             :  * Destructor.
    1897             :  **********************************************************************/
    1898        8496 : TABPolyline::~TABPolyline()
    1899             : {
    1900        8496 : }
    1901             : 
    1902             : /**********************************************************************
    1903             :  *                     TABPolyline::CloneTABFeature()
    1904             :  *
    1905             :  * Duplicate feature, including stuff specific to each TABFeature type.
    1906             :  *
    1907             :  * This method calls the generic TABFeature::CloneTABFeature() and
    1908             :  * then copies any members specific to its own type.
    1909             :  **********************************************************************/
    1910             : TABFeature *
    1911           0 : TABPolyline::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    1912             : {
    1913             :     /*-----------------------------------------------------------------
    1914             :      * Alloc new feature and copy the base stuff
    1915             :      *----------------------------------------------------------------*/
    1916           0 :     TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
    1917             : 
    1918           0 :     CopyTABFeatureBase(poNew);
    1919             : 
    1920             :     /*-----------------------------------------------------------------
    1921             :      * And members specific to this class
    1922             :      *----------------------------------------------------------------*/
    1923             :     // ITABFeaturePen
    1924           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    1925             : 
    1926           0 :     poNew->m_bSmooth = m_bSmooth;
    1927           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    1928           0 :     poNew->m_dCenterX = m_dCenterX;
    1929           0 :     poNew->m_dCenterY = m_dCenterY;
    1930             : 
    1931           0 :     return poNew;
    1932             : }
    1933             : 
    1934             : /**********************************************************************
    1935             :  *                   TABPolyline::GetNumParts()
    1936             :  *
    1937             :  * Return the total number of parts in this object.
    1938             :  *
    1939             :  * Returns 0 if the geometry contained in the object is invalid or missing.
    1940             :  **********************************************************************/
    1941           0 : int TABPolyline::GetNumParts()
    1942             : {
    1943           0 :     int numParts = 0;
    1944             : 
    1945           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1946           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    1947             :     {
    1948             :         /*-------------------------------------------------------------
    1949             :          * Simple polyline
    1950             :          *------------------------------------------------------------*/
    1951           0 :         numParts = 1;
    1952             :     }
    1953           0 :     else if (poGeom &&
    1954           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1955             :     {
    1956             :         /*-------------------------------------------------------------
    1957             :          * Multiple polyline
    1958             :          *------------------------------------------------------------*/
    1959           0 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    1960           0 :         numParts = poMultiLine->getNumGeometries();
    1961             :     }
    1962             : 
    1963           0 :     return numParts;
    1964             : }
    1965             : 
    1966             : /**********************************************************************
    1967             :  *                   TABPolyline::GetPartRef()
    1968             :  *
    1969             :  * Returns a reference to the specified OGRLineString number, hiding the
    1970             :  * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
    1971             :  *
    1972             :  * Returns NULL if the geometry contained in the object is invalid or
    1973             :  * missing or if the specified part index is invalid.
    1974             :  **********************************************************************/
    1975           0 : OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
    1976             : {
    1977           0 :     OGRGeometry *poGeom = GetGeometryRef();
    1978           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    1979             :         nPartIndex == 0)
    1980             :     {
    1981             :         /*-------------------------------------------------------------
    1982             :          * Simple polyline
    1983             :          *------------------------------------------------------------*/
    1984           0 :         return poGeom->toLineString();
    1985             :     }
    1986           0 :     else if (poGeom &&
    1987           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1988             :     {
    1989             :         /*-------------------------------------------------------------
    1990             :          * Multiple polyline
    1991             :          *------------------------------------------------------------*/
    1992           0 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    1993           0 :         if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
    1994             :         {
    1995           0 :             return poMultiLine->getGeometryRef(nPartIndex);
    1996             :         }
    1997             :         else
    1998           0 :             return nullptr;
    1999             :     }
    2000             : 
    2001           0 :     return nullptr;
    2002             : }
    2003             : 
    2004             : /**********************************************************************
    2005             :  *                   TABPolyline::ValidateMapInfoType()
    2006             :  *
    2007             :  * Check the feature's geometry part and return the corresponding
    2008             :  * mapinfo object type code.  The m_nMapInfoType member will also
    2009             :  * be updated for further calls to GetMapInfoType();
    2010             :  *
    2011             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    2012             :  * is expected for this object class.
    2013             :  **********************************************************************/
    2014         238 : TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    2015             : {
    2016             :     /*-----------------------------------------------------------------
    2017             :      * Fetch and validate geometry
    2018             :      *----------------------------------------------------------------*/
    2019         238 :     OGRGeometry *poGeom = GetGeometryRef();
    2020         238 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2021             :     {
    2022             :         /*-------------------------------------------------------------
    2023             :          * Simple polyline
    2024             :          *------------------------------------------------------------*/
    2025         230 :         OGRLineString *poLine = poGeom->toLineString();
    2026         230 :         if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
    2027             :         {
    2028           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
    2029             :         }
    2030         230 :         else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
    2031             :         {
    2032           0 :             m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
    2033             :         }
    2034         230 :         else if (poLine->getNumPoints() > 2)
    2035             :         {
    2036         207 :             m_nMapInfoType = TAB_GEOM_PLINE;
    2037             :         }
    2038          46 :         else if ((poLine->getNumPoints() == 2) &&
    2039          23 :                  (m_bWriteTwoPointLineAsPolyline == TRUE))
    2040             :         {
    2041           0 :             m_nMapInfoType = TAB_GEOM_PLINE;
    2042             :         }
    2043          46 :         else if ((poLine->getNumPoints() == 2) &&
    2044          23 :                  (m_bWriteTwoPointLineAsPolyline == FALSE))
    2045             :         {
    2046          23 :             m_nMapInfoType = TAB_GEOM_LINE;
    2047             :         }
    2048             :         else
    2049             :         {
    2050           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    2051             :                      "TABPolyline: Geometry must contain at least 2 points.");
    2052           0 :             m_nMapInfoType = TAB_GEOM_NONE;
    2053             :         }
    2054             :     }
    2055          16 :     else if (poGeom &&
    2056           8 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2057             :     {
    2058             :         /*-------------------------------------------------------------
    2059             :          * Multiple polyline... validate all components
    2060             :          *------------------------------------------------------------*/
    2061           8 :         GInt32 numPointsTotal = 0;
    2062           8 :         OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2063           8 :         int numLines = poMultiLine->getNumGeometries();
    2064             : 
    2065           8 :         m_nMapInfoType = TAB_GEOM_MULTIPLINE;
    2066             : 
    2067          18 :         for (int iLine = 0; iLine < numLines; iLine++)
    2068             :         {
    2069          10 :             poGeom = poMultiLine->getGeometryRef(iLine);
    2070          20 :             if (poGeom == nullptr ||
    2071          10 :                 wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    2072             :             {
    2073           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2074             :                          "TABPolyline: Object contains an invalid Geometry!");
    2075           0 :                 m_nMapInfoType = TAB_GEOM_NONE;
    2076           0 :                 numPointsTotal = 0;
    2077           0 :                 break;
    2078             :             }
    2079          10 :             OGRLineString *poLine = poGeom->toLineString();
    2080          10 :             numPointsTotal += poLine->getNumPoints();
    2081             :         }
    2082             : 
    2083           8 :         if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
    2084           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
    2085           8 :         else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
    2086           0 :             m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
    2087             :     }
    2088             :     else
    2089             :     {
    2090           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2091             :                  "TABPolyline: Missing or Invalid Geometry!");
    2092           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    2093             :     }
    2094             : 
    2095             :     /*-----------------------------------------------------------------
    2096             :      * Decide if coordinates should be compressed or not.
    2097             :      *
    2098             :      * __TODO__ We never write type LINE (2 points line) as compressed
    2099             :      * for the moment.  If we ever do it, then the decision to write
    2100             :      * a 2 point line in compressed coordinates or not should take into
    2101             :      * account the location of the object block MBR, so this would be
    2102             :      * better handled directly by TABMAPObjLine::WriteObject() since the
    2103             :      * object block center is not known until it is written to disk.
    2104             :      *----------------------------------------------------------------*/
    2105         238 :     if (m_nMapInfoType != TAB_GEOM_LINE)
    2106             :     {
    2107         215 :         ValidateCoordType(poMapFile);
    2108             :     }
    2109             :     else
    2110             :     {
    2111          23 :         UpdateMBR(poMapFile);
    2112             :     }
    2113             : 
    2114         238 :     return m_nMapInfoType;
    2115             : }
    2116             : 
    2117             : /**********************************************************************
    2118             :  *                   TABPolyline::ReadGeometryFromMAPFile()
    2119             :  *
    2120             :  * Fill the geometry and representation (color, etc...) part of the
    2121             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    2122             :  *
    2123             :  * It is assumed that poMAPFile currently points to the beginning of
    2124             :  * a map object.
    2125             :  *
    2126             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    2127             :  * been called.
    2128             :  **********************************************************************/
    2129        1577 : int TABPolyline::ReadGeometryFromMAPFile(
    2130             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    2131             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    2132             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    2133             : {
    2134        1577 :     GInt32 nX = 0;
    2135        1577 :     GInt32 nY = 0;
    2136        1577 :     double dX = 0.0;
    2137        1577 :     double dY = 0.0;
    2138        1577 :     double dXMin = 0.0;
    2139        1577 :     double dYMin = 0.0;
    2140        1577 :     double dXMax = 0.0;
    2141        1577 :     double dYMax = 0.0;
    2142        1577 :     OGRGeometry *poGeometry = nullptr;
    2143        1577 :     GBool bComprCoord = poObjHdr->IsCompressedType();
    2144        1577 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    2145             : 
    2146             :     /*-----------------------------------------------------------------
    2147             :      * Fetch and validate geometry type
    2148             :      *----------------------------------------------------------------*/
    2149        1577 :     m_nMapInfoType = poObjHdr->m_nType;
    2150             : 
    2151        1577 :     if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
    2152             :     {
    2153             :         /*=============================================================
    2154             :          * LINE (2 vertices)
    2155             :          *============================================================*/
    2156          21 :         TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
    2157             : 
    2158          21 :         m_bSmooth = FALSE;
    2159             : 
    2160          21 :         auto poLine = new OGRLineString();
    2161          21 :         poGeometry = poLine;
    2162          21 :         poLine->setNumPoints(2);
    2163             : 
    2164          21 :         poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
    2165             :                                 dYMin);
    2166          21 :         poLine->setPoint(0, dXMin, dYMin);
    2167             : 
    2168          21 :         poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
    2169             :                                 dYMax);
    2170          21 :         poLine->setPoint(1, dXMax, dYMax);
    2171             : 
    2172          21 :         if (!bCoordBlockDataOnly)
    2173             :         {
    2174          21 :             m_nPenDefIndex = poLineHdr->m_nPenId;  // Pen index
    2175          21 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2176          21 :         }
    2177             :     }
    2178        1556 :     else if (m_nMapInfoType == TAB_GEOM_PLINE ||
    2179        1551 :              m_nMapInfoType == TAB_GEOM_PLINE_C)
    2180             :     {
    2181             :         /*=============================================================
    2182             :          * PLINE ( > 2 vertices)
    2183             :          *============================================================*/
    2184             : 
    2185             :         /*-------------------------------------------------------------
    2186             :          * Copy data from poObjHdr
    2187             :          *------------------------------------------------------------*/
    2188        1546 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2189             : 
    2190        1546 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    2191        1546 :         const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
    2192        1546 :         if (nCoordDataSize > 1024 * 1024 &&
    2193           0 :             nCoordDataSize > poMapFile->GetFileSize())
    2194             :         {
    2195           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
    2196             :                      nCoordDataSize);
    2197           0 :             return -1;
    2198             :         }
    2199             :         // numLineSections = poPLineHdr->m_numLineSections; // Always 1
    2200        1546 :         m_bSmooth = poPLineHdr->m_bSmooth;
    2201             : 
    2202             :         // Centroid/label point
    2203        1546 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    2204             :                                 dX, dY);
    2205        1546 :         SetCenter(dX, dY);
    2206             : 
    2207             :         // Compressed coordinate origin (useful only in compressed case!)
    2208        1546 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    2209        1546 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    2210             : 
    2211             :         // MBR
    2212        1546 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    2213             :                                 dYMin);
    2214        1546 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    2215             :                                 dYMax);
    2216             : 
    2217        1546 :         if (!bCoordBlockDataOnly)
    2218             :         {
    2219        1378 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    2220        1378 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2221             :         }
    2222             : 
    2223             :         /*-------------------------------------------------------------
    2224             :          * Create Geometry and read coordinates
    2225             :          *------------------------------------------------------------*/
    2226        1546 :         const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
    2227             : 
    2228        1546 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2229         168 :             poCoordBlock = *ppoCoordBlock;
    2230             :         else
    2231        1378 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    2232        1546 :         if (poCoordBlock == nullptr)
    2233             :         {
    2234           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2235             :                      "Can't access coordinate block at offset %d",
    2236             :                      nCoordBlockPtr);
    2237           0 :             return -1;
    2238             :         }
    2239             : 
    2240        1546 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2241             : 
    2242        1546 :         auto poLine = new OGRLineString();
    2243        1546 :         poGeometry = poLine;
    2244        1546 :         poLine->setNumPoints(numPoints);
    2245             : 
    2246        1546 :         int nStatus = 0;
    2247       11886 :         for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2248             :         {
    2249       10340 :             nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
    2250       10340 :             if (nStatus != 0)
    2251           0 :                 break;
    2252       10340 :             poMapFile->Int2Coordsys(nX, nY, dX, dY);
    2253       10340 :             poLine->setPoint(i, dX, dY);
    2254             :         }
    2255             : 
    2256        1546 :         if (nStatus != 0)
    2257             :         {
    2258             :             // Failed ... error message has already been produced
    2259           0 :             delete poGeometry;
    2260           0 :             return nStatus;
    2261        1546 :         }
    2262             :     }
    2263          10 :     else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2264          10 :              m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2265           4 :              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2266           4 :              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2267           0 :              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2268           0 :              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
    2269             :     {
    2270             :         /*=============================================================
    2271             :          * PLINE MULTIPLE
    2272             :          *============================================================*/
    2273          10 :         const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    2274             : 
    2275             :         /*-------------------------------------------------------------
    2276             :          * Copy data from poObjHdr
    2277             :          *------------------------------------------------------------*/
    2278          10 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2279             : 
    2280          10 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    2281             :         /* GInt32 nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
    2282          10 :         GInt32 numLineSections = poPLineHdr->m_numLineSections;
    2283          10 :         m_bSmooth = poPLineHdr->m_bSmooth;
    2284             : 
    2285             :         // Centroid/label point
    2286          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    2287             :                                 dX, dY);
    2288          10 :         SetCenter(dX, dY);
    2289             : 
    2290             :         // Compressed coordinate origin (useful only in compressed case!)
    2291          10 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    2292          10 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    2293             : 
    2294             :         // MBR
    2295          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    2296             :                                 dYMin);
    2297          10 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    2298             :                                 dYMax);
    2299             : 
    2300          10 :         if (!bCoordBlockDataOnly)
    2301             :         {
    2302          10 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    2303          10 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    2304             :         }
    2305             : 
    2306          10 :         const int nMinSizeOfSection = 24;
    2307          10 :         if (numLineSections > INT_MAX / nMinSizeOfSection)
    2308             :         {
    2309           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    2310           0 :             return -1;
    2311             :         }
    2312          10 :         const GUInt32 nMinimumBytesForSections =
    2313          10 :             nMinSizeOfSection * numLineSections;
    2314          10 :         if (nMinimumBytesForSections > 1024 * 1024 &&
    2315           0 :             nMinimumBytesForSections > poMapFile->GetFileSize())
    2316             :         {
    2317           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    2318           0 :             return -1;
    2319             :         }
    2320             : 
    2321             :         /*-------------------------------------------------------------
    2322             :          * Read data from the coord. block
    2323             :          *------------------------------------------------------------*/
    2324             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    2325          10 :             VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
    2326          10 :         if (pasSecHdrs == nullptr)
    2327           0 :             return -1;
    2328             : 
    2329          10 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2330           4 :             poCoordBlock = *ppoCoordBlock;
    2331             :         else
    2332           6 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    2333             : 
    2334          10 :         GInt32 numPointsTotal = 0;
    2335          20 :         if (poCoordBlock == nullptr ||
    2336          10 :             poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
    2337             :                                            numLineSections, pasSecHdrs,
    2338             :                                            numPointsTotal) != 0)
    2339             :         {
    2340           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2341             :                      "Failed reading coordinate data at offset %d",
    2342             :                      nCoordBlockPtr);
    2343           0 :             CPLFree(pasSecHdrs);
    2344           0 :             return -1;
    2345             :         }
    2346             : 
    2347          10 :         const GUInt32 nMinimumBytesForPoints =
    2348          10 :             (bComprCoord ? 4 : 8) * numPointsTotal;
    2349          10 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    2350           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    2351             :         {
    2352           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
    2353           0 :             CPLFree(pasSecHdrs);
    2354           0 :             return -1;
    2355             :         }
    2356             : 
    2357          10 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2358             : 
    2359             :         GInt32 *panXY = static_cast<GInt32 *>(
    2360          10 :             VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
    2361          10 :         if (panXY == nullptr)
    2362             :         {
    2363           0 :             CPLFree(pasSecHdrs);
    2364           0 :             return -1;
    2365             :         }
    2366             : 
    2367          10 :         if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
    2368             :             0)
    2369             :         {
    2370           0 :             CPLError(CE_Failure, CPLE_FileIO,
    2371             :                      "Failed reading coordinate data at offset %d",
    2372             :                      nCoordBlockPtr);
    2373           0 :             CPLFree(pasSecHdrs);
    2374           0 :             CPLFree(panXY);
    2375           0 :             return -1;
    2376             :         }
    2377             : 
    2378             :         /*-------------------------------------------------------------
    2379             :          * Create a Geometry collection with one line geometry for
    2380             :          * each coordinates section
    2381             :          * If object contains only one section, then return a simple LineString
    2382             :          *------------------------------------------------------------*/
    2383          10 :         OGRMultiLineString *poMultiLine = nullptr;
    2384          10 :         if (numLineSections > 1)
    2385             :         {
    2386           6 :             poMultiLine = new OGRMultiLineString();
    2387           6 :             poGeometry = poMultiLine;
    2388             :         }
    2389             : 
    2390          26 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    2391             :         {
    2392          16 :             const int numSectionVertices = pasSecHdrs[iSection].numVertices;
    2393          16 :             GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
    2394             : 
    2395          16 :             auto poLine = new OGRLineString();
    2396          16 :             poLine->setNumPoints(numSectionVertices);
    2397             : 
    2398          48 :             for (int i = 0; i < numSectionVertices; i++)
    2399             :             {
    2400          32 :                 poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
    2401          32 :                 poLine->setPoint(i, dX, dY);
    2402          32 :                 pnXYPtr += 2;
    2403             :             }
    2404             : 
    2405          16 :             if (poGeometry == nullptr)
    2406           4 :                 poGeometry = poLine;
    2407          12 :             else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
    2408             :             {
    2409           0 :                 CPLAssert(false);  // Just in case lower-level lib is modified
    2410             :             }
    2411             :         }
    2412             : 
    2413          10 :         CPLFree(pasSecHdrs);
    2414          10 :         CPLFree(panXY);
    2415             :     }
    2416             :     else
    2417             :     {
    2418           0 :         CPLError(
    2419             :             CE_Failure, CPLE_AssertionFailed,
    2420             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    2421           0 :             m_nMapInfoType, m_nMapInfoType);
    2422           0 :         return -1;
    2423             :     }
    2424             : 
    2425        1577 :     SetGeometryDirectly(poGeometry);
    2426             : 
    2427        1577 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    2428        1577 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    2429             :               poObjHdr->m_nMaxY);
    2430             : 
    2431             :     /* Return a ref to coord block so that caller can continue reading
    2432             :      * after the end of this object (used by TABCollection and index splitting)
    2433             :      */
    2434        1577 :     if (ppoCoordBlock)
    2435         172 :         *ppoCoordBlock = poCoordBlock;
    2436             : 
    2437        1577 :     return 0;
    2438             : }
    2439             : 
    2440             : /**********************************************************************
    2441             :  *                   TABPolyline::WriteGeometryToMAPFile()
    2442             :  *
    2443             :  * Write the geometry and representation (color, etc...) part of the
    2444             :  * feature to the .MAP object pointed to by poMAPFile.
    2445             :  *
    2446             :  * It is assumed that poMAPFile currently points to a valid map object.
    2447             :  *
    2448             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    2449             :  * been called.
    2450             :  **********************************************************************/
    2451         406 : int TABPolyline::WriteGeometryToMAPFile(
    2452             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    2453             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    2454             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    2455             : {
    2456         406 :     GInt32 nX = 0;
    2457         406 :     GInt32 nY = 0;
    2458         406 :     OGRLineString *poLine = nullptr;
    2459         406 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    2460             : 
    2461             :     /*-----------------------------------------------------------------
    2462             :      * We assume that ValidateMapInfoType() was called already and that
    2463             :      * the type in poObjHdr->m_nType is valid.
    2464             :      *----------------------------------------------------------------*/
    2465         406 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    2466         406 :     CPLErrorReset();
    2467             : 
    2468             :     /*-----------------------------------------------------------------
    2469             :      * Fetch and validate geometry
    2470             :      *----------------------------------------------------------------*/
    2471         406 :     OGRGeometry *poGeom = GetGeometryRef();
    2472             : 
    2473        1195 :     if ((m_nMapInfoType == TAB_GEOM_LINE ||
    2474         406 :          m_nMapInfoType == TAB_GEOM_LINE_C) &&
    2475         835 :         poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    2476          23 :         (poLine = poGeom->toLineString())->getNumPoints() == 2)
    2477             :     {
    2478             :         /*=============================================================
    2479             :          * LINE (2 vertices)
    2480             :          *============================================================*/
    2481          23 :         TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
    2482             : 
    2483          23 :         poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
    2484          23 :                                 poLineHdr->m_nX1, poLineHdr->m_nY1);
    2485          23 :         poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
    2486          23 :                                 poLineHdr->m_nX2, poLineHdr->m_nY2);
    2487          23 :         poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
    2488             :                           poLineHdr->m_nY2);
    2489             : 
    2490          23 :         if (!bCoordBlockDataOnly)
    2491             :         {
    2492          23 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2493          23 :             poLineHdr->m_nPenId =
    2494          23 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2495             :         }
    2496             :     }
    2497        1146 :     else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
    2498         383 :               m_nMapInfoType == TAB_GEOM_PLINE_C) &&
    2499         766 :              poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2500             :     {
    2501             :         /*=============================================================
    2502             :          * PLINE ( > 2 vertices and less than 32767 vertices)
    2503             :          *============================================================*/
    2504         375 :         GBool bCompressed = poObjHdr->IsCompressedType();
    2505             : 
    2506             :         /*-------------------------------------------------------------
    2507             :          * Process geometry first...
    2508             :          *------------------------------------------------------------*/
    2509         375 :         poLine = poGeom->toLineString();
    2510         375 :         const int numPoints = poLine->getNumPoints();
    2511         375 :         CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
    2512             : 
    2513         375 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2514         168 :             poCoordBlock = *ppoCoordBlock;
    2515             :         else
    2516         207 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    2517         375 :         poCoordBlock->StartNewFeature();
    2518         375 :         const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    2519         375 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2520             : 
    2521         375 :         int nStatus = 0;
    2522        5207 :         for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2523             :         {
    2524        4832 :             poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
    2525        4832 :             if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
    2526             :                 0)
    2527             :             {
    2528             :                 // Failed ... error message has already been produced
    2529           0 :                 return nStatus;
    2530             :             }
    2531             :         }
    2532             : 
    2533         375 :         const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    2534             : 
    2535             :         /*-------------------------------------------------------------
    2536             :          * Copy info to poObjHdr
    2537             :          *------------------------------------------------------------*/
    2538         375 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2539             : 
    2540         375 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    2541         375 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    2542         375 :         poPLineHdr->m_numLineSections = 1;
    2543             : 
    2544         375 :         poPLineHdr->m_bSmooth = m_bSmooth;
    2545             : 
    2546             :         // MBR
    2547         375 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    2548             : 
    2549             :         // Polyline center/label point
    2550         375 :         double dX = 0.0;
    2551         375 :         double dY = 0.0;
    2552         375 :         if (GetCenter(dX, dY) != -1)
    2553             :         {
    2554         375 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    2555         375 :                                     poPLineHdr->m_nLabelY);
    2556             :         }
    2557             :         else
    2558             :         {
    2559           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    2560           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    2561             :         }
    2562             : 
    2563             :         // Compressed coordinate origin (useful only in compressed case!)
    2564         375 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    2565         375 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    2566             : 
    2567         375 :         if (!bCoordBlockDataOnly)
    2568             :         {
    2569         207 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2570         207 :             poPLineHdr->m_nPenId =
    2571         207 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2572             :         }
    2573             :     }
    2574          24 :     else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2575           8 :               m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2576           0 :               m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2577           0 :               m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2578           0 :               m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2579           8 :               m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
    2580          16 :              poGeom &&
    2581           8 :              (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
    2582           0 :               wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
    2583             :     {
    2584             :         /*=============================================================
    2585             :          * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
    2586             :          *============================================================*/
    2587             : 
    2588           8 :         CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
    2589             :                   m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
    2590             :                   m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
    2591             :                   m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
    2592             :                   m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
    2593             :                   m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
    2594             : 
    2595           8 :         int nStatus = 0;
    2596           8 :         OGREnvelope sEnvelope;
    2597           8 :         GBool bCompressed = poObjHdr->IsCompressedType();
    2598             : 
    2599             :         /*-------------------------------------------------------------
    2600             :          * Process geometry first...
    2601             :          *------------------------------------------------------------*/
    2602           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    2603           0 :             poCoordBlock = *ppoCoordBlock;
    2604             :         else
    2605           8 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    2606           8 :         poCoordBlock->StartNewFeature();
    2607           8 :         const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    2608           8 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    2609             : 
    2610           8 :         OGRMultiLineString *poMultiLine = nullptr;
    2611           8 :         GInt32 numLines = 1;
    2612           8 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2613             :         {
    2614           8 :             poMultiLine = poGeom->toMultiLineString();
    2615           8 :             numLines = poMultiLine->getNumGeometries();
    2616             :         }
    2617             :         // else
    2618             :         // {
    2619             :         //     poMultiLine = NULL;
    2620             :         //     numLines = 1;
    2621             :         // }
    2622             : 
    2623             :         /*-------------------------------------------------------------
    2624             :          * Build and write array of coord sections headers
    2625             :          *------------------------------------------------------------*/
    2626             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    2627           8 :             VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
    2628           8 :         if (pasSecHdrs == nullptr)
    2629             :         {
    2630           0 :             return -1;
    2631             :         }
    2632             : 
    2633             :         /*-------------------------------------------------------------
    2634             :          * In calculation of nDataOffset, we have to take into account that
    2635             :          * V450 header section uses int32 instead of int16 for numVertices
    2636             :          * and we add another 2 bytes to align with a 4 bytes boundary.
    2637             :          *------------------------------------------------------------*/
    2638           8 :         int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    2639             : 
    2640           8 :         const int nTotalHdrSizeUncompressed =
    2641           8 :             (nVersion >= 450 ? 28 : 24) * numLines;
    2642             : 
    2643           8 :         GInt32 numPointsTotal = 0;
    2644          18 :         for (int iLine = 0; iLine < numLines; iLine++)
    2645             :         {
    2646          10 :             if (poMultiLine)
    2647          10 :                 poGeom = poMultiLine->getGeometryRef(iLine);
    2648             : 
    2649          20 :             if (poGeom &&
    2650          10 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2651             :             {
    2652          10 :                 poLine = poGeom->toLineString();
    2653          10 :                 const GInt32 numPoints = poLine->getNumPoints();
    2654          10 :                 poLine->getEnvelope(&sEnvelope);
    2655             : 
    2656          10 :                 pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
    2657          10 :                 pasSecHdrs[iLine].numHoles = 0;  // It is a line!
    2658             : 
    2659          10 :                 poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
    2660          10 :                                         pasSecHdrs[iLine].nXMin,
    2661          10 :                                         pasSecHdrs[iLine].nYMin);
    2662          10 :                 poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
    2663          10 :                                         pasSecHdrs[iLine].nXMax,
    2664          10 :                                         pasSecHdrs[iLine].nYMax);
    2665          10 :                 pasSecHdrs[iLine].nDataOffset =
    2666          10 :                     nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
    2667          10 :                 pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
    2668             : 
    2669          10 :                 numPointsTotal += numPoints;
    2670             :             }
    2671             :             else
    2672             :             {
    2673           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2674             :                          "TABPolyline: Object contains an invalid Geometry!");
    2675           0 :                 nStatus = -1;
    2676             :             }
    2677             :         }
    2678             : 
    2679           8 :         if (nStatus == 0)
    2680           8 :             nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
    2681             :                                                       pasSecHdrs, bCompressed);
    2682             : 
    2683           8 :         CPLFree(pasSecHdrs);
    2684           8 :         pasSecHdrs = nullptr;
    2685             : 
    2686           8 :         if (nStatus != 0)
    2687           0 :             return nStatus;  // Error has already been reported.
    2688             : 
    2689             :         /*-------------------------------------------------------------
    2690             :          * Then write the coordinates themselves...
    2691             :          *------------------------------------------------------------*/
    2692          18 :         for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
    2693             :         {
    2694          10 :             if (poMultiLine)
    2695          10 :                 poGeom = poMultiLine->getGeometryRef(iLine);
    2696             : 
    2697          20 :             if (poGeom &&
    2698          10 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2699             :             {
    2700          10 :                 poLine = poGeom->toLineString();
    2701          10 :                 GInt32 numPoints = poLine->getNumPoints();
    2702             : 
    2703          30 :                 for (int i = 0; nStatus == 0 && i < numPoints; i++)
    2704             :                 {
    2705          20 :                     poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
    2706             :                                             nX, nY);
    2707          20 :                     if ((nStatus = poCoordBlock->WriteIntCoord(
    2708          20 :                              nX, nY, bCompressed)) != 0)
    2709             :                     {
    2710             :                         // Failed ... error message has already been produced
    2711           0 :                         return nStatus;
    2712             :                     }
    2713             :                 }
    2714             :             }
    2715             :             else
    2716             :             {
    2717           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2718             :                          "TABPolyline: Object contains an invalid Geometry!");
    2719           0 :                 return -1;
    2720             :             }
    2721             :         }
    2722             : 
    2723           8 :         const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    2724             : 
    2725             :         /*-------------------------------------------------------------
    2726             :          * ... and finally copy info to poObjHdr
    2727             :          *------------------------------------------------------------*/
    2728           8 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    2729             : 
    2730           8 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    2731           8 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    2732           8 :         poPLineHdr->m_numLineSections = numLines;
    2733             : 
    2734           8 :         poPLineHdr->m_bSmooth = m_bSmooth;
    2735             : 
    2736             :         // MBR
    2737           8 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    2738             : 
    2739             :         // Polyline center/label point
    2740           8 :         double dX = 0.0;
    2741           8 :         double dY = 0.0;
    2742           8 :         if (GetCenter(dX, dY) != -1)
    2743             :         {
    2744           8 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    2745           8 :                                     poPLineHdr->m_nLabelY);
    2746             :         }
    2747             :         else
    2748             :         {
    2749           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    2750           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    2751             :         }
    2752             : 
    2753             :         // Compressed coordinate origin (useful only in compressed case!)
    2754           8 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    2755           8 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    2756             : 
    2757           8 :         if (!bCoordBlockDataOnly)
    2758             :         {
    2759           8 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    2760           8 :             poPLineHdr->m_nPenId =
    2761           8 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    2762             :         }
    2763             :     }
    2764             :     else
    2765             :     {
    2766           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2767             :                  "TABPolyline: Object contains an invalid Geometry!");
    2768           0 :         return -1;
    2769             :     }
    2770             : 
    2771         406 :     if (CPLGetLastErrorType() == CE_Failure)
    2772           0 :         return -1;
    2773             : 
    2774             :     /* Return a ref to coord block so that caller can continue writing
    2775             :      * after the end of this object (used by index splitting)
    2776             :      */
    2777         406 :     if (ppoCoordBlock)
    2778         168 :         *ppoCoordBlock = poCoordBlock;
    2779             : 
    2780         406 :     return 0;
    2781             : }
    2782             : 
    2783             : /**********************************************************************
    2784             :  *                   TABPolyline::GetStyleString() const
    2785             :  *
    2786             :  * Return style string for this feature.
    2787             :  *
    2788             :  * Style String is built only once during the first call to GetStyleString().
    2789             :  **********************************************************************/
    2790          44 : const char *TABPolyline::GetStyleString() const
    2791             : {
    2792          44 :     if (m_pszStyleString == nullptr)
    2793             :     {
    2794          35 :         m_pszStyleString = CPLStrdup(GetPenStyleString());
    2795             :     }
    2796             : 
    2797          44 :     return m_pszStyleString;
    2798             : }
    2799             : 
    2800             : /**********************************************************************
    2801             :  *                   TABPolyline::DumpMIF()
    2802             :  *
    2803             :  * Dump feature geometry in a format similar to .MIF PLINEs.
    2804             :  **********************************************************************/
    2805           0 : void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
    2806             : {
    2807           0 :     OGRMultiLineString *poMultiLine = nullptr;
    2808           0 :     OGRLineString *poLine = nullptr;
    2809             :     int i, numPoints;
    2810             : 
    2811           0 :     if (fpOut == nullptr)
    2812           0 :         fpOut = stdout;
    2813             : 
    2814             :     /*-----------------------------------------------------------------
    2815             :      * Fetch and validate geometry
    2816             :      *----------------------------------------------------------------*/
    2817           0 :     OGRGeometry *poGeom = GetGeometryRef();
    2818           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2819             :     {
    2820             :         /*-------------------------------------------------------------
    2821             :          * Generate output for simple polyline
    2822             :          *------------------------------------------------------------*/
    2823           0 :         poLine = poGeom->toLineString();
    2824           0 :         numPoints = poLine->getNumPoints();
    2825           0 :         fprintf(fpOut, "PLINE %d\n", numPoints);
    2826           0 :         for (i = 0; i < numPoints; i++)
    2827           0 :             fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
    2828             :     }
    2829           0 :     else if (poGeom &&
    2830           0 :              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2831             :     {
    2832             :         /*-------------------------------------------------------------
    2833             :          * Generate output for multiple polyline
    2834             :          *------------------------------------------------------------*/
    2835             :         int iLine, numLines;
    2836           0 :         poMultiLine = poGeom->toMultiLineString();
    2837           0 :         numLines = poMultiLine->getNumGeometries();
    2838           0 :         fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
    2839           0 :         for (iLine = 0; iLine < numLines; iLine++)
    2840             :         {
    2841           0 :             poGeom = poMultiLine->getGeometryRef(iLine);
    2842           0 :             if (poGeom &&
    2843           0 :                 wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2844             :             {
    2845           0 :                 poLine = poGeom->toLineString();
    2846           0 :                 numPoints = poLine->getNumPoints();
    2847           0 :                 fprintf(fpOut, " %d\n", numPoints);
    2848           0 :                 for (i = 0; i < numPoints; i++)
    2849           0 :                     fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
    2850             :                             poLine->getY(i));
    2851             :             }
    2852             :             else
    2853             :             {
    2854           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    2855             :                          "TABPolyline: Object contains an invalid Geometry!");
    2856           0 :                 return;
    2857             :             }
    2858             :         }
    2859             :     }
    2860             :     else
    2861             :     {
    2862           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2863             :                  "TABPolyline: Missing or Invalid Geometry!");
    2864           0 :         return;
    2865             :     }
    2866             : 
    2867           0 :     if (m_bCenterIsSet)
    2868           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    2869             : 
    2870             :     // Finish with PEN/BRUSH/etc. clauses
    2871           0 :     DumpPenDef();
    2872             : 
    2873           0 :     fflush(fpOut);
    2874             : }
    2875             : 
    2876             : /**********************************************************************
    2877             :  *                   TABPolyline::GetCenter()
    2878             :  *
    2879             :  * Returns the center point of the line.  Compute one if it was not
    2880             :  * explicitly set:
    2881             :  *
    2882             :  * In MapInfo, for a simple or multiple polyline (pline), the center point
    2883             :  * in the object definition is supposed to be either the center point of
    2884             :  * the pline or the first section of a multiple pline (if an odd number of
    2885             :  * points in the pline or first section), or the midway point between the
    2886             :  * two central points (if an even number of points involved).
    2887             :  *
    2888             :  * Returns 0 on success, -1 on error.
    2889             :  **********************************************************************/
    2890         383 : int TABPolyline::GetCenter(double &dX, double &dY)
    2891             : {
    2892         383 :     if (!m_bCenterIsSet)
    2893             :     {
    2894         215 :         OGRLineString *poLine = nullptr;
    2895             : 
    2896         215 :         OGRGeometry *poGeom = GetGeometryRef();
    2897         215 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    2898             :         {
    2899         207 :             poLine = poGeom->toLineString();
    2900             :         }
    2901          16 :         else if (poGeom &&
    2902           8 :                  wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    2903             :         {
    2904           8 :             OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
    2905           8 :             if (poMultiLine->getNumGeometries() > 0)
    2906           8 :                 poLine = poMultiLine->getGeometryRef(0);
    2907             :         }
    2908             : 
    2909         215 :         if (poLine && poLine->getNumPoints() > 0)
    2910             :         {
    2911         215 :             int i = poLine->getNumPoints() / 2;
    2912         215 :             if (poLine->getNumPoints() % 2 == 0)
    2913             :             {
    2914             :                 // Return the midway between the 2 center points
    2915          15 :                 m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
    2916          15 :                 m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
    2917             :             }
    2918             :             else
    2919             :             {
    2920             :                 // Return the center point
    2921         200 :                 m_dCenterX = poLine->getX(i);
    2922         200 :                 m_dCenterY = poLine->getY(i);
    2923             :             }
    2924         215 :             m_bCenterIsSet = TRUE;
    2925             :         }
    2926             :     }
    2927             : 
    2928         383 :     if (!m_bCenterIsSet)
    2929           0 :         return -1;
    2930             : 
    2931         383 :     dX = m_dCenterX;
    2932         383 :     dY = m_dCenterY;
    2933         383 :     return 0;
    2934             : }
    2935             : 
    2936             : /**********************************************************************
    2937             :  *                   TABPolyline::SetCenter()
    2938             :  *
    2939             :  * Set the X,Y coordinates to use as center point for the line.
    2940             :  **********************************************************************/
    2941        1556 : void TABPolyline::SetCenter(double dX, double dY)
    2942             : {
    2943        1556 :     m_dCenterX = dX;
    2944        1556 :     m_dCenterY = dY;
    2945        1556 :     m_bCenterIsSet = TRUE;
    2946        1556 : }
    2947             : 
    2948             : /**********************************************************************
    2949             :  *                   TABPolyline::TwoPointLineAsPolyline()
    2950             :  *
    2951             :  * Returns the value of m_bWriteTwoPointLineAsPolyline
    2952             :  **********************************************************************/
    2953           0 : GBool TABPolyline::TwoPointLineAsPolyline()
    2954             : {
    2955           0 :     return m_bWriteTwoPointLineAsPolyline;
    2956             : }
    2957             : 
    2958             : /**********************************************************************
    2959             :  *                   TABPolyline::TwoPointLineAsPolyline()
    2960             :  *
    2961             :  * Sets the value of m_bWriteTwoPointLineAsPolyline
    2962             :  **********************************************************************/
    2963           0 : void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
    2964             : {
    2965           0 :     m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
    2966           0 : }
    2967             : 
    2968             : /*=====================================================================
    2969             :  *                      class TABRegion
    2970             :  *====================================================================*/
    2971             : 
    2972             : /**********************************************************************
    2973             :  *                   TABRegion::TABRegion()
    2974             :  *
    2975             :  * Constructor.
    2976             :  **********************************************************************/
    2977        1190 : TABRegion::TABRegion(const OGRFeatureDefn *poDefnIn)
    2978             :     : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
    2979        1190 :       m_dCenterX(0.0), m_dCenterY(0.0)
    2980             : {
    2981        1190 : }
    2982             : 
    2983             : /**********************************************************************
    2984             :  *                   TABRegion::~TABRegion()
    2985             :  *
    2986             :  * Destructor.
    2987             :  **********************************************************************/
    2988        2380 : TABRegion::~TABRegion()
    2989             : {
    2990        2380 : }
    2991             : 
    2992             : /**********************************************************************
    2993             :  *                     TABRegion::CloneTABFeature()
    2994             :  *
    2995             :  * Duplicate feature, including stuff specific to each TABFeature type.
    2996             :  *
    2997             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    2998             :  * then copies any members specific to its own type.
    2999             :  **********************************************************************/
    3000             : TABFeature *
    3001           0 : TABRegion::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    3002             : {
    3003             :     /*-----------------------------------------------------------------
    3004             :      * Alloc new feature and copy the base stuff
    3005             :      *----------------------------------------------------------------*/
    3006           0 :     TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
    3007             : 
    3008           0 :     CopyTABFeatureBase(poNew);
    3009             : 
    3010             :     /*-----------------------------------------------------------------
    3011             :      * And members specific to this class
    3012             :      *----------------------------------------------------------------*/
    3013             :     // ITABFeaturePen
    3014           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    3015             : 
    3016             :     // ITABFeatureBrush
    3017           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    3018             : 
    3019           0 :     poNew->m_bSmooth = m_bSmooth;
    3020           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    3021           0 :     poNew->m_dCenterX = m_dCenterX;
    3022           0 :     poNew->m_dCenterY = m_dCenterY;
    3023             : 
    3024           0 :     return poNew;
    3025             : }
    3026             : 
    3027             : /**********************************************************************
    3028             :  *                   TABRegion::ValidateMapInfoType()
    3029             :  *
    3030             :  * Check the feature's geometry part and return the corresponding
    3031             :  * mapinfo object type code.  The m_nMapInfoType member will also
    3032             :  * be updated for further calls to GetMapInfoType();
    3033             :  *
    3034             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    3035             :  * is expected for this object class.
    3036             :  **********************************************************************/
    3037          88 : TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    3038             : {
    3039             :     /*-----------------------------------------------------------------
    3040             :      * Fetch and validate geometry
    3041             :      *----------------------------------------------------------------*/
    3042          88 :     OGRGeometry *poGeom = GetGeometryRef();
    3043          94 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3044           6 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3045             :     {
    3046          88 :         GInt32 numPointsTotal = 0;
    3047          88 :         GInt32 numRings = GetNumRings();
    3048         176 :         for (int i = 0; i < numRings; i++)
    3049             :         {
    3050          88 :             OGRLinearRing *poRing = GetRingRef(i);
    3051          88 :             if (poRing)
    3052          88 :                 numPointsTotal += poRing->getNumPoints();
    3053             :         }
    3054          88 :         if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
    3055           0 :             m_nMapInfoType = TAB_GEOM_V800_REGION;
    3056          88 :         else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
    3057           0 :             m_nMapInfoType = TAB_GEOM_V450_REGION;
    3058             :         else
    3059          88 :             m_nMapInfoType = TAB_GEOM_REGION;
    3060             :     }
    3061             :     else
    3062             :     {
    3063           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3064             :                  "TABRegion: Missing or Invalid Geometry!");
    3065           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    3066             :     }
    3067             : 
    3068             :     /*-----------------------------------------------------------------
    3069             :      * Decide if coordinates should be compressed or not.
    3070             :      *----------------------------------------------------------------*/
    3071          88 :     ValidateCoordType(poMapFile);
    3072             : 
    3073          88 :     return m_nMapInfoType;
    3074             : }
    3075             : 
    3076             : /**********************************************************************
    3077             :  *                   TABRegion::ReadGeometryFromMAPFile()
    3078             :  *
    3079             :  * Fill the geometry and representation (color, etc...) part of the
    3080             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    3081             :  *
    3082             :  * It is assumed that poMAPFile currently points to the beginning of
    3083             :  * a map object.
    3084             :  *
    3085             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    3086             :  * been called.
    3087             :  **********************************************************************/
    3088         525 : int TABRegion::ReadGeometryFromMAPFile(
    3089             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    3090             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    3091             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    3092             : {
    3093         525 :     double dXMin = 0.0;
    3094         525 :     double dYMin = 0.0;
    3095         525 :     double dXMax = 0.0;
    3096         525 :     double dYMax = 0.0;
    3097         525 :     OGRGeometry *poGeometry = nullptr;
    3098         525 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    3099             : 
    3100             :     /*-----------------------------------------------------------------
    3101             :      * Fetch and validate geometry type
    3102             :      *----------------------------------------------------------------*/
    3103         525 :     m_nMapInfoType = poObjHdr->m_nType;
    3104             : 
    3105         525 :     if (m_nMapInfoType == TAB_GEOM_REGION ||
    3106         469 :         m_nMapInfoType == TAB_GEOM_REGION_C ||
    3107           4 :         m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3108           4 :         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3109           0 :         m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3110           0 :         m_nMapInfoType == TAB_GEOM_V800_REGION_C)
    3111             :     {
    3112             :         /*=============================================================
    3113             :          * REGION (Similar to PLINE MULTIPLE)
    3114             :          *============================================================*/
    3115             :         GInt32 /* nCoordDataSize, */ numPointsTotal;
    3116         525 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3117         525 :         OGRPolygon *poPolygon = nullptr;
    3118         525 :         GBool bComprCoord = poObjHdr->IsCompressedType();
    3119         525 :         int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    3120             : 
    3121             :         /*-------------------------------------------------------------
    3122             :          * Copy data from poObjHdr
    3123             :          *------------------------------------------------------------*/
    3124         525 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    3125             : 
    3126         525 :         GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
    3127             :         /* nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
    3128         525 :         GInt32 numLineSections = poPLineHdr->m_numLineSections;
    3129         525 :         m_bSmooth = poPLineHdr->m_bSmooth;
    3130             : 
    3131             :         // Centroid/label point
    3132         525 :         double dX = 0.0;
    3133         525 :         double dY = 0.0;
    3134         525 :         poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
    3135             :                                 dX, dY);
    3136         525 :         SetCenter(dX, dY);
    3137             : 
    3138             :         // Compressed coordinate origin (useful only in compressed case!)
    3139         525 :         m_nComprOrgX = poPLineHdr->m_nComprOrgX;
    3140         525 :         m_nComprOrgY = poPLineHdr->m_nComprOrgY;
    3141             : 
    3142             :         // MBR
    3143         525 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
    3144             :                                 dYMin);
    3145         525 :         poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
    3146             :                                 dYMax);
    3147             : 
    3148         525 :         if (!bCoordBlockDataOnly)
    3149             :         {
    3150         525 :             m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
    3151         525 :             poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    3152         525 :             m_nBrushDefIndex = poPLineHdr->m_nBrushId;  // Brush index
    3153         525 :             poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    3154             :         }
    3155             : 
    3156             :         /*-------------------------------------------------------------
    3157             :          * Read data from the coord. block
    3158             :          *------------------------------------------------------------*/
    3159             : 
    3160         525 :         const int nMinSizeOfSection = 24;
    3161         525 :         if (numLineSections > INT_MAX / nMinSizeOfSection)
    3162             :         {
    3163           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    3164           0 :             return -1;
    3165             :         }
    3166         525 :         const GUInt32 nMinimumBytesForSections =
    3167         525 :             nMinSizeOfSection * numLineSections;
    3168         525 :         if (nMinimumBytesForSections > 1024 * 1024 &&
    3169           0 :             nMinimumBytesForSections > poMapFile->GetFileSize())
    3170             :         {
    3171           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
    3172           0 :             return -1;
    3173             :         }
    3174             : 
    3175             :         TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    3176         525 :             VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
    3177         525 :         if (pasSecHdrs == nullptr)
    3178           0 :             return -1;
    3179             : 
    3180         525 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    3181           4 :             poCoordBlock = *ppoCoordBlock;
    3182             :         else
    3183         521 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    3184             : 
    3185         525 :         if (poCoordBlock)
    3186         525 :             poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    3187             : 
    3188        1050 :         if (poCoordBlock == nullptr ||
    3189         525 :             poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
    3190             :                                            numLineSections, pasSecHdrs,
    3191             :                                            numPointsTotal) != 0)
    3192             :         {
    3193           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3194             :                      "Failed reading coordinate data at offset %d",
    3195             :                      nCoordBlockPtr);
    3196           0 :             CPLFree(pasSecHdrs);
    3197           0 :             return -1;
    3198             :         }
    3199             : 
    3200         525 :         const GUInt32 nMinimumBytesForPoints =
    3201         525 :             (bComprCoord ? 4 : 8) * numPointsTotal;
    3202         525 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    3203           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    3204             :         {
    3205           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
    3206           0 :             CPLFree(pasSecHdrs);
    3207           0 :             return -1;
    3208             :         }
    3209             : 
    3210             :         GInt32 *panXY = static_cast<GInt32 *>(
    3211         525 :             VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
    3212         525 :         if (panXY == nullptr)
    3213             :         {
    3214           0 :             CPLFree(pasSecHdrs);
    3215           0 :             return -1;
    3216             :         }
    3217             : 
    3218         525 :         if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
    3219             :             0)
    3220             :         {
    3221           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3222             :                      "Failed reading coordinate data at offset %d",
    3223             :                      nCoordBlockPtr);
    3224           0 :             CPLFree(pasSecHdrs);
    3225           0 :             CPLFree(panXY);
    3226           0 :             return -1;
    3227             :         }
    3228             : 
    3229             :         /*-------------------------------------------------------------
    3230             :          * Decide if we should return an OGRPolygon or an OGRMultiPolygon
    3231             :          * depending on the number of outer rings found in CoordSecHdr blocks.
    3232             :          * The CoodSecHdr block for each outer ring in the region has a flag
    3233             :          * indicating the number of inner rings that follow.
    3234             :          * In older versions of the format, the count of inner rings was
    3235             :          * always zero, so in this case we would always return MultiPolygons.
    3236             :          *
    3237             :          * Note: The current implementation assumes that there cannot be
    3238             :          * holes inside holes (i.e. multiple levels of inner rings)... if
    3239             :          * that case was encountered then we would return an OGRMultiPolygon
    3240             :          * in which the topological relationship between the rings would
    3241             :          * be lost.
    3242             :          *------------------------------------------------------------*/
    3243         525 :         int numOuterRings = 0;
    3244        1050 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    3245             :         {
    3246             :             // Count this as an outer ring.
    3247         525 :             numOuterRings++;
    3248             :             // Skip inner rings... so loop continues on an outer ring.
    3249         525 :             iSection += pasSecHdrs[iSection].numHoles;
    3250             :         }
    3251             : 
    3252         525 :         if (numOuterRings > 1)
    3253             :         {
    3254           0 :             poMultiPolygon = new OGRMultiPolygon;
    3255           0 :             poGeometry = poMultiPolygon;
    3256             :         }
    3257             :         else
    3258             :         {
    3259         525 :             poGeometry = nullptr;  // Will be set later
    3260             :         }
    3261             : 
    3262             :         /*-------------------------------------------------------------
    3263             :          * OK, build the OGRGeometry object.
    3264             :          *------------------------------------------------------------*/
    3265         525 :         int numHolesToRead = 0;
    3266         525 :         poPolygon = nullptr;
    3267        1050 :         for (int iSection = 0; iSection < numLineSections; iSection++)
    3268             :         {
    3269             : 
    3270         525 :             if (poPolygon == nullptr)
    3271         525 :                 poPolygon = new OGRPolygon();
    3272             : 
    3273         525 :             if (numHolesToRead < 1)
    3274         525 :                 numHolesToRead = pasSecHdrs[iSection].numHoles;
    3275             :             else
    3276           0 :                 numHolesToRead--;
    3277             : 
    3278         525 :             int numSectionVertices = pasSecHdrs[iSection].numVertices;
    3279         525 :             GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
    3280             : 
    3281         525 :             OGRLinearRing *poRing = new OGRLinearRing();
    3282         525 :             poRing->setNumPoints(numSectionVertices);
    3283             : 
    3284       14261 :             for (int i = 0; i < numSectionVertices; i++)
    3285             :             {
    3286       13736 :                 poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
    3287       13736 :                 poRing->setPoint(i, dX, dY);
    3288       13736 :                 pnXYPtr += 2;
    3289             :             }
    3290             : 
    3291         525 :             poPolygon->addRingDirectly(poRing);
    3292         525 :             poRing = nullptr;
    3293             : 
    3294         525 :             if (numHolesToRead < 1)
    3295             :             {
    3296         525 :                 if (numOuterRings > 1)
    3297             :                 {
    3298           0 :                     poMultiPolygon->addGeometryDirectly(poPolygon);
    3299             :                 }
    3300             :                 else
    3301             :                 {
    3302         525 :                     poGeometry = poPolygon;
    3303         525 :                     CPLAssert(iSection == numLineSections - 1);
    3304             :                 }
    3305             : 
    3306         525 :                 poPolygon = nullptr;  // We'll alloc a new polygon next loop.
    3307             :             }
    3308             :         }
    3309         525 :         delete poPolygon;  // should only trigger on corrupted files
    3310             : 
    3311         525 :         CPLFree(pasSecHdrs);
    3312         525 :         CPLFree(panXY);
    3313             :     }
    3314             :     else
    3315             :     {
    3316           0 :         CPLError(
    3317             :             CE_Failure, CPLE_AssertionFailed,
    3318             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    3319           0 :             m_nMapInfoType, m_nMapInfoType);
    3320           0 :         return -1;
    3321             :     }
    3322             : 
    3323         525 :     SetGeometryDirectly(poGeometry);
    3324             : 
    3325         525 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    3326         525 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    3327             :               poObjHdr->m_nMaxY);
    3328             : 
    3329             :     /* Return a ref to coord block so that caller can continue reading
    3330             :      * after the end of this object (used by TABCollection and index splitting)
    3331             :      */
    3332         525 :     if (ppoCoordBlock)
    3333           4 :         *ppoCoordBlock = poCoordBlock;
    3334             : 
    3335         525 :     return 0;
    3336             : }
    3337             : 
    3338             : /**********************************************************************
    3339             :  *                   TABRegion::WriteGeometryToMAPFile()
    3340             :  *
    3341             :  * Write the geometry and representation (color, etc...) part of the
    3342             :  * feature to the .MAP object pointed to by poMAPFile.
    3343             :  *
    3344             :  * It is assumed that poMAPFile currently points to a valid map object.
    3345             :  *
    3346             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    3347             :  * been called.
    3348             :  **********************************************************************/
    3349          88 : int TABRegion::WriteGeometryToMAPFile(
    3350             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    3351             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    3352             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    3353             : {
    3354             :     /*-----------------------------------------------------------------
    3355             :      * We assume that ValidateMapInfoType() was called already and that
    3356             :      * the type in poObjHdr->m_nType is valid.
    3357             :      *----------------------------------------------------------------*/
    3358          88 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    3359             : 
    3360             :     /*-----------------------------------------------------------------
    3361             :      * Fetch and validate geometry
    3362             :      *----------------------------------------------------------------*/
    3363          88 :     OGRGeometry *poGeom = GetGeometryRef();
    3364          88 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    3365             : 
    3366         258 :     if ((m_nMapInfoType == TAB_GEOM_REGION ||
    3367          82 :          m_nMapInfoType == TAB_GEOM_REGION_C ||
    3368           0 :          m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3369           0 :          m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3370           0 :          m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3371          88 :          m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
    3372         176 :         poGeom &&
    3373          88 :         (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3374           6 :          wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3375             :     {
    3376             :         /*=============================================================
    3377             :          * REGIONs are similar to PLINE MULTIPLE
    3378             :          *
    3379             :          * We accept both OGRPolygons (with one or multiple rings) and
    3380             :          * OGRMultiPolygons as input.
    3381             :          *============================================================*/
    3382          88 :         GBool bCompressed = poObjHdr->IsCompressedType();
    3383             : 
    3384             :         /*-------------------------------------------------------------
    3385             :          * Process geometry first...
    3386             :          *------------------------------------------------------------*/
    3387          88 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    3388           0 :             poCoordBlock = *ppoCoordBlock;
    3389             :         else
    3390          88 :             poCoordBlock = poMapFile->GetCurCoordBlock();
    3391          88 :         poCoordBlock->StartNewFeature();
    3392          88 :         GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    3393          88 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    3394             : 
    3395             : #ifdef TABDUMP
    3396             :         printf(/*ok*/
    3397             :                "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
    3398             :                m_nComprOrgX, m_nComprOrgY);
    3399             : #endif
    3400             :         /*-------------------------------------------------------------
    3401             :          * Fetch total number of rings and build array of coord
    3402             :          * sections headers.
    3403             :          *------------------------------------------------------------*/
    3404          88 :         TABMAPCoordSecHdr *pasSecHdrs = nullptr;
    3405          88 :         int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
    3406          88 :         int nStatus = numRingsTotal == 0 ? -1 : 0;
    3407             : 
    3408             :         /*-------------------------------------------------------------
    3409             :          * Write the Coord. Section Header
    3410             :          *------------------------------------------------------------*/
    3411          88 :         const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    3412             : 
    3413          88 :         if (nStatus == 0)
    3414          88 :             nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
    3415             :                                                       pasSecHdrs, bCompressed);
    3416             : 
    3417          88 :         CPLFree(pasSecHdrs);
    3418          88 :         pasSecHdrs = nullptr;
    3419             : 
    3420          88 :         if (nStatus != 0)
    3421           0 :             return nStatus;  // Error has already been reported.
    3422             : 
    3423             :         /*-------------------------------------------------------------
    3424             :          * Go through all the rings in our OGRMultiPolygon or OGRPolygon
    3425             :          * to write the coordinates themselves...
    3426             :          *------------------------------------------------------------*/
    3427             : 
    3428          88 :         GInt32 nX = 0, nY = 0;
    3429         176 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3430             :         {
    3431          88 :             OGRLinearRing *poRing = GetRingRef(iRing);
    3432          88 :             if (poRing == nullptr)
    3433             :             {
    3434           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    3435             :                          "TABRegion: Object Geometry contains NULL rings!");
    3436           0 :                 return -1;
    3437             :             }
    3438             : 
    3439          88 :             int numPoints = poRing->getNumPoints();
    3440             : 
    3441        2515 :             for (int i = 0; nStatus == 0 && i < numPoints; i++)
    3442             :             {
    3443        2427 :                 poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
    3444             :                                         nY);
    3445        2427 :                 if ((nStatus =
    3446        2427 :                          poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
    3447             :                 {
    3448             :                     // Failed ... error message has already been produced
    3449           0 :                     return nStatus;
    3450             :                 }
    3451             :             }
    3452             :         } /* for iRing*/
    3453             : 
    3454          88 :         GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    3455             : 
    3456             :         /*-------------------------------------------------------------
    3457             :          * ... and finally copy info to poObjHdr
    3458             :          *------------------------------------------------------------*/
    3459          88 :         TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
    3460             : 
    3461          88 :         poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
    3462          88 :         poPLineHdr->m_nCoordDataSize = nCoordDataSize;
    3463          88 :         poPLineHdr->m_numLineSections = numRingsTotal;
    3464             : 
    3465          88 :         poPLineHdr->m_bSmooth = m_bSmooth;
    3466             : 
    3467             :         // MBR
    3468          88 :         poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    3469             : 
    3470             :         // Region center/label point
    3471          88 :         double dX = 0.0;
    3472          88 :         double dY = 0.0;
    3473          88 :         if (GetCenter(dX, dY) != -1)
    3474             :         {
    3475          88 :             poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
    3476          88 :                                     poPLineHdr->m_nLabelY);
    3477             :         }
    3478             :         else
    3479             :         {
    3480           0 :             poPLineHdr->m_nLabelX = m_nComprOrgX;
    3481           0 :             poPLineHdr->m_nLabelY = m_nComprOrgY;
    3482             :         }
    3483             : 
    3484             :         // Compressed coordinate origin (useful only in compressed case!)
    3485          88 :         poPLineHdr->m_nComprOrgX = m_nComprOrgX;
    3486          88 :         poPLineHdr->m_nComprOrgY = m_nComprOrgY;
    3487             : 
    3488          88 :         if (!bCoordBlockDataOnly)
    3489             :         {
    3490          88 :             m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    3491          88 :             poPLineHdr->m_nPenId =
    3492          88 :                 static_cast<GByte>(m_nPenDefIndex);  // Pen index
    3493             : 
    3494          88 :             m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    3495          88 :             poPLineHdr->m_nBrushId =
    3496          88 :                 static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    3497             :         }
    3498             :     }
    3499             :     else
    3500             :     {
    3501           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3502             :                  "TABRegion: Object contains an invalid Geometry!");
    3503           0 :         return -1;
    3504             :     }
    3505             : 
    3506          88 :     if (CPLGetLastErrorType() == CE_Failure)
    3507           0 :         return -1;
    3508             : 
    3509             :     /* Return a ref to coord block so that caller can continue writing
    3510             :      * after the end of this object (used by index splitting)
    3511             :      */
    3512          88 :     if (ppoCoordBlock)
    3513           0 :         *ppoCoordBlock = poCoordBlock;
    3514             : 
    3515          88 :     return 0;
    3516             : }
    3517             : 
    3518             : /**********************************************************************
    3519             :  *                   TABRegion::GetNumRings()
    3520             :  *
    3521             :  * Return the total number of rings in this object making it look like
    3522             :  * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
    3523             :  * of rings... hides the complexity of handling OGRMultiPolygons vs
    3524             :  * OGRPolygons, etc.
    3525             :  *
    3526             :  * Returns 0 if the geometry contained in the object is invalid or missing.
    3527             :  **********************************************************************/
    3528         109 : int TABRegion::GetNumRings()
    3529             : {
    3530         109 :     return ComputeNumRings(nullptr, nullptr);
    3531             : }
    3532             : 
    3533         197 : int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
    3534             :                                TABMAPFile *poMapFile)
    3535             : {
    3536         197 :     int numRingsTotal = 0;
    3537         197 :     int iLastSect = 0;
    3538             : 
    3539         197 :     if (ppasSecHdrs)
    3540          88 :         *ppasSecHdrs = nullptr;
    3541             : 
    3542         197 :     OGRGeometry *poGeom = GetGeometryRef();
    3543             : 
    3544         209 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3545          12 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3546             :     {
    3547             :         /*-------------------------------------------------------------
    3548             :          * Calculate total number of rings...
    3549             :          *------------------------------------------------------------*/
    3550         197 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3551             :         {
    3552          24 :             for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
    3553             :             {
    3554          12 :                 numRingsTotal += poPolygon->getNumInteriorRings() + 1;
    3555             : 
    3556          12 :                 if (ppasSecHdrs && poMapFile)
    3557             :                 {
    3558           6 :                     if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
    3559           6 :                                       iLastSect) != 0)
    3560           0 :                         return 0;  // An error happened, return count=0
    3561             :                 }
    3562             :             }  // for
    3563             :         }
    3564             :         else
    3565             :         {
    3566         185 :             OGRPolygon *poPolygon = poGeom->toPolygon();
    3567         185 :             numRingsTotal = poPolygon->getNumInteriorRings() + 1;
    3568             : 
    3569         185 :             if (ppasSecHdrs && poMapFile)
    3570             :             {
    3571          82 :                 if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
    3572          82 :                                   iLastSect) != 0)
    3573           0 :                     return 0;  // An error happened, return count=0
    3574             :             }
    3575             :         }
    3576             :     }
    3577             : 
    3578             :     /*-----------------------------------------------------------------
    3579             :      * If we're generating section header blocks, then init the
    3580             :      * coordinate offset values.
    3581             :      *
    3582             :      * In calculation of nDataOffset, we have to take into account that
    3583             :      * V450 header section uses int32 instead of int16 for numVertices
    3584             :      * and we add another 2 bytes to align with a 4 bytes boundary.
    3585             :      *------------------------------------------------------------*/
    3586         197 :     const int nTotalHdrSizeUncompressed =
    3587         394 :         (m_nMapInfoType == TAB_GEOM_V450_REGION ||
    3588         197 :          m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
    3589         197 :          m_nMapInfoType == TAB_GEOM_V800_REGION ||
    3590         197 :          m_nMapInfoType == TAB_GEOM_V800_REGION_C)
    3591         394 :             ? 28 * numRingsTotal
    3592             :             : 24 * numRingsTotal;
    3593             : 
    3594         197 :     if (ppasSecHdrs)
    3595             :     {
    3596          88 :         int numPointsTotal = 0;
    3597          88 :         CPLAssert(iLastSect == numRingsTotal);
    3598         176 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3599             :         {
    3600          88 :             (*ppasSecHdrs)[iRing].nDataOffset =
    3601          88 :                 nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
    3602          88 :             (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
    3603             : 
    3604          88 :             numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
    3605             :         }
    3606             :     }
    3607             : 
    3608         197 :     return numRingsTotal;
    3609             : }
    3610             : 
    3611             : /**********************************************************************
    3612             :  *                   TABRegion::AppendSecHdrs()
    3613             :  *
    3614             :  * (Private method)
    3615             :  *
    3616             :  * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
    3617             :  **********************************************************************/
    3618          88 : int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
    3619             :                              TABMAPCoordSecHdr *&pasSecHdrs,
    3620             :                              TABMAPFile *poMapFile, int &iLastRing)
    3621             : {
    3622             :     /*-------------------------------------------------------------
    3623             :      * Add a pasSecHdrs[] entry for each ring in this polygon.
    3624             :      * Note that the structs won't be fully initialized.
    3625             :      *------------------------------------------------------------*/
    3626          88 :     int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
    3627             : 
    3628          88 :     pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
    3629          88 :         CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
    3630             :                                    sizeof(TABMAPCoordSecHdr)));
    3631             : 
    3632         176 :     for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
    3633             :     {
    3634          88 :         OGRLinearRing *poRing = nullptr;
    3635          88 :         OGREnvelope sEnvelope;
    3636             : 
    3637          88 :         if (iRing == 0)
    3638          88 :             poRing = poPolygon->getExteriorRing();
    3639             :         else
    3640           0 :             poRing = poPolygon->getInteriorRing(iRing - 1);
    3641             : 
    3642          88 :         if (poRing == nullptr)
    3643             :         {
    3644           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    3645             :                      "Assertion Failed: Encountered NULL ring in OGRPolygon");
    3646           0 :             return -1;
    3647             :         }
    3648             : 
    3649          88 :         poRing->getEnvelope(&sEnvelope);
    3650             : 
    3651          88 :         pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
    3652             : 
    3653          88 :         if (iRing == 0)
    3654          88 :             pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
    3655             :         else
    3656           0 :             pasSecHdrs[iLastRing].numHoles = 0;
    3657             : 
    3658          88 :         poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
    3659          88 :                                 pasSecHdrs[iLastRing].nXMin,
    3660          88 :                                 pasSecHdrs[iLastRing].nYMin);
    3661          88 :         poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
    3662          88 :                                 pasSecHdrs[iLastRing].nXMax,
    3663          88 :                                 pasSecHdrs[iLastRing].nYMax);
    3664             : 
    3665          88 :         iLastRing++;
    3666             :     } /* for iRing*/
    3667             : 
    3668          88 :     return 0;
    3669             : }
    3670             : 
    3671             : /**********************************************************************
    3672             :  *                   TABRegion::GetRingRef()
    3673             :  *
    3674             :  * Returns a reference to the specified ring number making it look like
    3675             :  * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
    3676             :  * of rings... hides the complexity of handling OGRMultiPolygons vs
    3677             :  * OGRPolygons, etc.
    3678             :  *
    3679             :  * Returns NULL if the geometry contained in the object is invalid or
    3680             :  * missing or if the specified ring index is invalid.
    3681             :  **********************************************************************/
    3682         197 : OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
    3683             : {
    3684         197 :     OGRLinearRing *poRing = nullptr;
    3685             : 
    3686         197 :     OGRGeometry *poGeom = GetGeometryRef();
    3687             : 
    3688         209 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3689          12 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3690             :     {
    3691             :         /*-------------------------------------------------------------
    3692             :          * Establish number of polygons based on geometry type
    3693             :          *------------------------------------------------------------*/
    3694         197 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3695         197 :         int iCurRing = 0;
    3696         197 :         int numOGRPolygons = 0;
    3697             : 
    3698         197 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3699             :         {
    3700          12 :             poMultiPolygon = poGeom->toMultiPolygon();
    3701          12 :             numOGRPolygons = poMultiPolygon->getNumGeometries();
    3702             :         }
    3703             :         else
    3704             :         {
    3705         185 :             numOGRPolygons = 1;
    3706             :         }
    3707             : 
    3708             :         /*-------------------------------------------------------------
    3709             :          * Loop through polygons until we find the requested ring.
    3710             :          *------------------------------------------------------------*/
    3711         197 :         iCurRing = 0;
    3712         394 :         for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
    3713             :              iPoly++)
    3714             :         {
    3715         197 :             OGRPolygon *poPolygon = nullptr;
    3716         197 :             if (poMultiPolygon)
    3717          12 :                 poPolygon = poMultiPolygon->getGeometryRef(iPoly);
    3718             :             else
    3719         185 :                 poPolygon = poGeom->toPolygon();
    3720             : 
    3721         197 :             int numIntRings = poPolygon->getNumInteriorRings();
    3722             : 
    3723         197 :             if (iCurRing == nRequestedRingIndex)
    3724             :             {
    3725         197 :                 poRing = poPolygon->getExteriorRing();
    3726             :             }
    3727           0 :             else if (nRequestedRingIndex > iCurRing &&
    3728           0 :                      nRequestedRingIndex - (iCurRing + 1) < numIntRings)
    3729             :             {
    3730           0 :                 poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
    3731           0 :                                                     (iCurRing + 1));
    3732             :             }
    3733         197 :             iCurRing += numIntRings + 1;
    3734             :         }
    3735             :     }
    3736             : 
    3737         197 :     return poRing;
    3738             : }
    3739             : 
    3740             : /**********************************************************************
    3741             :  *                   TABRegion::RingIsHole()
    3742             :  *
    3743             :  * Return false if the requested ring index is the first of a polygon
    3744             :  **********************************************************************/
    3745           0 : GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
    3746             : {
    3747           0 :     OGRGeometry *poGeom = GetGeometryRef();
    3748             : 
    3749           0 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3750           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3751             :     {
    3752             :         /*-------------------------------------------------------------
    3753             :          * Establish number of polygons based on geometry type
    3754             :          *------------------------------------------------------------*/
    3755           0 :         OGRMultiPolygon *poMultiPolygon = nullptr;
    3756           0 :         int iCurRing = 0;
    3757           0 :         int numOGRPolygons = 0;
    3758             : 
    3759           0 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3760             :         {
    3761           0 :             poMultiPolygon = poGeom->toMultiPolygon();
    3762           0 :             numOGRPolygons = poMultiPolygon->getNumGeometries();
    3763             :         }
    3764             :         else
    3765             :         {
    3766           0 :             numOGRPolygons = 1;
    3767             :         }
    3768             : 
    3769             :         /*-------------------------------------------------------------
    3770             :          * Loop through polygons until we find the requested ring.
    3771             :          *------------------------------------------------------------*/
    3772           0 :         iCurRing = 0;
    3773           0 :         for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
    3774             :         {
    3775           0 :             OGRPolygon *poPolygon = nullptr;
    3776           0 :             if (poMultiPolygon)
    3777           0 :                 poPolygon = poMultiPolygon->getGeometryRef(iPoly);
    3778             :             else
    3779           0 :                 poPolygon = poGeom->toPolygon();
    3780             : 
    3781           0 :             int numIntRings = poPolygon->getNumInteriorRings();
    3782             : 
    3783           0 :             if (iCurRing == nRequestedRingIndex)
    3784             :             {
    3785           0 :                 return FALSE;
    3786             :             }
    3787           0 :             else if (nRequestedRingIndex > iCurRing &&
    3788           0 :                      nRequestedRingIndex - (iCurRing + 1) < numIntRings)
    3789             :             {
    3790           0 :                 return TRUE;
    3791             :             }
    3792           0 :             iCurRing += numIntRings + 1;
    3793             :         }
    3794             :     }
    3795             : 
    3796           0 :     return FALSE;
    3797             : }
    3798             : 
    3799             : /**********************************************************************
    3800             :  *                   TABRegion::GetStyleString() const
    3801             :  *
    3802             :  * Return style string for this feature.
    3803             :  *
    3804             :  * Style String is built only once during the first call to GetStyleString().
    3805             :  **********************************************************************/
    3806          67 : const char *TABRegion::GetStyleString() const
    3807             : {
    3808          67 :     if (m_pszStyleString == nullptr)
    3809             :     {
    3810             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    3811             :         // to use temporary buffers
    3812          39 :         char *pszPen = CPLStrdup(GetPenStyleString());
    3813          39 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    3814             : 
    3815          39 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    3816             : 
    3817          39 :         CPLFree(pszPen);
    3818          39 :         CPLFree(pszBrush);
    3819             :     }
    3820             : 
    3821          67 :     return m_pszStyleString;
    3822             : }
    3823             : 
    3824             : /**********************************************************************
    3825             :  *                   TABRegion::DumpMIF()
    3826             :  *
    3827             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    3828             :  **********************************************************************/
    3829           0 : void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
    3830             : {
    3831           0 :     if (fpOut == nullptr)
    3832           0 :         fpOut = stdout;
    3833             : 
    3834             :     /*-----------------------------------------------------------------
    3835             :      * Fetch and validate geometry
    3836             :      *----------------------------------------------------------------*/
    3837           0 :     OGRGeometry *poGeom = GetGeometryRef();
    3838           0 :     if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    3839           0 :                    wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
    3840             :     {
    3841             :         /*-------------------------------------------------------------
    3842             :          * Generate output for region
    3843             :          *
    3844             :          * Note that we want to handle both OGRPolygons and OGRMultiPolygons
    3845             :          * that's why we use the GetNumRings()/GetRingRef() interface.
    3846             :          *------------------------------------------------------------*/
    3847           0 :         int numRingsTotal = GetNumRings();
    3848             : 
    3849           0 :         fprintf(fpOut, "REGION %d\n", numRingsTotal);
    3850             : 
    3851           0 :         for (int iRing = 0; iRing < numRingsTotal; iRing++)
    3852             :         {
    3853           0 :             OGRLinearRing *poRing = GetRingRef(iRing);
    3854             : 
    3855           0 :             if (poRing == nullptr)
    3856             :             {
    3857           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    3858             :                          "TABRegion: Object Geometry contains NULL rings!");
    3859           0 :                 return;
    3860             :             }
    3861             : 
    3862           0 :             const int numPoints = poRing->getNumPoints();
    3863           0 :             fprintf(fpOut, " %d\n", numPoints);
    3864           0 :             for (int i = 0; i < numPoints; i++)
    3865           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    3866             :                         poRing->getY(i));
    3867             :         }
    3868             :     }
    3869             :     else
    3870             :     {
    3871           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    3872             :                  "TABRegion: Missing or Invalid Geometry!");
    3873           0 :         return;
    3874             :     }
    3875             : 
    3876           0 :     if (m_bCenterIsSet)
    3877           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    3878             : 
    3879             :     // Finish with PEN/BRUSH/etc. clauses
    3880           0 :     DumpPenDef();
    3881           0 :     DumpBrushDef();
    3882             : 
    3883           0 :     fflush(fpOut);
    3884             : }
    3885             : 
    3886             : /**********************************************************************
    3887             :  *                   TABRegion::GetCenter()
    3888             :  *
    3889             :  * Returns the center/label point of the region.
    3890             :  * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
    3891             :  * before.
    3892             :  *
    3893             :  * Returns 0 on success, -1 on error.
    3894             :  **********************************************************************/
    3895          88 : int TABRegion::GetCenter(double &dX, double &dY)
    3896             : {
    3897          88 :     if (!m_bCenterIsSet)
    3898             :     {
    3899             :         /*-------------------------------------------------------------
    3900             :          * Calculate label point.  If we have a multipolygon then we use
    3901             :          * the first OGRPolygon in the feature to calculate the point.
    3902             :          *------------------------------------------------------------*/
    3903          88 :         OGRGeometry *poGeom = GetGeometryRef();
    3904          88 :         if (poGeom == nullptr)
    3905           0 :             return -1;
    3906             : 
    3907          88 :         OGRPolygon *poPolygon = nullptr;
    3908             : 
    3909          88 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    3910             :         {
    3911           6 :             OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
    3912           6 :             if (poMultiPolygon->getNumGeometries() > 0)
    3913           6 :                 poPolygon = poMultiPolygon->getGeometryRef(0);
    3914             :         }
    3915          82 :         else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    3916             :         {
    3917          82 :             poPolygon = poGeom->toPolygon();
    3918             :         }
    3919             : 
    3920          88 :         OGRPoint oLabelPoint;
    3921         176 :         if (poPolygon != nullptr &&
    3922          88 :             OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
    3923             :         {
    3924          88 :             m_dCenterX = oLabelPoint.getX();
    3925          88 :             m_dCenterY = oLabelPoint.getY();
    3926             :         }
    3927             :         else
    3928             :         {
    3929           0 :             OGREnvelope oEnv;
    3930           0 :             poGeom->getEnvelope(&oEnv);
    3931           0 :             m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
    3932           0 :             m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
    3933             :         }
    3934             : 
    3935          88 :         m_bCenterIsSet = TRUE;
    3936             :     }
    3937             : 
    3938          88 :     if (!m_bCenterIsSet)
    3939           0 :         return -1;
    3940             : 
    3941          88 :     dX = m_dCenterX;
    3942          88 :     dY = m_dCenterY;
    3943          88 :     return 0;
    3944             : }
    3945             : 
    3946             : /**********************************************************************
    3947             :  *                   TABRegion::SetCenter()
    3948             :  *
    3949             :  * Set the X,Y coordinates to use as center/label point for the region.
    3950             :  **********************************************************************/
    3951         527 : void TABRegion::SetCenter(double dX, double dY)
    3952             : {
    3953         527 :     m_dCenterX = dX;
    3954         527 :     m_dCenterY = dY;
    3955         527 :     m_bCenterIsSet = TRUE;
    3956         527 : }
    3957             : 
    3958             : /*=====================================================================
    3959             :  *                      class TABRectangle
    3960             :  *====================================================================*/
    3961             : 
    3962             : /**********************************************************************
    3963             :  *                   TABRectangle::TABRectangle()
    3964             :  *
    3965             :  * Constructor.
    3966             :  **********************************************************************/
    3967         949 : TABRectangle::TABRectangle(const OGRFeatureDefn *poDefnIn)
    3968             :     : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
    3969         949 :       m_dRoundYRadius(0.0)
    3970             : {
    3971         949 : }
    3972             : 
    3973             : /**********************************************************************
    3974             :  *                   TABRectangle::~TABRectangle()
    3975             :  *
    3976             :  * Destructor.
    3977             :  **********************************************************************/
    3978        1898 : TABRectangle::~TABRectangle()
    3979             : {
    3980        1898 : }
    3981             : 
    3982             : /**********************************************************************
    3983             :  *                     TABRectangle::CloneTABFeature()
    3984             :  *
    3985             :  * Duplicate feature, including stuff specific to each TABFeature type.
    3986             :  *
    3987             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    3988             :  * then copies any members specific to its own type.
    3989             :  **********************************************************************/
    3990             : TABFeature *
    3991           0 : TABRectangle::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    3992             : {
    3993             :     /*-----------------------------------------------------------------
    3994             :      * Alloc new feature and copy the base stuff
    3995             :      *----------------------------------------------------------------*/
    3996             :     TABRectangle *poNew =
    3997           0 :         new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
    3998             : 
    3999           0 :     CopyTABFeatureBase(poNew);
    4000             : 
    4001             :     /*-----------------------------------------------------------------
    4002             :      * And members specific to this class
    4003             :      *----------------------------------------------------------------*/
    4004             :     // ITABFeaturePen
    4005           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4006             : 
    4007             :     // ITABFeatureBrush
    4008           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    4009             : 
    4010           0 :     poNew->m_bRoundCorners = m_bRoundCorners;
    4011           0 :     poNew->m_dRoundXRadius = m_dRoundXRadius;
    4012           0 :     poNew->m_dRoundYRadius = m_dRoundYRadius;
    4013             : 
    4014           0 :     return poNew;
    4015             : }
    4016             : 
    4017             : /**********************************************************************
    4018             :  *                   TABRectangle::ValidateMapInfoType()
    4019             :  *
    4020             :  * Check the feature's geometry part and return the corresponding
    4021             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4022             :  * be updated for further calls to GetMapInfoType();
    4023             :  *
    4024             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4025             :  * is expected for this object class.
    4026             :  **********************************************************************/
    4027           0 : TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4028             : {
    4029             :     /*-----------------------------------------------------------------
    4030             :      * Fetch and validate geometry
    4031             :      *----------------------------------------------------------------*/
    4032           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4033           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4034             :     {
    4035           0 :         if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    4036           0 :             m_nMapInfoType = TAB_GEOM_ROUNDRECT;
    4037             :         else
    4038           0 :             m_nMapInfoType = TAB_GEOM_RECT;
    4039             :     }
    4040             :     else
    4041             :     {
    4042           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4043             :                  "TABRectangle: Missing or Invalid Geometry!");
    4044           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4045             :     }
    4046             : 
    4047             :     /*-----------------------------------------------------------------
    4048             :      * Decide if coordinates should be compressed or not.
    4049             :      *----------------------------------------------------------------*/
    4050             :     // __TODO__ For now we always write uncompressed for this class...
    4051             :     // ValidateCoordType(poMapFile);
    4052           0 :     UpdateMBR(poMapFile);
    4053             : 
    4054           0 :     return m_nMapInfoType;
    4055             : }
    4056             : 
    4057             : /**********************************************************************
    4058             :  *                   TABRectangle::UpdateMBR()
    4059             :  *
    4060             :  * Update the feature MBR members using the geometry
    4061             :  *
    4062             :  * Returns 0 on success, or -1 if there is no geometry in object
    4063             :  **********************************************************************/
    4064           0 : int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4065             : {
    4066           0 :     OGREnvelope sEnvelope;
    4067             : 
    4068             :     /*-----------------------------------------------------------------
    4069             :      * Fetch and validate geometry
    4070             :      *----------------------------------------------------------------*/
    4071           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4072           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4073           0 :         poGeom->getEnvelope(&sEnvelope);
    4074             :     else
    4075             :     {
    4076           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4077             :                  "TABRectangle: Missing or Invalid Geometry!");
    4078           0 :         return -1;
    4079             :     }
    4080             : 
    4081             :     /*-----------------------------------------------------------------
    4082             :      * Note that we will simply use the rectangle's MBR and don't really
    4083             :      * read the polygon geometry... this should be OK unless the
    4084             :      * polygon geometry was not really a rectangle.
    4085             :      *----------------------------------------------------------------*/
    4086           0 :     m_dXMin = sEnvelope.MinX;
    4087           0 :     m_dYMin = sEnvelope.MinY;
    4088           0 :     m_dXMax = sEnvelope.MaxX;
    4089           0 :     m_dYMax = sEnvelope.MaxY;
    4090             : 
    4091           0 :     if (poMapFile)
    4092             :     {
    4093           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4094           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4095             :     }
    4096             : 
    4097           0 :     return 0;
    4098             : }
    4099             : 
    4100             : /**********************************************************************
    4101             :  *                   TABRectangle::ReadGeometryFromMAPFile()
    4102             :  *
    4103             :  * Fill the geometry and representation (color, etc...) part of the
    4104             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4105             :  *
    4106             :  * It is assumed that poMAPFile currently points to the beginning of
    4107             :  * a map object.
    4108             :  *
    4109             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4110             :  * been called.
    4111             :  **********************************************************************/
    4112           8 : int TABRectangle::ReadGeometryFromMAPFile(
    4113             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4114             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4115             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4116             : {
    4117             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4118           8 :     if (bCoordBlockDataOnly)
    4119           0 :         return 0;
    4120             : 
    4121             :     /*-----------------------------------------------------------------
    4122             :      * Fetch and validate geometry type
    4123             :      *----------------------------------------------------------------*/
    4124           8 :     m_nMapInfoType = poObjHdr->m_nType;
    4125             : 
    4126           8 :     if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
    4127           4 :         m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
    4128           0 :         m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
    4129             :     {
    4130           0 :         CPLError(
    4131             :             CE_Failure, CPLE_AssertionFailed,
    4132             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    4133           0 :             m_nMapInfoType, m_nMapInfoType);
    4134           0 :         return -1;
    4135             :     }
    4136             : 
    4137             :     /*-----------------------------------------------------------------
    4138             :      * Read object information
    4139             :      *----------------------------------------------------------------*/
    4140             :     TABMAPObjRectEllipse *poRectHdr =
    4141           8 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4142             : 
    4143             :     // Read the corners radius
    4144             : 
    4145           8 :     if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
    4146           4 :         m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
    4147             :     {
    4148             :         // Read the corner's diameters
    4149           4 :         poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
    4150           4 :                                     poRectHdr->m_nCornerHeight, m_dRoundXRadius,
    4151           4 :                                     m_dRoundYRadius);
    4152             : 
    4153             :         // Divide by 2 since we store the corner's radius
    4154           4 :         m_dRoundXRadius /= 2.0;
    4155           4 :         m_dRoundYRadius /= 2.0;
    4156             : 
    4157           4 :         m_bRoundCorners = TRUE;
    4158             :     }
    4159             :     else
    4160             :     {
    4161           4 :         m_bRoundCorners = FALSE;
    4162           4 :         m_dRoundXRadius = 0.0;
    4163           4 :         m_dRoundYRadius = 0.0;
    4164             :     }
    4165             : 
    4166             :     // A rectangle is defined by its MBR
    4167             : 
    4168           8 :     double dXMin = 0.0;
    4169           8 :     double dYMin = 0.0;
    4170           8 :     double dXMax = 0.0;
    4171           8 :     double dYMax = 0.0;
    4172           8 :     poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
    4173             :                             dYMin);
    4174           8 :     poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
    4175             :                             dYMax);
    4176             : 
    4177           8 :     m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
    4178           8 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    4179             : 
    4180           8 :     m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
    4181           8 :     poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    4182             : 
    4183             :     /*-----------------------------------------------------------------
    4184             :      * Call SetMBR() and GetMBR() now to make sure that min values are
    4185             :      * really smaller than max values.
    4186             :      *----------------------------------------------------------------*/
    4187           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    4188           8 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4189             : 
    4190             :     /* Copy int MBR to feature class members */
    4191           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    4192             :               poObjHdr->m_nMaxY);
    4193             : 
    4194             :     /*-----------------------------------------------------------------
    4195             :      * Create and fill geometry object
    4196             :      *----------------------------------------------------------------*/
    4197           8 :     OGRPolygon *poPolygon = new OGRPolygon;
    4198           8 :     OGRLinearRing *poRing = new OGRLinearRing();
    4199           8 :     if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
    4200             :     {
    4201             :         /*-------------------------------------------------------------
    4202             :          * For rounded rectangles, we generate arcs with 45 line
    4203             :          * segments for each corner.  We start with lower-left corner
    4204             :          * and proceed counterclockwise
    4205             :          * We also have to make sure that rounding radius is not too
    4206             :          * large for the MBR in the generated polygon... however, we
    4207             :          * always return the true X/Y radius (not adjusted) since this
    4208             :          * is the way MapInfo seems to do it when a radius bigger than
    4209             :          * the MBR is passed from TBA to MIF.
    4210             :          *------------------------------------------------------------*/
    4211             :         const double dXRadius =
    4212           4 :             std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
    4213             :         const double dYRadius =
    4214           4 :             std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
    4215           4 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
    4216             :                        dYRadius, M_PI, 3.0 * M_PI / 2.0);
    4217           4 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
    4218             :                        dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
    4219           4 :         TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
    4220             :                        dYRadius, 0.0, M_PI / 2.0);
    4221           4 :         TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
    4222             :                        dYRadius, M_PI / 2.0, M_PI);
    4223             : 
    4224           4 :         TABCloseRing(poRing);
    4225             :     }
    4226             :     else
    4227             :     {
    4228           4 :         poRing->addPoint(dXMin, dYMin);
    4229           4 :         poRing->addPoint(dXMax, dYMin);
    4230           4 :         poRing->addPoint(dXMax, dYMax);
    4231           4 :         poRing->addPoint(dXMin, dYMax);
    4232           4 :         poRing->addPoint(dXMin, dYMin);
    4233             :     }
    4234             : 
    4235           8 :     poPolygon->addRingDirectly(poRing);
    4236           8 :     SetGeometryDirectly(poPolygon);
    4237             : 
    4238           8 :     return 0;
    4239             : }
    4240             : 
    4241             : /**********************************************************************
    4242             :  *                   TABRectangle::WriteGeometryToMAPFile()
    4243             :  *
    4244             :  * Write the geometry and representation (color, etc...) part of the
    4245             :  * feature to the .MAP object pointed to by poMAPFile.
    4246             :  *
    4247             :  * It is assumed that poMAPFile currently points to a valid map object.
    4248             :  *
    4249             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4250             :  * been called.
    4251             :  **********************************************************************/
    4252           0 : int TABRectangle::WriteGeometryToMAPFile(
    4253             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4254             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4255             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4256             : {
    4257             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4258           0 :     if (bCoordBlockDataOnly)
    4259           0 :         return 0;
    4260             : 
    4261             :     /*-----------------------------------------------------------------
    4262             :      * We assume that ValidateMapInfoType() was called already and that
    4263             :      * the type in poObjHdr->m_nType is valid.
    4264             :      *----------------------------------------------------------------*/
    4265           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    4266             : 
    4267             :     /*-----------------------------------------------------------------
    4268             :      * Fetch and validate geometry and update MBR
    4269             :      * Note that we will simply use the geometry's MBR and don't really
    4270             :      * read the polygon geometry... this should be OK unless the
    4271             :      * polygon geometry was not really a rectangle.
    4272             :      *----------------------------------------------------------------*/
    4273           0 :     if (UpdateMBR(poMapFile) != 0)
    4274           0 :         return -1; /* Error already reported */
    4275             : 
    4276             :     /*-----------------------------------------------------------------
    4277             :      * Copy object information
    4278             :      *----------------------------------------------------------------*/
    4279             :     TABMAPObjRectEllipse *poRectHdr =
    4280           0 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4281             : 
    4282           0 :     if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
    4283           0 :         m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
    4284             :     {
    4285           0 :         poMapFile->Coordsys2IntDist(
    4286           0 :             m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
    4287           0 :             poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
    4288             :     }
    4289             :     else
    4290             :     {
    4291           0 :         poRectHdr->m_nCornerWidth = 0;
    4292           0 :         poRectHdr->m_nCornerHeight = 0;
    4293             :     }
    4294             : 
    4295             :     // A rectangle is defined by its MBR (values were set in UpdateMBR())
    4296           0 :     poRectHdr->m_nMinX = m_nXMin;
    4297           0 :     poRectHdr->m_nMinY = m_nYMin;
    4298           0 :     poRectHdr->m_nMaxX = m_nXMax;
    4299           0 :     poRectHdr->m_nMaxY = m_nYMax;
    4300             : 
    4301           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    4302           0 :     poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    4303             : 
    4304           0 :     m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    4305           0 :     poRectHdr->m_nBrushId =
    4306           0 :         static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    4307             : 
    4308           0 :     if (CPLGetLastErrorType() == CE_Failure)
    4309           0 :         return -1;
    4310             : 
    4311           0 :     return 0;
    4312             : }
    4313             : 
    4314             : /**********************************************************************
    4315             :  *                   TABRectangle::GetStyleString() const
    4316             :  *
    4317             :  * Return style string for this feature.
    4318             :  *
    4319             :  * Style String is built only once during the first call to GetStyleString().
    4320             :  **********************************************************************/
    4321          18 : const char *TABRectangle::GetStyleString() const
    4322             : {
    4323          18 :     if (m_pszStyleString == nullptr)
    4324             :     {
    4325             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    4326             :         // to use temporary buffers
    4327          10 :         char *pszPen = CPLStrdup(GetPenStyleString());
    4328          10 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    4329             : 
    4330          10 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    4331             : 
    4332          10 :         CPLFree(pszPen);
    4333          10 :         CPLFree(pszBrush);
    4334             :     }
    4335             : 
    4336          18 :     return m_pszStyleString;
    4337             : }
    4338             : 
    4339             : /**********************************************************************
    4340             :  *                   TABRectangle::DumpMIF()
    4341             :  *
    4342             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    4343             :  **********************************************************************/
    4344           0 : void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
    4345             : {
    4346           0 :     if (fpOut == nullptr)
    4347           0 :         fpOut = stdout;
    4348             : 
    4349             :     /*-----------------------------------------------------------------
    4350             :      * Output RECT or ROUNDRECT parameters
    4351             :      *----------------------------------------------------------------*/
    4352           0 :     double dXMin = 0.0;
    4353           0 :     double dYMin = 0.0;
    4354           0 :     double dXMax = 0.0;
    4355           0 :     double dYMax = 0.0;
    4356           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4357             : 
    4358           0 :     if (m_bRoundCorners)
    4359           0 :         fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g    %.15g %.15g)\n",
    4360             :                 dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
    4361             :     else
    4362           0 :         fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
    4363             :                 dYMax);
    4364             : 
    4365             :     /*-----------------------------------------------------------------
    4366             :      * Fetch and validate geometry
    4367             :      *----------------------------------------------------------------*/
    4368           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4369           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4370             :     {
    4371             :         /*-------------------------------------------------------------
    4372             :          * Generate rectangle output as a region
    4373             :          * We could also output as a RECT or ROUNDRECT in a real MIF generator
    4374             :          *------------------------------------------------------------*/
    4375           0 :         OGRPolygon *poPolygon = poGeom->toPolygon();
    4376           0 :         int numIntRings = poPolygon->getNumInteriorRings();
    4377           0 :         fprintf(fpOut, "REGION %d\n", numIntRings + 1);
    4378             :         // In this loop, iRing=-1 for the outer ring.
    4379           0 :         for (int iRing = -1; iRing < numIntRings; iRing++)
    4380             :         {
    4381           0 :             OGRLinearRing *poRing = nullptr;
    4382             : 
    4383           0 :             if (iRing == -1)
    4384           0 :                 poRing = poPolygon->getExteriorRing();
    4385             :             else
    4386           0 :                 poRing = poPolygon->getInteriorRing(iRing);
    4387             : 
    4388           0 :             if (poRing == nullptr)
    4389             :             {
    4390           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    4391             :                          "TABRectangle: Object Geometry contains NULL rings!");
    4392           0 :                 return;
    4393             :             }
    4394             : 
    4395           0 :             const int numPoints = poRing->getNumPoints();
    4396           0 :             fprintf(fpOut, " %d\n", numPoints);
    4397           0 :             for (int i = 0; i < numPoints; i++)
    4398           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    4399             :                         poRing->getY(i));
    4400             :         }
    4401             :     }
    4402             :     else
    4403             :     {
    4404           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4405             :                  "TABRectangle: Missing or Invalid Geometry!");
    4406           0 :         return;
    4407             :     }
    4408             : 
    4409             :     // Finish with PEN/BRUSH/etc. clauses
    4410           0 :     DumpPenDef();
    4411           0 :     DumpBrushDef();
    4412             : 
    4413           0 :     fflush(fpOut);
    4414             : }
    4415             : 
    4416             : /*=====================================================================
    4417             :  *                      class TABEllipse
    4418             :  *====================================================================*/
    4419             : 
    4420             : /**********************************************************************
    4421             :  *                   TABEllipse::TABEllipse()
    4422             :  *
    4423             :  * Constructor.
    4424             :  **********************************************************************/
    4425         401 : TABEllipse::TABEllipse(const OGRFeatureDefn *poDefnIn)
    4426             :     : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
    4427         401 :       m_dYRadius(0.0)
    4428             : {
    4429         401 : }
    4430             : 
    4431             : /**********************************************************************
    4432             :  *                   TABEllipse::~TABEllipse()
    4433             :  *
    4434             :  * Destructor.
    4435             :  **********************************************************************/
    4436         802 : TABEllipse::~TABEllipse()
    4437             : {
    4438         802 : }
    4439             : 
    4440             : /**********************************************************************
    4441             :  *                     TABEllipse::CloneTABFeature()
    4442             :  *
    4443             :  * Duplicate feature, including stuff specific to each TABFeature type.
    4444             :  *
    4445             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    4446             :  * then copies any members specific to its own type.
    4447             :  **********************************************************************/
    4448             : TABFeature *
    4449           0 : TABEllipse::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    4450             : {
    4451             :     /*-----------------------------------------------------------------
    4452             :      * Alloc new feature and copy the base stuff
    4453             :      *----------------------------------------------------------------*/
    4454           0 :     TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
    4455             : 
    4456           0 :     CopyTABFeatureBase(poNew);
    4457             : 
    4458             :     /*-----------------------------------------------------------------
    4459             :      * And members specific to this class
    4460             :      *----------------------------------------------------------------*/
    4461             :     // ITABFeaturePen
    4462           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4463             : 
    4464             :     // ITABFeatureBrush
    4465           0 :     *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
    4466             : 
    4467           0 :     poNew->m_dCenterX = m_dCenterX;
    4468           0 :     poNew->m_dCenterY = m_dCenterY;
    4469           0 :     poNew->m_dXRadius = m_dXRadius;
    4470           0 :     poNew->m_dYRadius = m_dYRadius;
    4471             : 
    4472           0 :     return poNew;
    4473             : }
    4474             : 
    4475             : /**********************************************************************
    4476             :  *                   TABEllipse::ValidateMapInfoType()
    4477             :  *
    4478             :  * Check the feature's geometry part and return the corresponding
    4479             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4480             :  * be updated for further calls to GetMapInfoType();
    4481             :  *
    4482             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4483             :  * is expected for this object class.
    4484             :  **********************************************************************/
    4485           0 : TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4486             : {
    4487             :     /*-----------------------------------------------------------------
    4488             :      * Fetch and validate geometry
    4489             :      *----------------------------------------------------------------*/
    4490           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4491           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    4492           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4493             :     {
    4494           0 :         m_nMapInfoType = TAB_GEOM_ELLIPSE;
    4495             :     }
    4496             :     else
    4497             :     {
    4498           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4499             :                  "TABEllipse: Missing or Invalid Geometry!");
    4500           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4501             :     }
    4502             : 
    4503             :     /*-----------------------------------------------------------------
    4504             :      * Decide if coordinates should be compressed or not.
    4505             :      *----------------------------------------------------------------*/
    4506             :     // __TODO__ For now we always write uncompressed for this class...
    4507             :     // ValidateCoordType(poMapFile);
    4508           0 :     UpdateMBR(poMapFile);
    4509             : 
    4510           0 :     return m_nMapInfoType;
    4511             : }
    4512             : 
    4513             : /**********************************************************************
    4514             :  *                   TABEllipse::UpdateMBR()
    4515             :  *
    4516             :  * Update the feature MBR members using the geometry
    4517             :  *
    4518             :  * Returns 0 on success, or -1 if there is no geometry in object
    4519             :  **********************************************************************/
    4520           0 : int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4521             : {
    4522           0 :     OGREnvelope sEnvelope;
    4523             : 
    4524             :     /*-----------------------------------------------------------------
    4525             :      * Fetch and validate geometry... Polygon and point are accepted.
    4526             :      * Note that we will simply use the ellipse's MBR and don't really
    4527             :      * read the polygon geometry... this should be OK unless the
    4528             :      * polygon geometry was not really an ellipse.
    4529             :      *----------------------------------------------------------------*/
    4530           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4531           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
    4532           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4533           0 :         poGeom->getEnvelope(&sEnvelope);
    4534             :     else
    4535             :     {
    4536           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4537             :                  "TABEllipse: Missing or Invalid Geometry!");
    4538           0 :         return -1;
    4539             :     }
    4540             : 
    4541             :     /*-----------------------------------------------------------------
    4542             :      * We use the center of the MBR as the ellipse center, and the
    4543             :      * X/Y radius to define the MBR size.  If X/Y radius are null then
    4544             :      * we'll try to use the MBR to recompute them.
    4545             :      *----------------------------------------------------------------*/
    4546           0 :     const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
    4547           0 :     const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
    4548           0 :     if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
    4549             :     {
    4550           0 :         m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
    4551           0 :         m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
    4552             :     }
    4553             : 
    4554           0 :     m_dXMin = dXCenter - m_dXRadius;
    4555           0 :     m_dYMin = dYCenter - m_dYRadius;
    4556           0 :     m_dXMax = dXCenter + m_dXRadius;
    4557           0 :     m_dYMax = dYCenter + m_dYRadius;
    4558             : 
    4559           0 :     if (poMapFile)
    4560             :     {
    4561           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4562           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4563             :     }
    4564             : 
    4565           0 :     return 0;
    4566             : }
    4567             : 
    4568             : /**********************************************************************
    4569             :  *                   TABEllipse::ReadGeometryFromMAPFile()
    4570             :  *
    4571             :  * Fill the geometry and representation (color, etc...) part of the
    4572             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    4573             :  *
    4574             :  * It is assumed that poMAPFile currently points to the beginning of
    4575             :  * a map object.
    4576             :  *
    4577             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4578             :  * been called.
    4579             :  **********************************************************************/
    4580           4 : int TABEllipse::ReadGeometryFromMAPFile(
    4581             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4582             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4583             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4584             : {
    4585             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4586           4 :     if (bCoordBlockDataOnly)
    4587           0 :         return 0;
    4588             : 
    4589             :     /*-----------------------------------------------------------------
    4590             :      * Fetch and validate geometry type
    4591             :      *----------------------------------------------------------------*/
    4592           4 :     m_nMapInfoType = poObjHdr->m_nType;
    4593             : 
    4594           4 :     if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
    4595           0 :         m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
    4596             :     {
    4597           0 :         CPLError(
    4598             :             CE_Failure, CPLE_AssertionFailed,
    4599             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    4600           0 :             m_nMapInfoType, m_nMapInfoType);
    4601           0 :         return -1;
    4602             :     }
    4603             : 
    4604             :     /*-----------------------------------------------------------------
    4605             :      * Read object information
    4606             :      *----------------------------------------------------------------*/
    4607             :     TABMAPObjRectEllipse *poRectHdr =
    4608           4 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4609             : 
    4610             :     // An ellipse is defined by its MBR
    4611             : 
    4612           4 :     double dXMin = 0.0;
    4613           4 :     double dYMin = 0.0;
    4614           4 :     double dXMax = 0.0;
    4615           4 :     double dYMax = 0.0;
    4616           4 :     poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
    4617             :                             dYMin);
    4618           4 :     poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
    4619             :                             dYMax);
    4620             : 
    4621           4 :     m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
    4622           4 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    4623             : 
    4624           4 :     m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
    4625           4 :     poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
    4626             : 
    4627             :     /*-----------------------------------------------------------------
    4628             :      * Save info about the ellipse def. inside class members
    4629             :      *----------------------------------------------------------------*/
    4630           4 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    4631           4 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    4632           4 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    4633           4 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    4634             : 
    4635           4 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    4636             : 
    4637           4 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    4638             :               poObjHdr->m_nMaxY);
    4639             : 
    4640             :     /*-----------------------------------------------------------------
    4641             :      * Create and fill geometry object
    4642             :      *----------------------------------------------------------------*/
    4643           4 :     OGRPolygon *poPolygon = new OGRPolygon;
    4644           4 :     OGRLinearRing *poRing = new OGRLinearRing();
    4645             : 
    4646             :     /*-----------------------------------------------------------------
    4647             :      * For the OGR geometry, we generate an ellipse with 2 degrees line
    4648             :      * segments.
    4649             :      *----------------------------------------------------------------*/
    4650           4 :     TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
    4651             :                    0.0, 2.0 * M_PI);
    4652           4 :     TABCloseRing(poRing);
    4653             : 
    4654           4 :     poPolygon->addRingDirectly(poRing);
    4655           4 :     SetGeometryDirectly(poPolygon);
    4656             : 
    4657           4 :     return 0;
    4658             : }
    4659             : 
    4660             : /**********************************************************************
    4661             :  *                   TABEllipse::WriteGeometryToMAPFile()
    4662             :  *
    4663             :  * Write the geometry and representation (color, etc...) part of the
    4664             :  * feature to the .MAP object pointed to by poMAPFile.
    4665             :  *
    4666             :  * It is assumed that poMAPFile currently points to a valid map object.
    4667             :  *
    4668             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    4669             :  * been called.
    4670             :  **********************************************************************/
    4671           0 : int TABEllipse::WriteGeometryToMAPFile(
    4672             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    4673             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    4674             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    4675             : {
    4676             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    4677           0 :     if (bCoordBlockDataOnly)
    4678           0 :         return 0;
    4679             : 
    4680             :     /*-----------------------------------------------------------------
    4681             :      * We assume that ValidateMapInfoType() was called already and that
    4682             :      * the type in poObjHdr->m_nType is valid.
    4683             :      *----------------------------------------------------------------*/
    4684           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    4685             : 
    4686             :     /*-----------------------------------------------------------------
    4687             :      * Fetch and validate geometry... Polygon and point are accepted.
    4688             :      * Note that we will simply use the ellipse's MBR and don't really
    4689             :      * read the polygon geometry... this should be OK unless the
    4690             :      * polygon geometry was not really an ellipse.
    4691             :      *
    4692             :      * We use the center of the MBR as the ellipse center, and the
    4693             :      * X/Y radius to define the MBR size.  If X/Y radius are null then
    4694             :      * we'll try to use the MBR to recompute them.
    4695             :      *----------------------------------------------------------------*/
    4696           0 :     if (UpdateMBR(poMapFile) != 0)
    4697           0 :         return -1; /* Error already reported */
    4698             : 
    4699             :     /*-----------------------------------------------------------------
    4700             :      * Copy object information
    4701             :      *----------------------------------------------------------------*/
    4702             :     TABMAPObjRectEllipse *poRectHdr =
    4703           0 :         cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
    4704             : 
    4705             :     // Reset RoundRect Corner members... just in case (unused for ellipse)
    4706           0 :     poRectHdr->m_nCornerWidth = 0;
    4707           0 :     poRectHdr->m_nCornerHeight = 0;
    4708             : 
    4709             :     // An ellipse is defined by its MBR (values were set in UpdateMBR())
    4710           0 :     poRectHdr->m_nMinX = m_nXMin;
    4711           0 :     poRectHdr->m_nMinY = m_nYMin;
    4712           0 :     poRectHdr->m_nMaxX = m_nXMax;
    4713           0 :     poRectHdr->m_nMaxY = m_nYMax;
    4714             : 
    4715           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    4716           0 :     poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    4717             : 
    4718           0 :     m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
    4719           0 :     poRectHdr->m_nBrushId =
    4720           0 :         static_cast<GByte>(m_nBrushDefIndex);  // Brush index
    4721             : 
    4722           0 :     if (CPLGetLastErrorType() == CE_Failure)
    4723           0 :         return -1;
    4724             : 
    4725           0 :     return 0;
    4726             : }
    4727             : 
    4728             : /**********************************************************************
    4729             :  *                   TABEllipse::GetStyleString() const
    4730             :  *
    4731             :  * Return style string for this feature.
    4732             :  *
    4733             :  * Style String is built only once during the first call to GetStyleString().
    4734             :  **********************************************************************/
    4735           9 : const char *TABEllipse::GetStyleString() const
    4736             : {
    4737           9 :     if (m_pszStyleString == nullptr)
    4738             :     {
    4739             :         // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
    4740             :         // to use temporary buffers
    4741           5 :         char *pszPen = CPLStrdup(GetPenStyleString());
    4742           5 :         char *pszBrush = CPLStrdup(GetBrushStyleString());
    4743             : 
    4744           5 :         m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
    4745             : 
    4746           5 :         CPLFree(pszPen);
    4747           5 :         CPLFree(pszBrush);
    4748             :     }
    4749             : 
    4750           9 :     return m_pszStyleString;
    4751             : }
    4752             : 
    4753             : /**********************************************************************
    4754             :  *                   TABEllipse::DumpMIF()
    4755             :  *
    4756             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    4757             :  **********************************************************************/
    4758           0 : void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
    4759             : {
    4760           0 :     if (fpOut == nullptr)
    4761           0 :         fpOut = stdout;
    4762             : 
    4763             :     /*-----------------------------------------------------------------
    4764             :      * Output ELLIPSE parameters
    4765             :      *----------------------------------------------------------------*/
    4766           0 :     double dXMin = 0.0;
    4767           0 :     double dYMin = 0.0;
    4768           0 :     double dXMax = 0.0;
    4769           0 :     double dYMax = 0.0;
    4770           0 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    4771           0 :     fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
    4772             :             dYMax);
    4773             : 
    4774             :     /*-----------------------------------------------------------------
    4775             :      * Fetch and validate geometry
    4776             :      *----------------------------------------------------------------*/
    4777           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4778           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    4779             :     {
    4780             :         /*-------------------------------------------------------------
    4781             :          * Generate ellipse output as a region
    4782             :          * We could also output as an ELLIPSE in a real MIF generator
    4783             :          *------------------------------------------------------------*/
    4784           0 :         OGRPolygon *poPolygon = poGeom->toPolygon();
    4785           0 :         int numIntRings = poPolygon->getNumInteriorRings();
    4786           0 :         fprintf(fpOut, "REGION %d\n", numIntRings + 1);
    4787             :         // In this loop, iRing=-1 for the outer ring.
    4788           0 :         for (int iRing = -1; iRing < numIntRings; iRing++)
    4789             :         {
    4790           0 :             OGRLinearRing *poRing = nullptr;
    4791             : 
    4792           0 :             if (iRing == -1)
    4793           0 :                 poRing = poPolygon->getExteriorRing();
    4794             :             else
    4795           0 :                 poRing = poPolygon->getInteriorRing(iRing);
    4796             : 
    4797           0 :             if (poRing == nullptr)
    4798             :             {
    4799           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    4800             :                          "TABEllipse: Object Geometry contains NULL rings!");
    4801           0 :                 return;
    4802             :             }
    4803             : 
    4804           0 :             int numPoints = poRing->getNumPoints();
    4805           0 :             fprintf(fpOut, " %d\n", numPoints);
    4806           0 :             for (int i = 0; i < numPoints; i++)
    4807           0 :                 fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
    4808             :                         poRing->getY(i));
    4809             :         }
    4810             :     }
    4811             :     else
    4812             :     {
    4813           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4814             :                  "TABEllipse: Missing or Invalid Geometry!");
    4815           0 :         return;
    4816             :     }
    4817             : 
    4818             :     // Finish with PEN/BRUSH/etc. clauses
    4819           0 :     DumpPenDef();
    4820           0 :     DumpBrushDef();
    4821             : 
    4822           0 :     fflush(fpOut);
    4823             : }
    4824             : 
    4825             : /*=====================================================================
    4826             :  *                      class TABArc
    4827             :  *====================================================================*/
    4828             : 
    4829             : /**********************************************************************
    4830             :  *                   TABArc::TABArc()
    4831             :  *
    4832             :  * Constructor.
    4833             :  **********************************************************************/
    4834         680 : TABArc::TABArc(const OGRFeatureDefn *poDefnIn)
    4835             :     : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
    4836         680 :       m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
    4837             : {
    4838         680 : }
    4839             : 
    4840             : /**********************************************************************
    4841             :  *                   TABArc::~TABArc()
    4842             :  *
    4843             :  * Destructor.
    4844             :  **********************************************************************/
    4845        1360 : TABArc::~TABArc()
    4846             : {
    4847        1360 : }
    4848             : 
    4849             : /**********************************************************************
    4850             :  *                     TABArc::CloneTABFeature()
    4851             :  *
    4852             :  * Duplicate feature, including stuff specific to each TABFeature type.
    4853             :  *
    4854             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    4855             :  * then copies any members specific to its own type.
    4856             :  **********************************************************************/
    4857           0 : TABFeature *TABArc::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    4858             : {
    4859             :     /*-----------------------------------------------------------------
    4860             :      * Alloc new feature and copy the base stuff
    4861             :      *----------------------------------------------------------------*/
    4862           0 :     TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
    4863             : 
    4864           0 :     CopyTABFeatureBase(poNew);
    4865             : 
    4866             :     /*-----------------------------------------------------------------
    4867             :      * And members specific to this class
    4868             :      *----------------------------------------------------------------*/
    4869             :     // ITABFeaturePen
    4870           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    4871             : 
    4872           0 :     poNew->SetStartAngle(GetStartAngle());
    4873           0 :     poNew->SetEndAngle(GetEndAngle());
    4874             : 
    4875           0 :     poNew->m_dCenterX = m_dCenterX;
    4876           0 :     poNew->m_dCenterY = m_dCenterY;
    4877           0 :     poNew->m_dXRadius = m_dXRadius;
    4878           0 :     poNew->m_dYRadius = m_dYRadius;
    4879             : 
    4880           0 :     return poNew;
    4881             : }
    4882             : 
    4883             : /**********************************************************************
    4884             :  *                   TABArc::ValidateMapInfoType()
    4885             :  *
    4886             :  * Check the feature's geometry part and return the corresponding
    4887             :  * mapinfo object type code.  The m_nMapInfoType member will also
    4888             :  * be updated for further calls to GetMapInfoType();
    4889             :  *
    4890             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    4891             :  * is expected for this object class.
    4892             :  **********************************************************************/
    4893           0 : TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    4894             : {
    4895             :     /*-----------------------------------------------------------------
    4896             :      * Fetch and validate geometry
    4897             :      *----------------------------------------------------------------*/
    4898           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4899           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
    4900           0 :         (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4901             :     {
    4902           0 :         m_nMapInfoType = TAB_GEOM_ARC;
    4903             :     }
    4904             :     else
    4905             :     {
    4906           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4907             :                  "TABArc: Missing or Invalid Geometry!");
    4908           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    4909             :     }
    4910             : 
    4911             :     /*-----------------------------------------------------------------
    4912             :      * Decide if coordinates should be compressed or not.
    4913             :      *----------------------------------------------------------------*/
    4914             :     // __TODO__ For now we always write uncompressed for this class...
    4915             :     // ValidateCoordType(poMapFile);
    4916           0 :     UpdateMBR(poMapFile);
    4917             : 
    4918           0 :     return m_nMapInfoType;
    4919             : }
    4920             : 
    4921             : /**********************************************************************
    4922             :  *                   TABArc::UpdateMBR()
    4923             :  *
    4924             :  * Update the feature MBR members using the geometry
    4925             :  *
    4926             :  * Returns 0 on success, or -1 if there is no geometry in object
    4927             :  **********************************************************************/
    4928           0 : int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    4929             : {
    4930           0 :     OGREnvelope sEnvelope;
    4931             : 
    4932           0 :     OGRGeometry *poGeom = GetGeometryRef();
    4933           0 :     if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
    4934             :     {
    4935             :         /*-------------------------------------------------------------
    4936             :          * POLYGON geometry:
    4937             :          * Note that we will simply use the ellipse's MBR and don't really
    4938             :          * read the polygon geometry... this should be OK unless the
    4939             :          * polygon geometry was not really an ellipse.
    4940             :          * In the case of a polygon geometry. the m_dCenterX/Y values MUST
    4941             :          * have been set by the caller.
    4942             :          *------------------------------------------------------------*/
    4943           0 :         poGeom->getEnvelope(&sEnvelope);
    4944             :     }
    4945           0 :     else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
    4946             :     {
    4947             :         /*-------------------------------------------------------------
    4948             :          * In the case of a POINT GEOMETRY, we will make sure the
    4949             :          * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
    4950             :          *
    4951             :          * In this case we have to reconstruct the arc inside a temporary
    4952             :          * geometry object in order to find its real MBR.
    4953             :          *------------------------------------------------------------*/
    4954           0 :         OGRPoint *poPoint = poGeom->toPoint();
    4955           0 :         m_dCenterX = poPoint->getX();
    4956           0 :         m_dCenterY = poPoint->getY();
    4957             : 
    4958           0 :         OGRLineString oTmpLine;
    4959           0 :         int numPts = 0;
    4960           0 :         if (m_dEndAngle < m_dStartAngle)
    4961           0 :             numPts = static_cast<int>(
    4962           0 :                 std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
    4963             :         else
    4964           0 :             numPts = static_cast<int>(
    4965           0 :                 std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
    4966           0 :         numPts = std::max(2, numPts);
    4967             : 
    4968           0 :         TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    4969           0 :                        m_dYRadius, m_dStartAngle * M_PI / 180.0,
    4970           0 :                        m_dEndAngle * M_PI / 180.0);
    4971             : 
    4972           0 :         oTmpLine.getEnvelope(&sEnvelope);
    4973             :     }
    4974             :     else
    4975             :     {
    4976           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    4977             :                  "TABArc: Missing or Invalid Geometry!");
    4978           0 :         return -1;
    4979             :     }
    4980             : 
    4981             :     // Update the Arc's MBR
    4982           0 :     m_dXMin = sEnvelope.MinX;
    4983           0 :     m_dYMin = sEnvelope.MinY;
    4984           0 :     m_dXMax = sEnvelope.MaxX;
    4985           0 :     m_dYMax = sEnvelope.MaxY;
    4986             : 
    4987           0 :     if (poMapFile)
    4988             :     {
    4989           0 :         poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    4990           0 :         poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    4991             :     }
    4992             : 
    4993           0 :     return 0;
    4994             : }
    4995             : 
    4996             : /**********************************************************************
    4997             :  *                   TABArc::ReadGeometryFromMAPFile()
    4998             :  *
    4999             :  * Fill the geometry and representation (color, etc...) part of the
    5000             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    5001             :  *
    5002             :  * It is assumed that poMAPFile currently points to the beginning of
    5003             :  * a map object.
    5004             :  *
    5005             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5006             :  * been called.
    5007             :  **********************************************************************/
    5008           8 : int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
    5009             :                                     TABMAPObjHdr *poObjHdr,
    5010             :                                     GBool bCoordBlockDataOnly /*=FALSE*/,
    5011             :                                     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    5012             : {
    5013             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    5014           8 :     if (bCoordBlockDataOnly)
    5015           0 :         return 0;
    5016             : 
    5017             :     /*-----------------------------------------------------------------
    5018             :      * Fetch and validate geometry type
    5019             :      *----------------------------------------------------------------*/
    5020           8 :     m_nMapInfoType = poObjHdr->m_nType;
    5021             : 
    5022           8 :     if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
    5023             :     {
    5024           0 :         CPLError(
    5025             :             CE_Failure, CPLE_AssertionFailed,
    5026             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    5027           0 :             m_nMapInfoType, m_nMapInfoType);
    5028           0 :         return -1;
    5029             :     }
    5030             : 
    5031             :     /*-----------------------------------------------------------------
    5032             :      * Read object information
    5033             :      *----------------------------------------------------------------*/
    5034           8 :     TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
    5035             : 
    5036             :     /*-------------------------------------------------------------
    5037             :      * Start/End angles
    5038             :      * Since the angles are specified for integer coordinates, and
    5039             :      * that these coordinates can have the X axis reversed, we have to
    5040             :      * adjust the angle values for the change in the X axis
    5041             :      * direction.
    5042             :      *
    5043             :      * This should be necessary only when X axis is flipped.
    5044             :      * __TODO__ Why is order of start/end values reversed as well???
    5045             :      *------------------------------------------------------------*/
    5046             : 
    5047             :     /*-------------------------------------------------------------
    5048             :      * OK, Arc angles again!!!!!!!!!!!!
    5049             :      * After some tests in 1999-11, it appeared that the angle values
    5050             :      * ALWAYS had to be flipped (read order= end angle followed by
    5051             :      * start angle), no matter which quadrant the file is in.
    5052             :      * This does not make any sense, so I suspect that there is something
    5053             :      * that we are missing here!
    5054             :      *
    5055             :      * 2000-01-14.... Again!!!  Based on some sample data files:
    5056             :      *  File         Ver Quadr  ReflXAxis  Read_Order   Adjust_Angle
    5057             :      * test_symb.tab 300    2        1      end,start    X=yes Y=no
    5058             :      * alltypes.tab: 300    1        0      start,end    X=no  Y=no
    5059             :      * arcs.tab:     300    2        0      end,start    X=yes Y=no
    5060             :      *
    5061             :      * Until we prove it wrong, the rule would be:
    5062             :      *  -> Quadrant 1 and 3, angles order = start, end
    5063             :      *  -> Quadrant 2 and 4, angles order = end, start
    5064             :      * + Always adjust angles for x and y axis based on quadrant.
    5065             :      *
    5066             :      * This was confirmed using some more files in which the quadrant was
    5067             :      * manually changed, but whether these are valid results is
    5068             :      * disputable.
    5069             :      *
    5070             :      * The ReflectXAxis flag seems to have no effect here...
    5071             :      *------------------------------------------------------------*/
    5072             : 
    5073             :     /*-------------------------------------------------------------
    5074             :      * In version 100 .tab files (version 400 .map), it is possible
    5075             :      * to have a quadrant value of 0 and it should be treated the
    5076             :      * same way as quadrant 3
    5077             :      *------------------------------------------------------------*/
    5078           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
    5079           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5080           0 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5081             :     {
    5082             :         // Quadrants 1 and 3 ... read order = start, end
    5083           8 :         m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
    5084           8 :         m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
    5085             :     }
    5086             :     else
    5087             :     {
    5088             :         // Quadrants 2 and 4 ... read order = end, start
    5089           0 :         m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
    5090           0 :         m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
    5091             :     }
    5092             : 
    5093           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
    5094          16 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5095           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5096             :     {
    5097             :         // X axis direction is flipped... adjust angle
    5098           0 :         m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
    5099           0 :                                                  : (540.0 - m_dStartAngle);
    5100           0 :         m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
    5101           0 :                                              : (540.0 - m_dEndAngle);
    5102             :     }
    5103             : 
    5104           8 :     if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
    5105             :     {
    5106           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5107             :                  "Wrong start and end angles: %f %f", m_dStartAngle,
    5108             :                  m_dEndAngle);
    5109           0 :         return -1;
    5110             :     }
    5111             : 
    5112           8 :     if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
    5113          16 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
    5114           8 :         poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
    5115             :     {
    5116             :         // Y axis direction is flipped... this reverses angle direction
    5117             :         // Unfortunately we never found any file that contains this case,
    5118             :         // but this should be the behavior to expect!!!
    5119             :         //
    5120             :         // 2000-01-14: some files in which quadrant was set to 3 and 4
    5121             :         // manually seemed to confirm that this is the right thing to do.
    5122           0 :         m_dStartAngle = 360.0 - m_dStartAngle;
    5123           0 :         m_dEndAngle = 360.0 - m_dEndAngle;
    5124             :     }
    5125             : 
    5126             :     // An arc is defined by its defining ellipse's MBR:
    5127             : 
    5128           8 :     double dXMin = 0.0;
    5129           8 :     double dYMin = 0.0;
    5130           8 :     double dXMax = 0.0;
    5131           8 :     double dYMax = 0.0;
    5132             : 
    5133           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
    5134             :                             poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
    5135           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
    5136             :                             poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
    5137             : 
    5138           8 :     m_dCenterX = (dXMin + dXMax) / 2.0;
    5139           8 :     m_dCenterY = (dYMin + dYMax) / 2.0;
    5140           8 :     m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
    5141           8 :     m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
    5142             : 
    5143             :     // Read the Arc's MBR and use that as this feature's MBR
    5144           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
    5145           8 :     poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
    5146           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    5147             : 
    5148           8 :     m_nPenDefIndex = poArcHdr->m_nPenId;  // Pen index
    5149           8 :     poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    5150             : 
    5151             :     /*-----------------------------------------------------------------
    5152             :      * Create and fill geometry object
    5153             :      * For the OGR geometry, we generate an arc with 2 degrees line
    5154             :      * segments.
    5155             :      *----------------------------------------------------------------*/
    5156           8 :     OGRLineString *poLine = new OGRLineString;
    5157             : 
    5158             :     const int numPts = std::max(
    5159          16 :         2,
    5160           8 :         (m_dEndAngle < m_dStartAngle
    5161           8 :              ? static_cast<int>(
    5162           0 :                    std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
    5163           8 :              : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
    5164           8 :                                 1)));
    5165             : 
    5166           8 :     TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
    5167           8 :                    m_dYRadius, m_dStartAngle * M_PI / 180.0,
    5168           8 :                    m_dEndAngle * M_PI / 180.0);
    5169             : 
    5170           8 :     SetGeometryDirectly(poLine);
    5171             : 
    5172           8 :     return 0;
    5173             : }
    5174             : 
    5175             : /**********************************************************************
    5176             :  *                   TABArc::WriteGeometryToMAPFile()
    5177             :  *
    5178             :  * Write the geometry and representation (color, etc...) part of the
    5179             :  * feature to the .MAP object pointed to by poMAPFile.
    5180             :  *
    5181             :  * It is assumed that poMAPFile currently points to a valid map object.
    5182             :  *
    5183             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5184             :  * been called.
    5185             :  **********************************************************************/
    5186           0 : int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    5187             :                                    TABMAPObjHdr *poObjHdr,
    5188             :                                    GBool bCoordBlockDataOnly /*=FALSE*/,
    5189             :                                    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    5190             : {
    5191             :     /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
    5192           0 :     if (bCoordBlockDataOnly)
    5193           0 :         return 0;
    5194             : 
    5195             :     /*-----------------------------------------------------------------
    5196             :      * We assume that ValidateMapInfoType() was called already and that
    5197             :      * the type in poObjHdr->m_nType is valid.
    5198             :      *----------------------------------------------------------------*/
    5199           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    5200             : 
    5201             :     /*-----------------------------------------------------------------
    5202             :      * Fetch and validate geometry
    5203             :      * In the case of ARCs, this is all done inside UpdateMBR()
    5204             :      *----------------------------------------------------------------*/
    5205           0 :     if (UpdateMBR(poMapFile) != 0)
    5206           0 :         return -1; /* Error already reported */
    5207             : 
    5208             :     /*-----------------------------------------------------------------
    5209             :      * Copy object information
    5210             :      *----------------------------------------------------------------*/
    5211           0 :     TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
    5212             : 
    5213             :     /*-------------------------------------------------------------
    5214             :      * Start/End angles
    5215             :      * Since we ALWAYS produce files in quadrant 1 then we can
    5216             :      * ignore the special angle conversion required by flipped axis.
    5217             :      *
    5218             :      * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
    5219             :      *------------------------------------------------------------*/
    5220           0 :     CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
    5221             : 
    5222           0 :     poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
    5223           0 :     poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
    5224             : 
    5225             :     // An arc is defined by its defining ellipse's MBR:
    5226           0 :     poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
    5227           0 :                             poArcHdr->m_nArcEllipseMinX,
    5228           0 :                             poArcHdr->m_nArcEllipseMinY);
    5229           0 :     poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
    5230           0 :                             poArcHdr->m_nArcEllipseMaxX,
    5231           0 :                             poArcHdr->m_nArcEllipseMaxY);
    5232             : 
    5233             :     // Pass the Arc's actual MBR (values were set in UpdateMBR())
    5234           0 :     poArcHdr->m_nMinX = m_nXMin;
    5235           0 :     poArcHdr->m_nMinY = m_nYMin;
    5236           0 :     poArcHdr->m_nMaxX = m_nXMax;
    5237           0 :     poArcHdr->m_nMaxY = m_nYMax;
    5238             : 
    5239           0 :     m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    5240           0 :     poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
    5241             : 
    5242           0 :     if (CPLGetLastErrorType() == CE_Failure)
    5243           0 :         return -1;
    5244             : 
    5245           0 :     return 0;
    5246             : }
    5247             : 
    5248             : /**********************************************************************
    5249             :  *                   TABArc::SetStart/EndAngle()
    5250             :  *
    5251             :  * Set the start/end angle values in degrees, making sure the values are
    5252             :  * always in the range [0..360]
    5253             :  **********************************************************************/
    5254           0 : void TABArc::SetStartAngle(double dAngle)
    5255             : {
    5256           0 :     dAngle = fmod(dAngle, 360.0);
    5257           0 :     if (dAngle < 0.0)
    5258           0 :         dAngle += 360.0;
    5259             : 
    5260           0 :     m_dStartAngle = dAngle;
    5261           0 : }
    5262             : 
    5263           0 : void TABArc::SetEndAngle(double dAngle)
    5264             : {
    5265           0 :     dAngle = fmod(dAngle, 360.0);
    5266           0 :     if (dAngle < 0.0)
    5267           0 :         dAngle += 360.0;
    5268             : 
    5269           0 :     m_dEndAngle = dAngle;
    5270           0 : }
    5271             : 
    5272             : /**********************************************************************
    5273             :  *                   TABArc::GetStyleString() const
    5274             :  *
    5275             :  * Return style string for this feature.
    5276             :  *
    5277             :  * Style String is built only once during the first call to GetStyleString().
    5278             :  **********************************************************************/
    5279          14 : const char *TABArc::GetStyleString() const
    5280             : {
    5281          14 :     if (m_pszStyleString == nullptr)
    5282             :     {
    5283          10 :         m_pszStyleString = CPLStrdup(GetPenStyleString());
    5284             :     }
    5285             : 
    5286          14 :     return m_pszStyleString;
    5287             : }
    5288             : 
    5289             : /**********************************************************************
    5290             :  *                   TABArc::DumpMIF()
    5291             :  *
    5292             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    5293             :  **********************************************************************/
    5294           0 : void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
    5295             : {
    5296           0 :     if (fpOut == nullptr)
    5297           0 :         fpOut = stdout;
    5298             : 
    5299             :     /*-----------------------------------------------------------------
    5300             :      * Output ARC parameters
    5301             :      *----------------------------------------------------------------*/
    5302           0 :     fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g   %d %d)\n",
    5303           0 :             m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
    5304           0 :             m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
    5305           0 :             static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
    5306             : 
    5307             :     /*-----------------------------------------------------------------
    5308             :      * Fetch and validate geometry
    5309             :      *----------------------------------------------------------------*/
    5310           0 :     OGRGeometry *poGeom = GetGeometryRef();
    5311           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    5312             :     {
    5313             :         /*-------------------------------------------------------------
    5314             :          * Generate arc output as a simple polyline
    5315             :          * We could also output as an ELLIPSE in a real MIF generator
    5316             :          *------------------------------------------------------------*/
    5317           0 :         OGRLineString *poLine = poGeom->toLineString();
    5318           0 :         const int numPoints = poLine->getNumPoints();
    5319           0 :         fprintf(fpOut, "PLINE %d\n", numPoints);
    5320           0 :         for (int i = 0; i < numPoints; i++)
    5321           0 :             fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
    5322             :     }
    5323             :     else
    5324             :     {
    5325           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5326             :                  "TABArc: Missing or Invalid Geometry!");
    5327           0 :         return;
    5328             :     }
    5329             : 
    5330             :     // Finish with PEN/BRUSH/etc. clauses
    5331           0 :     DumpPenDef();
    5332             : 
    5333           0 :     fflush(fpOut);
    5334             : }
    5335             : 
    5336             : /*=====================================================================
    5337             :  *                      class TABText
    5338             :  *====================================================================*/
    5339             : 
    5340             : /**********************************************************************
    5341             :  *                   TABText::TABText()
    5342             :  *
    5343             :  * Constructor.
    5344             :  **********************************************************************/
    5345         309 : TABText::TABText(const OGRFeatureDefn *poDefnIn)
    5346             :     : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
    5347             :       m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
    5348             :       m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
    5349             :       m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
    5350         309 :       m_nFontStyle(0)
    5351             : {
    5352         309 : }
    5353             : 
    5354             : /**********************************************************************
    5355             :  *                   TABText::~TABText()
    5356             :  *
    5357             :  * Destructor.
    5358             :  **********************************************************************/
    5359         618 : TABText::~TABText()
    5360             : {
    5361         309 :     CPLFree(m_pszString);
    5362         618 : }
    5363             : 
    5364             : /**********************************************************************
    5365             :  *                     TABText::CloneTABFeature()
    5366             :  *
    5367             :  * Duplicate feature, including stuff specific to each TABFeature type.
    5368             :  *
    5369             :  * This method calls the generic TABFeature::CopyTABFeatureBase() and
    5370             :  * then copies any members specific to its own type.
    5371             :  **********************************************************************/
    5372           0 : TABFeature *TABText::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    5373             : {
    5374             :     /*-----------------------------------------------------------------
    5375             :      * Alloc new feature and copy the base stuff
    5376             :      *----------------------------------------------------------------*/
    5377           0 :     TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
    5378             : 
    5379           0 :     CopyTABFeatureBase(poNew);
    5380             : 
    5381             :     /*-----------------------------------------------------------------
    5382             :      * And members specific to this class
    5383             :      *----------------------------------------------------------------*/
    5384             :     // ITABFeaturePen
    5385           0 :     *(poNew->GetPenDefRef()) = *GetPenDefRef();
    5386             : 
    5387             :     // ITABFeatureFont
    5388           0 :     *(poNew->GetFontDefRef()) = *GetFontDefRef();
    5389             : 
    5390           0 :     poNew->SetTextString(GetTextString());
    5391           0 :     poNew->SetTextAngle(GetTextAngle());
    5392           0 :     poNew->SetTextBoxHeight(GetTextBoxHeight());
    5393           0 :     poNew->SetTextBoxWidth(GetTextBoxWidth());
    5394           0 :     poNew->SetFontStyleTABValue(GetFontStyleTABValue());
    5395           0 :     poNew->SetFontBGColor(GetFontBGColor());
    5396           0 :     poNew->SetFontFGColor(GetFontFGColor());
    5397           0 :     poNew->SetFontOColor(GetFontOColor());
    5398           0 :     poNew->SetFontSColor(GetFontSColor());
    5399             : 
    5400           0 :     poNew->SetTextJustification(GetTextJustification());
    5401           0 :     poNew->SetTextSpacing(GetTextSpacing());
    5402             :     // Note: Text arrow/line coordinates are not transported... but
    5403             :     //       we ignore them most of the time anyways.
    5404           0 :     poNew->SetTextLineType(TABTLNoLine);
    5405             : 
    5406           0 :     return poNew;
    5407             : }
    5408             : 
    5409             : /**********************************************************************
    5410             :  *                   TABText::ValidateMapInfoType()
    5411             :  *
    5412             :  * Check the feature's geometry part and return the corresponding
    5413             :  * mapinfo object type code.  The m_nMapInfoType member will also
    5414             :  * be updated for further calls to GetMapInfoType();
    5415             :  *
    5416             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    5417             :  * is expected for this object class.
    5418             :  **********************************************************************/
    5419           4 : TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    5420             : {
    5421             :     /*-----------------------------------------------------------------
    5422             :      * Fetch and validate geometry
    5423             :      *----------------------------------------------------------------*/
    5424           4 :     OGRGeometry *poGeom = GetGeometryRef();
    5425           4 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5426             :     {
    5427           4 :         m_nMapInfoType = TAB_GEOM_TEXT;
    5428             :     }
    5429             :     else
    5430             :     {
    5431           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5432             :                  "TABText: Missing or Invalid Geometry!");
    5433           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    5434             :     }
    5435             : 
    5436             :     /*-----------------------------------------------------------------
    5437             :      * Decide if coordinates should be compressed or not.
    5438             :      *----------------------------------------------------------------*/
    5439             :     // __TODO__ For now we always write uncompressed for this class...
    5440             :     // ValidateCoordType(poMapFile);
    5441           4 :     UpdateMBR(poMapFile);
    5442             : 
    5443           4 :     return m_nMapInfoType;
    5444             : }
    5445             : 
    5446             : /**********************************************************************
    5447             :  *                   TABText::ReadGeometryFromMAPFile()
    5448             :  *
    5449             :  * Fill the geometry and representation (color, etc...) part of the
    5450             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    5451             :  *
    5452             :  * It is assumed that poMAPFile currently points to the beginning of
    5453             :  * a map object.
    5454             :  *
    5455             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5456             :  * been called.
    5457             :  **********************************************************************/
    5458           8 : int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
    5459             :                                      TABMAPObjHdr *poObjHdr,
    5460             :                                      GBool bCoordBlockDataOnly /*=FALSE*/,
    5461             :                                      TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    5462             : {
    5463             :     /*-----------------------------------------------------------------
    5464             :      * Fetch and validate geometry type
    5465             :      *----------------------------------------------------------------*/
    5466           8 :     m_nMapInfoType = poObjHdr->m_nType;
    5467             : 
    5468           8 :     if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
    5469             :     {
    5470           0 :         CPLError(
    5471             :             CE_Failure, CPLE_AssertionFailed,
    5472             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    5473           0 :             m_nMapInfoType, m_nMapInfoType);
    5474           0 :         return -1;
    5475             :     }
    5476             : 
    5477             :     /*=============================================================
    5478             :      * TEXT
    5479             :      *============================================================*/
    5480             : 
    5481             :     /*-----------------------------------------------------------------
    5482             :      * Read object information
    5483             :      *----------------------------------------------------------------*/
    5484           8 :     TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
    5485             : 
    5486           8 :     const GInt32 nCoordBlockPtr =
    5487             :         poTextHdr->m_nCoordBlockPtr;                     // String position
    5488           8 :     const int nStringLen = poTextHdr->m_nCoordDataSize;  // String length
    5489           8 :     m_nTextAlignment = poTextHdr->m_nTextAlignment;      // just./spacing/arrow
    5490             : 
    5491             :     /*-------------------------------------------------------------
    5492             :      * Text Angle, in tenths of degree.
    5493             :      * Contrary to arc start/end angles, no conversion based on
    5494             :      * origin quadrant is required here.
    5495             :      *------------------------------------------------------------*/
    5496           8 :     m_dAngle = poTextHdr->m_nAngle / 10.0;
    5497             : 
    5498           8 :     m_nFontStyle = poTextHdr->m_nFontStyle;  // Font style
    5499             : 
    5500           8 :     m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
    5501           8 :                        poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
    5502           8 :     m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
    5503           8 :                        poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
    5504           8 :     m_rgbOutline = m_rgbBackground;
    5505             :     // In MapInfo, the shadow color is always gray (128,128,128)
    5506           8 :     m_rgbShadow = 0x808080;
    5507             : 
    5508             :     // arrow endpoint
    5509           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
    5510           8 :                             m_dfLineEndX, m_dfLineEndY);
    5511           8 :     m_bLineEndSet = TRUE;
    5512             : 
    5513             :     // Text Height
    5514           8 :     double dJunk = 0.0;
    5515           8 :     poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
    5516             : 
    5517           8 :     if (!bCoordBlockDataOnly)
    5518             :     {
    5519           8 :         m_nFontDefIndex = poTextHdr->m_nFontId;  // Font name index
    5520           8 :         poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
    5521             :     }
    5522             : 
    5523             :     // MBR after rotation
    5524           8 :     double dXMin = 0.0;
    5525           8 :     double dYMin = 0.0;
    5526           8 :     double dXMax = 0.0;
    5527           8 :     double dYMax = 0.0;
    5528           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
    5529             :                             dYMin);
    5530           8 :     poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
    5531             :                             dYMax);
    5532             : 
    5533           8 :     if (!bCoordBlockDataOnly)
    5534             :     {
    5535           8 :         m_nPenDefIndex = poTextHdr->m_nPenId;  // Pen index for line
    5536           8 :         poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
    5537             :     }
    5538             : 
    5539             :     /*-------------------------------------------------------------
    5540             :      * Read text string from the coord. block
    5541             :      * Note that the string may contain binary '\n' and '\\' chars
    5542             :      * that we keep to an unescaped form internally. This is to
    5543             :      * be like OGR drivers. See bug 1107 for details.
    5544             :      *------------------------------------------------------------*/
    5545             :     char *pszTmpString =
    5546           8 :         static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
    5547             : 
    5548           8 :     if (nStringLen > 0)
    5549             :     {
    5550           8 :         TABMAPCoordBlock *poCoordBlock = nullptr;
    5551             : 
    5552           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    5553           0 :             poCoordBlock = *ppoCoordBlock;
    5554             :         else
    5555           8 :             poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
    5556          16 :         if (poCoordBlock == nullptr ||
    5557           8 :             poCoordBlock->ReadBytes(
    5558             :                 nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
    5559             :         {
    5560           0 :             CPLError(CE_Failure, CPLE_FileIO,
    5561             :                      "Failed reading text string at offset %d", nCoordBlockPtr);
    5562           0 :             CPLFree(pszTmpString);
    5563           0 :             return -1;
    5564             :         }
    5565             : 
    5566             :         /* Return a ref to coord block so that caller can continue reading
    5567             :          * after the end of this object (used by index splitting)
    5568             :          */
    5569           8 :         if (ppoCoordBlock)
    5570           0 :             *ppoCoordBlock = poCoordBlock;
    5571             :     }
    5572             : 
    5573           8 :     pszTmpString[nStringLen] = '\0';
    5574             : 
    5575           8 :     if (!poMapFile->GetEncoding().empty())
    5576             :     {
    5577             :         char *pszUtf8String =
    5578           1 :             CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
    5579           1 :         CPLFree(pszTmpString);
    5580           1 :         pszTmpString = pszUtf8String;
    5581             :     }
    5582             : 
    5583           8 :     CPLFree(m_pszString);
    5584           8 :     m_pszString = pszTmpString;  // This string was Escaped before 20050714
    5585             : 
    5586             :     /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
    5587             :      */
    5588           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    5589           8 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    5590             : 
    5591             :     /* Copy int MBR to feature class members */
    5592           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    5593             :               poObjHdr->m_nMaxY);
    5594             : 
    5595             :     /*-----------------------------------------------------------------
    5596             :      * Create an OGRPoint Geometry...
    5597             :      * The point X,Y values will be the coords of the lower-left corner before
    5598             :      * rotation is applied.  (Note that the rotation in MapInfo is done around
    5599             :      * the upper-left corner)
    5600             :      * We need to calculate the true lower left corner of the text based
    5601             :      * on the MBR after rotation, the text height and the rotation angle.
    5602             :      *----------------------------------------------------------------*/
    5603           8 :     double dSin = sin(m_dAngle * M_PI / 180.0);
    5604           8 :     double dCos = cos(m_dAngle * M_PI / 180.0);
    5605           8 :     double dX = 0.0;
    5606           8 :     double dY = 0.0;
    5607           8 :     if (dSin > 0.0 && dCos > 0.0)
    5608             :     {
    5609           7 :         dX = dXMin + m_dHeight * dSin;
    5610           7 :         dY = dYMin;
    5611             :     }
    5612           1 :     else if (dSin > 0.0 && dCos < 0.0)
    5613             :     {
    5614           0 :         dX = dXMax;
    5615           0 :         dY = dYMin - m_dHeight * dCos;
    5616             :     }
    5617           1 :     else if (dSin < 0.0 && dCos < 0.0)
    5618             :     {
    5619           0 :         dX = dXMax + m_dHeight * dSin;
    5620           0 :         dY = dYMax;
    5621             :     }
    5622             :     else  // dSin < 0 && dCos > 0
    5623             :     {
    5624           1 :         dX = dXMin;
    5625           1 :         dY = dYMax - m_dHeight * dCos;
    5626             :     }
    5627             : 
    5628           8 :     OGRGeometry *poGeometry = new OGRPoint(dX, dY);
    5629             : 
    5630           8 :     SetGeometryDirectly(poGeometry);
    5631             : 
    5632             :     /*-----------------------------------------------------------------
    5633             :      * Compute Text Width: the width of the Text MBR before rotation
    5634             :      * in ground units... unfortunately this value is not stored in the
    5635             :      * file, so we have to compute it with the MBR after rotation and
    5636             :      * the height of the MBR before rotation:
    5637             :      * With  W = Width of MBR before rotation
    5638             :      *       H = Height of MBR before rotation
    5639             :      *       dX = Width of MBR after rotation
    5640             :      *       dY = Height of MBR after rotation
    5641             :      *       teta = rotation angle
    5642             :      *
    5643             :      *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
    5644             :      *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
    5645             :      *
    5646             :      * and for other teta values, use:
    5647             :      *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
    5648             :      *----------------------------------------------------------------*/
    5649           8 :     dSin = std::abs(dSin);
    5650           8 :     dCos = std::abs(dCos);
    5651           8 :     if (m_dHeight == 0.0)
    5652           0 :         m_dWidth = 0.0;
    5653           8 :     else if (dCos > dSin)
    5654           8 :         m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
    5655           8 :                    (m_dHeight * dCos);
    5656             :     else
    5657           0 :         m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
    5658           0 :                    (m_dHeight * dSin);
    5659           8 :     m_dWidth = std::abs(m_dWidth);
    5660             : 
    5661           8 :     return 0;
    5662             : }
    5663             : 
    5664             : /**********************************************************************
    5665             :  *                   TABText::WriteGeometryToMAPFile()
    5666             :  *
    5667             :  * Write the geometry and representation (color, etc...) part of the
    5668             :  * feature to the .MAP object pointed to by poMAPFile.
    5669             :  *
    5670             :  * It is assumed that poMAPFile currently points to a valid map object.
    5671             :  *
    5672             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    5673             :  * been called.
    5674             :  **********************************************************************/
    5675           4 : int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
    5676             :                                     TABMAPObjHdr *poObjHdr,
    5677             :                                     GBool bCoordBlockDataOnly /*=FALSE*/,
    5678             :                                     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    5679             : {
    5680             :     GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
    5681             : 
    5682             :     /*-----------------------------------------------------------------
    5683             :      * We assume that ValidateMapInfoType() was called already and that
    5684             :      * the type in poObjHdr->m_nType is valid.
    5685             :      *----------------------------------------------------------------*/
    5686           4 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    5687             : 
    5688             :     /*-----------------------------------------------------------------
    5689             :      * Fetch and validate geometry
    5690             :      *----------------------------------------------------------------*/
    5691           4 :     OGRGeometry *poGeom = GetGeometryRef();
    5692           4 :     OGRPoint *poPoint = nullptr;
    5693           4 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5694           4 :         poPoint = poGeom->toPoint();
    5695             :     else
    5696             :     {
    5697           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    5698             :                  "TABText: Missing or Invalid Geometry!");
    5699           0 :         return -1;
    5700             :     }
    5701             : 
    5702           4 :     poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    5703             : 
    5704             :     /*-----------------------------------------------------------------
    5705             :      * Write string to a coord block first...
    5706             :      * Note that the string may contain unescaped '\n' and '\\'
    5707             :      * that we have to keep like that for the MAP file.
    5708             :      * See MapTools bug 1107 for more details.
    5709             :      *----------------------------------------------------------------*/
    5710           4 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    5711           4 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    5712           0 :         poCoordBlock = *ppoCoordBlock;
    5713             :     else
    5714           4 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    5715           4 :     poCoordBlock->StartNewFeature();
    5716           4 :     GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
    5717             : 
    5718             :     // This string was escaped before 20050714
    5719           8 :     CPLString oTmpString(m_pszString ? m_pszString : "");
    5720           4 :     if (!poMapFile->GetEncoding().empty())
    5721             :     {
    5722           0 :         oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
    5723             :     }
    5724             : 
    5725           4 :     int nStringLen = static_cast<int>(oTmpString.length());
    5726             : 
    5727           4 :     if (nStringLen > 0)
    5728             :     {
    5729           3 :         poCoordBlock->WriteBytes(
    5730           3 :             nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
    5731             :     }
    5732             :     else
    5733             :     {
    5734           1 :         nCoordBlockPtr = 0;
    5735             :     }
    5736             : 
    5737             :     /*-----------------------------------------------------------------
    5738             :      * Copy object information
    5739             :      *----------------------------------------------------------------*/
    5740           4 :     TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
    5741             : 
    5742           4 :     poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr;    // String position
    5743           4 :     poTextHdr->m_nCoordDataSize = nStringLen;        // String length
    5744           4 :     poTextHdr->m_nTextAlignment = m_nTextAlignment;  // just./spacing/arrow
    5745             : 
    5746             :     /*-----------------------------------------------------------------
    5747             :      * Text Angle, (written in tenths of degrees)
    5748             :      * Contrary to arc start/end angles, no conversion based on
    5749             :      * origin quadrant is required here.
    5750             :      *----------------------------------------------------------------*/
    5751           4 :     poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
    5752             : 
    5753           4 :     poTextHdr->m_nFontStyle = m_nFontStyle;  // Font style/effect
    5754             : 
    5755           4 :     poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
    5756           4 :     poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
    5757           4 :     poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
    5758             : 
    5759           4 :     poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
    5760           4 :     poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
    5761           4 :     poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
    5762             : 
    5763             :     /*-----------------------------------------------------------------
    5764             :      * The OGRPoint's X,Y values were the coords of the lower-left corner
    5765             :      * before rotation was applied.  (Note that the rotation in MapInfo is
    5766             :      * done around the upper-left corner)
    5767             :      * The Feature's MBR is the MBR of the text after rotation... that's
    5768             :      * what MapInfo uses to define the text location.
    5769             :      *----------------------------------------------------------------*/
    5770           4 :     double dXMin = 0.0;
    5771           4 :     double dYMin = 0.0;
    5772           4 :     double dXMax = 0.0;
    5773           4 :     double dYMax = 0.0;
    5774             :     // Make sure Feature MBR is in sync with other params
    5775             : 
    5776           4 :     UpdateMBR();
    5777           4 :     GetMBR(dXMin, dYMin, dXMax, dYMax);
    5778             : 
    5779           4 :     poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
    5780           4 :     poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
    5781             : 
    5782             :     // Label line end point
    5783           4 :     double dX = 0.0;
    5784           4 :     double dY = 0.0;
    5785           4 :     GetTextLineEndPoint(dX, dY);  // Make sure a default line end point is set
    5786           4 :     poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
    5787           4 :                             poTextHdr->m_nLineEndY);
    5788             : 
    5789             :     // Text Height
    5790           4 :     poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
    5791           4 :     poTextHdr->m_nHeight = nY;
    5792             : 
    5793           4 :     if (!bCoordBlockDataOnly)
    5794             :     {
    5795             :         // Font name
    5796           4 :         m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
    5797           4 :         poTextHdr->m_nFontId =
    5798           4 :             static_cast<GByte>(m_nFontDefIndex);  // Font name index
    5799             :     }
    5800             : 
    5801             :     // MBR after rotation
    5802           4 :     poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
    5803             : 
    5804           4 :     if (!bCoordBlockDataOnly)
    5805             :     {
    5806           4 :         m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
    5807           4 :         poTextHdr->m_nPenId =
    5808           4 :             static_cast<GByte>(m_nPenDefIndex);  // Pen index for line/arrow
    5809             :     }
    5810             : 
    5811           4 :     if (CPLGetLastErrorType() == CE_Failure)
    5812           0 :         return -1;
    5813             : 
    5814             :     /* Return a ref to coord block so that caller can continue writing
    5815             :      * after the end of this object (used by index splitting)
    5816             :      */
    5817           4 :     if (ppoCoordBlock)
    5818           0 :         *ppoCoordBlock = poCoordBlock;
    5819             : 
    5820           4 :     return 0;
    5821             : }
    5822             : 
    5823             : /**********************************************************************
    5824             :  *                   TABText::GetTextString()
    5825             :  *
    5826             :  * Return ref to text string value.
    5827             :  *
    5828             :  * Returned string is a reference to the internal string buffer and should
    5829             :  * not be modified or freed by the caller.
    5830             :  **********************************************************************/
    5831         300 : const char *TABText::GetTextString() const
    5832             : {
    5833         300 :     if (m_pszString == nullptr)
    5834           0 :         return "";
    5835             : 
    5836         300 :     return m_pszString;
    5837             : }
    5838             : 
    5839             : /**********************************************************************
    5840             :  *                   TABText::SetTextString()
    5841             :  *
    5842             :  * Set new text string value.
    5843             :  *
    5844             :  * Note: The text string may contain "\n" chars or "\\" chars
    5845             :  * and we expect to receive them in a 2 chars escaped form as
    5846             :  * described in the MIF format specs.
    5847             :  **********************************************************************/
    5848           3 : void TABText::SetTextString(const char *pszNewStr)
    5849             : {
    5850           3 :     CPLFree(m_pszString);
    5851           3 :     m_pszString = CPLStrdup(pszNewStr);
    5852           3 : }
    5853             : 
    5854             : /**********************************************************************
    5855             :  *                   TABText::GetTextAngle()
    5856             :  *
    5857             :  * Return text angle in degrees.
    5858             :  **********************************************************************/
    5859          10 : double TABText::GetTextAngle() const
    5860             : {
    5861          10 :     return m_dAngle;
    5862             : }
    5863             : 
    5864         211 : void TABText::SetTextAngle(double dAngle)
    5865             : {
    5866             :     // Make sure angle is in the range [0..360]
    5867         211 :     dAngle = fmod(dAngle, 360.0);
    5868         211 :     if (dAngle < 0.0)
    5869           0 :         dAngle += 360.0;
    5870         211 :     m_dAngle = dAngle;
    5871         211 :     UpdateMBR();
    5872         211 : }
    5873             : 
    5874             : /**********************************************************************
    5875             :  *                   TABText::GetTextBoxHeight()
    5876             :  *
    5877             :  * Return text height in Y axis coord. units of the text box before rotation.
    5878             :  **********************************************************************/
    5879          10 : double TABText::GetTextBoxHeight() const
    5880             : {
    5881          10 :     return m_dHeight;
    5882             : }
    5883             : 
    5884           3 : void TABText::SetTextBoxHeight(double dHeight)
    5885             : {
    5886           3 :     m_dHeight = dHeight;
    5887           3 :     UpdateMBR();
    5888           3 : }
    5889             : 
    5890             : /**********************************************************************
    5891             :  *                   TABText::GetTextBoxWidth()
    5892             :  *
    5893             :  * Return text width in X axis coord. units. of the text box before rotation.
    5894             :  *
    5895             :  * If value has not been set, then we force a default value that assumes
    5896             :  * that one char's box width is 60% of its height... and we ignore
    5897             :  * the multiline case.  This should not matter when the user PROPERLY sets
    5898             :  * the value.
    5899             :  **********************************************************************/
    5900          12 : double TABText::GetTextBoxWidth() const
    5901             : {
    5902          12 :     if (m_dWidth == 0.0 && m_pszString)
    5903             :     {
    5904           3 :         m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
    5905             :     }
    5906          12 :     return m_dWidth;
    5907             : }
    5908             : 
    5909           0 : void TABText::SetTextBoxWidth(double dWidth)
    5910             : {
    5911           0 :     m_dWidth = dWidth;
    5912           0 :     UpdateMBR();
    5913           0 : }
    5914             : 
    5915             : /**********************************************************************
    5916             :  *                   TABText::GetTextLineEndPoint()
    5917             :  *
    5918             :  * Return X,Y coordinates of the text label line end point.
    5919             :  * Default is the center of the text MBR.
    5920             :  **********************************************************************/
    5921           4 : void TABText::GetTextLineEndPoint(double &dX, double &dY)
    5922             : {
    5923           4 :     if (!m_bLineEndSet)
    5924             :     {
    5925             :         // Set default location at center of text MBR
    5926           4 :         double dXMin = 0.0;
    5927           4 :         double dYMin = 0.0;
    5928           4 :         double dXMax = 0.0;
    5929           4 :         double dYMax = 0.0;
    5930           4 :         UpdateMBR();
    5931           4 :         GetMBR(dXMin, dYMin, dXMax, dYMax);
    5932           4 :         m_dfLineEndX = (dXMin + dXMax) / 2.0;
    5933           4 :         m_dfLineEndY = (dYMin + dYMax) / 2.0;
    5934           4 :         m_bLineEndSet = TRUE;
    5935             :     }
    5936             : 
    5937             :     // Return values
    5938           4 :     dX = m_dfLineEndX;
    5939           4 :     dY = m_dfLineEndY;
    5940           4 : }
    5941             : 
    5942         178 : void TABText::SetTextLineEndPoint(double dX, double dY)
    5943             : {
    5944         178 :     m_dfLineEndX = dX;
    5945         178 :     m_dfLineEndY = dY;
    5946         178 :     m_bLineEndSet = TRUE;
    5947         178 : }
    5948             : 
    5949             : /**********************************************************************
    5950             :  *                   TABText::UpdateMBR()
    5951             :  *
    5952             :  * Update the feature MBR using the text origin (OGRPoint geometry), the
    5953             :  * rotation angle, and the Width/height before rotation.
    5954             :  *
    5955             :  * This function cannot perform properly unless all the above have been set.
    5956             :  *
    5957             :  * Returns 0 on success, or -1 if there is no geometry in object
    5958             :  **********************************************************************/
    5959         226 : int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
    5960             : {
    5961         226 :     OGRGeometry *poGeom = GetGeometryRef();
    5962         226 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    5963             :     {
    5964          12 :         OGRPoint *poPoint = poGeom->toPoint();
    5965             : 
    5966          12 :         const double dX0 = poPoint->getX();
    5967          12 :         const double dY0 = poPoint->getY();
    5968             : 
    5969          12 :         const double dSin = sin(m_dAngle * M_PI / 180.0);
    5970          12 :         const double dCos = cos(m_dAngle * M_PI / 180.0);
    5971             : 
    5972          12 :         GetTextBoxWidth();  // Force default width value if necessary.
    5973             : 
    5974          12 :         const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
    5975          12 :         const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
    5976             : 
    5977          12 :         SetMBR(dX0, dY0, dX0, dY0);
    5978          60 :         for (int i = 0; i < 4; i++)
    5979             :         {
    5980             :             // Rotate one of the box corners
    5981          48 :             const double dX1 =
    5982          48 :                 dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
    5983          48 :             const double dY1 =
    5984          48 :                 dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
    5985             : 
    5986             :             // And update feature MBR with rotated coordinate
    5987          48 :             if (dX1 < m_dXMin)
    5988           9 :                 m_dXMin = dX1;
    5989          48 :             if (dX1 > m_dXMax)
    5990           9 :                 m_dXMax = dX1;
    5991          48 :             if (dY1 < m_dYMin)
    5992           0 :                 m_dYMin = dY1;
    5993          48 :             if (dY1 > m_dYMax)
    5994          18 :                 m_dYMax = dY1;
    5995             :         }
    5996             : 
    5997          12 :         if (poMapFile)
    5998             :         {
    5999           4 :             poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
    6000           4 :             poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
    6001             :         }
    6002             : 
    6003          12 :         return 0;
    6004             :     }
    6005             : 
    6006         214 :     return -1;
    6007             : }
    6008             : 
    6009             : /**********************************************************************
    6010             :  *                   TABText::GetFontBGColor()
    6011             :  *
    6012             :  * Return background color.
    6013             :  **********************************************************************/
    6014           8 : GInt32 TABText::GetFontBGColor() const
    6015             : {
    6016           8 :     return m_rgbBackground;
    6017             : }
    6018             : 
    6019         258 : void TABText::SetFontBGColor(GInt32 rgbColor)
    6020             : {
    6021         258 :     m_rgbBackground = rgbColor;
    6022         258 : }
    6023             : 
    6024             : /**********************************************************************
    6025             :  *                   TABText::GetFontOColor()
    6026             :  *
    6027             :  * Return outline color.
    6028             :  **********************************************************************/
    6029           1 : GInt32 TABText::GetFontOColor() const
    6030             : {
    6031           1 :     return m_rgbOutline;
    6032             : }
    6033             : 
    6034           1 : void TABText::SetFontOColor(GInt32 rgbColor)
    6035             : {
    6036           1 :     m_rgbOutline = rgbColor;
    6037           1 : }
    6038             : 
    6039             : /**********************************************************************
    6040             :  *                   TABText::GetFontSColor()
    6041             :  *
    6042             :  * Return shadow color.
    6043             :  **********************************************************************/
    6044           0 : GInt32 TABText::GetFontSColor() const
    6045             : {
    6046           0 :     return m_rgbShadow;
    6047             : }
    6048             : 
    6049           0 : void TABText::SetFontSColor(GInt32 rgbColor)
    6050             : {
    6051           0 :     m_rgbShadow = rgbColor;
    6052           0 : }
    6053             : 
    6054             : /**********************************************************************
    6055             :  *                   TABText::GetFontFGColor()
    6056             :  *
    6057             :  * Return foreground color.
    6058             :  **********************************************************************/
    6059          10 : GInt32 TABText::GetFontFGColor() const
    6060             : {
    6061          10 :     return m_rgbForeground;
    6062             : }
    6063             : 
    6064         265 : void TABText::SetFontFGColor(GInt32 rgbColor)
    6065             : {
    6066         265 :     m_rgbForeground = rgbColor;
    6067         265 : }
    6068             : 
    6069             : /**********************************************************************
    6070             :  *                   TABText::GetTextJustification()
    6071             :  *
    6072             :  * Return text justification.  Default is TABTJLeft
    6073             :  **********************************************************************/
    6074          10 : TABTextJust TABText::GetTextJustification() const
    6075             : {
    6076          10 :     TABTextJust eJust = TABTJLeft;
    6077             : 
    6078          10 :     if (m_nTextAlignment & 0x0200)
    6079           9 :         eJust = TABTJCenter;
    6080           1 :     else if (m_nTextAlignment & 0x0400)
    6081           0 :         eJust = TABTJRight;
    6082             : 
    6083          10 :     return eJust;
    6084             : }
    6085             : 
    6086         222 : void TABText::SetTextJustification(TABTextJust eJustification)
    6087             : {
    6088             :     // Flush current value... default is TABTJLeft
    6089         222 :     m_nTextAlignment &= ~0x0600;
    6090             :     // ... and set new one.
    6091         222 :     if (eJustification == TABTJCenter)
    6092         222 :         m_nTextAlignment |= 0x0200;
    6093           0 :     else if (eJustification == TABTJRight)
    6094           0 :         m_nTextAlignment |= 0x0400;
    6095         222 : }
    6096             : 
    6097             : /**********************************************************************
    6098             :  *                   TABText::GetTextSpacing()
    6099             :  *
    6100             :  * Return text vertical spacing factor.  Default is TABTSSingle
    6101             :  **********************************************************************/
    6102           0 : TABTextSpacing TABText::GetTextSpacing() const
    6103             : {
    6104           0 :     TABTextSpacing eSpacing = TABTSSingle;
    6105             : 
    6106           0 :     if (m_nTextAlignment & 0x0800)
    6107           0 :         eSpacing = TABTS1_5;
    6108           0 :     else if (m_nTextAlignment & 0x1000)
    6109           0 :         eSpacing = TABTSDouble;
    6110             : 
    6111           0 :     return eSpacing;
    6112             : }
    6113             : 
    6114         237 : void TABText::SetTextSpacing(TABTextSpacing eSpacing)
    6115             : {
    6116             :     // Flush current value... default is TABTSSingle
    6117         237 :     m_nTextAlignment &= ~0x1800;
    6118             :     // ... and set new one.
    6119         237 :     if (eSpacing == TABTS1_5)
    6120           0 :         m_nTextAlignment |= 0x0800;
    6121         237 :     else if (eSpacing == TABTSDouble)
    6122         237 :         m_nTextAlignment |= 0x1000;
    6123         237 : }
    6124             : 
    6125             : /**********************************************************************
    6126             :  *                   TABText::GetTextLineType()
    6127             :  *
    6128             :  * Return text line (arrow) type.  Default is TABTLNoLine
    6129             :  **********************************************************************/
    6130           0 : TABTextLineType TABText::GetTextLineType() const
    6131             : {
    6132           0 :     TABTextLineType eLine = TABTLNoLine;
    6133             : 
    6134           0 :     if (m_nTextAlignment & 0x2000)
    6135           0 :         eLine = TABTLSimple;
    6136           0 :     else if (m_nTextAlignment & 0x4000)
    6137           0 :         eLine = TABTLArrow;
    6138             : 
    6139           0 :     return eLine;
    6140             : }
    6141             : 
    6142         178 : void TABText::SetTextLineType(TABTextLineType eLineType)
    6143             : {
    6144             :     // Flush current value... default is TABTLNoLine
    6145         178 :     m_nTextAlignment &= ~0x6000;
    6146             :     // ... and set new one.
    6147         178 :     if (eLineType == TABTLSimple)
    6148         178 :         m_nTextAlignment |= 0x2000;
    6149           0 :     else if (eLineType == TABTLArrow)
    6150           0 :         m_nTextAlignment |= 0x4000;
    6151         178 : }
    6152             : 
    6153             : /**********************************************************************
    6154             :  *                   TABText::QueryFontStyle()
    6155             :  *
    6156             :  * Return TRUE if the specified font style attribute is turned ON,
    6157             :  * or FALSE otherwise.  See enum TABFontStyle for the list of styles
    6158             :  * that can be queried on.
    6159             :  **********************************************************************/
    6160         385 : GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
    6161             : {
    6162         385 :     return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
    6163             : }
    6164             : 
    6165         264 : void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
    6166             : {
    6167         264 :     if (bStyleOn)
    6168         264 :         m_nFontStyle |= static_cast<int>(eStyleToToggle);
    6169             :     else
    6170           0 :         m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
    6171         264 : }
    6172             : 
    6173             : /**********************************************************************
    6174             :  *                   TABText::GetFontStyleMIFValue()
    6175             :  *
    6176             :  * Return the Font Style value for this object using the style values
    6177             :  * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
    6178             :  *
    6179             :  * The reason why we have to differentiate between the TAB and the MIF font
    6180             :  * style values is that in TAB, TABFSBox is included in the style value
    6181             :  * as code 0x100, but in MIF it is not included, instead it is implied by
    6182             :  * the presence of the BG color in the FONT() clause (the BG color is
    6183             :  * present only when TABFSBox or TABFSHalo is set).
    6184             :  * This also has the effect of shifting all the other style values > 0x100
    6185             :  * by 1 byte.
    6186             :  **********************************************************************/
    6187           0 : int TABText::GetFontStyleMIFValue() const
    6188             : {
    6189             :     // The conversion is simply to remove bit 0x100 from the value and shift
    6190             :     // down all values past this bit.
    6191           0 :     return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
    6192             : }
    6193             : 
    6194         261 : void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
    6195             : {
    6196         261 :     m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
    6197             :     // When BG color is set, then either BOX or HALO should be set.
    6198         261 :     if (bBGColorSet && !QueryFontStyle(TABFSHalo))
    6199         254 :         ToggleFontStyle(TABFSBox, TRUE);
    6200         261 : }
    6201             : 
    6202          10 : int TABText::IsFontBGColorUsed() const
    6203             : {
    6204             :     // Font BG color is used only when BOX is set.
    6205          10 :     return QueryFontStyle(TABFSBox);
    6206             : }
    6207             : 
    6208          10 : int TABText::IsFontOColorUsed() const
    6209             : {
    6210             :     // Font outline color is used only when HALO is set.
    6211          10 :     return QueryFontStyle(TABFSHalo);
    6212             : }
    6213             : 
    6214          10 : int TABText::IsFontSColorUsed() const
    6215             : {
    6216             :     // Font shadow color is used only when Shadow is set.
    6217          10 :     return QueryFontStyle(TABFSShadow);
    6218             : }
    6219             : 
    6220          10 : int TABText::IsFontBold() const
    6221             : {
    6222             :     // Font bold is used only when Bold is set.
    6223          10 :     return QueryFontStyle(TABFSBold);
    6224             : }
    6225             : 
    6226          10 : int TABText::IsFontItalic() const
    6227             : {
    6228             :     // Font italic is used only when Italic is set.
    6229          10 :     return QueryFontStyle(TABFSItalic);
    6230             : }
    6231             : 
    6232          10 : int TABText::IsFontUnderline() const
    6233             : {
    6234             :     // Font underline is used only when Underline is set.
    6235          10 :     return QueryFontStyle(TABFSUnderline);
    6236             : }
    6237             : 
    6238             : /**********************************************************************
    6239             :  *                   TABText::GetLabelStyleString()
    6240             :  *
    6241             :  * This is not the correct location, it should be in ITABFeatureFont,
    6242             :  * but it is really more easy to put it here.  This fct return a complete
    6243             :  * string for the representation with the string to display
    6244             :  **********************************************************************/
    6245          10 : const char *TABText::GetLabelStyleString() const
    6246             : {
    6247          10 :     const char *pszStyle = nullptr;
    6248          10 :     int nStringLen = static_cast<int>(strlen(GetTextString()));
    6249             :     // ALL Caps, Extpanded need to modify the string value
    6250             :     char *pszTextString =
    6251          10 :         static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
    6252             :     /* char szPattern[20]; */
    6253          10 :     int nJustification = 1;
    6254             : 
    6255          10 :     strcpy(pszTextString, GetTextString());
    6256             :     /* szPattern[0] = '\0'; */
    6257             : 
    6258          10 :     switch (GetTextJustification())
    6259             :     {
    6260           9 :         case TABTJCenter:
    6261           9 :             nJustification = 2;
    6262           9 :             break;
    6263           0 :         case TABTJRight:
    6264           0 :             nJustification = 3;
    6265           0 :             break;
    6266           1 :         case TABTJLeft:
    6267             :         default:
    6268           1 :             nJustification = 1;
    6269           1 :             break;
    6270             :     }
    6271             : 
    6272             :     // Compute real font size, taking number of lines ("\\n", "\n") and line
    6273             :     // spacing into account.
    6274          10 :     int numLines = 1;
    6275          61 :     for (int i = 0; pszTextString[i];
    6276          51 :          numLines +=
    6277         102 :          ((pszTextString[i] == '\n' ||
    6278          51 :            (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
    6279           0 :           pszTextString[i + 1] != '\0'),
    6280             :              ++i)
    6281             :         ;
    6282             : 
    6283          10 :     double dHeight = GetTextBoxHeight() / numLines;
    6284             : 
    6285             :     // In all cases, take out 20% of font height to account for line spacing
    6286          10 :     if (numLines > 1)
    6287             :     {
    6288           0 :         switch (GetTextSpacing())
    6289             :         {
    6290           0 :             case TABTS1_5:
    6291           0 :                 dHeight *= (0.80 * 0.69);
    6292           0 :                 break;
    6293           0 :             case TABTSDouble:
    6294           0 :                 dHeight *= (0.66 * 0.69);
    6295           0 :                 break;
    6296           0 :             default:
    6297           0 :                 dHeight *= 0.69;
    6298             :         }
    6299             :     }
    6300             :     else
    6301             :     {
    6302          10 :         dHeight *= 0.69;
    6303             :     }
    6304             : 
    6305          10 :     if (QueryFontStyle(TABFSAllCaps))
    6306           0 :         for (int i = 0; pszTextString[i]; ++i)
    6307           0 :             if (isalpha(static_cast<unsigned char>(pszTextString[i])))
    6308           0 :                 pszTextString[i] = static_cast<char>(
    6309           0 :                     CPLToupper(static_cast<unsigned char>(pszTextString[i])));
    6310             : 
    6311             :     /* Escape the double quote chars and expand the text */
    6312          10 :     char *pszTmpTextString = nullptr;
    6313             : 
    6314          10 :     if (QueryFontStyle(TABFSExpanded))
    6315             :         pszTmpTextString = static_cast<char *>(
    6316           0 :             CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
    6317             :     else
    6318             :         pszTmpTextString = static_cast<char *>(
    6319          10 :             CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
    6320             : 
    6321          10 :     int j = 0;
    6322          61 :     for (int i = 0; i < nStringLen; ++i, ++j)
    6323             :     {
    6324          51 :         if (pszTextString[i] == '"')
    6325             :         {
    6326           0 :             pszTmpTextString[j] = '\\';
    6327           0 :             pszTmpTextString[j + 1] = pszTextString[i];
    6328           0 :             ++j;
    6329             :         }
    6330             :         else
    6331          51 :             pszTmpTextString[j] = pszTextString[i];
    6332             : 
    6333          51 :         if (QueryFontStyle(TABFSExpanded))
    6334             :         {
    6335           0 :             pszTmpTextString[j + 1] = ' ';
    6336           0 :             ++j;
    6337             :         }
    6338             :     }
    6339             : 
    6340          10 :     pszTmpTextString[j] = '\0';
    6341          10 :     CPLFree(pszTextString);
    6342             :     pszTextString = static_cast<char *>(
    6343          10 :         CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
    6344          10 :     strcpy(pszTextString, pszTmpTextString);
    6345          10 :     CPLFree(pszTmpTextString);
    6346             : 
    6347             :     const char *pszBGColor =
    6348          10 :         IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
    6349             :     const char *pszOColor =
    6350          10 :         IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
    6351             :     const char *pszSColor =
    6352          10 :         IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
    6353          10 :     const char *pszBold = IsFontBold() ? ",bo:1" : "";
    6354          10 :     const char *pszItalic = IsFontItalic() ? ",it:1" : "";
    6355          10 :     const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
    6356             : 
    6357          10 :     pszStyle = CPLSPrintf(
    6358             :         "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
    6359             :         pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
    6360             :         pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
    6361             :         GetFontNameRef());
    6362             : 
    6363          10 :     CPLFree(pszTextString);
    6364          10 :     return pszStyle;
    6365             : }
    6366             : 
    6367             : /**********************************************************************
    6368             :  *                   TABText::GetStyleString() const
    6369             :  *
    6370             :  * Return style string for this feature.
    6371             :  *
    6372             :  * Style String is built only once during the first call to GetStyleString().
    6373             :  **********************************************************************/
    6374          10 : const char *TABText::GetStyleString() const
    6375             : {
    6376          10 :     if (m_pszStyleString == nullptr)
    6377             :     {
    6378          10 :         m_pszStyleString = CPLStrdup(GetLabelStyleString());
    6379             :     }
    6380             : 
    6381          10 :     return m_pszStyleString;
    6382             : }
    6383             : 
    6384           4 : void TABText::SetLabelFromStyleString(const char *pszStyleString)
    6385             : {
    6386             :     // Use the Style Manager to retrieve all the information we need.
    6387           4 :     auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
    6388           0 :     std::unique_ptr<OGRStyleTool> poStylePart;
    6389             : 
    6390             :     // Init the StyleMgr with the StyleString.
    6391           4 :     poStyleMgr->InitStyleString(pszStyleString);
    6392             : 
    6393             :     // Retrieve the Symbol info.
    6394           4 :     const int numParts = poStyleMgr->GetPartCount();
    6395           4 :     for (int i = 0; i < numParts; i++)
    6396             :     {
    6397           4 :         poStylePart.reset(poStyleMgr->GetPart(i));
    6398           4 :         if (poStylePart == nullptr)
    6399             :         {
    6400           0 :             continue;
    6401             :         }
    6402             : 
    6403           4 :         if (poStylePart->GetType() == OGRSTCLabel)
    6404             :         {
    6405           4 :             break;
    6406             :         }
    6407             :         else
    6408             :         {
    6409           0 :             poStylePart.reset();
    6410             :         }
    6411             :     }
    6412             : 
    6413             :     // If the no Symbol found, do nothing.
    6414           4 :     if (poStylePart == nullptr)
    6415             :     {
    6416           0 :         return;
    6417             :     }
    6418             : 
    6419           4 :     auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
    6420             : 
    6421           4 :     GBool bIsNull = 0;
    6422           4 :     const char *pszText = poLabelStyle->TextString(bIsNull);
    6423           4 :     if (!bIsNull && pszText)
    6424             :     {
    6425           3 :         SetTextString(pszText);
    6426             : 
    6427           3 :         poLabelStyle->SetUnit(OGRSTUMM);
    6428           3 :         double dfSize = poLabelStyle->Size(bIsNull);
    6429           3 :         if (!bIsNull)
    6430             :         {
    6431           3 :             dfSize /= 1000;
    6432             : 
    6433             :             // Compute text box height, taking number of lines ("\\n", "\n") and
    6434             :             // line spacing into account.
    6435           3 :             int numLines = 1;
    6436          18 :             for (int i = 0; pszText[i];
    6437          45 :                  numLines += ((pszText[i] == '\n' ||
    6438          15 :                                (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
    6439           0 :                               pszText[i + 1] != '\0'),
    6440             :                      ++i)
    6441             :                 ;
    6442             : 
    6443             :             // Cf GetLabelStyleString() for 0.69. We should likely also take
    6444             :             // into account line spacing if we knew how to compute it.
    6445           3 :             SetTextBoxHeight(dfSize / 0.69 * numLines);
    6446             :         }
    6447             :     }
    6448             : 
    6449           4 :     if (poLabelStyle->Bold(bIsNull))
    6450           3 :         ToggleFontStyle(TABFSBold, true);
    6451             : 
    6452           4 :     if (poLabelStyle->Italic(bIsNull))
    6453           1 :         ToggleFontStyle(TABFSItalic, true);
    6454             : 
    6455           4 :     if (poLabelStyle->Underline(bIsNull))
    6456           1 :         ToggleFontStyle(TABFSUnderline, true);
    6457             : 
    6458           4 :     const char *pszFontName = poLabelStyle->FontName(bIsNull);
    6459           4 :     if (!bIsNull && pszFontName)
    6460           4 :         SetFontName(pszFontName);
    6461             : 
    6462             :     // Set the ForeColor
    6463           4 :     const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
    6464           4 :     if (bIsNull)
    6465           0 :         pszForeColor = nullptr;
    6466           4 :     if (pszForeColor)
    6467             :     {
    6468           4 :         if (pszForeColor[0] == '#')
    6469           4 :             pszForeColor++;
    6470           8 :         CPLString osForeColor(pszForeColor);
    6471           4 :         if (strlen(pszForeColor) > 6)
    6472           1 :             osForeColor.resize(6);
    6473           4 :         const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
    6474           4 :         SetFontFGColor(static_cast<GInt32>(nColor));
    6475             :     }
    6476             : 
    6477             :     // Set the BackgroundColor
    6478           4 :     const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
    6479           4 :     if (bIsNull)
    6480           0 :         pszBackColor = nullptr;
    6481           4 :     if (pszBackColor)
    6482             :     {
    6483           4 :         if (pszBackColor[0] == '#')
    6484           4 :             pszBackColor++;
    6485           8 :         CPLString osBackColor(pszBackColor);
    6486           4 :         if (strlen(pszBackColor) > 6)
    6487           1 :             osBackColor.resize(6);
    6488           4 :         const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
    6489           4 :         ToggleFontStyle(TABFSBox, true);
    6490           4 :         SetFontBGColor(static_cast<GInt32>(nColor));
    6491             :     }
    6492             : 
    6493             :     // Set the OutlineColor
    6494           4 :     const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
    6495           4 :     if (bIsNull)
    6496           3 :         pszOutlineColor = nullptr;
    6497           4 :     if (pszOutlineColor)
    6498             :     {
    6499           1 :         if (pszOutlineColor[0] == '#')
    6500           1 :             pszOutlineColor++;
    6501           2 :         CPLString osOutlineColor(pszOutlineColor);
    6502           1 :         if (strlen(pszOutlineColor) > 6)
    6503           0 :             osOutlineColor.resize(6);
    6504             :         const int nColor =
    6505           1 :             static_cast<int>(strtol(osOutlineColor, nullptr, 16));
    6506           1 :         ToggleFontStyle(TABFSHalo, true);
    6507           1 :         SetFontOColor(static_cast<GInt32>(nColor));
    6508             :     }
    6509             : 
    6510             : #if 0
    6511             :     // Commented out since it is hardcoded to 0x808080.
    6512             :     // Set the ShadowColor
    6513             :     const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
    6514             :     if(bIsNull) pszShadowColor = nullptr;
    6515             :     if(pszShadowColor)
    6516             :     {
    6517             :         if(pszShadowColor[0] == '#')
    6518             :             pszShadowColor++;
    6519             :         CPLString osShadowColor(pszShadowColor);
    6520             :         if( strlen(pszShadowColor) > 6 )
    6521             :             osShadowColor.resize(6);
    6522             :         const int nColor =
    6523             :             static_cast<int>(strtol(osShadowColor, nullptr, 16));
    6524             :         ToggleFontStyle(TABFSShadow, true);
    6525             :         SetFontSColor(static_cast<GInt32>(nColor));
    6526             :     }
    6527             : #endif
    6528             : 
    6529           4 :     const double dfAngle = poLabelStyle->Angle(bIsNull);
    6530           4 :     if (!bIsNull)
    6531           3 :         SetTextAngle(dfAngle);
    6532             : 
    6533           4 :     const int nAnchor = poLabelStyle->Anchor(bIsNull);
    6534           4 :     if (!bIsNull)
    6535             :     {
    6536           3 :         switch ((nAnchor - 1) % 3)
    6537             :         {
    6538           0 :             case 0:
    6539           0 :                 SetTextJustification(TABTJLeft);
    6540           0 :                 break;
    6541           3 :             case 1:
    6542           3 :                 SetTextJustification(TABTJCenter);
    6543           3 :                 break;
    6544           0 :             default /* 2 */:
    6545           0 :                 SetTextJustification(TABTJRight);
    6546           0 :                 break;
    6547             :         }
    6548             :     }
    6549             : }
    6550             : 
    6551             : /**********************************************************************
    6552             :  *                   TABText::DumpMIF()
    6553             :  *
    6554             :  * Dump feature geometry in a format similar to .MIF REGIONs.
    6555             :  **********************************************************************/
    6556           0 : void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
    6557             : {
    6558           0 :     if (fpOut == nullptr)
    6559           0 :         fpOut = stdout;
    6560             : 
    6561             :     /*-----------------------------------------------------------------
    6562             :      * Fetch and validate geometry
    6563             :      *----------------------------------------------------------------*/
    6564           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6565           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6566             :     {
    6567             :         /*-------------------------------------------------------------
    6568             :          * Generate output for text object
    6569             :          *------------------------------------------------------------*/
    6570           0 :         OGRPoint *poPoint = poGeom->toPoint();
    6571             : 
    6572           0 :         fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
    6573           0 :                 m_pszString ? m_pszString : "", poPoint->getX(),
    6574             :                 poPoint->getY());
    6575             : 
    6576           0 :         fprintf(fpOut, "  m_pszString = '%s'\n", m_pszString);
    6577           0 :         fprintf(fpOut, "  m_dAngle    = %.15g\n", m_dAngle);
    6578           0 :         fprintf(fpOut, "  m_dHeight   = %.15g\n", m_dHeight);
    6579           0 :         fprintf(fpOut, "  m_rgbForeground  = 0x%6.6x (%d)\n", m_rgbForeground,
    6580             :                 m_rgbForeground);
    6581           0 :         fprintf(fpOut, "  m_rgbBackground  = 0x%6.6x (%d)\n", m_rgbBackground,
    6582             :                 m_rgbBackground);
    6583           0 :         fprintf(fpOut, "  m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
    6584           0 :         fprintf(fpOut, "  m_nFontStyle     = 0x%4.4x\n", m_nFontStyle);
    6585             :     }
    6586             :     else
    6587             :     {
    6588           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6589             :                  "TABText: Missing or Invalid Geometry!");
    6590           0 :         return;
    6591             :     }
    6592             : 
    6593             :     // Finish with PEN/BRUSH/etc. clauses
    6594           0 :     DumpPenDef();
    6595           0 :     DumpFontDef();
    6596             : 
    6597           0 :     fflush(fpOut);
    6598             : }
    6599             : 
    6600             : /*=====================================================================
    6601             :  *                      class TABMultiPoint
    6602             :  *====================================================================*/
    6603             : 
    6604             : /**********************************************************************
    6605             :  *                   TABMultiPoint::TABMultiPoint()
    6606             :  *
    6607             :  * Constructor.
    6608             :  **********************************************************************/
    6609         191 : TABMultiPoint::TABMultiPoint(const OGRFeatureDefn *poDefnIn)
    6610             :     : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
    6611         191 :       m_dCenterY(0.0)
    6612             : {
    6613         191 : }
    6614             : 
    6615             : /**********************************************************************
    6616             :  *                   TABMultiPoint::~TABMultiPoint()
    6617             :  *
    6618             :  * Destructor.
    6619             :  **********************************************************************/
    6620         382 : TABMultiPoint::~TABMultiPoint()
    6621             : {
    6622         382 : }
    6623             : 
    6624             : /**********************************************************************
    6625             :  *                     TABMultiPoint::CloneTABFeature()
    6626             :  *
    6627             :  * Duplicate feature, including stuff specific to each TABFeature type.
    6628             :  *
    6629             :  * This method calls the generic TABFeature::CloneTABFeature() and
    6630             :  * then copies any members specific to its own type.
    6631             :  **********************************************************************/
    6632             : TABFeature *
    6633           0 : TABMultiPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    6634             : {
    6635             :     /*-----------------------------------------------------------------
    6636             :      * Alloc new feature and copy the base stuff
    6637             :      *----------------------------------------------------------------*/
    6638             :     TABMultiPoint *poNew =
    6639           0 :         new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
    6640             : 
    6641           0 :     CopyTABFeatureBase(poNew);
    6642             : 
    6643             :     /*-----------------------------------------------------------------
    6644             :      * And members specific to this class
    6645             :      *----------------------------------------------------------------*/
    6646             :     // ITABFeatureSymbol
    6647           0 :     *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
    6648             : 
    6649           0 :     poNew->m_bCenterIsSet = m_bCenterIsSet;
    6650           0 :     poNew->m_dCenterX = m_dCenterX;
    6651           0 :     poNew->m_dCenterY = m_dCenterY;
    6652             : 
    6653           0 :     return poNew;
    6654             : }
    6655             : 
    6656             : /**********************************************************************
    6657             :  *                   TABMultiPoint::ValidateMapInfoType()
    6658             :  *
    6659             :  * Check the feature's geometry part and return the corresponding
    6660             :  * mapinfo object type code.  The m_nMapInfoType member will also
    6661             :  * be updated for further calls to GetMapInfoType();
    6662             :  *
    6663             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    6664             :  * is expected for this object class.
    6665             :  **********************************************************************/
    6666           0 : TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    6667             : {
    6668             :     /*-----------------------------------------------------------------
    6669             :      * Fetch and validate geometry
    6670             :      *----------------------------------------------------------------*/
    6671           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6672           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6673             :     {
    6674           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    6675             : 
    6676           0 :         if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
    6677           0 :             m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
    6678             :         else
    6679           0 :             m_nMapInfoType = TAB_GEOM_MULTIPOINT;
    6680             :     }
    6681             :     else
    6682             :     {
    6683           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6684             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6685           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    6686             :     }
    6687             : 
    6688             :     /*-----------------------------------------------------------------
    6689             :      * Decide if coordinates should be compressed or not.
    6690             :      *----------------------------------------------------------------*/
    6691           0 :     ValidateCoordType(poMapFile);
    6692             : 
    6693           0 :     return m_nMapInfoType;
    6694             : }
    6695             : 
    6696             : /**********************************************************************
    6697             :  *                   TABMultiPoint::ReadGeometryFromMAPFile()
    6698             :  *
    6699             :  * Fill the geometry and representation (color, etc...) part of the
    6700             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    6701             :  *
    6702             :  * It is assumed that poMAPFile currently points to the beginning of
    6703             :  * a map object.
    6704             :  *
    6705             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    6706             :  * been called.
    6707             :  **********************************************************************/
    6708           8 : int TABMultiPoint::ReadGeometryFromMAPFile(
    6709             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    6710             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    6711             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    6712             : {
    6713           8 :     double dXMin = 0.0;
    6714           8 :     double dYMin = 0.0;
    6715           8 :     double dXMax = 0.0;
    6716           8 :     double dYMax = 0.0;
    6717           8 :     OGRGeometry *poGeometry = nullptr;
    6718           8 :     GBool bComprCoord = poObjHdr->IsCompressedType();
    6719           8 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    6720             : 
    6721             :     /*-----------------------------------------------------------------
    6722             :      * Fetch and validate geometry type
    6723             :      *----------------------------------------------------------------*/
    6724           8 :     m_nMapInfoType = poObjHdr->m_nType;
    6725             : 
    6726             :     /*-----------------------------------------------------------------
    6727             :      * Read object information
    6728             :      *----------------------------------------------------------------*/
    6729           8 :     if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
    6730           8 :         m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
    6731           0 :         m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
    6732           0 :         m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
    6733             :     {
    6734             :         /*-------------------------------------------------------------
    6735             :          * Copy data from poObjHdr
    6736             :          *------------------------------------------------------------*/
    6737             :         TABMAPObjMultiPoint *poMPointHdr =
    6738           8 :             cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
    6739             : 
    6740           8 :         const GUInt32 nMinimumBytesForPoints =
    6741           8 :             (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
    6742           8 :         if (nMinimumBytesForPoints > 1024 * 1024 &&
    6743           0 :             nMinimumBytesForPoints > poMapFile->GetFileSize())
    6744             :         {
    6745           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
    6746           0 :             return -1;
    6747             :         }
    6748             : 
    6749             :         // MBR
    6750           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
    6751             :                                 dXMin, dYMin);
    6752           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
    6753             :                                 dXMax, dYMax);
    6754             : 
    6755           8 :         if (!bCoordBlockDataOnly)
    6756             :         {
    6757           8 :             m_nSymbolDefIndex = poMPointHdr->m_nSymbolId;  // Symbol index
    6758           8 :             poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
    6759             :         }
    6760             : 
    6761           8 :         double dX = 0.0;
    6762           8 :         double dY = 0.0;
    6763             :         // Centroid/label point
    6764           8 :         poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
    6765             :                                 dX, dY);
    6766           8 :         SetCenter(dX, dY);
    6767             : 
    6768             :         // Compressed coordinate origin (useful only in compressed case!)
    6769           8 :         m_nComprOrgX = poMPointHdr->m_nComprOrgX;
    6770           8 :         m_nComprOrgY = poMPointHdr->m_nComprOrgY;
    6771             : 
    6772             :         /*-------------------------------------------------------------
    6773             :          * Read Point Coordinates
    6774             :          *------------------------------------------------------------*/
    6775           8 :         OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
    6776           8 :         poGeometry = poMultiPoint;
    6777             : 
    6778           8 :         if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    6779           4 :             poCoordBlock = *ppoCoordBlock;
    6780             :         else
    6781             :             poCoordBlock =
    6782           4 :                 poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
    6783           8 :         if (poCoordBlock == nullptr)
    6784             :         {
    6785           0 :             delete poGeometry;
    6786           0 :             return -1;
    6787             :         }
    6788           8 :         poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    6789             : 
    6790          24 :         for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
    6791             :         {
    6792          16 :             GInt32 nX = 0;
    6793          16 :             GInt32 nY = 0;
    6794          16 :             if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
    6795             :             {
    6796           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    6797             :                          "Failed reading coordinate data at offset %d",
    6798             :                          poMPointHdr->m_nCoordBlockPtr);
    6799           0 :                 delete poGeometry;
    6800           0 :                 return -1;
    6801             :             }
    6802             : 
    6803          16 :             poMapFile->Int2Coordsys(nX, nY, dX, dY);
    6804          16 :             OGRPoint *poPoint = new OGRPoint(dX, dY);
    6805             : 
    6806          16 :             if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
    6807             :             {
    6808           0 :                 CPLAssert(false);  // Just in case lower-level lib is modified
    6809             :             }
    6810           8 :         }
    6811             :     }
    6812             :     else
    6813             :     {
    6814           0 :         CPLError(
    6815             :             CE_Failure, CPLE_AssertionFailed,
    6816             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    6817           0 :             m_nMapInfoType, m_nMapInfoType);
    6818           0 :         return -1;
    6819             :     }
    6820             : 
    6821           8 :     SetGeometryDirectly(poGeometry);
    6822             : 
    6823           8 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    6824             : 
    6825             :     /* Copy int MBR to feature class members */
    6826           8 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    6827             :               poObjHdr->m_nMaxY);
    6828             : 
    6829             :     /* Return a ref to coord block so that caller can continue reading
    6830             :      * after the end of this object (used by TABCollection and index splitting)
    6831             :      */
    6832           8 :     if (ppoCoordBlock)
    6833           4 :         *ppoCoordBlock = poCoordBlock;
    6834             : 
    6835           8 :     return 0;
    6836             : }
    6837             : 
    6838             : /**********************************************************************
    6839             :  *                   TABMultiPoint::WriteGeometryToMAPFile()
    6840             :  *
    6841             :  * Write the geometry and representation (color, etc...) part of the
    6842             :  * feature to the .MAP object pointed to by poMAPFile.
    6843             :  *
    6844             :  * It is assumed that poMAPFile currently points to a valid map object.
    6845             :  *
    6846             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    6847             :  * been called.
    6848             :  **********************************************************************/
    6849           0 : int TABMultiPoint::WriteGeometryToMAPFile(
    6850             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    6851             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    6852             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    6853             : {
    6854             :     GInt32 nX, nY;
    6855             : 
    6856             :     /*-----------------------------------------------------------------
    6857             :      * We assume that ValidateMapInfoType() was called already and that
    6858             :      * the type in poObjHdr->m_nType is valid.
    6859             :      *----------------------------------------------------------------*/
    6860           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    6861             : 
    6862             :     TABMAPObjMultiPoint *poMPointHdr =
    6863           0 :         cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
    6864             : 
    6865             :     /*-----------------------------------------------------------------
    6866             :      * Fetch and validate geometry
    6867             :      *----------------------------------------------------------------*/
    6868           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6869           0 :     OGRMultiPoint *poMPoint = nullptr;
    6870           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6871           0 :         poMPoint = poGeom->toMultiPoint();
    6872             :     else
    6873             :     {
    6874           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6875             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6876           0 :         return -1;
    6877             :     }
    6878             : 
    6879           0 :     poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
    6880             : 
    6881             :     /*-----------------------------------------------------------------
    6882             :      * Write data to coordinate block
    6883             :      *----------------------------------------------------------------*/
    6884           0 :     const GBool bCompressed = poObjHdr->IsCompressedType();
    6885             : 
    6886           0 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    6887           0 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    6888           0 :         poCoordBlock = *ppoCoordBlock;
    6889             :     else
    6890           0 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    6891           0 :     poCoordBlock->StartNewFeature();
    6892           0 :     poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    6893           0 :     poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    6894             : 
    6895           0 :     for (int iPoint = 0, nStatus = 0;
    6896           0 :          nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
    6897             :     {
    6898           0 :         poGeom = poMPoint->getGeometryRef(iPoint);
    6899             : 
    6900           0 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6901             :         {
    6902           0 :             OGRPoint *poPoint = poGeom->toPoint();
    6903             : 
    6904           0 :             poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
    6905           0 :             if (iPoint == 0)
    6906             :             {
    6907             :                 // Default to the first point, we may use explicit value below
    6908           0 :                 poMPointHdr->m_nLabelX = nX;
    6909           0 :                 poMPointHdr->m_nLabelY = nY;
    6910             :             }
    6911             : 
    6912           0 :             if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
    6913             :                 0)
    6914             :             {
    6915             :                 // Failed ... error message has already been produced
    6916           0 :                 return nStatus;
    6917             :             }
    6918             :         }
    6919             :         else
    6920             :         {
    6921           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    6922             :                      "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
    6923           0 :             return -1;
    6924             :         }
    6925             :     }
    6926             : 
    6927             :     /*-----------------------------------------------------------------
    6928             :      * Copy object information
    6929             :      *----------------------------------------------------------------*/
    6930             : 
    6931             :     // Compressed coordinate origin (useful only in compressed case!)
    6932           0 :     poMPointHdr->m_nComprOrgX = m_nComprOrgX;
    6933           0 :     poMPointHdr->m_nComprOrgY = m_nComprOrgY;
    6934             : 
    6935           0 :     poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
    6936           0 :     poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    6937             : 
    6938             :     // Center/label point (default value already set above)
    6939           0 :     double dX = 0.0;
    6940           0 :     double dY = 0.0;
    6941           0 :     if (GetCenter(dX, dY) != -1)
    6942             :     {
    6943           0 :         poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
    6944           0 :                                 poMPointHdr->m_nLabelY);
    6945             :     }
    6946             : 
    6947           0 :     if (!bCoordBlockDataOnly)
    6948             :     {
    6949           0 :         m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
    6950           0 :         poMPointHdr->m_nSymbolId =
    6951           0 :             static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
    6952             :     }
    6953             : 
    6954           0 :     if (CPLGetLastErrorType() == CE_Failure)
    6955           0 :         return -1;
    6956             : 
    6957             :     /* Return a ref to coord block so that caller can continue writing
    6958             :      * after the end of this object (used by index splitting)
    6959             :      */
    6960           0 :     if (ppoCoordBlock)
    6961           0 :         *ppoCoordBlock = poCoordBlock;
    6962             : 
    6963           0 :     return 0;
    6964             : }
    6965             : 
    6966             : /**********************************************************************
    6967             :  *                   TABMultiPoint::GetXY()
    6968             :  *
    6969             :  * Return this point's X,Y coordinates.
    6970             :  **********************************************************************/
    6971           0 : int TABMultiPoint::GetXY(int i, double &dX, double &dY)
    6972             : {
    6973             :     /*-----------------------------------------------------------------
    6974             :      * Fetch and validate geometry
    6975             :      *----------------------------------------------------------------*/
    6976           0 :     OGRGeometry *poGeom = GetGeometryRef();
    6977           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    6978             :     {
    6979           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    6980             : 
    6981           0 :         if (i >= 0 && i < poMPoint->getNumGeometries() &&
    6982           0 :             (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
    6983           0 :             wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    6984             :         {
    6985           0 :             OGRPoint *poPoint = poGeom->toPoint();
    6986             : 
    6987           0 :             dX = poPoint->getX();
    6988           0 :             dY = poPoint->getY();
    6989             :         }
    6990             :     }
    6991             :     else
    6992             :     {
    6993           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    6994             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    6995           0 :         dX = 0.0;
    6996           0 :         dY = 0.0;
    6997           0 :         return -1;
    6998             :     }
    6999             : 
    7000           0 :     return 0;
    7001             : }
    7002             : 
    7003             : /**********************************************************************
    7004             :  *                   TABMultiPoint::GetNumPoints()
    7005             :  *
    7006             :  * Return the number of points in this multipoint object
    7007             :  **********************************************************************/
    7008           0 : int TABMultiPoint::GetNumPoints()
    7009             : {
    7010             :     /*-----------------------------------------------------------------
    7011             :      * Fetch and validate geometry
    7012             :      *----------------------------------------------------------------*/
    7013           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7014           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    7015             :     {
    7016           0 :         OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
    7017             : 
    7018           0 :         return poMPoint->getNumGeometries();
    7019             :     }
    7020             :     else
    7021             :     {
    7022           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7023             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7024           0 :         return 0;
    7025             :     }
    7026             : }
    7027             : 
    7028             : /**********************************************************************
    7029             :  *                   TABMultiPoint::GetStyleString() const
    7030             :  *
    7031             :  * Return style string for this feature.
    7032             :  *
    7033             :  * Style String is built only once during the first call to GetStyleString().
    7034             :  **********************************************************************/
    7035           3 : const char *TABMultiPoint::GetStyleString() const
    7036             : {
    7037           3 :     if (m_pszStyleString == nullptr)
    7038             :     {
    7039           3 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    7040             :     }
    7041             : 
    7042           3 :     return m_pszStyleString;
    7043             : }
    7044             : 
    7045             : /**********************************************************************
    7046             :  *                   TABMultiPoint::GetCenter()
    7047             :  *
    7048             :  * Returns the center point (or label point?) of the object.  Compute one
    7049             :  * if it was not explicitly set:
    7050             :  *
    7051             :  * The default seems to be to use the first point in the collection as
    7052             :  * the center.. so we'll use that.
    7053             :  *
    7054             :  * Returns 0 on success, -1 on error.
    7055             :  **********************************************************************/
    7056           0 : int TABMultiPoint::GetCenter(double &dX, double &dY)
    7057             : {
    7058           0 :     if (!m_bCenterIsSet && GetNumPoints() > 0)
    7059             :     {
    7060             :         // The default seems to be to use the first point in the collection
    7061             :         // as the center... so we'll use that.
    7062           0 :         if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
    7063           0 :             m_bCenterIsSet = TRUE;
    7064             :     }
    7065             : 
    7066           0 :     if (!m_bCenterIsSet)
    7067           0 :         return -1;
    7068             : 
    7069           0 :     dX = m_dCenterX;
    7070           0 :     dY = m_dCenterY;
    7071           0 :     return 0;
    7072             : }
    7073             : 
    7074             : /**********************************************************************
    7075             :  *                   TABMultiPoint::SetCenter()
    7076             :  *
    7077             :  * Set the X,Y coordinates to use as center point (or label point?)
    7078             :  **********************************************************************/
    7079         173 : void TABMultiPoint::SetCenter(double dX, double dY)
    7080             : {
    7081         173 :     m_dCenterX = dX;
    7082         173 :     m_dCenterY = dY;
    7083         173 :     m_bCenterIsSet = TRUE;
    7084         173 : }
    7085             : 
    7086             : /**********************************************************************
    7087             :  *                   TABMultiPoint::DumpMIF()
    7088             :  *
    7089             :  * Dump feature geometry in a format similar to .MIF POINTs.
    7090             :  **********************************************************************/
    7091           0 : void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
    7092             : {
    7093           0 :     if (fpOut == nullptr)
    7094           0 :         fpOut = stdout;
    7095             : 
    7096             :     /*-----------------------------------------------------------------
    7097             :      * Fetch and validate geometry
    7098             :      *----------------------------------------------------------------*/
    7099           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7100           0 :     OGRMultiPoint *poMPoint = nullptr;
    7101           0 :     if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    7102           0 :         poMPoint = poGeom->toMultiPoint();
    7103             :     else
    7104             :     {
    7105           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7106             :                  "TABMultiPoint: Missing or Invalid Geometry!");
    7107           0 :         return;
    7108             :     }
    7109             : 
    7110             :     /*-----------------------------------------------------------------
    7111             :      * Generate output
    7112             :      *----------------------------------------------------------------*/
    7113           0 :     fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
    7114             : 
    7115           0 :     for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
    7116             :     {
    7117           0 :         poGeom = poMPoint->getGeometryRef(iPoint);
    7118             : 
    7119           0 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    7120             :         {
    7121           0 :             OGRPoint *poPoint = poGeom->toPoint();
    7122           0 :             fprintf(fpOut, "  %.15g %.15g\n", poPoint->getX(), poPoint->getY());
    7123             :         }
    7124             :         else
    7125             :         {
    7126           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    7127             :                      "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
    7128           0 :             return;
    7129             :         }
    7130             :     }
    7131             : 
    7132           0 :     DumpSymbolDef(fpOut);
    7133             : 
    7134           0 :     if (m_bCenterIsSet)
    7135           0 :         fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
    7136             : 
    7137           0 :     fflush(fpOut);
    7138             : }
    7139             : 
    7140             : /*=====================================================================
    7141             :  *                      class TABCollection
    7142             :  *====================================================================*/
    7143             : 
    7144             : /**********************************************************************
    7145             :  *                   TABCollection::TABCollection()
    7146             :  *
    7147             :  * Constructor.
    7148             :  **********************************************************************/
    7149         103 : TABCollection::TABCollection(const OGRFeatureDefn *poDefnIn)
    7150             :     : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
    7151         103 :       m_poMpoint(nullptr)
    7152             : {
    7153         103 : }
    7154             : 
    7155             : /**********************************************************************
    7156             :  *                   TABCollection::~TABCollection()
    7157             :  *
    7158             :  * Destructor.
    7159             :  **********************************************************************/
    7160         206 : TABCollection::~TABCollection()
    7161             : {
    7162         103 :     EmptyCollection();
    7163         206 : }
    7164             : 
    7165             : /**********************************************************************
    7166             :  *                   TABCollection::EmptyCollection()
    7167             :  *
    7168             :  * Delete/free all collection components.
    7169             :  **********************************************************************/
    7170         204 : void TABCollection::EmptyCollection()
    7171             : {
    7172             : 
    7173         204 :     if (m_poRegion)
    7174             :     {
    7175          54 :         delete m_poRegion;
    7176          54 :         m_poRegion = nullptr;
    7177             :     }
    7178             : 
    7179         204 :     if (m_poPline)
    7180             :     {
    7181          37 :         delete m_poPline;
    7182          37 :         m_poPline = nullptr;
    7183             :     }
    7184             : 
    7185         204 :     if (m_poMpoint)
    7186             :     {
    7187           6 :         delete m_poMpoint;
    7188           6 :         m_poMpoint = nullptr;
    7189             :     }
    7190             : 
    7191             :     // Empty OGR Geometry Collection as well
    7192         204 :     SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
    7193         204 : }
    7194             : 
    7195             : /**********************************************************************
    7196             :  *                     TABCollection::CloneTABFeature()
    7197             :  *
    7198             :  * Duplicate feature, including stuff specific to each TABFeature type.
    7199             :  *
    7200             :  * This method calls the generic TABFeature::CloneTABFeature() and
    7201             :  * then copies any members specific to its own type.
    7202             :  **********************************************************************/
    7203             : TABFeature *
    7204           0 : TABCollection::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
    7205             : {
    7206             :     /*-----------------------------------------------------------------
    7207             :      * Alloc new feature and copy the base stuff
    7208             :      *----------------------------------------------------------------*/
    7209             :     TABCollection *poNew =
    7210           0 :         new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
    7211             : 
    7212           0 :     CopyTABFeatureBase(poNew);
    7213             : 
    7214             :     /*-----------------------------------------------------------------
    7215             :      * And members specific to this class
    7216             :      *----------------------------------------------------------------*/
    7217             : 
    7218           0 :     if (m_poRegion)
    7219           0 :         poNew->SetRegionDirectly(
    7220           0 :             cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
    7221             : 
    7222           0 :     if (m_poPline)
    7223           0 :         poNew->SetPolylineDirectly(
    7224           0 :             cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
    7225             : 
    7226           0 :     if (m_poMpoint)
    7227           0 :         poNew->SetMultiPointDirectly(
    7228           0 :             cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
    7229             : 
    7230           0 :     return poNew;
    7231             : }
    7232             : 
    7233             : /**********************************************************************
    7234             :  *                   TABCollection::ValidateMapInfoType()
    7235             :  *
    7236             :  * Check the feature's geometry part and return the corresponding
    7237             :  * mapinfo object type code.  The m_nMapInfoType member will also
    7238             :  * be updated for further calls to GetMapInfoType();
    7239             :  *
    7240             :  * Returns TAB_GEOM_NONE if the geometry is not compatible with what
    7241             :  * is expected for this object class.
    7242             :  **********************************************************************/
    7243           0 : TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
    7244             : {
    7245           0 :     int nRegionType = TAB_GEOM_NONE;
    7246           0 :     int nPLineType = TAB_GEOM_NONE;
    7247           0 :     int nMPointType = TAB_GEOM_NONE;
    7248           0 :     int nVersion = 650;
    7249             : 
    7250             :     /*-----------------------------------------------------------------
    7251             :      * Fetch and validate geometry
    7252             :      *----------------------------------------------------------------*/
    7253           0 :     OGRGeometry *poGeom = GetGeometryRef();
    7254           0 :     if (poGeom &&
    7255           0 :         wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
    7256             :     {
    7257           0 :         m_nMapInfoType = TAB_GEOM_COLLECTION;
    7258             :     }
    7259             :     else
    7260             :     {
    7261           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    7262             :                  "TABCollection: Missing or Invalid Geometry!");
    7263           0 :         m_nMapInfoType = TAB_GEOM_NONE;
    7264             :     }
    7265             : 
    7266             :     /*-----------------------------------------------------------------
    7267             :      * Decide if coordinates should be compressed or not.
    7268             :      *----------------------------------------------------------------*/
    7269           0 :     GBool bComprCoord = ValidateCoordType(poMapFile);
    7270             : 
    7271             :     /*-----------------------------------------------------------------
    7272             :      * Since all members of the collection share the same compressed coord
    7273             :      * origin, we should force the compressed origin in all components
    7274             :      * to be the same.
    7275             :      * This also implies that ValidateMapInfoType() should *NOT* be called
    7276             :      * again until the collection components are written by WriteGeom...()
    7277             :      *----------------------------------------------------------------*/
    7278             : 
    7279             :     // First pass to figure collection type...
    7280           0 :     if (m_poRegion)
    7281             :     {
    7282           0 :         m_poRegion->ValidateCoordType(poMapFile);
    7283           0 :         nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
    7284           0 :         if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
    7285           0 :             nVersion = TAB_GEOM_GET_VERSION(nRegionType);
    7286             :     }
    7287             : 
    7288           0 :     if (m_poPline)
    7289             :     {
    7290           0 :         m_poPline->ValidateCoordType(poMapFile);
    7291           0 :         nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
    7292           0 :         if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
    7293           0 :             nVersion = TAB_GEOM_GET_VERSION(nPLineType);
    7294             :     }
    7295             : 
    7296           0 :     if (m_poMpoint)
    7297             :     {
    7298           0 :         m_poMpoint->ValidateCoordType(poMapFile);
    7299           0 :         nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
    7300           0 :         if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
    7301           0 :             nVersion = TAB_GEOM_GET_VERSION(nMPointType);
    7302             :     }
    7303             : 
    7304             :     // Need to upgrade native type of collection?
    7305           0 :     if (nVersion == 800)
    7306             :     {
    7307           0 :         m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
    7308             :     }
    7309             : 
    7310             :     // Make another pass updating native type and coordinates type and origin
    7311             :     // of each component
    7312           0 :     if (m_poRegion && nRegionType != TAB_GEOM_NONE)
    7313             :     {
    7314           0 :         GInt32 nXMin = 0;
    7315           0 :         GInt32 nYMin = 0;
    7316           0 :         GInt32 nXMax = 0;
    7317           0 :         GInt32 nYMax = 0;
    7318           0 :         m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7319           0 :         m_poRegion->ForceCoordTypeAndOrigin(
    7320             :             (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
    7321             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7322             :             nYMax);
    7323             :     }
    7324             : 
    7325           0 :     if (m_poPline && nPLineType != TAB_GEOM_NONE)
    7326             :     {
    7327             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    7328           0 :         m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7329           0 :         m_poPline->ForceCoordTypeAndOrigin(
    7330             :             (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
    7331             :                              : TAB_GEOM_V450_MULTIPLINE),
    7332             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7333             :             nYMax);
    7334             :     }
    7335             : 
    7336           0 :     if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
    7337             :     {
    7338             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    7339           0 :         m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
    7340           0 :         m_poMpoint->ForceCoordTypeAndOrigin(
    7341             :             (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
    7342             :             bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
    7343             :             nYMax);
    7344             :     }
    7345             : 
    7346           0 :     return m_nMapInfoType;
    7347             : }
    7348             : 
    7349             : /**********************************************************************
    7350             :  *                   TABCollection::ReadLabelAndMBR()
    7351             :  *
    7352             :  * Reads the label and MBR elements of the header of a collection component
    7353             :  *
    7354             :  * Returns 0 on success, -1 on failure.
    7355             :  **********************************************************************/
    7356          12 : int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
    7357             :                                    GBool bComprCoord, GInt32 nComprOrgX,
    7358             :                                    GInt32 nComprOrgY, GInt32 &pnMinX,
    7359             :                                    GInt32 &pnMinY, GInt32 &pnMaxX,
    7360             :                                    GInt32 &pnMaxY, GInt32 &pnLabelX,
    7361             :                                    GInt32 &pnLabelY)
    7362             : {
    7363             :     //
    7364             :     // The sections in the collection's coord blocks start with center/label
    7365             :     // point + MBR that are normally found in the object data blocks
    7366             :     // of regular region/pline/mulitpoint objects.
    7367             :     //
    7368             : 
    7369          12 :     if (bComprCoord)
    7370             :     {
    7371             :         // Region center/label point, relative to compr. coord. origin
    7372             :         // No it is not relative to the Object block center
    7373          12 :         pnLabelX = poCoordBlock->ReadInt16();
    7374          12 :         pnLabelY = poCoordBlock->ReadInt16();
    7375             : 
    7376          12 :         TABSaturatedAdd(pnLabelX, nComprOrgX);
    7377          12 :         TABSaturatedAdd(pnLabelY, nComprOrgY);
    7378             : 
    7379          12 :         pnMinX = poCoordBlock->ReadInt16();  // Read MBR
    7380          12 :         pnMinY = poCoordBlock->ReadInt16();
    7381          12 :         pnMaxX = poCoordBlock->ReadInt16();
    7382          12 :         pnMaxY = poCoordBlock->ReadInt16();
    7383          12 :         TABSaturatedAdd(pnMinX, nComprOrgX);
    7384          12 :         TABSaturatedAdd(pnMinY, nComprOrgY);
    7385          12 :         TABSaturatedAdd(pnMaxX, nComprOrgX);
    7386          12 :         TABSaturatedAdd(pnMaxY, nComprOrgY);
    7387             :     }
    7388             :     else
    7389             :     {
    7390             :         // Region center/label point, relative to compr. coord. origin
    7391             :         // No it is not relative to the Object block center
    7392           0 :         pnLabelX = poCoordBlock->ReadInt32();
    7393           0 :         pnLabelY = poCoordBlock->ReadInt32();
    7394             : 
    7395           0 :         pnMinX = poCoordBlock->ReadInt32();  // Read MBR
    7396           0 :         pnMinY = poCoordBlock->ReadInt32();
    7397           0 :         pnMaxX = poCoordBlock->ReadInt32();
    7398           0 :         pnMaxY = poCoordBlock->ReadInt32();
    7399             :     }
    7400             : 
    7401          12 :     return 0;
    7402             : }
    7403             : 
    7404             : /**********************************************************************
    7405             :  *                   TABCollection::WriteLabelAndMBR()
    7406             :  *
    7407             :  * Writes the label and MBR elements of the header of a collection component
    7408             :  *
    7409             :  * Returns 0 on success, -1 on failure.
    7410             :  **********************************************************************/
    7411           0 : int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
    7412             :                                     GBool bComprCoord, GInt32 nMinX,
    7413             :                                     GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
    7414             :                                     GInt32 nLabelX, GInt32 nLabelY)
    7415             : {
    7416             :     //
    7417             :     // The sections in the collection's coord blocks start with center/label
    7418             :     // point + MBR that are normally found in the object data blocks
    7419             :     // of regular region/pline/mulitpoint objects.
    7420             :     //
    7421             : 
    7422           0 :     int nStatus = 0;
    7423           0 :     if ((nStatus =
    7424           0 :              poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
    7425           0 :         (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
    7426           0 :             0 ||
    7427           0 :         (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
    7428             :     {
    7429             :         // Failed ... error message has already been produced
    7430           0 :         return nStatus;
    7431             :     }
    7432             : 
    7433           0 :     return 0;
    7434             : }
    7435             : 
    7436             : /**********************************************************************
    7437             :  *                   TABCollection::ReadGeometryFromMAPFile()
    7438             :  *
    7439             :  * Fill the geometry and representation (color, etc...) part of the
    7440             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    7441             :  *
    7442             :  * It is assumed that poMAPFile currently points to the beginning of
    7443             :  * a map object.
    7444             :  *
    7445             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    7446             :  * been called.
    7447             :  **********************************************************************/
    7448           4 : int TABCollection::ReadGeometryFromMAPFile(
    7449             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    7450             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    7451             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    7452             : {
    7453           4 :     const GBool bComprCoord = poObjHdr->IsCompressedType();
    7454             : 
    7455             :     /*-----------------------------------------------------------------
    7456             :      * Fetch and validate geometry type
    7457             :      *----------------------------------------------------------------*/
    7458           4 :     m_nMapInfoType = poObjHdr->m_nType;
    7459             : 
    7460           4 :     if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
    7461           4 :         m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
    7462           0 :         m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
    7463           0 :         m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
    7464             :     {
    7465           0 :         CPLError(
    7466             :             CE_Failure, CPLE_AssertionFailed,
    7467             :             "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
    7468           0 :             m_nMapInfoType, m_nMapInfoType);
    7469           0 :         return -1;
    7470             :     }
    7471             : 
    7472           4 :     int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    7473             : 
    7474             :     // Make sure collection is empty
    7475           4 :     EmptyCollection();
    7476             : 
    7477             :     /*-------------------------------------------------------------
    7478             :      * Copy data from poObjHdr
    7479             :      *------------------------------------------------------------*/
    7480             :     TABMAPObjCollection *poCollHdr =
    7481           4 :         cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
    7482             : 
    7483             :     // MBR
    7484           4 :     double dXMin = 0.0;
    7485           4 :     double dYMin = 0.0;
    7486           4 :     double dXMax = 0.0;
    7487           4 :     double dYMax = 0.0;
    7488           4 :     poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
    7489             :                             dYMin);
    7490           4 :     poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
    7491             :                             dYMax);
    7492             : 
    7493           4 :     SetMBR(dXMin, dYMin, dXMax, dYMax);
    7494             : 
    7495           4 :     SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    7496             :               poObjHdr->m_nMaxY);
    7497             : 
    7498           4 :     int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
    7499           4 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    7500           4 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    7501           0 :         poCoordBlock = *ppoCoordBlock;
    7502             :     else
    7503           4 :         poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
    7504             : 
    7505             :     // Compressed coordinate origin (useful only in compressed case!)
    7506           4 :     m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7507           4 :     m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7508             : 
    7509             :     /*-----------------------------------------------------------------
    7510             :      * Region Component
    7511             :      *----------------------------------------------------------------*/
    7512           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
    7513             :     {
    7514             :         //
    7515             :         // Build fake coord section header to pass to TABRegion::ReadGeom...()
    7516             :         //
    7517           4 :         TABMAPObjPLine oRegionHdr;
    7518             : 
    7519           4 :         oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7520           4 :         oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7521             : 
    7522             :         //
    7523             :         // The region section in the coord block starts with center/label
    7524             :         // point + MBR that are normally found in the object data blocks
    7525             :         // of regular region objects.
    7526             :         //
    7527             : 
    7528             :         // In V800 the mini-header starts with a copy of num_parts
    7529           4 :         if (nVersion >= 800)
    7530             :         {
    7531             :             // int numParts = poCoordBlock->ReadInt32();
    7532           0 :             CPLAssert(poCoordBlock->ReadInt32() ==
    7533             :                       poCollHdr->m_nNumRegSections);
    7534             :         }
    7535             : 
    7536           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
    7537             :                         oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
    7538             :                         oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
    7539             :                         oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
    7540             :                         oRegionHdr.m_nLabelY);
    7541             : 
    7542             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7543           4 :         oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7544             : 
    7545           4 :         if (bComprCoord)
    7546           4 :             oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
    7547             :         else
    7548           0 :             oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
    7549           4 :         if (nVersion == 800)
    7550           0 :             oRegionHdr.m_nType = static_cast<TABGeomType>(
    7551           0 :                 oRegionHdr.m_nType +
    7552             :                 (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
    7553             : 
    7554           4 :         oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
    7555           4 :         oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
    7556           4 :         oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
    7557           4 :         oRegionHdr.m_bSmooth = 0;  // TODO
    7558             : 
    7559             :         //
    7560             :         // Use a TABRegion to read/store the Region coord data
    7561             :         //
    7562           4 :         m_poRegion = new TABRegion(GetDefnRef());
    7563           4 :         if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
    7564             :                                                 bCoordBlockDataOnly,
    7565           4 :                                                 &poCoordBlock) != 0)
    7566           0 :             return -1;
    7567             : 
    7568             :         // Set new coord block ptr for next object
    7569             :         /*if (poCoordBlock)
    7570             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7571             :     }
    7572             : 
    7573             :     /*-----------------------------------------------------------------
    7574             :      * PLine Component
    7575             :      *----------------------------------------------------------------*/
    7576           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
    7577             :     {
    7578             :         //
    7579             :         // Build fake coord section header to pass to TABPolyline::ReadGeom..()
    7580             :         //
    7581           4 :         TABMAPObjPLine oPLineHdr;
    7582             : 
    7583           4 :         oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7584           4 :         oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7585             : 
    7586             :         //
    7587             :         // The pline section in the coord block starts with center/label
    7588             :         // point + MBR that are normally found in the object data blocks
    7589             :         // of regular pline objects.
    7590             :         //
    7591             : 
    7592             :         // In V800 the mini-header starts with a copy of num_parts
    7593           4 :         if (nVersion >= 800)
    7594             :         {
    7595             :             // int numParts = poCoordBlock->ReadInt32();
    7596           0 :             CPLAssert(poCoordBlock->ReadInt32() ==
    7597             :                       poCollHdr->m_nNumPLineSections);
    7598             :         }
    7599             : 
    7600           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
    7601             :                         oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
    7602             :                         oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
    7603             :                         oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
    7604             : 
    7605             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7606           4 :         oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7607             : 
    7608           4 :         if (bComprCoord)
    7609           4 :             oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
    7610             :         else
    7611           0 :             oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
    7612           4 :         if (nVersion == 800)
    7613           0 :             oPLineHdr.m_nType = static_cast<TABGeomType>(
    7614           0 :                 oPLineHdr.m_nType +
    7615             :                 (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
    7616             : 
    7617           4 :         oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
    7618           4 :         oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
    7619           4 :         oPLineHdr.m_bSmooth = 0;  // TODO
    7620             : 
    7621             :         //
    7622             :         // Use a TABPolyline to read/store the Polyline coord data
    7623             :         //
    7624           4 :         m_poPline = new TABPolyline(GetDefnRef());
    7625           4 :         if (m_poPline->ReadGeometryFromMAPFile(
    7626           4 :                 poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
    7627           0 :             return -1;
    7628             : 
    7629             :         // Set new coord block ptr for next object
    7630             :         /*if (poCoordBlock)
    7631             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7632             :     }
    7633             : 
    7634             :     /*-----------------------------------------------------------------
    7635             :      * MultiPoint Component
    7636             :      *----------------------------------------------------------------*/
    7637           4 :     if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
    7638             :     {
    7639             :         //
    7640             :         // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
    7641             :         //
    7642           4 :         TABMAPObjMultiPoint oMPointHdr;
    7643             : 
    7644           4 :         oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
    7645           4 :         oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
    7646             : 
    7647             :         //
    7648             :         // The pline section in the coord block starts with center/label
    7649             :         // point + MBR that are normally found in the object data blocks
    7650             :         // of regular pline objects.
    7651             :         //
    7652           4 :         ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
    7653             :                         oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
    7654             :                         oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
    7655             :                         oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
    7656             :                         oMPointHdr.m_nLabelY);
    7657             : 
    7658             :         // Set CoordBlockPtr so that TABRegion continues reading here
    7659           4 :         oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7660             : 
    7661           4 :         if (bComprCoord)
    7662           4 :             oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
    7663             :         else
    7664           0 :             oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
    7665           4 :         if (nVersion == 800)
    7666           0 :             oMPointHdr.m_nType = static_cast<TABGeomType>(
    7667           0 :                 oMPointHdr.m_nType +
    7668             :                 (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
    7669             : 
    7670           4 :         oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
    7671           4 :         oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
    7672             : 
    7673             :         //
    7674             :         // Use a TABMultiPoint to read/store the coord data
    7675             :         //
    7676           4 :         m_poMpoint = new TABMultiPoint(GetDefnRef());
    7677           4 :         if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
    7678             :                                                 bCoordBlockDataOnly,
    7679           4 :                                                 &poCoordBlock) != 0)
    7680           0 :             return -1;
    7681             : 
    7682             :         // Set new coord block ptr for next object (not really useful here)
    7683             :         /*if (poCoordBlock)
    7684             :             nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
    7685             :     }
    7686             : 
    7687             :     /*-----------------------------------------------------------------
    7688             :      * Set the main OGRFeature Geometry
    7689             :      * (this is actually duplicating geometries from each member)
    7690             :      *----------------------------------------------------------------*/
    7691           4 :     if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
    7692           0 :         return -1;
    7693             : 
    7694             :     /* Return a ref to coord block so that caller can continue reading
    7695             :      * after the end of this object (used by index splitting)
    7696             :      */
    7697           4 :     if (ppoCoordBlock)
    7698           0 :         *ppoCoordBlock = poCoordBlock;
    7699             : 
    7700           4 :     return 0;
    7701             : }
    7702             : 
    7703             : /**********************************************************************
    7704             :  *                   TABCollection::WriteGeometryToMAPFile()
    7705             :  *
    7706             :  * Write the geometry and representation (color, etc...) part of the
    7707             :  * feature to the .MAP object pointed to by poMAPFile.
    7708             :  *
    7709             :  * It is assumed that poMAPFile currently points to a valid map object.
    7710             :  *
    7711             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    7712             :  * been called.
    7713             :  **********************************************************************/
    7714           0 : int TABCollection::WriteGeometryToMAPFile(
    7715             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    7716             :     GBool bCoordBlockDataOnly /*=FALSE*/,
    7717             :     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
    7718             : {
    7719             :     /*-----------------------------------------------------------------
    7720             :      * Note that the current implementation does not allow setting the
    7721             :      * Geometry via OGRFeature::SetGeometry(). The geometries must be set
    7722             :      * via the SetRegion/Pline/MpointDirectly() methods which will take
    7723             :      * care of keeping the OGRFeature's geometry in sync.
    7724             :      *
    7725             :      * TODO: If we ever want to support sync'ing changes from the OGRFeature's
    7726             :      * geometry to the m_poRegion/Pline/Mpoint then a call should be added
    7727             :      * here, or perhaps in ValidateMapInfoType(), or even better in
    7728             :      * custom TABCollection::SetGeometry*()... but then this last option
    7729             :      * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
    7730             :      *----------------------------------------------------------------*/
    7731             : 
    7732             :     /*-----------------------------------------------------------------
    7733             :      * We assume that ValidateMapInfoType() was called already and that
    7734             :      * the type in poObjHdr->m_nType is valid.
    7735             :      *----------------------------------------------------------------*/
    7736           0 :     CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
    7737             : 
    7738             :     TABMAPObjCollection *poCollHdr =
    7739           0 :         cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
    7740             : 
    7741             :     /*-----------------------------------------------------------------
    7742             :      * Write data to coordinate block for each component...
    7743             :      *
    7744             :      * Note that at this point, the caller (TABFile) has called
    7745             :      * TABCollection::ValidateMapInfoType() which in turn has called
    7746             :      * each component's respective ValidateMapInfoType() and
    7747             :      * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
    7748             :      * their respective WriteGeometryToMapFile() called.
    7749             :      *----------------------------------------------------------------*/
    7750           0 :     const GBool bCompressed = poObjHdr->IsCompressedType();
    7751             :     // TODO: ??? Do we need to track overall collection coord data size???
    7752           0 :     int nTotalFeatureDataSize = 0;
    7753             : 
    7754           0 :     const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
    7755             : 
    7756           0 :     TABMAPCoordBlock *poCoordBlock = nullptr;
    7757           0 :     if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
    7758           0 :         poCoordBlock = *ppoCoordBlock;
    7759             :     else
    7760           0 :         poCoordBlock = poMapFile->GetCurCoordBlock();
    7761           0 :     poCoordBlock->StartNewFeature();
    7762           0 :     poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
    7763           0 :     poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
    7764             : 
    7765             :     /*-----------------------------------------------------------------
    7766             :      * Region component
    7767             :      *----------------------------------------------------------------*/
    7768           0 :     if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
    7769             :     {
    7770           0 :         CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
    7771             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
    7772             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
    7773             :                   m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
    7774             : 
    7775           0 :         TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
    7776           0 :             TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
    7777             : 
    7778             :         // Update count of objects by type in header
    7779           0 :         if (!bCoordBlockDataOnly)
    7780           0 :             poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
    7781             : 
    7782             :         // Write a placeholder for centroid/label point and MBR mini-header
    7783             :         // and we'll come back later to write the real values.
    7784             :         //
    7785             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7786             :         // StartNewFeature() as well, so we need to track the current
    7787             :         // value before calling it
    7788             : 
    7789           0 :         poCoordBlock->StartNewFeature();
    7790           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7791             : 
    7792             :         // In V800 the mini-header starts with a copy of num_parts
    7793           0 :         if (nVersion >= 800)
    7794             :         {
    7795           0 :             poCoordBlock->WriteInt32(0);
    7796             :         }
    7797           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7798           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7799             : 
    7800           0 :         if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
    7801             :                                                bCoordBlockDataOnly,
    7802           0 :                                                &poCoordBlock) != 0)
    7803             :         {
    7804           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7805             :                      "Failed writing Region part in collection.");
    7806           0 :             delete poRegionHdr;
    7807           0 :             return -1;
    7808             :         }
    7809             : 
    7810           0 :         nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
    7811             : 
    7812             :         // Come back to write the real values in the mini-header
    7813           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7814           0 :         poCoordBlock->StartNewFeature();
    7815             : 
    7816           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7817             :         {
    7818           0 :             delete poRegionHdr;
    7819           0 :             return -1;
    7820             :         }
    7821             : 
    7822             :         // In V800 the mini-header starts with a copy of num_parts
    7823           0 :         if (nVersion >= 800)
    7824             :         {
    7825           0 :             poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
    7826             :         }
    7827           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
    7828             :                          poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
    7829             :                          poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
    7830             :                          poRegionHdr->m_nLabelY);
    7831             : 
    7832             :         // And finally move the pointer back to the end of this component
    7833           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    7834             :         {
    7835           0 :             delete poRegionHdr;
    7836           0 :             return -1;
    7837             :         }
    7838             : 
    7839             :         // Copy other header members to the main collection header
    7840             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    7841             :         //       mini-header???
    7842           0 :         poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
    7843           0 :         poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
    7844             : 
    7845           0 :         if (!bCoordBlockDataOnly)
    7846             :         {
    7847           0 :             poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
    7848           0 :             poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
    7849             :             // TODO: Smooth flag         = poRegionHdr->m_bSmooth;
    7850             :         }
    7851             : 
    7852           0 :         delete poRegionHdr;
    7853             :     }
    7854             :     else
    7855             :     {
    7856             :         // No Region component. Set corresponding header fields to 0
    7857             : 
    7858           0 :         poCollHdr->m_nRegionDataSize = 0;
    7859           0 :         poCollHdr->m_nNumRegSections = 0;
    7860           0 :         poCollHdr->m_nRegionPenId = 0;
    7861           0 :         poCollHdr->m_nRegionBrushId = 0;
    7862             :     }
    7863             : 
    7864             :     /*-----------------------------------------------------------------
    7865             :      * PLine component
    7866             :      *----------------------------------------------------------------*/
    7867           0 :     if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
    7868             :     {
    7869           0 :         CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
    7870             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
    7871             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
    7872             :                   m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
    7873             : 
    7874           0 :         TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
    7875           0 :             TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
    7876             : 
    7877             :         // Update count of objects by type in header
    7878           0 :         if (!bCoordBlockDataOnly)
    7879           0 :             poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
    7880             : 
    7881             :         // Write a placeholder for centroid/label point and MBR mini-header
    7882             :         // and we'll come back later to write the real values.
    7883             :         //
    7884             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7885             :         // StartNewFeature() as well, so we need to track the current
    7886             :         // value before calling it
    7887             : 
    7888           0 :         poCoordBlock->StartNewFeature();
    7889           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7890             : 
    7891             :         // In V800 the mini-header starts with a copy of num_parts
    7892           0 :         if (nVersion >= 800)
    7893             :         {
    7894           0 :             poCoordBlock->WriteInt32(0);
    7895             :         }
    7896           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7897           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7898             : 
    7899           0 :         if (m_poPline->WriteGeometryToMAPFile(
    7900           0 :                 poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
    7901             :         {
    7902           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7903             :                      "Failed writing Region part in collection.");
    7904           0 :             delete poPlineHdr;
    7905           0 :             return -1;
    7906             :         }
    7907             : 
    7908           0 :         nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
    7909             : 
    7910             :         // Come back to write the real values in the mini-header
    7911           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    7912           0 :         poCoordBlock->StartNewFeature();
    7913             : 
    7914           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    7915             :         {
    7916           0 :             delete poPlineHdr;
    7917           0 :             return -1;
    7918             :         }
    7919             : 
    7920             :         // In V800 the mini-header starts with a copy of num_parts
    7921           0 :         if (nVersion >= 800)
    7922             :         {
    7923           0 :             poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
    7924             :         }
    7925           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
    7926             :                          poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
    7927             :                          poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
    7928             :                          poPlineHdr->m_nLabelY);
    7929             : 
    7930             :         // And finally move the pointer back to the end of this component
    7931           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    7932             :         {
    7933           0 :             delete poPlineHdr;
    7934           0 :             return -1;
    7935             :         }
    7936             : 
    7937             :         // Copy other header members to the main collection header
    7938             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    7939             :         //       mini-header???
    7940           0 :         poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
    7941           0 :         poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
    7942           0 :         if (!bCoordBlockDataOnly)
    7943             :         {
    7944           0 :             poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
    7945             :             // TODO: Smooth flag           = poPlineHdr->m_bSmooth;
    7946             :         }
    7947             : 
    7948           0 :         delete poPlineHdr;
    7949             :     }
    7950             :     else
    7951             :     {
    7952             :         // No Polyline component. Set corresponding header fields to 0
    7953             : 
    7954           0 :         poCollHdr->m_nPolylineDataSize = 0;
    7955           0 :         poCollHdr->m_nNumPLineSections = 0;
    7956           0 :         poCollHdr->m_nPolylinePenId = 0;
    7957             :     }
    7958             : 
    7959             :     /*-----------------------------------------------------------------
    7960             :      * MultiPoint component
    7961             :      *----------------------------------------------------------------*/
    7962           0 :     if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
    7963             :     {
    7964           0 :         CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
    7965             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
    7966             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
    7967             :                   m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
    7968             : 
    7969             :         TABMAPObjMultiPoint *poMpointHdr =
    7970           0 :             cpl::down_cast<TABMAPObjMultiPoint *>(
    7971           0 :                 TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
    7972             : 
    7973             :         // Update count of objects by type in header
    7974           0 :         if (!bCoordBlockDataOnly)
    7975           0 :             poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
    7976             : 
    7977             :         // Write a placeholder for centroid/label point and MBR mini-header
    7978             :         // and we'll come back later to write the real values.
    7979             :         //
    7980             :         // Note that the call to WriteGeometryToMAPFile() below will call
    7981             :         // StartNewFeature() as well, so we need to track the current
    7982             :         // value before calling it
    7983             : 
    7984           0 :         poCoordBlock->StartNewFeature();
    7985           0 :         int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
    7986             : 
    7987           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
    7988           0 :         nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
    7989             : 
    7990           0 :         if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
    7991             :                                                bCoordBlockDataOnly,
    7992           0 :                                                &poCoordBlock) != 0)
    7993             :         {
    7994           0 :             CPLError(CE_Failure, CPLE_FileIO,
    7995             :                      "Failed writing Region part in collection.");
    7996           0 :             delete poMpointHdr;
    7997           0 :             return -1;
    7998             :         }
    7999             : 
    8000           0 :         nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
    8001             : 
    8002             :         // Come back to write the real values in the mini-header
    8003           0 :         int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
    8004           0 :         poCoordBlock->StartNewFeature();
    8005             : 
    8006           0 :         if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
    8007             :         {
    8008           0 :             delete poMpointHdr;
    8009           0 :             return -1;
    8010             :         }
    8011             : 
    8012           0 :         WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
    8013             :                          poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
    8014             :                          poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
    8015             :                          poMpointHdr->m_nLabelY);
    8016             : 
    8017             :         // And finally move the pointer back to the end of this component
    8018           0 :         if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
    8019             :         {
    8020           0 :             delete poMpointHdr;
    8021           0 :             return -1;
    8022             :         }
    8023             : 
    8024             :         // Copy other header members to the main collection header
    8025             :         // TODO: Does m_nRegionDataSize need to include the centroid+mbr
    8026             :         //       mini-header???
    8027           0 :         poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
    8028           0 :         poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
    8029           0 :         if (!bCoordBlockDataOnly)
    8030             :         {
    8031           0 :             poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
    8032             :         }
    8033             : 
    8034           0 :         delete poMpointHdr;
    8035             :     }
    8036             :     else
    8037             :     {
    8038             :         // No Multipoint component. Set corresponding header fields to 0
    8039             : 
    8040           0 :         poCollHdr->m_nMPointDataSize = 0;
    8041           0 :         poCollHdr->m_nNumMultiPoints = 0;
    8042           0 :         poCollHdr->m_nMultiPointSymbolId = 0;
    8043             :     }
    8044             : 
    8045             :     /*-----------------------------------------------------------------
    8046             :      * Copy object information
    8047             :      *----------------------------------------------------------------*/
    8048             : 
    8049             :     // Compressed coordinate origin (useful only in compressed case!)
    8050           0 :     poCollHdr->m_nComprOrgX = m_nComprOrgX;
    8051           0 :     poCollHdr->m_nComprOrgY = m_nComprOrgY;
    8052             : 
    8053           0 :     poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
    8054             : 
    8055           0 :     poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
    8056             : 
    8057           0 :     if (CPLGetLastErrorType() == CE_Failure)
    8058           0 :         return -1;
    8059             : 
    8060             :     /* Return a ref to coord block so that caller can continue writing
    8061             :      * after the end of this object (used by index splitting)
    8062             :      */
    8063           0 :     if (ppoCoordBlock)
    8064           0 :         *ppoCoordBlock = poCoordBlock;
    8065             : 
    8066           0 :     return 0;
    8067             : }
    8068             : 
    8069             : /**********************************************************************
    8070             :  *                   TABCollection::SyncOGRGeometryCollection()
    8071             :  *
    8072             :  * Copy the region/pline/multipoint's geometries to the OGRFeature's
    8073             :  * geometry.
    8074             :  **********************************************************************/
    8075         208 : int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
    8076             :                                              GBool bSyncPline,
    8077             :                                              GBool bSyncMpoint)
    8078             : {
    8079         208 :     OGRGeometry *poThisGeom = GetGeometryRef();
    8080         208 :     OGRGeometryCollection *poGeomColl = nullptr;
    8081             : 
    8082             :     // poGeometry is defined in the OGRFeature class
    8083         208 :     if (poThisGeom == nullptr)
    8084             :     {
    8085         103 :         poGeomColl = new OGRGeometryCollection();
    8086             :     }
    8087         105 :     else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
    8088             :     {
    8089         105 :         poGeomColl = poThisGeom->toGeometryCollection();
    8090             :     }
    8091             :     else
    8092             :     {
    8093           0 :         CPLError(
    8094             :             CE_Failure, CPLE_AssertionFailed,
    8095             :             "TABCollection: Invalid Geometry. Type must be OGRCollection.");
    8096           0 :         return -1;
    8097             :     }
    8098             : 
    8099             :     /*-----------------------------------------------------------------
    8100             :      * Start by removing geometries that need to be replaced
    8101             :      * In theory there should be a single geometry of each type, but
    8102             :      * just in case, we'll loop over the whole collection and delete all
    8103             :      * instances of each type if there are some.
    8104             :      *----------------------------------------------------------------*/
    8105         208 :     int numGeometries = poGeomColl->getNumGeometries();
    8106         220 :     for (int i = 0; i < numGeometries; i++)
    8107             :     {
    8108          12 :         OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
    8109          12 :         if (!poGeom)
    8110           0 :             continue;
    8111             : 
    8112          24 :         if ((bSyncRegion &&
    8113          12 :              (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
    8114           6 :               wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
    8115           6 :             (bSyncPline &&
    8116           6 :              (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
    8117          30 :               wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
    8118           6 :             (bSyncMpoint &&
    8119           6 :              (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
    8120             :         {
    8121             :             // Remove this geometry
    8122          12 :             poGeomColl->removeGeometry(i);
    8123             : 
    8124             :             // Unless this was the last geometry, we need to restart
    8125             :             // scanning the collection since we modified it
    8126          12 :             if (i != numGeometries - 1)
    8127             :             {
    8128           6 :                 i = 0;
    8129           6 :                 numGeometries = poGeomColl->getNumGeometries();
    8130             :             }
    8131             :         }
    8132             :     }
    8133             : 
    8134             :     /*-----------------------------------------------------------------
    8135             :      * Copy TAB Feature geometries to OGRGeometryCollection
    8136             :      *----------------------------------------------------------------*/
    8137         208 :     if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
    8138           4 :         poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
    8139             : 
    8140         208 :     if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
    8141           4 :         poGeomColl->addGeometry(m_poPline->GetGeometryRef());
    8142             : 
    8143         208 :     if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
    8144           4 :         poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
    8145             : 
    8146         208 :     if (poThisGeom == nullptr)
    8147         103 :         SetGeometryDirectly(poGeomColl);
    8148             : 
    8149         208 :     return 0;
    8150             : }
    8151             : 
    8152             : /**********************************************************************
    8153             :  *                   TABCollection::SetRegionDirectly()
    8154             :  *
    8155             :  * Set the region component of the collection, deleting the current
    8156             :  * region component if there is one. The object is then owned by the
    8157             :  * TABCollection object. Passing NULL just deletes it.
    8158             :  *
    8159             :  * Note that an intentional side-effect is that calling this method
    8160             :  * with the same poRegion pointer that is already owned by this object
    8161             :  * will force resync'ing the OGR Geometry member.
    8162             :  **********************************************************************/
    8163           0 : int TABCollection::SetRegionDirectly(TABRegion *poRegion)
    8164             : {
    8165           0 :     if (m_poRegion && m_poRegion != poRegion)
    8166           0 :         delete m_poRegion;
    8167           0 :     m_poRegion = poRegion;
    8168             : 
    8169             :     // Update OGRGeometryCollection component as well
    8170           0 :     return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
    8171             : }
    8172             : 
    8173             : /**********************************************************************
    8174             :  *                   TABCollection::SetPolylineDirectly()
    8175             :  *
    8176             :  * Set the polyline component of the collection, deleting the current
    8177             :  * polyline component if there is one. The object is then owned by the
    8178             :  * TABCollection object. Passing NULL just deletes it.
    8179             :  *
    8180             :  * Note that an intentional side-effect is that calling this method
    8181             :  * with the same poPline pointer that is already owned by this object
    8182             :  * will force resync'ing the OGR Geometry member.
    8183             :  **********************************************************************/
    8184           0 : int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
    8185             : {
    8186           0 :     if (m_poPline && m_poPline != poPline)
    8187           0 :         delete m_poPline;
    8188           0 :     m_poPline = poPline;
    8189             : 
    8190             :     // Update OGRGeometryCollection component as well
    8191           0 :     return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
    8192             : }
    8193             : 
    8194             : /**********************************************************************
    8195             :  *                   TABCollection::SetMultiPointDirectly()
    8196             :  *
    8197             :  * Set the multipoint component of the collection, deleting the current
    8198             :  * multipoint component if there is one. The object is then owned by the
    8199             :  * TABCollection object. Passing NULL just deletes it.
    8200             :  *
    8201             :  * Note that an intentional side-effect is that calling this method
    8202             :  * with the same poMpoint pointer that is already owned by this object
    8203             :  * will force resync'ing the OGR Geometry member.
    8204             :  **********************************************************************/
    8205           0 : int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
    8206             : {
    8207           0 :     if (m_poMpoint && m_poMpoint != poMpoint)
    8208           0 :         delete m_poMpoint;
    8209           0 :     m_poMpoint = poMpoint;
    8210             : 
    8211             :     // Update OGRGeometryCollection component as well
    8212           0 :     return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
    8213             : }
    8214             : 
    8215             : /**********************************************************************
    8216             :  *                   TABCollection::GetStyleString() const
    8217             :  *
    8218             :  * Return style string for this feature.
    8219             :  *
    8220             :  * Style String is built only once during the first call to GetStyleString().
    8221             :  **********************************************************************/
    8222           3 : const char *TABCollection::GetStyleString() const
    8223             : {
    8224           3 :     if (m_pszStyleString == nullptr)
    8225             :     {
    8226           3 :         m_pszStyleString = CPLStrdup(GetSymbolStyleString());
    8227             :     }
    8228             : 
    8229           3 :     return m_pszStyleString;
    8230             : }
    8231             : 
    8232             : /**********************************************************************
    8233             :  *                   TABCollection::DumpMIF()
    8234             :  *
    8235             :  * Dump feature geometry
    8236             :  **********************************************************************/
    8237           0 : void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
    8238             : {
    8239           0 :     if (fpOut == nullptr)
    8240           0 :         fpOut = stdout;
    8241             : 
    8242             :     /*-----------------------------------------------------------------
    8243             :      * Generate output
    8244             :      *----------------------------------------------------------------*/
    8245           0 :     int numParts = 0;
    8246           0 :     if (m_poRegion)
    8247           0 :         numParts++;
    8248           0 :     if (m_poPline)
    8249           0 :         numParts++;
    8250           0 :     if (m_poMpoint)
    8251           0 :         numParts++;
    8252             : 
    8253           0 :     fprintf(fpOut, "COLLECTION %d\n", numParts);
    8254             : 
    8255           0 :     if (m_poRegion)
    8256           0 :         m_poRegion->DumpMIF(fpOut);
    8257             : 
    8258           0 :     if (m_poPline)
    8259           0 :         m_poPline->DumpMIF(fpOut);
    8260             : 
    8261           0 :     if (m_poMpoint)
    8262           0 :         m_poMpoint->DumpMIF(fpOut);
    8263             : 
    8264           0 :     DumpSymbolDef(fpOut);
    8265             : 
    8266           0 :     fflush(fpOut);
    8267           0 : }
    8268             : 
    8269             : /*=====================================================================
    8270             :  *                      class TABDebugFeature
    8271             :  *====================================================================*/
    8272             : 
    8273             : /**********************************************************************
    8274             :  *                   TABDebugFeature::TABDebugFeature()
    8275             :  *
    8276             :  * Constructor.
    8277             :  **********************************************************************/
    8278           0 : TABDebugFeature::TABDebugFeature(const OGRFeatureDefn *poDefnIn)
    8279           0 :     : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
    8280             : {
    8281           0 :     memset(m_abyBuf, 0, sizeof(m_abyBuf));
    8282           0 : }
    8283             : 
    8284             : /**********************************************************************
    8285             :  *                   TABDebugFeature::~TABDebugFeature()
    8286             :  *
    8287             :  * Destructor.
    8288             :  **********************************************************************/
    8289           0 : TABDebugFeature::~TABDebugFeature()
    8290             : {
    8291           0 : }
    8292             : 
    8293             : /**********************************************************************
    8294             :  *                   TABDebugFeature::ReadGeometryFromMAPFile()
    8295             :  *
    8296             :  * Fill the geometry and representation (color, etc...) part of the
    8297             :  * feature from the contents of the .MAP object pointed to by poMAPFile.
    8298             :  *
    8299             :  * It is assumed that poMAPFile currently points to the beginning of
    8300             :  * a map object.
    8301             :  *
    8302             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    8303             :  * been called.
    8304             :  **********************************************************************/
    8305           0 : int TABDebugFeature::ReadGeometryFromMAPFile(
    8306             :     TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
    8307             :     GBool /*bCoordBlockDataOnly=FALSE*/,
    8308             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    8309             : {
    8310             :     /*-----------------------------------------------------------------
    8311             :      * Fetch geometry type
    8312             :      *----------------------------------------------------------------*/
    8313           0 :     m_nMapInfoType = poObjHdr->m_nType;
    8314             : 
    8315           0 :     TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
    8316           0 :     TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
    8317             : 
    8318             :     /*-----------------------------------------------------------------
    8319             :      * If object type has coords in a type 3 block, then its position
    8320             :      * follows
    8321             :      *----------------------------------------------------------------*/
    8322           0 :     if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
    8323             :     {
    8324           0 :         m_nCoordDataPtr = poObjBlock->ReadInt32();
    8325           0 :         m_nCoordDataSize = poObjBlock->ReadInt32();
    8326             :     }
    8327             :     else
    8328             :     {
    8329           0 :         m_nCoordDataPtr = -1;
    8330           0 :         m_nCoordDataSize = 0;
    8331             :     }
    8332             : 
    8333           0 :     m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
    8334           0 :     if (m_nSize > 0)
    8335             :     {
    8336           0 :         poObjBlock->GotoByteRel(-5);  // Go back to beginning of header
    8337           0 :         poObjBlock->ReadBytes(
    8338           0 :             std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
    8339             :     }
    8340             : 
    8341           0 :     return 0;
    8342             : }
    8343             : 
    8344             : /**********************************************************************
    8345             :  *                   TABDebugFeature::WriteGeometryToMAPFile()
    8346             :  *
    8347             :  * Write the geometry and representation (color, etc...) part of the
    8348             :  * feature to the .MAP object pointed to by poMAPFile.
    8349             :  *
    8350             :  * It is assumed that poMAPFile currently points to a valid map object.
    8351             :  *
    8352             :  * Returns 0 on success, -1 on error, in which case CPLError() will have
    8353             :  * been called.
    8354             :  **********************************************************************/
    8355           0 : int TABDebugFeature::WriteGeometryToMAPFile(
    8356             :     TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
    8357             :     GBool /*bCoordBlockDataOnly=FALSE*/,
    8358             :     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
    8359             : {
    8360             :     // Nothing to do here!
    8361             : 
    8362           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    8363             :              "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
    8364             : 
    8365           0 :     return -1;
    8366             : }
    8367             : 
    8368             : /**********************************************************************
    8369             :  *                   TABDebugFeature::DumpMIF()
    8370             :  *
    8371             :  * Dump feature contents... available only in DEBUG mode.
    8372             :  **********************************************************************/
    8373           0 : void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
    8374             : {
    8375           0 :     if (fpOut == nullptr)
    8376           0 :         fpOut = stdout;
    8377             : 
    8378           0 :     fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
    8379           0 :             GetMapInfoType());
    8380           0 :     fprintf(fpOut, "  Object size: %d bytes\n", m_nSize);
    8381           0 :     fprintf(fpOut, "  m_nCoordDataPtr  = %d\n", m_nCoordDataPtr);
    8382           0 :     fprintf(fpOut, "  m_nCoordDataSize = %d\n", m_nCoordDataSize);
    8383           0 :     fprintf(fpOut, "  ");
    8384             : 
    8385           0 :     for (int i = 0; i < m_nSize; i++)
    8386           0 :         fprintf(fpOut, " %2.2x", m_abyBuf[i]);
    8387             : 
    8388           0 :     fprintf(fpOut, "  \n");
    8389             : 
    8390           0 :     fflush(fpOut);
    8391           0 : }
    8392             : 
    8393             : /*=====================================================================
    8394             :  *                      class ITABFeaturePen
    8395             :  *====================================================================*/
    8396             : 
    8397             : /**********************************************************************
    8398             :  *                   ITABFeaturePen::ITABFeaturePen()
    8399             :  **********************************************************************/
    8400             : 
    8401             : // MI default is PEN(1, 2, 0)
    8402             : static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
    8403             : 
    8404        7777 : ITABFeaturePen::ITABFeaturePen()
    8405        7777 :     : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
    8406             : {
    8407        7777 : }
    8408             : 
    8409             : /**********************************************************************
    8410             :  *                   ITABFeaturePen::~ITABFeaturePen()
    8411             :  **********************************************************************/
    8412             : 
    8413             : ITABFeaturePen::~ITABFeaturePen() = default;
    8414             : 
    8415             : /**********************************************************************
    8416             :  *                   ITABFeaturePen::GetPenWidthPixel()
    8417             :  *                   ITABFeaturePen::SetPenWidthPixel()
    8418             :  *                   ITABFeaturePen::GetPenWidthPoint()
    8419             :  *                   ITABFeaturePen::SetPenWidthPoint()
    8420             :  *
    8421             :  * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
    8422             :  * in points (value from 0.1 to 203.7 points). The default pen width
    8423             :  * in MapInfo is 1 pixel.  Pen width in points exist only in file version 450.
    8424             :  *
    8425             :  * The following methods hide the way the pen width is stored in the files.
    8426             :  *
    8427             :  * In order to establish if a given pen def had its width specified in
    8428             :  * pixels or in points, one should first call GetPenWidthPoint(), and if
    8429             :  * it returns 0 then the Pixel width should be used instead:
    8430             :  *    if (GetPenWidthPoint() == 0)
    8431             :  *       ... use pen width in points ...
    8432             :  *    else
    8433             :  *       ... use Pixel width from GetPenWidthPixel()
    8434             :  *
    8435             :  * Note that the reverse is not true: the default pixel width is always 1,
    8436             :  * even when the pen width was actually set in points.
    8437             :  **********************************************************************/
    8438             : 
    8439          99 : GByte ITABFeaturePen::GetPenWidthPixel() const
    8440             : {
    8441          99 :     return m_sPenDef.nPixelWidth;
    8442             : }
    8443             : 
    8444          29 : void ITABFeaturePen::SetPenWidthPixel(GByte val)
    8445             : {
    8446          29 :     const GByte nPixelWidthMin = 1;
    8447          29 :     const GByte nPixelWidthMax = 7;
    8448          29 :     m_sPenDef.nPixelWidth =
    8449          29 :         std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
    8450          29 :     m_sPenDef.nPointWidth = 0;
    8451          29 : }
    8452             : 
    8453           0 : double ITABFeaturePen::GetPenWidthPoint() const
    8454             : {
    8455             :     // We store point width internally as tenths of points
    8456           0 :     return m_sPenDef.nPointWidth / 10.0;
    8457             : }
    8458             : 
    8459           0 : void ITABFeaturePen::SetPenWidthPoint(double val)
    8460             : {
    8461           0 :     m_sPenDef.nPointWidth =
    8462           0 :         std::min(std::max(static_cast<int>(val * 10), 1), 2037);
    8463           0 :     m_sPenDef.nPixelWidth = 1;
    8464           0 : }
    8465             : 
    8466             : /**********************************************************************
    8467             :  *                   ITABFeaturePen::GetPenWidthMIF()
    8468             :  *                   ITABFeaturePen::SetPenWidthMIF()
    8469             :  *
    8470             :  * The MIF representation for pen width is either a value from 1 to 7
    8471             :  * for a pen width in pixels, or a value from 11 to 2047 for a pen
    8472             :  * width in points = 10 + (point_width*10)
    8473             :  **********************************************************************/
    8474          21 : int ITABFeaturePen::GetPenWidthMIF() const
    8475             : {
    8476          21 :     return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
    8477          21 :                                       : m_sPenDef.nPixelWidth);
    8478             : }
    8479             : 
    8480        2434 : void ITABFeaturePen::SetPenWidthMIF(int val)
    8481             : {
    8482        2434 :     if (val > 10)
    8483             :     {
    8484           0 :         m_sPenDef.nPointWidth = std::min((val - 10), 2037);
    8485           0 :         m_sPenDef.nPixelWidth = 0;
    8486             :     }
    8487             :     else
    8488             :     {
    8489        2434 :         m_sPenDef.nPixelWidth =
    8490        2434 :             static_cast<GByte>(std::min(std::max(val, 1), 7));
    8491        2434 :         m_sPenDef.nPointWidth = 0;
    8492             :     }
    8493        2434 : }
    8494             : 
    8495             : /**********************************************************************
    8496             :  *                   ITABFeaturePen::GetPenStyleString()
    8497             :  *
    8498             :  *  Return a PEN() string. All representations info for the pen are here.
    8499             :  **********************************************************************/
    8500          99 : const char *ITABFeaturePen::GetPenStyleString() const
    8501             : {
    8502          99 :     const char *pszStyle = nullptr;
    8503          99 :     int nOGRStyle = 0;
    8504             :     char szPattern[20];
    8505             : 
    8506          99 :     szPattern[0] = '\0';
    8507             : 
    8508             :     // For now, I only add the 25 first styles
    8509          99 :     switch (GetPenPattern())
    8510             :     {
    8511           0 :         case 1:
    8512           0 :             nOGRStyle = 1;
    8513           0 :             break;
    8514          99 :         case 2:
    8515          99 :             nOGRStyle = 0;
    8516          99 :             break;
    8517           0 :         case 3:
    8518           0 :             nOGRStyle = 3;
    8519           0 :             strcpy(szPattern, "1 1");
    8520           0 :             break;
    8521           0 :         case 4:
    8522           0 :             nOGRStyle = 3;
    8523           0 :             strcpy(szPattern, "2 1");
    8524           0 :             break;
    8525           0 :         case 5:
    8526           0 :             nOGRStyle = 3;
    8527           0 :             strcpy(szPattern, "3 1");
    8528           0 :             break;
    8529           0 :         case 6:
    8530           0 :             nOGRStyle = 3;
    8531           0 :             strcpy(szPattern, "6 1");
    8532           0 :             break;
    8533           0 :         case 7:
    8534           0 :             nOGRStyle = 4;
    8535           0 :             strcpy(szPattern, "12 2");
    8536           0 :             break;
    8537           0 :         case 8:
    8538           0 :             nOGRStyle = 4;
    8539           0 :             strcpy(szPattern, "24 4");
    8540           0 :             break;
    8541           0 :         case 9:
    8542           0 :             nOGRStyle = 3;
    8543           0 :             strcpy(szPattern, "4 3");
    8544           0 :             break;
    8545           0 :         case 10:
    8546           0 :             nOGRStyle = 5;
    8547           0 :             strcpy(szPattern, "1 4");
    8548           0 :             break;
    8549           0 :         case 11:
    8550           0 :             nOGRStyle = 3;
    8551           0 :             strcpy(szPattern, "4 6");
    8552           0 :             break;
    8553           0 :         case 12:
    8554           0 :             nOGRStyle = 3;
    8555           0 :             strcpy(szPattern, "6 4");
    8556           0 :             break;
    8557           0 :         case 13:
    8558           0 :             nOGRStyle = 4;
    8559           0 :             strcpy(szPattern, "12 12");
    8560           0 :             break;
    8561           0 :         case 14:
    8562           0 :             nOGRStyle = 6;
    8563           0 :             strcpy(szPattern, "8 2 1 2");
    8564           0 :             break;
    8565           0 :         case 15:
    8566           0 :             nOGRStyle = 6;
    8567           0 :             strcpy(szPattern, "12 1 1 1");
    8568           0 :             break;
    8569           0 :         case 16:
    8570           0 :             nOGRStyle = 6;
    8571           0 :             strcpy(szPattern, "12 1 3 1");
    8572           0 :             break;
    8573           0 :         case 17:
    8574           0 :             nOGRStyle = 6;
    8575           0 :             strcpy(szPattern, "24 6 4 6");
    8576           0 :             break;
    8577           0 :         case 18:
    8578           0 :             nOGRStyle = 7;
    8579           0 :             strcpy(szPattern, "24 3 3 3 3 3");
    8580           0 :             break;
    8581           0 :         case 19:
    8582           0 :             nOGRStyle = 7;
    8583           0 :             strcpy(szPattern, "24 3 3 3 3 3 3 3");
    8584           0 :             break;
    8585           0 :         case 20:
    8586           0 :             nOGRStyle = 7;
    8587           0 :             strcpy(szPattern, "6 3 1 3 1 3");
    8588           0 :             break;
    8589           0 :         case 21:
    8590           0 :             nOGRStyle = 7;
    8591           0 :             strcpy(szPattern, "12 2 1 2 1 2");
    8592           0 :             break;
    8593           0 :         case 22:
    8594           0 :             nOGRStyle = 7;
    8595           0 :             strcpy(szPattern, "12 2 1 2 1 2 1 2");
    8596           0 :             break;
    8597           0 :         case 23:
    8598           0 :             nOGRStyle = 6;
    8599           0 :             strcpy(szPattern, "4 1 1 1");
    8600           0 :             break;
    8601           0 :         case 24:
    8602           0 :             nOGRStyle = 7;
    8603           0 :             strcpy(szPattern, "4 1 1 1 1");
    8604           0 :             break;
    8605           0 :         case 25:
    8606           0 :             nOGRStyle = 6;
    8607           0 :             strcpy(szPattern, "4 1 1 1 2 1 1 1");
    8608           0 :             break;
    8609             : 
    8610           0 :         default:
    8611           0 :             nOGRStyle = 0;
    8612           0 :             break;
    8613             :     }
    8614             : 
    8615             :     // note - MapInfo renders all lines using a round pen cap and round pen join
    8616             :     // which are not the default values for OGR pen cap/join styles. So we need
    8617             :     // to explicitly include the cap/j parameters in these strings
    8618          99 :     if (strlen(szPattern) != 0)
    8619             :     {
    8620           0 :         if (m_sPenDef.nPointWidth > 0)
    8621           0 :             pszStyle = CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
    8622             :                                   "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
    8623           0 :                                   static_cast<int>(GetPenWidthPoint()),
    8624           0 :                                   m_sPenDef.rgbColor, GetPenPattern(),
    8625             :                                   nOGRStyle, szPattern);
    8626             :         else
    8627           0 :             pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
    8628             :                                   "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
    8629           0 :                                   GetPenWidthPixel(), m_sPenDef.rgbColor,
    8630           0 :                                   GetPenPattern(), nOGRStyle, szPattern);
    8631             :     }
    8632             :     else
    8633             :     {
    8634          99 :         if (m_sPenDef.nPointWidth > 0)
    8635             :             pszStyle =
    8636           0 :                 CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\""
    8637             :                            "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
    8638           0 :                            static_cast<int>(GetPenWidthPoint()),
    8639           0 :                            m_sPenDef.rgbColor, GetPenPattern(), nOGRStyle);
    8640             :         else
    8641          99 :             pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
    8642             :                                   "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
    8643          99 :                                   GetPenWidthPixel(), m_sPenDef.rgbColor,
    8644          99 :                                   GetPenPattern(), nOGRStyle);
    8645             :     }
    8646             : 
    8647          99 :     return pszStyle;
    8648             : }
    8649             : 
    8650             : /**********************************************************************
    8651             :  *                   ITABFeaturePen::SetPenFromStyleString()
    8652             :  *
    8653             :  *  Init the Pen properties from a style string.
    8654             :  **********************************************************************/
    8655          32 : void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
    8656             : {
    8657          32 :     GBool bIsNull = 0;
    8658             : 
    8659             :     // Use the Style Manager to retrieve all the information we need.
    8660          32 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    8661          32 :     OGRStyleTool *poStylePart = nullptr;
    8662             : 
    8663             :     // Init the StyleMgr with the StyleString.
    8664          32 :     poStyleMgr->InitStyleString(pszStyleString);
    8665             : 
    8666             :     // Retrieve the Pen info.
    8667          32 :     const int numParts = poStyleMgr->GetPartCount();
    8668          51 :     for (int i = 0; i < numParts; i++)
    8669             :     {
    8670          49 :         poStylePart = poStyleMgr->GetPart(i);
    8671          49 :         if (poStylePart == nullptr)
    8672           0 :             continue;
    8673             : 
    8674          49 :         if (poStylePart->GetType() == OGRSTCPen)
    8675             :         {
    8676          30 :             break;
    8677             :         }
    8678             :         else
    8679             :         {
    8680          19 :             delete poStylePart;
    8681          19 :             poStylePart = nullptr;
    8682             :         }
    8683             :     }
    8684             : 
    8685             :     // If the no Pen found, do nothing.
    8686          32 :     if (poStylePart == nullptr)
    8687             :     {
    8688           2 :         delete poStyleMgr;
    8689           2 :         return;
    8690             :     }
    8691             : 
    8692          30 :     OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
    8693             : 
    8694             :     // With Pen, we always want to output points or pixels (which are the same,
    8695             :     // so just use points).
    8696             :     //
    8697             :     // It's very important to set the output unit of the feature.
    8698             :     // The default value is meter. If we don't do it all numerical values
    8699             :     // will be assumed to be converted from the input unit to meter when we
    8700             :     // will get them via GetParam...() functions.
    8701             :     // See OGRStyleTool::Parse() for more details.
    8702          30 :     poPenStyle->SetUnit(OGRSTUPoints, 1);
    8703             : 
    8704             :     // Get the Pen Id or pattern
    8705          30 :     const char *pszPenName = poPenStyle->Id(bIsNull);
    8706          30 :     if (bIsNull)
    8707           1 :         pszPenName = nullptr;
    8708             : 
    8709             :     // Set the width
    8710          30 :     if (poPenStyle->Width(bIsNull) != 0.0)
    8711             :     {
    8712          29 :         const double nPenWidth = poPenStyle->Width(bIsNull);
    8713             :         // Width < 10 is a pixel
    8714          29 :         if (nPenWidth > 10)
    8715           0 :             SetPenWidthPoint(nPenWidth);
    8716             :         else
    8717          29 :             SetPenWidthPixel(static_cast<GByte>(nPenWidth));
    8718             :     }
    8719             : 
    8720             :     // Set the color
    8721          30 :     const char *pszPenColor = poPenStyle->Color(bIsNull);
    8722          30 :     if (pszPenColor != nullptr)
    8723             :     {
    8724          30 :         if (pszPenColor[0] == '#')
    8725          30 :             pszPenColor++;
    8726             :         // The Pen color is an Hexa string that need to be convert in a int
    8727             :         const GInt32 nPenColor =
    8728          30 :             static_cast<int>(strtol(pszPenColor, nullptr, 16));
    8729          30 :         SetPenColor(nPenColor);
    8730             :     }
    8731             : 
    8732          30 :     const char *pszPenPattern = nullptr;
    8733             : 
    8734             :     // Set the Id of the Pen, use Pattern if necessary.
    8735          30 :     if (pszPenName &&
    8736          29 :         (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
    8737             :     {
    8738          29 :         const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
    8739          29 :         if (pszPenId != nullptr)
    8740             :         {
    8741          29 :             const int nPenId = atoi(pszPenId + 12);
    8742          29 :             SetPenPattern(static_cast<GByte>(nPenId));
    8743             :         }
    8744             :         else
    8745             :         {
    8746           0 :             pszPenId = strstr(pszPenName, "ogr-pen-");
    8747           0 :             if (pszPenId != nullptr)
    8748             :             {
    8749           0 :                 int nPenId = atoi(pszPenId + 8);
    8750           0 :                 if (nPenId == 0)
    8751           0 :                     nPenId = 2;
    8752           0 :                 SetPenPattern(static_cast<GByte>(nPenId));
    8753             :             }
    8754          29 :         }
    8755             :     }
    8756             :     else
    8757             :     {
    8758             :         // If no Pen Id, use the Pen Pattern to retrieve the Id.
    8759           1 :         pszPenPattern = poPenStyle->Pattern(bIsNull);
    8760           1 :         if (bIsNull)
    8761           1 :             pszPenPattern = nullptr;
    8762             :         else
    8763             :         {
    8764           0 :             if (strcmp(pszPenPattern, "1 1") == 0)
    8765           0 :                 SetPenPattern(3);
    8766           0 :             else if (strcmp(pszPenPattern, "2 1") == 0)
    8767           0 :                 SetPenPattern(4);
    8768           0 :             else if (strcmp(pszPenPattern, "3 1") == 0)
    8769           0 :                 SetPenPattern(5);
    8770           0 :             else if (strcmp(pszPenPattern, "6 1") == 0)
    8771           0 :                 SetPenPattern(6);
    8772           0 :             else if (strcmp(pszPenPattern, "12 2") == 0)
    8773           0 :                 SetPenPattern(7);
    8774           0 :             else if (strcmp(pszPenPattern, "24 4") == 0)
    8775           0 :                 SetPenPattern(8);
    8776           0 :             else if (strcmp(pszPenPattern, "4 3") == 0)
    8777           0 :                 SetPenPattern(9);
    8778           0 :             else if (strcmp(pszPenPattern, "1 4") == 0)
    8779           0 :                 SetPenPattern(10);
    8780           0 :             else if (strcmp(pszPenPattern, "4 6") == 0)
    8781           0 :                 SetPenPattern(11);
    8782           0 :             else if (strcmp(pszPenPattern, "6 4") == 0)
    8783           0 :                 SetPenPattern(12);
    8784           0 :             else if (strcmp(pszPenPattern, "12 12") == 0)
    8785           0 :                 SetPenPattern(13);
    8786           0 :             else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
    8787           0 :                 SetPenPattern(14);
    8788           0 :             else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
    8789           0 :                 SetPenPattern(15);
    8790           0 :             else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
    8791           0 :                 SetPenPattern(16);
    8792           0 :             else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
    8793           0 :                 SetPenPattern(17);
    8794           0 :             else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
    8795           0 :                 SetPenPattern(18);
    8796           0 :             else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
    8797           0 :                 SetPenPattern(19);
    8798           0 :             else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
    8799           0 :                 SetPenPattern(20);
    8800           0 :             else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
    8801           0 :                 SetPenPattern(21);
    8802           0 :             else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
    8803           0 :                 SetPenPattern(22);
    8804           0 :             else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
    8805           0 :                 SetPenPattern(23);
    8806           0 :             else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
    8807           0 :                 SetPenPattern(24);
    8808           0 :             else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
    8809           0 :                 SetPenPattern(25);
    8810             :         }
    8811             :     }
    8812             : 
    8813          30 :     delete poStyleMgr;
    8814          30 :     delete poStylePart;
    8815             : 
    8816          30 :     return;
    8817             : }
    8818             : 
    8819             : /**********************************************************************
    8820             :  *                   ITABFeaturePen::DumpPenDef()
    8821             :  *
    8822             :  * Dump pen definition information.
    8823             :  **********************************************************************/
    8824           0 : void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
    8825             : {
    8826           0 :     if (fpOut == nullptr)
    8827           0 :         fpOut = stdout;
    8828             : 
    8829           0 :     fprintf(fpOut, "  m_nPenDefIndex         = %d\n", m_nPenDefIndex);
    8830           0 :     fprintf(fpOut, "  m_sPenDef.nRefCount    = %d\n", m_sPenDef.nRefCount);
    8831           0 :     fprintf(fpOut, "  m_sPenDef.nPixelWidth  = %u\n", m_sPenDef.nPixelWidth);
    8832           0 :     fprintf(fpOut, "  m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
    8833           0 :     fprintf(fpOut, "  m_sPenDef.nPointWidth  = %d\n", m_sPenDef.nPointWidth);
    8834           0 :     fprintf(fpOut, "  m_sPenDef.rgbColor     = 0x%6.6x (%d)\n",
    8835             :             m_sPenDef.rgbColor, m_sPenDef.rgbColor);
    8836             : 
    8837           0 :     fflush(fpOut);
    8838           0 : }
    8839             : 
    8840             : /*=====================================================================
    8841             :  *                      class ITABFeatureBrush
    8842             :  *====================================================================*/
    8843             : 
    8844             : /**********************************************************************
    8845             :  *                   ITABFeatureBrush::ITABFeatureBrush()
    8846             :  **********************************************************************/
    8847             : 
    8848             : // MI default is BRUSH(2, 16777215, 16777215)
    8849             : static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
    8850             : 
    8851        2540 : ITABFeatureBrush::ITABFeatureBrush()
    8852        2540 :     : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
    8853             : {
    8854        2540 : }
    8855             : 
    8856             : /**********************************************************************
    8857             :  *                   ITABFeatureBrush::~ITABFeatureBrush()
    8858             :  **********************************************************************/
    8859             : 
    8860             : ITABFeatureBrush::~ITABFeatureBrush() = default;
    8861             : 
    8862             : /**********************************************************************
    8863             :  *                   ITABFeatureBrush::GetBrushStyleString()
    8864             :  *
    8865             :  *  Return a Brush() string. All representations info for the Brush are here.
    8866             :  **********************************************************************/
    8867          54 : const char *ITABFeatureBrush::GetBrushStyleString() const
    8868             : {
    8869          54 :     const char *pszStyle = nullptr;
    8870          54 :     int nOGRStyle = 0;
    8871             :     /* char szPattern[20]; */
    8872             :     //* szPattern[0] = '\0'; */
    8873             : 
    8874          54 :     if (m_sBrushDef.nFillPattern == 1)
    8875          53 :         nOGRStyle = 1;
    8876           1 :     else if (m_sBrushDef.nFillPattern == 3)
    8877           0 :         nOGRStyle = 2;
    8878           1 :     else if (m_sBrushDef.nFillPattern == 4)
    8879           0 :         nOGRStyle = 3;
    8880           1 :     else if (m_sBrushDef.nFillPattern == 5)
    8881           0 :         nOGRStyle = 5;
    8882           1 :     else if (m_sBrushDef.nFillPattern == 6)
    8883           0 :         nOGRStyle = 4;
    8884           1 :     else if (m_sBrushDef.nFillPattern == 7)
    8885           0 :         nOGRStyle = 6;
    8886           1 :     else if (m_sBrushDef.nFillPattern == 8)
    8887           0 :         nOGRStyle = 7;
    8888             : 
    8889          54 :     if (GetBrushTransparent())
    8890             :     {
    8891             :         /* Omit BG Color for transparent brushes */
    8892           2 :         pszStyle = CPLSPrintf(
    8893             :             "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
    8894           2 :             m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
    8895             :     }
    8896             :     else
    8897             :     {
    8898          52 :         pszStyle = CPLSPrintf(
    8899             :             "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
    8900          52 :             m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
    8901          52 :             m_sBrushDef.nFillPattern, nOGRStyle);
    8902             :     }
    8903             : 
    8904          54 :     return pszStyle;
    8905             : }
    8906             : 
    8907             : /**********************************************************************
    8908             :  *                   ITABFeatureBrush::SetBrushFromStyleString()
    8909             :  *
    8910             :  *  Set all Brush elements from a StyleString.
    8911             :  *  Use StyleMgr to do so.
    8912             :  **********************************************************************/
    8913          19 : void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
    8914             : {
    8915          19 :     GBool bIsNull = 0;
    8916             : 
    8917             :     // Use the Style Manager to retrieve all the information we need.
    8918          19 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    8919          19 :     OGRStyleTool *poStylePart = nullptr;
    8920             : 
    8921             :     // Init the StyleMgr with the StyleString.
    8922          19 :     poStyleMgr->InitStyleString(pszStyleString);
    8923             : 
    8924             :     // Retrieve the Brush info.
    8925          19 :     const int numParts = poStyleMgr->GetPartCount();
    8926          19 :     for (int i = 0; i < numParts; i++)
    8927             :     {
    8928          19 :         poStylePart = poStyleMgr->GetPart(i);
    8929          19 :         if (poStylePart == nullptr)
    8930           0 :             continue;
    8931             : 
    8932          19 :         if (poStylePart->GetType() == OGRSTCBrush)
    8933             :         {
    8934          19 :             break;
    8935             :         }
    8936             :         else
    8937             :         {
    8938           0 :             delete poStylePart;
    8939           0 :             poStylePart = nullptr;
    8940             :         }
    8941             :     }
    8942             : 
    8943             :     // If the no Brush found, do nothing.
    8944          19 :     if (poStylePart == nullptr)
    8945             :     {
    8946           0 :         delete poStyleMgr;
    8947           0 :         return;
    8948             :     }
    8949             : 
    8950          19 :     OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
    8951             : 
    8952             :     // Set the Brush Id (FillPattern)
    8953          19 :     const char *pszBrushId = poBrushStyle->Id(bIsNull);
    8954          19 :     if (bIsNull)
    8955           2 :         pszBrushId = nullptr;
    8956          19 :     bool bHasBrushId = false;
    8957             : 
    8958          19 :     if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
    8959           0 :                        strstr(pszBrushId, "ogr-brush-")))
    8960             :     {
    8961          17 :         if (strstr(pszBrushId, "mapinfo-brush-"))
    8962             :         {
    8963          17 :             const int nBrushId = atoi(pszBrushId + 14);
    8964          17 :             SetBrushPattern(static_cast<GByte>(nBrushId));
    8965          17 :             bHasBrushId = true;
    8966             :         }
    8967           0 :         else if (strstr(pszBrushId, "ogr-brush-"))
    8968             :         {
    8969           0 :             int nBrushId = atoi(pszBrushId + 10);
    8970           0 :             if (nBrushId > 1)
    8971           0 :                 nBrushId++;
    8972           0 :             SetBrushPattern(static_cast<GByte>(nBrushId));
    8973           0 :             bHasBrushId = true;
    8974             :         }
    8975             :     }
    8976             : 
    8977             :     // Set the BackColor, if not set, then it is transparent
    8978          19 :     const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
    8979          19 :     if (bIsNull)
    8980           1 :         pszBrushColor = nullptr;
    8981             : 
    8982          19 :     if (pszBrushColor)
    8983             :     {
    8984          18 :         if (pszBrushColor[0] == '#')
    8985          18 :             pszBrushColor++;
    8986          18 :         if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
    8987           1 :             pszBrushColor[7] == '0')
    8988             :         {
    8989           1 :             SetBrushTransparent(1);
    8990             :         }
    8991             :         else
    8992             :         {
    8993          34 :             CPLString osBrushColor(pszBrushColor);
    8994          17 :             if (strlen(pszBrushColor) > 6)
    8995           0 :                 osBrushColor.resize(6);
    8996             :             const int nBrushColor =
    8997          17 :                 static_cast<int>(strtol(osBrushColor, nullptr, 16));
    8998          17 :             SetBrushBGColor(static_cast<GInt32>(nBrushColor));
    8999             :         }
    9000             :     }
    9001             :     else
    9002             :     {
    9003           1 :         SetBrushTransparent(1);
    9004             :     }
    9005             : 
    9006             :     // Set the ForeColor
    9007          19 :     pszBrushColor = poBrushStyle->ForeColor(bIsNull);
    9008          19 :     if (bIsNull)
    9009           0 :         pszBrushColor = nullptr;
    9010             : 
    9011          19 :     if (pszBrushColor)
    9012             :     {
    9013          19 :         if (pszBrushColor[0] == '#')
    9014          19 :             pszBrushColor++;
    9015          19 :         if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
    9016           1 :             pszBrushColor[7] == '0')
    9017             :         {
    9018           1 :             if (!bHasBrushId)
    9019           1 :                 SetBrushPattern(static_cast<GByte>(1));  // No-fill
    9020             :         }
    9021             :         else
    9022             :         {
    9023          18 :             if (!bHasBrushId)
    9024           1 :                 SetBrushPattern(static_cast<GByte>(2));  // Solid-fill
    9025             :         }
    9026             : 
    9027          38 :         CPLString osBrushColor(pszBrushColor);
    9028          19 :         if (strlen(pszBrushColor) > 6)
    9029           1 :             osBrushColor.resize(6);
    9030             :         const int nBrushColor =
    9031          19 :             static_cast<int>(strtol(osBrushColor, nullptr, 16));
    9032          19 :         SetBrushFGColor(static_cast<GInt32>(nBrushColor));
    9033             :     }
    9034             : 
    9035          19 :     delete poStyleMgr;
    9036          19 :     delete poStylePart;
    9037             : 
    9038          19 :     return;
    9039             : }
    9040             : 
    9041             : /**********************************************************************
    9042             :  *                   ITABFeatureBrush::DumpBrushDef()
    9043             :  *
    9044             :  * Dump Brush definition information.
    9045             :  **********************************************************************/
    9046           0 : void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
    9047             : {
    9048           0 :     if (fpOut == nullptr)
    9049           0 :         fpOut = stdout;
    9050             : 
    9051           0 :     fprintf(fpOut, "  m_nBrushDefIndex         = %d\n", m_nBrushDefIndex);
    9052           0 :     fprintf(fpOut, "  m_sBrushDef.nRefCount    = %d\n", m_sBrushDef.nRefCount);
    9053           0 :     fprintf(fpOut, "  m_sBrushDef.nFillPattern = %d\n",
    9054           0 :             static_cast<int>(m_sBrushDef.nFillPattern));
    9055           0 :     fprintf(fpOut, "  m_sBrushDef.bTransparentFill = %d\n",
    9056           0 :             static_cast<int>(m_sBrushDef.bTransparentFill));
    9057           0 :     fprintf(fpOut, "  m_sBrushDef.rgbFGColor   = 0x%6.6x (%d)\n",
    9058             :             m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
    9059           0 :     fprintf(fpOut, "  m_sBrushDef.rgbBGColor   = 0x%6.6x (%d)\n",
    9060             :             m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
    9061             : 
    9062           0 :     fflush(fpOut);
    9063           0 : }
    9064             : 
    9065             : /*=====================================================================
    9066             :  *                      class ITABFeatureFont
    9067             :  *====================================================================*/
    9068             : 
    9069             : /**********************************************************************
    9070             :  *                   ITABFeatureFont::ITABFeatureFont()
    9071             :  **********************************************************************/
    9072             : 
    9073             : // MI default is Font("Arial", 0, 0, 0)
    9074             : static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
    9075             : 
    9076        1638 : ITABFeatureFont::ITABFeatureFont()
    9077        1638 :     : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
    9078             : {
    9079        1638 : }
    9080             : 
    9081             : /**********************************************************************
    9082             :  *                   ITABFeatureFont::~ITABFeatureFont()
    9083             :  **********************************************************************/
    9084             : 
    9085             : ITABFeatureFont::~ITABFeatureFont() = default;
    9086             : 
    9087             : /**********************************************************************
    9088             :  *                   ITABFeatureFont::SetFontName()
    9089             :  **********************************************************************/
    9090        1582 : void ITABFeatureFont::SetFontName(const char *pszName)
    9091             : {
    9092        1582 :     strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
    9093        1582 :     m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
    9094        1582 : }
    9095             : 
    9096             : /**********************************************************************
    9097             :  *                   ITABFeatureFont::DumpFontDef()
    9098             :  *
    9099             :  * Dump Font definition information.
    9100             :  **********************************************************************/
    9101           0 : void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
    9102             : {
    9103           0 :     if (fpOut == nullptr)
    9104           0 :         fpOut = stdout;
    9105             : 
    9106           0 :     fprintf(fpOut, "  m_nFontDefIndex       = %d\n", m_nFontDefIndex);
    9107           0 :     fprintf(fpOut, "  m_sFontDef.nRefCount  = %d\n", m_sFontDef.nRefCount);
    9108           0 :     fprintf(fpOut, "  m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
    9109             : 
    9110           0 :     fflush(fpOut);
    9111           0 : }
    9112             : 
    9113             : /*=====================================================================
    9114             :  *                      class ITABFeatureSymbol
    9115             :  *====================================================================*/
    9116             : 
    9117             : /**********************************************************************
    9118             :  *                   ITABFeatureSymbol::ITABFeatureSymbol()
    9119             :  **********************************************************************/
    9120             : 
    9121             : // MI default is Symbol(35, 0, 12)
    9122             : static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
    9123             : 
    9124      566233 : ITABFeatureSymbol::ITABFeatureSymbol()
    9125      566233 :     : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
    9126             : {
    9127      566233 : }
    9128             : 
    9129             : /**********************************************************************
    9130             :  *                   ITABFeatureSymbol::GetSymbolStyleString()
    9131             :  *
    9132             :  *  Return a Symbol() string. All representations info for the Symbol are here.
    9133             :  **********************************************************************/
    9134          33 : const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
    9135             : {
    9136          33 :     const char *pszStyle = nullptr;
    9137          33 :     int nOGRStyle = 0;
    9138             :     /* char szPattern[20]; */
    9139          33 :     int nAngle = 0;
    9140             :     /* szPattern[0] = '\0'; */
    9141             : 
    9142          33 :     switch (m_sSymbolDef.nSymbolNo)
    9143             :     {
    9144           0 :         case 31:
    9145             :             // this is actually a "null" symbol in MapInfo!
    9146           0 :             nOGRStyle = 0;
    9147           0 :             break;
    9148           0 :         case 32:  // filled square
    9149           0 :             nOGRStyle = 5;
    9150           0 :             break;
    9151           0 :         case 33:  // filled diamond
    9152           0 :             nAngle = 45;
    9153           0 :             nOGRStyle = 5;
    9154           0 :             break;
    9155           0 :         case 34:  // filled circle
    9156           0 :             nOGRStyle = 3;
    9157           0 :             break;
    9158          33 :         case 35:  // filled star
    9159          33 :             nOGRStyle = 9;
    9160          33 :             break;
    9161           0 :         case 36:  // filled upward pointing triangle
    9162           0 :             nOGRStyle = 7;
    9163           0 :             break;
    9164           0 :         case 37:  // filled downward pointing triangle
    9165           0 :             nAngle = 180;
    9166           0 :             nOGRStyle = 7;
    9167           0 :             break;
    9168           0 :         case 38:  // hollow square
    9169           0 :             nOGRStyle = 4;
    9170           0 :             break;
    9171           0 :         case 39:  // hollow diamond
    9172           0 :             nAngle = 45;
    9173           0 :             nOGRStyle = 4;
    9174           0 :             break;
    9175           0 :         case 40:  // hollow circle
    9176           0 :             nOGRStyle = 2;
    9177           0 :             break;
    9178           0 :         case 41:  // hollow star
    9179           0 :             nOGRStyle = 8;
    9180           0 :             break;
    9181           0 :         case 42:  // hollow upward pointing triangle
    9182           0 :             nOGRStyle = 6;
    9183           0 :             break;
    9184           0 :         case 43:  // hollow downward pointing triangle
    9185           0 :             nAngle = 180;
    9186           0 :             nOGRStyle = 6;
    9187           0 :             break;
    9188           0 :         case 44:  // filled square (with shadow)
    9189           0 :             nOGRStyle = 5;
    9190           0 :             break;
    9191           0 :         case 45:  // filled upward triangle (with shadow)
    9192           0 :             nOGRStyle = 7;
    9193           0 :             break;
    9194           0 :         case 46:  // filled circle (with shadow)
    9195           0 :             nOGRStyle = 3;
    9196           0 :             break;
    9197           0 :         case 49:  // crossed lines
    9198           0 :             nOGRStyle = 0;
    9199           0 :             break;
    9200           0 :         case 50:  // X crossed lines
    9201           0 :             nOGRStyle = 1;
    9202           0 :             break;
    9203             :     }
    9204             : 
    9205          33 :     nAngle += static_cast<int>(dfAngle);
    9206             : 
    9207          66 :     pszStyle = CPLSPrintf(
    9208             :         "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
    9209          33 :         m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
    9210             :         nOGRStyle);
    9211             : 
    9212          33 :     return pszStyle;
    9213             : }
    9214             : 
    9215             : /**********************************************************************
    9216             :  *                   ITABFeatureSymbol::SetSymbolFromStyleString()
    9217             :  *
    9218             :  *  Set all Symbol var from a OGRStyleSymbol.
    9219             :  **********************************************************************/
    9220         109 : void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
    9221             : {
    9222         109 :     GBool bIsNull = 0;
    9223             : 
    9224             :     // Set the Symbol Id (SymbolNo)
    9225         109 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    9226         109 :     if (bIsNull)
    9227           0 :         pszSymbolId = nullptr;
    9228             : 
    9229         109 :     if (pszSymbolId)
    9230             :     {
    9231         109 :         if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
    9232             :         {
    9233         105 :             const int nSymbolId = atoi(pszSymbolId + 12);
    9234         105 :             SetSymbolNo(static_cast<GByte>(nSymbolId));
    9235             :         }
    9236           4 :         else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
    9237             :         {
    9238           0 :             const int nSymbolId = atoi(pszSymbolId + 8);
    9239             : 
    9240             :             // The OGR symbol is not the MapInfo one
    9241             :             // Here's some mapping
    9242           0 :             switch (nSymbolId)
    9243             :             {
    9244           0 :                 case 0:
    9245           0 :                     SetSymbolNo(49);
    9246           0 :                     break;
    9247           0 :                 case 1:
    9248           0 :                     SetSymbolNo(50);
    9249           0 :                     break;
    9250           0 :                 case 2:
    9251           0 :                     SetSymbolNo(40);
    9252           0 :                     break;
    9253           0 :                 case 3:
    9254           0 :                     SetSymbolNo(34);
    9255           0 :                     break;
    9256           0 :                 case 4:
    9257           0 :                     SetSymbolNo(38);
    9258           0 :                     break;
    9259           0 :                 case 5:
    9260           0 :                     SetSymbolNo(32);
    9261           0 :                     break;
    9262           0 :                 case 6:
    9263           0 :                     SetSymbolNo(42);
    9264           0 :                     break;
    9265           0 :                 case 7:
    9266           0 :                     SetSymbolNo(36);
    9267           0 :                     break;
    9268           0 :                 case 8:
    9269           0 :                     SetSymbolNo(41);
    9270           0 :                     break;
    9271           0 :                 case 9:
    9272           0 :                     SetSymbolNo(35);
    9273           0 :                     break;
    9274           0 :                 case 10:  // vertical bar -- no mapinfo equivalent, so use
    9275             :                           // crosshairs as closest match
    9276           0 :                     SetSymbolNo(49);
    9277           0 :                     break;
    9278             :             }
    9279             :         }
    9280             :     }
    9281             : 
    9282             :     // Set SymbolSize
    9283         109 :     const double dSymbolSize = poSymbolStyle->Size(bIsNull);
    9284         109 :     if (dSymbolSize != 0.0)
    9285             :     {
    9286         109 :         SetSymbolSize(static_cast<GInt16>(dSymbolSize));
    9287             :     }
    9288             : 
    9289             :     // Set Symbol Color
    9290         109 :     const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
    9291         109 :     if (pszSymbolColor)
    9292             :     {
    9293         109 :         if (pszSymbolColor[0] == '#')
    9294         109 :             pszSymbolColor++;
    9295             :         int nSymbolColor =
    9296         109 :             static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
    9297         109 :         SetSymbolColor(static_cast<GInt32>(nSymbolColor));
    9298             :     }
    9299         109 : }
    9300             : 
    9301             : /**********************************************************************
    9302             :  *                   ITABFeatureSymbol::SetSymbolFromStyleString()
    9303             :  *
    9304             :  *  Set all Symbol var from a StyleString. Use StyleMgr to do so.
    9305             :  **********************************************************************/
    9306         109 : void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
    9307             : {
    9308             :     // Use the Style Manager to retrieve all the information we need.
    9309         109 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    9310         109 :     OGRStyleTool *poStylePart = nullptr;
    9311             : 
    9312             :     // Init the StyleMgr with the StyleString.
    9313         109 :     poStyleMgr->InitStyleString(pszStyleString);
    9314             : 
    9315             :     // Retrieve the Symbol info.
    9316         109 :     const int numParts = poStyleMgr->GetPartCount();
    9317         109 :     for (int i = 0; i < numParts; i++)
    9318             :     {
    9319         109 :         poStylePart = poStyleMgr->GetPart(i);
    9320         109 :         if (poStylePart == nullptr)
    9321           0 :             continue;
    9322             : 
    9323         109 :         if (poStylePart->GetType() == OGRSTCSymbol)
    9324             :         {
    9325         109 :             break;
    9326             :         }
    9327             :         else
    9328             :         {
    9329           0 :             delete poStylePart;
    9330           0 :             poStylePart = nullptr;
    9331             :         }
    9332             :     }
    9333             : 
    9334             :     // If the no Symbol found, do nothing.
    9335         109 :     if (poStylePart == nullptr)
    9336             :     {
    9337           0 :         delete poStyleMgr;
    9338           0 :         return;
    9339             :     }
    9340             : 
    9341             :     OGRStyleSymbol *poSymbolStyle =
    9342         109 :         cpl::down_cast<OGRStyleSymbol *>(poStylePart);
    9343             : 
    9344             :     // With Symbol, we always want to output points
    9345             :     //
    9346             :     // It's very important to set the output unit of the feature.
    9347             :     // The default value is meter. If we don't do it all numerical values
    9348             :     // will be assumed to be converted from the input unit to meter when we
    9349             :     // will get them via GetParam...() functions.
    9350             :     // See OGRStyleTool::Parse() for more details.
    9351         109 :     poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
    9352             : 
    9353         109 :     SetSymbolFromStyle(poSymbolStyle);
    9354             : 
    9355         109 :     delete poStyleMgr;
    9356         109 :     delete poStylePart;
    9357             : 
    9358         109 :     return;
    9359             : }
    9360             : 
    9361             : /**********************************************************************
    9362             :  *                   ITABFeatureSymbol::GetSymbolFeatureClass()
    9363             :  *
    9364             :  *  Return the feature class needed to represent the style string.
    9365             :  **********************************************************************/
    9366             : TABFeatureClass
    9367         109 : ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
    9368             : {
    9369             :     // Use the Style Manager to retrieve all the information we need.
    9370         109 :     OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
    9371         109 :     OGRStyleTool *poStylePart = nullptr;
    9372             : 
    9373             :     // Init the StyleMgr with the StyleString.
    9374         109 :     poStyleMgr->InitStyleString(pszStyleString);
    9375             : 
    9376             :     // Retrieve the Symbol info.
    9377         109 :     const int numParts = poStyleMgr->GetPartCount();
    9378         109 :     for (int i = 0; i < numParts; i++)
    9379             :     {
    9380         109 :         poStylePart = poStyleMgr->GetPart(i);
    9381         109 :         if (poStylePart == nullptr)
    9382             :         {
    9383           0 :             continue;
    9384             :         }
    9385             : 
    9386         109 :         if (poStylePart->GetType() == OGRSTCSymbol)
    9387             :         {
    9388         109 :             break;
    9389             :         }
    9390             :         else
    9391             :         {
    9392           0 :             delete poStylePart;
    9393           0 :             poStylePart = nullptr;
    9394             :         }
    9395             :     }
    9396             : 
    9397         109 :     TABFeatureClass result = TABFCPoint;
    9398             : 
    9399             :     // If the no Symbol found, do nothing.
    9400         109 :     if (poStylePart == nullptr)
    9401             :     {
    9402           0 :         delete poStyleMgr;
    9403           0 :         return result;
    9404             :     }
    9405             : 
    9406             :     OGRStyleSymbol *poSymbolStyle =
    9407         109 :         cpl::down_cast<OGRStyleSymbol *>(poStylePart);
    9408             : 
    9409         109 :     GBool bIsNull = 0;
    9410             : 
    9411             :     // Set the Symbol Id (SymbolNo)
    9412         109 :     const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
    9413         109 :     if (bIsNull)
    9414           0 :         pszSymbolId = nullptr;
    9415             : 
    9416         109 :     if (pszSymbolId)
    9417             :     {
    9418         109 :         if (STARTS_WITH(pszSymbolId, "font-sym-"))
    9419             :         {
    9420           2 :             result = TABFCFontPoint;
    9421             :         }
    9422         107 :         else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
    9423             :         {
    9424           2 :             result = TABFCCustomPoint;
    9425             :         }
    9426             :     }
    9427             : 
    9428         109 :     delete poStyleMgr;
    9429         109 :     delete poStylePart;
    9430             : 
    9431         109 :     return result;
    9432             : }
    9433             : 
    9434             : /**********************************************************************
    9435             :  *                   ITABFeatureSymbol::DumpSymbolDef()
    9436             :  *
    9437             :  * Dump Symbol definition information.
    9438             :  **********************************************************************/
    9439           0 : void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
    9440             : {
    9441           0 :     if (fpOut == nullptr)
    9442           0 :         fpOut = stdout;
    9443             : 
    9444           0 :     fprintf(fpOut, "  m_nSymbolDefIndex       = %d\n", m_nSymbolDefIndex);
    9445           0 :     fprintf(fpOut, "  m_sSymbolDef.nRefCount  = %d\n", m_sSymbolDef.nRefCount);
    9446           0 :     fprintf(fpOut, "  m_sSymbolDef.nSymbolNo  = %d\n", m_sSymbolDef.nSymbolNo);
    9447           0 :     fprintf(fpOut, "  m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
    9448           0 :     fprintf(fpOut, "  m_sSymbolDef._unknown_  = %d\n",
    9449           0 :             static_cast<int>(m_sSymbolDef._nUnknownValue_));
    9450           0 :     fprintf(fpOut, "  m_sSymbolDef.rgbColor   = 0x%6.6x (%d)\n",
    9451             :             m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
    9452             : 
    9453           0 :     fflush(fpOut);
    9454           0 : }

Generated by: LCOV version 1.14