Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/mitab/mitab_feature.cpp
Line
Count
Source (jump to first uncovered line)
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
TABFeature::TABFeature(OGRFeatureDefn *poDefnIn)
52
875k
    : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
53
875k
      m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
54
875k
      m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
55
875k
{
56
875k
}
57
58
/**********************************************************************
59
 *                   TABFeature::~TABFeature()
60
 *
61
 * Destructor.
62
 **********************************************************************/
63
TABFeature::~TABFeature()
64
875k
{
65
875k
}
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
TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
75
                                              OGRFeatureDefn *poDefn)
76
38.1k
{
77
38.1k
    TABFeature *poFeature = nullptr;
78
79
    /*-----------------------------------------------------------------
80
     * Create new feature object of the right type
81
     *----------------------------------------------------------------*/
82
38.1k
    switch (nMapInfoType)
83
38.1k
    {
84
37.5k
        case TAB_GEOM_NONE:
85
37.5k
            poFeature = new TABFeature(poDefn);
86
37.5k
            break;
87
8
        case TAB_GEOM_SYMBOL_C:
88
73
        case TAB_GEOM_SYMBOL:
89
73
            poFeature = new TABPoint(poDefn);
90
73
            break;
91
0
        case TAB_GEOM_FONTSYMBOL_C:
92
39
        case TAB_GEOM_FONTSYMBOL:
93
39
            poFeature = new TABFontPoint(poDefn);
94
39
            break;
95
1
        case TAB_GEOM_CUSTOMSYMBOL_C:
96
62
        case TAB_GEOM_CUSTOMSYMBOL:
97
62
            poFeature = new TABCustomPoint(poDefn);
98
62
            break;
99
2
        case TAB_GEOM_LINE_C:
100
126
        case TAB_GEOM_LINE:
101
127
        case TAB_GEOM_PLINE_C:
102
127
        case TAB_GEOM_PLINE:
103
158
        case TAB_GEOM_MULTIPLINE_C:
104
158
        case TAB_GEOM_MULTIPLINE:
105
160
        case TAB_GEOM_V450_MULTIPLINE_C:
106
162
        case TAB_GEOM_V450_MULTIPLINE:
107
162
        case TAB_GEOM_V800_MULTIPLINE_C:
108
162
        case TAB_GEOM_V800_MULTIPLINE:
109
162
            poFeature = new TABPolyline(poDefn);
110
162
            break;
111
1
        case TAB_GEOM_ARC_C:
112
44
        case TAB_GEOM_ARC:
113
44
            poFeature = new TABArc(poDefn);
114
44
            break;
115
116
16
        case TAB_GEOM_REGION_C:
117
54
        case TAB_GEOM_REGION:
118
54
        case TAB_GEOM_V450_REGION_C:
119
54
        case TAB_GEOM_V450_REGION:
120
54
        case TAB_GEOM_V800_REGION_C:
121
54
        case TAB_GEOM_V800_REGION:
122
54
            poFeature = new TABRegion(poDefn);
123
54
            break;
124
2
        case TAB_GEOM_RECT_C:
125
32
        case TAB_GEOM_RECT:
126
40
        case TAB_GEOM_ROUNDRECT_C:
127
78
        case TAB_GEOM_ROUNDRECT:
128
78
            poFeature = new TABRectangle(poDefn);
129
78
            break;
130
0
        case TAB_GEOM_ELLIPSE_C:
131
31
        case TAB_GEOM_ELLIPSE:
132
31
            poFeature = new TABEllipse(poDefn);
133
31
            break;
134
0
        case TAB_GEOM_TEXT_C:
135
16
        case TAB_GEOM_TEXT:
136
16
            poFeature = new TABText(poDefn);
137
16
            break;
138
17
        case TAB_GEOM_MULTIPOINT_C:
139
17
        case TAB_GEOM_MULTIPOINT:
140
18
        case TAB_GEOM_V800_MULTIPOINT_C:
141
18
        case TAB_GEOM_V800_MULTIPOINT:
142
18
            poFeature = new TABMultiPoint(poDefn);
143
18
            break;
144
2
        case TAB_GEOM_COLLECTION_C:
145
4
        case TAB_GEOM_COLLECTION:
146
4
        case TAB_GEOM_V800_COLLECTION_C:
147
4
        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
0
                CE_Warning,
162
0
                static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
163
0
                "Unsupported object type %d (0x%2.2x).  Feature will be "
164
0
                "returned with NONE geometry.",
165
0
                nMapInfoType, nMapInfoType);
166
38.1k
    }
167
168
38.1k
    return poFeature;
169
38.1k
}
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
void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature)
185
13.4k
{
186
    /*-----------------------------------------------------------------
187
     * Copy fields only if OGRFeatureDefn is the same
188
     *----------------------------------------------------------------*/
189
13.4k
    OGRFeatureDefn *poThisDefnRef = GetDefnRef();
190
191
13.4k
    if (poThisDefnRef == poDestFeature->GetDefnRef())
192
0
    {
193
0
        for (int i = 0; i < poThisDefnRef->GetFieldCount(); i++)
194
0
        {
195
0
            poDestFeature->SetField(i, GetRawFieldRef(i));
196
0
        }
197
0
    }
198
199
    /*-----------------------------------------------------------------
200
     * Copy the geometry
201
     *----------------------------------------------------------------*/
202
13.4k
    poDestFeature->SetGeometry(GetGeometryRef());
203
204
13.4k
    double dXMin = 0.0;
205
13.4k
    double dYMin = 0.0;
206
13.4k
    double dXMax = 0.0;
207
13.4k
    double dYMax = 0.0;
208
13.4k
    GetMBR(dXMin, dYMin, dXMax, dYMax);
209
13.4k
    poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
210
211
13.4k
    GInt32 nXMin = 0;
212
13.4k
    GInt32 nYMin = 0;
213
13.4k
    GInt32 nXMax = 0;
214
13.4k
    GInt32 nYMax = 0;
215
13.4k
    GetIntMBR(nXMin, nYMin, nXMax, nYMax);
216
13.4k
    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
13.4k
}
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 *TABFeature::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
237
13.4k
{
238
    /*-----------------------------------------------------------------
239
     * Alloc new feature and copy the base stuff
240
     *----------------------------------------------------------------*/
241
13.4k
    TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
242
243
13.4k
    CopyTABFeatureBase(poNew);
244
245
    /*-----------------------------------------------------------------
246
     * And members specific to this class
247
     *----------------------------------------------------------------*/
248
    // Nothing to do for this class
249
250
13.4k
    return poNew;
251
13.4k
}
252
253
/**********************************************************************
254
 *                   TABFeature::SetMBR()
255
 *
256
 * Set the values for the MBR corners for this feature.
257
 **********************************************************************/
258
void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
259
363k
{
260
363k
    m_dXMin = std::min(dXMin, dXMax);
261
363k
    m_dYMin = std::min(dYMin, dYMax);
262
363k
    m_dXMax = std::max(dXMin, dXMax);
263
363k
    m_dYMax = std::max(dYMin, dYMax);
264
363k
}
265
266
/**********************************************************************
267
 *                   TABFeature::GetMBR()
268
 *
269
 * Return the values for the MBR corners for this feature.
270
 **********************************************************************/
271
void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
272
                        double &dYMax)
273
132k
{
274
132k
    dXMin = m_dXMin;
275
132k
    dYMin = m_dYMin;
276
132k
    dXMax = m_dXMax;
277
132k
    dYMax = m_dYMax;
278
132k
}
279
280
/**********************************************************************
281
 *                   TABFeature::SetIntMBR()
282
 *
283
 * Set the integer coordinates values of the MBR of this feature.
284
 **********************************************************************/
285
void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
286
                           GInt32 nYMax)
287
13.9k
{
288
13.9k
    m_nXMin = nXMin;
289
13.9k
    m_nYMin = nYMin;
290
13.9k
    m_nXMax = nXMax;
291
13.9k
    m_nYMax = nYMax;
292
13.9k
}
293
294
/**********************************************************************
295
 *                   TABFeature::GetIntMBR()
296
 *
297
 * Return the integer coordinates values of the MBR of this feature.
298
 **********************************************************************/
299
void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
300
                           GInt32 &nYMax)
301
14.2k
{
302
14.2k
    nXMin = m_nXMin;
303
14.2k
    nYMin = m_nYMin;
304
14.2k
    nXMax = m_nXMax;
305
14.2k
    nYMax = m_nYMax;
306
14.2k
}
307
308
/**********************************************************************
309
 *                   TABFeature::ReadRecordFromDATFile()
310
 *
311
 * Fill the fields part of the feature from the contents of the
312
 * table record pointed to by poDATFile.
313
 *
314
 * It is assumed that poDATFile currently points to the beginning of
315
 * the table record and that this feature's OGRFeatureDefn has been
316
 * properly initialized for this table.
317
 **********************************************************************/
318
int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
319
38.1k
{
320
38.1k
    CPLAssert(poDATFile);
321
322
38.1k
    const int numFields = poDATFile->GetNumFields();
323
324
581k
    for (int iField = 0; iField < numFields; iField++)
325
543k
    {
326
543k
        switch (poDATFile->GetFieldType(iField))
327
543k
        {
328
14.1k
            case TABFChar:
329
14.1k
            {
330
14.1k
                int iWidth(poDATFile->GetFieldWidth(iField));
331
14.1k
                CPLString osValue(poDATFile->ReadCharField(iWidth));
332
333
14.1k
                if (!poDATFile->GetEncoding().empty())
334
0
                {
335
0
                    osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
336
0
                }
337
14.1k
                SetField(iField, osValue);
338
14.1k
                break;
339
0
            }
340
106
            case TABFDecimal:
341
106
            {
342
106
                const double dValue = poDATFile->ReadDecimalField(
343
106
                    poDATFile->GetFieldWidth(iField));
344
106
                SetField(iField, dValue);
345
106
                break;
346
0
            }
347
13.4k
            case TABFInteger:
348
13.4k
            {
349
13.4k
                const int nValue = poDATFile->ReadIntegerField(
350
13.4k
                    poDATFile->GetFieldWidth(iField));
351
13.4k
                SetField(iField, nValue);
352
13.4k
                break;
353
0
            }
354
0
            case TABFSmallInt:
355
0
            {
356
0
                const int nValue = poDATFile->ReadSmallIntField(
357
0
                    poDATFile->GetFieldWidth(iField));
358
0
                SetField(iField, nValue);
359
0
                break;
360
0
            }
361
0
            case TABFLargeInt:
362
0
            {
363
0
                const GInt64 nValue = poDATFile->ReadLargeIntField(
364
0
                    poDATFile->GetFieldWidth(iField));
365
0
                SetField(iField, nValue);
366
0
                break;
367
0
            }
368
0
            case TABFFloat:
369
0
            {
370
0
                const double dValue =
371
0
                    poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
372
0
                SetField(iField, dValue);
373
0
                break;
374
0
            }
375
0
            case TABFLogical:
376
0
            {
377
0
                const bool bValue = poDATFile->ReadLogicalField(
378
0
                    poDATFile->GetFieldWidth(iField));
379
0
                SetField(iField, bValue ? 1 : 0);
380
0
                break;
381
0
            }
382
17.9k
            case TABFDate:
383
17.9k
            {
384
17.9k
#ifdef MITAB_USE_OFTDATETIME
385
17.9k
                int nYear = 0;
386
17.9k
                int nMonth = 0;
387
17.9k
                int nDay = 0;
388
17.9k
                const int status = poDATFile->ReadDateField(
389
17.9k
                    poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
390
17.9k
                if (status == 0)
391
16.7k
                {
392
16.7k
                    SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
393
16.7k
                }
394
#else
395
                const char *pszValue =
396
                    poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
397
                SetField(iField, pszValue);
398
#endif
399
17.9k
                break;
400
0
            }
401
6.10k
            case TABFTime:
402
6.10k
            {
403
6.10k
#ifdef MITAB_USE_OFTDATETIME
404
6.10k
                int nHour = 0;
405
6.10k
                int nMin = 0;
406
6.10k
                int nMS = 0;
407
6.10k
                int nSec = 0;
408
6.10k
                const int status =
409
6.10k
                    poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
410
6.10k
                                             &nHour, &nMin, &nSec, &nMS);
411
6.10k
                if (status == 0)
412
64
                {
413
64
                    int nYear = 0;
414
64
                    int nMonth = 0;
415
64
                    int nDay = 0;
416
64
                    SetField(iField, nYear, nMonth, nDay, nHour, nMin,
417
64
                             nSec + nMS / 1000.0f, 0);
418
64
                }
419
#else
420
                const char *pszValue =
421
                    poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
422
                SetField(iField, pszValue);
423
#endif
424
6.10k
                break;
425
0
            }
426
0
            case TABFDateTime:
427
0
            {
428
0
#ifdef MITAB_USE_OFTDATETIME
429
0
                int nYear = 0;
430
0
                int nMonth = 0;
431
0
                int nDay = 0;
432
0
                int nHour = 0;
433
0
                int nMin = 0;
434
0
                int nMS = 0;
435
0
                int nSec = 0;
436
0
                const int status = poDATFile->ReadDateTimeField(
437
0
                    poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
438
0
                    &nHour, &nMin, &nSec, &nMS);
439
0
                if (status == 0)
440
0
                {
441
0
                    SetField(iField, nYear, nMonth, nDay, nHour, nMin,
442
0
                             nSec + nMS / 1000.0f, 0);
443
0
                }
444
#else
445
                const char *pszValue = poDATFile->ReadDateTimeField(
446
                    poDATFile->GetFieldWidth(iField));
447
                SetField(iField, pszValue);
448
#endif
449
0
                break;
450
0
            }
451
491k
            default:
452
                // Other type???  Impossible!
453
491k
                CPLError(CE_Failure, CPLE_AssertionFailed,
454
491k
                         "Unsupported field type for field %d!", iField);
455
543k
        }
456
543k
    }
457
458
38.1k
    return 0;
459
38.1k
}
460
461
/**********************************************************************
462
 *                   TABFeature::WriteRecordToDATFile()
463
 *
464
 * Write the attribute part of the feature to the .DAT file.
465
 *
466
 * It is assumed that poDATFile currently points to the beginning of
467
 * the table record and that this feature's OGRFeatureDefn has been
468
 * properly initialized for this table.
469
 *
470
 * Returns 0 on success, -1 on error.
471
 **********************************************************************/
472
int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
473
                                     TABINDFile *poINDFile, int *panIndexNo)
474
245k
{
475
245k
#ifdef MITAB_USE_OFTDATETIME
476
245k
    int nYear = 0;
477
245k
    int nMon = 0;
478
245k
    int nDay = 0;
479
245k
    int nHour = 0;
480
245k
    int nMin = 0;
481
245k
    int nTZFlag = 0;
482
245k
    float fSec = 0.0f;
483
245k
#endif
484
485
245k
    CPLAssert(poDATFile);
486
487
245k
    const int numFields = poDATFile->GetNumFields();
488
489
245k
    poDATFile->MarkRecordAsExisting();
490
491
245k
    int nStatus = 0;
492
802k
    for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
493
557k
    {
494
        // Hack for "extra" introduced field.
495
557k
        if (iField >= GetDefnRef()->GetFieldCount())
496
0
        {
497
0
            CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
498
0
                      iField == 0);
499
0
            nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
500
0
                                                   poINDFile, 0);
501
0
            continue;
502
0
        }
503
557k
        CPLAssert(panIndexNo != nullptr);
504
505
557k
        switch (poDATFile->GetFieldType(iField))
506
557k
        {
507
536k
            case TABFChar:
508
536k
            {
509
536k
                CPLString osValue(GetFieldAsString(iField));
510
536k
                if (!poDATFile->GetEncoding().empty())
511
0
                {
512
0
                    osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
513
0
                }
514
536k
                nStatus = poDATFile->WriteCharField(
515
536k
                    osValue, poDATFile->GetFieldWidth(iField), poINDFile,
516
536k
                    panIndexNo[iField]);
517
536k
            }
518
536k
            break;
519
0
            case TABFDecimal:
520
0
                nStatus = poDATFile->WriteDecimalField(
521
0
                    GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
522
0
                    poDATFile->GetFieldPrecision(iField), poINDFile,
523
0
                    panIndexNo[iField]);
524
0
                break;
525
14.3k
            case TABFInteger:
526
14.3k
                nStatus = poDATFile->WriteIntegerField(
527
14.3k
                    GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
528
14.3k
                break;
529
0
            case TABFSmallInt:
530
0
                nStatus = poDATFile->WriteSmallIntField(
531
0
                    static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
532
0
                    panIndexNo[iField]);
533
0
                break;
534
0
            case TABFLargeInt:
535
0
                nStatus = poDATFile->WriteLargeIntField(
536
0
                    static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
537
0
                    panIndexNo[iField]);
538
0
                break;
539
6.38k
            case TABFFloat:
540
6.38k
                nStatus = poDATFile->WriteFloatField(
541
6.38k
                    GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
542
6.38k
                break;
543
0
            case TABFLogical:
544
0
                nStatus =
545
0
                    poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
546
0
                                                 poINDFile, panIndexNo[iField]);
547
0
                break;
548
0
            case TABFDate:
549
0
#ifdef MITAB_USE_OFTDATETIME
550
0
                if (IsFieldSetAndNotNull(iField))
551
0
                {
552
0
                    GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
553
0
                                       &nMin, &fSec, &nTZFlag);
554
0
                }
555
0
                else
556
0
                {
557
0
                    nYear = 0;
558
0
                    nMon = 0;
559
0
                    nDay = 0;
560
0
                }
561
562
0
                nStatus = poDATFile->WriteDateField(
563
0
                    nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
564
#else
565
                nStatus = poDATFile->WriteDateField(
566
                    GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
567
#endif
568
0
                break;
569
0
            case TABFTime:
570
0
#ifdef MITAB_USE_OFTDATETIME
571
0
                if (IsFieldSetAndNotNull(iField))
572
0
                {
573
0
                    GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
574
0
                                       &nMin, &fSec, &nTZFlag);
575
0
                }
576
0
                else
577
0
                {
578
                    // Put negative values, so that WriteTimeField() forges
579
                    // a negative value, and ultimately write -1 in the binary
580
                    // field
581
0
                    nHour = -1;
582
0
                    nMin = -1;
583
0
                    fSec = -1;
584
0
                }
585
0
                nStatus = poDATFile->WriteTimeField(
586
0
                    nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
587
0
                    poINDFile, panIndexNo[iField]);
588
589
#else
590
                nStatus = poDATFile->WriteTimeField(
591
                    GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
592
#endif
593
0
                break;
594
0
            case TABFDateTime:
595
0
#ifdef MITAB_USE_OFTDATETIME
596
0
                if (IsFieldSetAndNotNull(iField))
597
0
                {
598
0
                    GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
599
0
                                       &nMin, &fSec, &nTZFlag);
600
0
                }
601
0
                else
602
0
                {
603
0
                    nYear = 0;
604
0
                    nMon = 0;
605
0
                    nDay = 0;
606
0
                    nHour = 0;
607
0
                    nMin = 0;
608
0
                    fSec = 0;
609
0
                }
610
611
0
                nStatus = poDATFile->WriteDateTimeField(
612
0
                    nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
613
0
                    OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
614
#else
615
                nStatus = poDATFile->WriteDateTimeField(
616
                    GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
617
#endif
618
0
                break;
619
0
            default:
620
                // Other type???  Impossible!
621
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
622
0
                         "Unsupported field type!");
623
557k
        }
624
557k
    }
625
626
245k
    if (nStatus != 0)
627
0
        return nStatus;
628
629
245k
    if (poDATFile->CommitRecordToFile() != 0)
630
0
        return -1;
631
632
245k
    return 0;
633
245k
}
634
635
/**********************************************************************
636
 *                   TABFeature::ReadGeometryFromMAPFile()
637
 *
638
 * In derived classes, this method should be reimplemented to
639
 * fill the geometry and representation (color, etc...) part of the
640
 * feature from the contents of the .MAP object pointed to by poMAPFile.
641
 *
642
 * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
643
 * currently points to the beginning of a map object.
644
 *
645
 * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
646
 * the CoordBlock data during splitting of object blocks. In this case we
647
 * need to process only the information related to the CoordBlock. One
648
 * important thing to avoid is reading/writing pen/brush/symbol definitions
649
 * as that would screw up their ref counters.
650
 *
651
 * ppoCoordBlock is used by TABCollection and by index splitting code
652
 * to provide a CoordBlock to use instead of the one from the poMAPFile and
653
 * return the current pointer at the end of the call.
654
 *
655
 * The current implementation does nothing since instances of TABFeature
656
 * objects contain no geometry (i.e. TAB_GEOM_NONE).
657
 *
658
 * Returns 0 on success, -1 on error, in which case CPLError() will have
659
 * been called.
660
 **********************************************************************/
661
int TABFeature::ReadGeometryFromMAPFile(
662
    TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
663
    GBool /*bCoordBlockDataOnly=FALSE*/,
664
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
665
37.5k
{
666
    // Nothing to do. Instances of TABFeature objects contain no geometry.
667
37.5k
    return 0;
668
37.5k
}
669
670
/**********************************************************************
671
 *                   TABFeature::UpdateMBR()
672
 *
673
 * Fetch envelope of poGeom and update MBR.
674
 * Integer coord MBR is updated only if poMapFile is not NULL.
675
 *
676
 * Returns 0 on success, or -1 if there is no geometry in object
677
 **********************************************************************/
678
int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
679
966
{
680
966
    OGRGeometry *poGeom = GetGeometryRef();
681
682
966
    if (poGeom)
683
966
    {
684
966
        OGREnvelope oEnv;
685
966
        poGeom->getEnvelope(&oEnv);
686
687
966
        m_dXMin = oEnv.MinX;
688
966
        m_dYMin = oEnv.MinY;
689
966
        m_dXMax = oEnv.MaxX;
690
966
        m_dYMax = oEnv.MaxY;
691
692
966
        if (poMapFile)
693
966
        {
694
966
            poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
695
966
            poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
696
            // Coordsy2Int can transform a min value to a max one and vice
697
            // versa.
698
966
            if (m_nXMin > m_nXMax)
699
0
            {
700
0
                std::swap(m_nXMin, m_nXMax);
701
0
            }
702
966
            if (m_nYMin > m_nYMax)
703
0
            {
704
0
                std::swap(m_nYMin, m_nYMax);
705
0
            }
706
966
        }
707
708
966
        return 0;
709
966
    }
710
711
0
    return -1;
712
966
}
713
714
/**********************************************************************
715
 *                   TABFeature::ValidateCoordType()
716
 *
717
 * Checks the feature envelope to establish if the feature should be
718
 * written using Compressed coordinates or not and adjust m_nMapInfoType
719
 * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
720
 * m_nXMax, m_nYMax
721
 *
722
 * This function should be used only by the ValidateMapInfoType()
723
 * implementations.
724
 *
725
 * Returns TRUE if coord. should be compressed, FALSE otherwise
726
 **********************************************************************/
727
GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
728
616
{
729
616
    GBool bCompr = FALSE;
730
731
    /*-------------------------------------------------------------
732
     * Decide if coordinates should be compressed or not.
733
     *------------------------------------------------------------*/
734
616
    if (UpdateMBR(poMapFile) == 0)
735
616
    {
736
        /* Test for max range < 65535 here instead of < 65536 to avoid
737
         * compressed coordinate overflows in some boundary situations
738
         */
739
616
        if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
740
616
            (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
741
577
        {
742
577
            bCompr = TRUE;
743
577
        }
744
616
        m_nComprOrgX =
745
616
            static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
746
616
        m_nComprOrgY =
747
616
            static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
748
616
    }
749
750
    /*-------------------------------------------------------------
751
     * Adjust native type
752
     *------------------------------------------------------------*/
753
616
    if (bCompr && ((m_nMapInfoType % 3) == 2))
754
395
        m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
755
395
                                                  1);  // compr = 1, 4, 7, ...
756
221
    else if (!bCompr && ((m_nMapInfoType % 3) == 1))
757
0
        m_nMapInfoType = static_cast<TABGeomType>(
758
0
            m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
759
760
616
    return bCompr;
761
616
}
762
763
/**********************************************************************
764
 *                   TABFeature::ForceCoordTypeAndOrigin()
765
 *
766
 * This function is used by TABCollection::ValidateMapInfoType() to force
767
 * the coord type and compressed origin of all members of a collection
768
 * to be the same. (A replacement for ValidateCoordType() for this
769
 * specific case)
770
 **********************************************************************/
771
void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
772
                                         GInt32 nComprOrgX, GInt32 nComprOrgY,
773
                                         GInt32 nXMin, GInt32 nYMin,
774
                                         GInt32 nXMax, GInt32 nYMax)
775
0
{
776
    /*-------------------------------------------------------------
777
     * Set Compressed Origin and adjust native type
778
     *------------------------------------------------------------*/
779
0
    m_nComprOrgX = nComprOrgX;
780
0
    m_nComprOrgY = nComprOrgY;
781
782
0
    m_nMapInfoType = nMapInfoType;
783
784
0
    if (bCompr && ((m_nMapInfoType % 3) == 2))
785
0
        m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
786
0
                                                  1);  // compr = 1, 4, 7, ...
787
0
    else if (!bCompr && ((m_nMapInfoType % 3) == 1))
788
0
        m_nMapInfoType = static_cast<TABGeomType>(
789
0
            m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
790
791
0
    m_nXMin = nXMin;
792
0
    m_nYMin = nYMin;
793
0
    m_nXMax = nXMax;
794
0
    m_nYMax = nYMax;
795
0
}
796
797
/**********************************************************************
798
 *                   TABFeature::WriteGeometryToMAPFile()
799
 *
800
 *
801
 * In derived classes, this method should be reimplemented to
802
 * write the geometry and representation (color, etc...) part of the
803
 * feature to the .MAP object pointed to by poMAPFile.
804
 *
805
 * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
806
 * currently points to a valid map object.
807
 *
808
 * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
809
 * the CoordBlock data during splitting of object blocks. In this case we
810
 * need to process only the information related to the CoordBlock. One
811
 * important thing to avoid is reading/writing pen/brush/symbol definitions
812
 * as that would screw up their ref counters.
813
 *
814
 * ppoCoordBlock is used by TABCollection and by index splitting code
815
 * to provide a CoordBlock to use instead of the one from the poMAPFile and
816
 * return the current pointer at the end of the call.
817
 *
818
 * The current implementation does nothing since instances of TABFeature
819
 * objects contain no geometry (i.e. TAB_GEOM_NONE).
820
 *
821
 * Returns 0 on success, -1 on error, in which case CPLError() will have
822
 * been called.
823
 **********************************************************************/
824
int TABFeature::WriteGeometryToMAPFile(
825
    TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
826
    GBool /*bCoordBlockDataOnly=FALSE*/,
827
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
828
244k
{
829
    /*-----------------------------------------------------------------
830
     * Nothing to do... instances of TABFeature objects contain no geometry.
831
     *----------------------------------------------------------------*/
832
833
244k
    return 0;
834
244k
}
835
836
/**********************************************************************
837
 *                   TABFeature::DumpMID()
838
 *
839
 * Dump feature attributes in a format similar to .MID data records.
840
 **********************************************************************/
841
void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
842
0
{
843
0
    OGRFeatureDefn *l_poDefn = GetDefnRef();
844
845
0
    if (fpOut == nullptr)
846
0
        fpOut = stdout;
847
848
0
    for (int iField = 0; iField < GetFieldCount(); iField++)
849
0
    {
850
0
        OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
851
852
0
        fprintf(fpOut, "  %s (%s) = %s\n", poFDefn->GetNameRef(),
853
0
                OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
854
0
                GetFieldAsString(iField));
855
0
    }
856
857
0
    fflush(fpOut);
858
0
}
859
860
/**********************************************************************
861
 *                   TABFeature::DumpMIF()
862
 *
863
 * Dump feature geometry in a format similar to .MIF files.
864
 **********************************************************************/
865
void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
866
0
{
867
0
    if (fpOut == nullptr)
868
0
        fpOut = stdout;
869
870
    /*-----------------------------------------------------------------
871
     * Generate output... not much to do, feature contains no geometry.
872
     *----------------------------------------------------------------*/
873
0
    fprintf(fpOut, "NONE\n");
874
875
0
    fflush(fpOut);
876
0
}
877
878
/*=====================================================================
879
 *                      class TABPoint
880
 *====================================================================*/
881
882
/**********************************************************************
883
 *                   TABPoint::TABPoint()
884
 *
885
 * Constructor.
886
 **********************************************************************/
887
34.0k
TABPoint::TABPoint(OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
888
34.0k
{
889
34.0k
}
890
891
/**********************************************************************
892
 *                   TABPoint::~TABPoint()
893
 *
894
 * Destructor.
895
 **********************************************************************/
896
TABPoint::~TABPoint()
897
34.0k
{
898
34.0k
}
899
900
/**********************************************************************
901
 *                     TABPoint::CloneTABFeature()
902
 *
903
 * Duplicate feature, including stuff specific to each TABFeature type.
904
 *
905
 * This method calls the generic TABFeature::CloneTABFeature() and
906
 * then copies any members specific to its own type.
907
 **********************************************************************/
908
TABFeature *TABPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
909
13
{
910
    /*-----------------------------------------------------------------
911
     * Alloc new feature and copy the base stuff
912
     *----------------------------------------------------------------*/
913
13
    TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
914
915
13
    CopyTABFeatureBase(poNew);
916
917
    /*-----------------------------------------------------------------
918
     * And members specific to this class
919
     *----------------------------------------------------------------*/
920
    // ITABFeatureSymbol
921
13
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
922
923
13
    return poNew;
924
13
}
925
926
/**********************************************************************
927
 *                   TABPoint::ValidateMapInfoType()
928
 *
929
 * Check the feature's geometry part and return the corresponding
930
 * mapinfo object type code.  The m_nMapInfoType member will also
931
 * be updated for further calls to GetMapInfoType();
932
 *
933
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
934
 * is expected for this object class.
935
 **********************************************************************/
936
TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
937
300
{
938
    /*-----------------------------------------------------------------
939
     * Fetch and validate geometry
940
     * __TODO__ For now we always write in uncompressed format (until we
941
     * find that this is not correct... note that at this point the
942
     * decision to use compressed/uncompressed will likely be based on
943
     * the distance between the point and the object block center in
944
     * integer coordinates being > 32767 or not... remains to be verified)
945
     *----------------------------------------------------------------*/
946
300
    OGRGeometry *poGeom = GetGeometryRef();
947
300
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
948
300
    {
949
300
        switch (GetFeatureClass())
950
300
        {
951
0
            case TABFCFontPoint:
952
0
                m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
953
0
                break;
954
0
            case TABFCCustomPoint:
955
0
                m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
956
0
                break;
957
300
            case TABFCPoint:
958
300
            default:
959
300
                m_nMapInfoType = TAB_GEOM_SYMBOL;
960
300
                break;
961
300
        }
962
300
    }
963
0
    else
964
0
    {
965
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
966
0
                 "TABPoint: Missing or Invalid Geometry!");
967
0
        m_nMapInfoType = TAB_GEOM_NONE;
968
0
    }
969
970
300
    UpdateMBR(poMapFile);
971
972
300
    return m_nMapInfoType;
973
300
}
974
975
/**********************************************************************
976
 *                   TABPoint::ReadGeometryFromMAPFile()
977
 *
978
 * Fill the geometry and representation (color, etc...) part of the
979
 * feature from the contents of the .MAP object pointed to by poMAPFile.
980
 *
981
 * It is assumed that poMAPFile currently points to the beginning of
982
 * a map object.
983
 *
984
 * Returns 0 on success, -1 on error, in which case CPLError() will have
985
 * been called.
986
 **********************************************************************/
987
int TABPoint::ReadGeometryFromMAPFile(
988
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
989
    GBool bCoordBlockDataOnly /*=FALSE*/,
990
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
991
72
{
992
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
993
72
    if (bCoordBlockDataOnly)
994
0
        return 0;
995
996
    /*-----------------------------------------------------------------
997
     * Fetch and validate geometry type
998
     *----------------------------------------------------------------*/
999
72
    m_nMapInfoType = poObjHdr->m_nType;
1000
1001
72
    if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
1002
72
        m_nMapInfoType != TAB_GEOM_SYMBOL_C)
1003
0
    {
1004
0
        CPLError(
1005
0
            CE_Failure, CPLE_AssertionFailed,
1006
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1007
0
            m_nMapInfoType, m_nMapInfoType);
1008
0
        return -1;
1009
0
    }
1010
1011
    /*-----------------------------------------------------------------
1012
     * Read object information
1013
     *----------------------------------------------------------------*/
1014
72
    TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1015
1016
72
    m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
1017
1018
72
    poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1019
1020
    /*-----------------------------------------------------------------
1021
     * Create and fill geometry object
1022
     *----------------------------------------------------------------*/
1023
72
    double dX = 0.0;
1024
72
    double dY = 0.0;
1025
1026
72
    poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1027
72
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1028
1029
72
    SetGeometryDirectly(poGeometry);
1030
1031
72
    SetMBR(dX, dY, dX, dY);
1032
72
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1033
72
              poObjHdr->m_nMaxY);
1034
1035
72
    return 0;
1036
72
}
1037
1038
/**********************************************************************
1039
 *                   TABPoint::WriteGeometryToMAPFile()
1040
 *
1041
 * Write the geometry and representation (color, etc...) part of the
1042
 * feature to the .MAP object pointed to by poMAPFile.
1043
 *
1044
 * It is assumed that poMAPFile currently points to a valid map object.
1045
 *
1046
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1047
 * been called.
1048
 **********************************************************************/
1049
int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
1050
                                     TABMAPObjHdr *poObjHdr,
1051
                                     GBool bCoordBlockDataOnly /*=FALSE*/,
1052
                                     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1053
300
{
1054
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1055
300
    if (bCoordBlockDataOnly)
1056
0
        return 0;
1057
1058
    /*-----------------------------------------------------------------
1059
     * We assume that ValidateMapInfoType() was called already and that
1060
     * the type in poObjHdr->m_nType is valid.
1061
     *----------------------------------------------------------------*/
1062
300
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1063
1064
    /*-----------------------------------------------------------------
1065
     * Fetch and validate geometry
1066
     *----------------------------------------------------------------*/
1067
300
    OGRGeometry *poGeom = GetGeometryRef();
1068
300
    OGRPoint *poPoint = nullptr;
1069
300
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1070
300
        poPoint = poGeom->toPoint();
1071
0
    else
1072
0
    {
1073
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1074
0
                 "TABPoint: Missing or Invalid Geometry!");
1075
0
        return -1;
1076
0
    }
1077
1078
300
    GInt32 nX = 0;
1079
300
    GInt32 nY = 0;
1080
300
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1081
1082
    /*-----------------------------------------------------------------
1083
     * Copy object information
1084
     *----------------------------------------------------------------*/
1085
300
    TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1086
1087
300
    poPointHdr->m_nX = nX;
1088
300
    poPointHdr->m_nY = nY;
1089
300
    poPointHdr->SetMBR(nX, nY, nX, nY);
1090
1091
300
    m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1092
300
    poPointHdr->m_nSymbolId =
1093
300
        static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
1094
1095
300
    if (CPLGetLastErrorType() == CE_Failure)
1096
0
        return -1;
1097
1098
300
    return 0;
1099
300
}
1100
1101
/**********************************************************************
1102
 *                   TABPoint::GetX()
1103
 *
1104
 * Return this point's X coordinate.
1105
 **********************************************************************/
1106
double TABPoint::GetX()
1107
0
{
1108
1109
    /*-----------------------------------------------------------------
1110
     * Fetch and validate geometry
1111
     *----------------------------------------------------------------*/
1112
0
    OGRGeometry *poGeom = GetGeometryRef();
1113
0
    OGRPoint *poPoint = nullptr;
1114
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1115
0
        poPoint = poGeom->toPoint();
1116
0
    else
1117
0
    {
1118
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1119
0
                 "TABPoint: Missing or Invalid Geometry!");
1120
0
        return 0.0;
1121
0
    }
1122
1123
0
    return poPoint->getX();
1124
0
}
1125
1126
/**********************************************************************
1127
 *                   TABPoint::GetY()
1128
 *
1129
 * Return this point's Y coordinate.
1130
 **********************************************************************/
1131
double TABPoint::GetY()
1132
0
{
1133
    /*-----------------------------------------------------------------
1134
     * Fetch and validate geometry
1135
     *----------------------------------------------------------------*/
1136
0
    OGRGeometry *poGeom = GetGeometryRef();
1137
0
    OGRPoint *poPoint = nullptr;
1138
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1139
0
        poPoint = poGeom->toPoint();
1140
0
    else
1141
0
    {
1142
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1143
0
                 "TABPoint: Missing or Invalid Geometry!");
1144
0
        return 0.0;
1145
0
    }
1146
1147
0
    return poPoint->getY();
1148
0
}
1149
1150
/**********************************************************************
1151
 *                   TABPoint::GetStyleString() const
1152
 *
1153
 * Return style string for this feature.
1154
 *
1155
 * Style String is built only once during the first call to GetStyleString().
1156
 **********************************************************************/
1157
const char *TABPoint::GetStyleString() const
1158
0
{
1159
0
    if (m_pszStyleString == nullptr)
1160
0
    {
1161
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1162
0
    }
1163
1164
0
    return m_pszStyleString;
1165
0
}
1166
1167
/**********************************************************************
1168
 *                   TABPoint::DumpMIF()
1169
 *
1170
 * Dump feature geometry in a format similar to .MIF POINTs.
1171
 **********************************************************************/
1172
void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
1173
0
{
1174
0
    if (fpOut == nullptr)
1175
0
        fpOut = stdout;
1176
1177
    /*-----------------------------------------------------------------
1178
     * Fetch and validate geometry
1179
     *----------------------------------------------------------------*/
1180
0
    OGRGeometry *poGeom = GetGeometryRef();
1181
0
    OGRPoint *poPoint = nullptr;
1182
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1183
0
        poPoint = poGeom->toPoint();
1184
0
    else
1185
0
    {
1186
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1187
0
                 "TABPoint: Missing or Invalid Geometry!");
1188
0
        return;
1189
0
    }
1190
1191
    /*-----------------------------------------------------------------
1192
     * Generate output
1193
     *----------------------------------------------------------------*/
1194
0
    fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
1195
1196
0
    DumpSymbolDef(fpOut);
1197
1198
    /*-----------------------------------------------------------------
1199
     * Handle stuff specific to derived classes
1200
     *----------------------------------------------------------------*/
1201
    // cppcheck-suppress knownConditionTrueFalse
1202
0
    if (GetFeatureClass() == TABFCFontPoint)
1203
0
    {
1204
0
        TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
1205
0
        fprintf(fpOut, "  m_nFontStyle     = 0x%2.2x (%d)\n",
1206
0
                poFeature->GetFontStyleTABValue(),
1207
0
                poFeature->GetFontStyleTABValue());
1208
1209
0
        poFeature->DumpFontDef(fpOut);
1210
0
    }
1211
    // cppcheck-suppress knownConditionTrueFalse
1212
0
    if (GetFeatureClass() == TABFCCustomPoint)
1213
0
    {
1214
0
        TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
1215
1216
0
        fprintf(fpOut, "  m_nUnknown_      = 0x%2.2x (%d)\n",
1217
0
                poFeature->m_nUnknown_, poFeature->m_nUnknown_);
1218
0
        fprintf(fpOut, "  m_nCustomStyle   = 0x%2.2x (%d)\n",
1219
0
                poFeature->GetCustomSymbolStyle(),
1220
0
                poFeature->GetCustomSymbolStyle());
1221
1222
0
        poFeature->DumpFontDef(fpOut);
1223
0
    }
1224
1225
0
    fflush(fpOut);
1226
0
}
1227
1228
/*=====================================================================
1229
 *                      class TABFontPoint
1230
 *====================================================================*/
1231
1232
/**********************************************************************
1233
 *                   TABFontPoint::TABFontPoint()
1234
 *
1235
 * Constructor.
1236
 **********************************************************************/
1237
TABFontPoint::TABFontPoint(OGRFeatureDefn *poDefnIn)
1238
9.32k
    : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
1239
9.32k
{
1240
9.32k
}
1241
1242
/**********************************************************************
1243
 *                   TABFontPoint::~TABFontPoint()
1244
 *
1245
 * Destructor.
1246
 **********************************************************************/
1247
TABFontPoint::~TABFontPoint()
1248
9.32k
{
1249
9.32k
}
1250
1251
/**********************************************************************
1252
 *                     TABFontPoint::CloneTABFeature()
1253
 *
1254
 * Duplicate feature, including stuff specific to each TABFeature type.
1255
 *
1256
 * This method calls the generic TABFeature::CloneTABFeature() and
1257
 * then copies any members specific to its own type.
1258
 **********************************************************************/
1259
TABFeature *TABFontPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1260
0
{
1261
    /*-----------------------------------------------------------------
1262
     * Alloc new feature and copy the base stuff
1263
     *----------------------------------------------------------------*/
1264
0
    TABFontPoint *poNew =
1265
0
        new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
1266
1267
0
    CopyTABFeatureBase(poNew);
1268
1269
    /*-----------------------------------------------------------------
1270
     * And members specific to this class
1271
     *----------------------------------------------------------------*/
1272
    // ITABFeatureSymbol
1273
0
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1274
1275
    // ITABFeatureFont
1276
0
    *(poNew->GetFontDefRef()) = *GetFontDefRef();
1277
1278
0
    poNew->SetSymbolAngle(GetSymbolAngle());
1279
0
    poNew->SetFontStyleTABValue(GetFontStyleTABValue());
1280
1281
0
    return poNew;
1282
0
}
1283
1284
/**********************************************************************
1285
 *                   TABFontPoint::ReadGeometryFromMAPFile()
1286
 *
1287
 * Fill the geometry and representation (color, etc...) part of the
1288
 * feature from the contents of the .MAP object pointed to by poMAPFile.
1289
 *
1290
 * It is assumed that poMAPFile currently points to the beginning of
1291
 * a map object.
1292
 *
1293
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1294
 * been called.
1295
 **********************************************************************/
1296
int TABFontPoint::ReadGeometryFromMAPFile(
1297
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1298
    GBool bCoordBlockDataOnly /*=FALSE*/,
1299
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1300
39
{
1301
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1302
39
    if (bCoordBlockDataOnly)
1303
0
        return 0;
1304
1305
    /*-----------------------------------------------------------------
1306
     * Fetch and validate geometry type
1307
     *----------------------------------------------------------------*/
1308
39
    m_nMapInfoType = poObjHdr->m_nType;
1309
1310
39
    if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
1311
39
        m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
1312
0
    {
1313
0
        CPLError(
1314
0
            CE_Failure, CPLE_AssertionFailed,
1315
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1316
0
            m_nMapInfoType, m_nMapInfoType);
1317
0
        return -1;
1318
0
    }
1319
1320
    /*-----------------------------------------------------------------
1321
     * Read object information
1322
     * NOTE: This symbol type does not contain a reference to a
1323
     * SymbolDef block in the file, but we still use the m_sSymbolDef
1324
     * structure to store the information inside the class so that the
1325
     * ITABFeatureSymbol methods work properly for the class user.
1326
     *----------------------------------------------------------------*/
1327
39
    TABMAPObjFontPoint *poPointHdr =
1328
39
        cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1329
1330
39
    m_nSymbolDefIndex = -1;
1331
39
    m_sSymbolDef.nRefCount = 0;
1332
1333
39
    m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId;    // shape
1334
39
    m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize;  // point size
1335
1336
39
    m_nFontStyle = poPointHdr->m_nFontStyle;  // font style
1337
1338
39
    m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
1339
39
                            poPointHdr->m_nG * 256 + poPointHdr->m_nB;
1340
1341
    /*-------------------------------------------------------------
1342
     * Symbol Angle, in tenths of degree.
1343
     * Contrary to arc start/end angles, no conversion based on
1344
     * origin quadrant is required here.
1345
     *------------------------------------------------------------*/
1346
39
    m_dAngle = poPointHdr->m_nAngle / 10.0;
1347
1348
39
    m_nFontDefIndex = poPointHdr->m_nFontId;  // Font name index
1349
1350
39
    poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1351
1352
    /*-----------------------------------------------------------------
1353
     * Create and fill geometry object
1354
     *----------------------------------------------------------------*/
1355
39
    double dX = 0.0;
1356
39
    double dY = 0.0;
1357
39
    poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1358
39
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1359
1360
39
    SetGeometryDirectly(poGeometry);
1361
1362
39
    SetMBR(dX, dY, dX, dY);
1363
39
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1364
39
              poObjHdr->m_nMaxY);
1365
1366
39
    return 0;
1367
39
}
1368
1369
/**********************************************************************
1370
 *                   TABFontPoint::WriteGeometryToMAPFile()
1371
 *
1372
 * Write the geometry and representation (color, etc...) part of the
1373
 * feature to the .MAP object pointed to by poMAPFile.
1374
 *
1375
 * It is assumed that poMAPFile currently points to a valid map object.
1376
 *
1377
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1378
 * been called.
1379
 **********************************************************************/
1380
int TABFontPoint::WriteGeometryToMAPFile(
1381
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1382
    GBool bCoordBlockDataOnly /*=FALSE*/,
1383
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1384
0
{
1385
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1386
0
    if (bCoordBlockDataOnly)
1387
0
        return 0;
1388
1389
    /*-----------------------------------------------------------------
1390
     * We assume that ValidateMapInfoType() was called already and that
1391
     * the type in poObjHdr->m_nType is valid.
1392
     *----------------------------------------------------------------*/
1393
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1394
1395
    /*-----------------------------------------------------------------
1396
     * Fetch and validate geometry
1397
     *----------------------------------------------------------------*/
1398
0
    OGRGeometry *poGeom = GetGeometryRef();
1399
0
    OGRPoint *poPoint = nullptr;
1400
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1401
0
        poPoint = poGeom->toPoint();
1402
0
    else
1403
0
    {
1404
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1405
0
                 "TABFontPoint: Missing or Invalid Geometry!");
1406
0
        return -1;
1407
0
    }
1408
1409
0
    GInt32 nX = 0;
1410
0
    GInt32 nY = 0;
1411
0
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1412
1413
    /*-----------------------------------------------------------------
1414
     * Copy object information
1415
     * NOTE: This symbol type does not contain a reference to a
1416
     * SymbolDef block in the file, but we still use the m_sSymbolDef
1417
     * structure to store the information inside the class so that the
1418
     * ITABFeatureSymbol methods work properly for the class user.
1419
     *----------------------------------------------------------------*/
1420
0
    TABMAPObjFontPoint *poPointHdr =
1421
0
        cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1422
1423
0
    poPointHdr->m_nX = nX;
1424
0
    poPointHdr->m_nY = nY;
1425
0
    poPointHdr->SetMBR(nX, nY, nX, nY);
1426
1427
0
    poPointHdr->m_nSymbolId =
1428
0
        static_cast<GByte>(m_sSymbolDef.nSymbolNo);  // shape
1429
0
    poPointHdr->m_nPointSize =
1430
0
        static_cast<GByte>(m_sSymbolDef.nPointSize);  // point size
1431
0
    poPointHdr->m_nFontStyle = m_nFontStyle;          // font style
1432
1433
0
    poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
1434
0
    poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
1435
0
    poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
1436
1437
    /*-------------------------------------------------------------
1438
     * Symbol Angle, in tenths of degree.
1439
     * Contrary to arc start/end angles, no conversion based on
1440
     * origin quadrant is required here.
1441
     *------------------------------------------------------------*/
1442
0
    poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
1443
1444
    // Write Font Def
1445
0
    m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1446
0
    poPointHdr->m_nFontId =
1447
0
        static_cast<GByte>(m_nFontDefIndex);  // Font name index
1448
1449
0
    if (CPLGetLastErrorType() == CE_Failure)
1450
0
        return -1;
1451
1452
0
    return 0;
1453
0
}
1454
1455
/**********************************************************************
1456
 *                   TABFontPoint::QueryFontStyle()
1457
 *
1458
 * Return TRUE if the specified font style attribute is turned ON,
1459
 * or FALSE otherwise.  See enum TABFontStyle for the list of styles
1460
 * that can be queried on.
1461
 **********************************************************************/
1462
GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
1463
0
{
1464
0
    return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
1465
0
}
1466
1467
void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
1468
0
{
1469
0
    if (bStyleOn)
1470
0
        m_nFontStyle |= static_cast<int>(eStyleToToggle);
1471
0
    else
1472
0
        m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
1473
0
}
1474
1475
/**********************************************************************
1476
 *                   TABFontPoint::GetFontStyleMIFValue()
1477
 *
1478
 * Return the Font Style value for this object using the style values
1479
 * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
1480
 *
1481
 * The reason why we have to differentiate between the TAB and the MIF font
1482
 * style values is that in TAB, TABFSBox is included in the style value
1483
 * as code 0x100, but in MIF it is not included, instead it is implied by
1484
 * the presence of the BG color in the FONT() clause (the BG color is
1485
 * present only when TABFSBox or TABFSHalo is set).
1486
 * This also has the effect of shifting all the other style values > 0x100
1487
 * by 1 byte.
1488
 *
1489
 * NOTE: Even if there is no BG color for font symbols, we inherit this
1490
 * problem because Font Point styles use the same codes as Text Font styles.
1491
 **********************************************************************/
1492
int TABFontPoint::GetFontStyleMIFValue()
1493
0
{
1494
    // The conversion is simply to remove bit 0x100 from the value and shift
1495
    // down all values past this bit.
1496
0
    return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
1497
0
}
1498
1499
void TABFontPoint::SetFontStyleMIFValue(int nStyle)
1500
9.28k
{
1501
9.28k
    m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
1502
9.28k
}
1503
1504
/**********************************************************************
1505
 *                   TABFontPoint::SetSymbolAngle()
1506
 *
1507
 * Set the symbol angle value in degrees, making sure the value is
1508
 * always in the range [0..360]
1509
 **********************************************************************/
1510
void TABFontPoint::SetSymbolAngle(double dAngle)
1511
9.28k
{
1512
9.28k
    dAngle = fmod(dAngle, 360.0);
1513
9.28k
    if (dAngle < 0.0)
1514
1.72k
        dAngle += 360.0;
1515
1516
9.28k
    m_dAngle = dAngle;
1517
9.28k
}
1518
1519
/**********************************************************************
1520
 *                   TABFontPoint::GetSymbolStyleString()
1521
 *
1522
 *  Return a Symbol() string. All representations info for the Symbol are here.
1523
 **********************************************************************/
1524
const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
1525
0
{
1526
    /* Get the SymbolStyleString, and add the outline Color
1527
       (halo/border in MapInfo Symbol terminology) */
1528
0
    const char *outlineColor = nullptr;
1529
0
    if (m_nFontStyle & 16)
1530
0
        outlineColor = ",o:#000000";
1531
0
    else if (m_nFontStyle & 512)
1532
0
        outlineColor = ",o:#ffffff";
1533
0
    else
1534
0
        outlineColor = "";
1535
1536
0
    int nAngle = static_cast<int>(dfAngle);
1537
0
    const char *pszStyle;
1538
1539
0
    pszStyle = CPLSPrintf(
1540
0
        "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
1541
0
        nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
1542
0
        m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
1543
0
    return pszStyle;
1544
0
}
1545
1546
/**********************************************************************
1547
 *                   TABFontPoint::GetStyleString() const
1548
 *
1549
 * Return style string for this feature.
1550
 *
1551
 * Style String is built only once during the first call to GetStyleString().
1552
 **********************************************************************/
1553
const char *TABFontPoint::GetStyleString() const
1554
0
{
1555
0
    if (m_pszStyleString == nullptr)
1556
0
    {
1557
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
1558
0
    }
1559
1560
0
    return m_pszStyleString;
1561
0
}
1562
1563
/**********************************************************************
1564
 *                   TABFontPoint::SetSymbolFromStyle()
1565
 *
1566
 *  Set all Symbol var from a OGRStyleSymbol.
1567
 **********************************************************************/
1568
void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1569
0
{
1570
0
    ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1571
1572
0
    GBool bIsNull = 0;
1573
1574
    // Try to set font glyph number
1575
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1576
0
    if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
1577
0
    {
1578
0
        const int nSymbolId = atoi(pszSymbolId + 9);
1579
0
        SetSymbolNo(static_cast<GInt16>(nSymbolId));
1580
0
    }
1581
1582
0
    const char *pszFontName = poSymbolStyle->FontName(bIsNull);
1583
0
    if ((!bIsNull) && pszFontName)
1584
0
    {
1585
0
        SetFontName(pszFontName);
1586
0
    }
1587
0
}
1588
1589
/*=====================================================================
1590
 *                      class TABCustomPoint
1591
 *====================================================================*/
1592
1593
/**********************************************************************
1594
 *                   TABCustomPoint::TABCustomPoint()
1595
 *
1596
 * Constructor.
1597
 **********************************************************************/
1598
TABCustomPoint::TABCustomPoint(OGRFeatureDefn *poDefnIn)
1599
5.57k
    : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
1600
5.57k
{
1601
5.57k
}
1602
1603
/**********************************************************************
1604
 *                   TABCustomPoint::~TABCustomPoint()
1605
 *
1606
 * Destructor.
1607
 **********************************************************************/
1608
TABCustomPoint::~TABCustomPoint()
1609
5.57k
{
1610
5.57k
}
1611
1612
/**********************************************************************
1613
 *                     TABCustomPoint::CloneTABFeature()
1614
 *
1615
 * Duplicate feature, including stuff specific to each TABFeature type.
1616
 *
1617
 * This method calls the generic TABFeature::CloneTABFeature() and
1618
 * then copies any members specific to its own type.
1619
 **********************************************************************/
1620
TABFeature *TABCustomPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1621
0
{
1622
    /*-----------------------------------------------------------------
1623
     * Alloc new feature and copy the base stuff
1624
     *----------------------------------------------------------------*/
1625
0
    TABCustomPoint *poNew =
1626
0
        new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
1627
1628
0
    CopyTABFeatureBase(poNew);
1629
1630
    /*-----------------------------------------------------------------
1631
     * And members specific to this class
1632
     *----------------------------------------------------------------*/
1633
    // ITABFeatureSymbol
1634
0
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1635
1636
    // ITABFeatureFont
1637
0
    *(poNew->GetFontDefRef()) = *GetFontDefRef();
1638
1639
0
    poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
1640
1641
0
    return poNew;
1642
0
}
1643
1644
/**********************************************************************
1645
 *                   TABCustomPoint::ReadGeometryFromMAPFile()
1646
 *
1647
 * Fill the geometry and representation (color, etc...) part of the
1648
 * feature from the contents of the .MAP object pointed to by poMAPFile.
1649
 *
1650
 * It is assumed that poMAPFile currently points to the beginning of
1651
 * a map object.
1652
 *
1653
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1654
 * been called.
1655
 **********************************************************************/
1656
int TABCustomPoint::ReadGeometryFromMAPFile(
1657
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1658
    GBool bCoordBlockDataOnly /*=FALSE*/,
1659
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1660
62
{
1661
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1662
62
    if (bCoordBlockDataOnly)
1663
0
        return 0;
1664
1665
    /*-----------------------------------------------------------------
1666
     * Fetch and validate geometry type
1667
     *----------------------------------------------------------------*/
1668
62
    m_nMapInfoType = poObjHdr->m_nType;
1669
1670
62
    if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
1671
62
        m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
1672
0
    {
1673
0
        CPLError(
1674
0
            CE_Failure, CPLE_AssertionFailed,
1675
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1676
0
            m_nMapInfoType, m_nMapInfoType);
1677
0
        return -1;
1678
0
    }
1679
1680
    /*-----------------------------------------------------------------
1681
     * Read object information
1682
     *----------------------------------------------------------------*/
1683
62
    TABMAPObjCustomPoint *poPointHdr =
1684
62
        cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1685
1686
62
    m_nUnknown_ = poPointHdr->m_nUnknown_;        // ???
1687
62
    m_nCustomStyle = poPointHdr->m_nCustomStyle;  // 0x01=Show BG,
1688
                                                  // 0x02=Apply Color
1689
1690
62
    m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
1691
62
    poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1692
1693
62
    m_nFontDefIndex = poPointHdr->m_nFontId;  // Font index
1694
62
    poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1695
1696
    /*-----------------------------------------------------------------
1697
     * Create and fill geometry object
1698
     *----------------------------------------------------------------*/
1699
62
    double dX = 0.0;
1700
62
    double dY = 0.0;
1701
62
    poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1702
62
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1703
1704
62
    SetGeometryDirectly(poGeometry);
1705
1706
62
    SetMBR(dX, dY, dX, dY);
1707
62
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1708
62
              poObjHdr->m_nMaxY);
1709
1710
62
    return 0;
1711
62
}
1712
1713
/**********************************************************************
1714
 *                   TABCustomPoint::WriteGeometryToMAPFile()
1715
 *
1716
 * Write the geometry and representation (color, etc...) part of the
1717
 * feature to the .MAP object pointed to by poMAPFile.
1718
 *
1719
 * It is assumed that poMAPFile currently points to a valid map object.
1720
 *
1721
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1722
 * been called.
1723
 **********************************************************************/
1724
int TABCustomPoint::WriteGeometryToMAPFile(
1725
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1726
    GBool bCoordBlockDataOnly /*=FALSE*/,
1727
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1728
0
{
1729
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1730
0
    if (bCoordBlockDataOnly)
1731
0
        return 0;
1732
1733
    /*-----------------------------------------------------------------
1734
     * We assume that ValidateMapInfoType() was called already and that
1735
     * the type in poObjHdr->m_nType is valid.
1736
     *----------------------------------------------------------------*/
1737
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1738
1739
    /*-----------------------------------------------------------------
1740
     * Fetch and validate geometry
1741
     *----------------------------------------------------------------*/
1742
0
    OGRGeometry *poGeom = GetGeometryRef();
1743
0
    OGRPoint *poPoint = nullptr;
1744
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1745
0
        poPoint = poGeom->toPoint();
1746
0
    else
1747
0
    {
1748
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1749
0
                 "TABCustomPoint: Missing or Invalid Geometry!");
1750
0
        return -1;
1751
0
    }
1752
1753
0
    GInt32 nX = 0;
1754
0
    GInt32 nY = 0;
1755
0
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1756
1757
    /*-----------------------------------------------------------------
1758
     * Copy object information
1759
     *----------------------------------------------------------------*/
1760
0
    TABMAPObjCustomPoint *poPointHdr =
1761
0
        cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1762
1763
0
    poPointHdr->m_nX = nX;
1764
0
    poPointHdr->m_nY = nY;
1765
0
    poPointHdr->SetMBR(nX, nY, nX, nY);
1766
0
    poPointHdr->m_nUnknown_ = m_nUnknown_;
1767
0
    poPointHdr->m_nCustomStyle = m_nCustomStyle;  // 0x01=Show BG,
1768
                                                  // 0x02=Apply Color
1769
1770
0
    m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1771
0
    poPointHdr->m_nSymbolId =
1772
0
        static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
1773
1774
0
    m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1775
0
    poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex);  // Font index
1776
1777
0
    if (CPLGetLastErrorType() == CE_Failure)
1778
0
        return -1;
1779
1780
0
    return 0;
1781
0
}
1782
1783
/**********************************************************************
1784
 *                   TABCustomPoint::GetSymbolStyleString()
1785
 *
1786
 *  Return a Symbol() string. All representations info for the Symbol are here.
1787
 **********************************************************************/
1788
const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
1789
0
{
1790
    /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
1791
     * "apply color". */
1792
0
    const char *color = nullptr;
1793
0
    if (m_nCustomStyle & 0x02)
1794
0
        color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
1795
0
    else
1796
0
        color = "";
1797
1798
0
    int nAngle = static_cast<int>(dfAngle);
1799
0
    const char *pszStyle;
1800
0
    const std::string osExt = CPLGetExtensionSafe(GetSymbolNameRef());
1801
0
    char szLowerExt[8] = "";
1802
0
    const char *pszPtr = osExt.c_str();
1803
0
    int i;
1804
1805
0
    for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
1806
0
    {
1807
0
        szLowerExt[i] =
1808
0
            static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
1809
0
    }
1810
0
    szLowerExt[i] = '\0';
1811
1812
0
    pszStyle = CPLSPrintf(
1813
0
        "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
1814
0
        nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
1815
0
        GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
1816
0
    return pszStyle;
1817
0
}
1818
1819
/**********************************************************************
1820
 *                   TABCustomPoint::SetSymbolFromStyle()
1821
 *
1822
 *  Set all Symbol var from a OGRStyleSymbol.
1823
 **********************************************************************/
1824
void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1825
0
{
1826
0
    ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1827
1828
0
    GBool bIsNull = 0;
1829
1830
    // Try to set font glyph number
1831
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1832
0
    if ((!bIsNull) && pszSymbolId &&
1833
0
        STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
1834
0
    {
1835
0
        const int nSymbolStyle = atoi(pszSymbolId + 19);
1836
0
        SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
1837
1838
0
        const char *pszPtr = pszSymbolId + 19;
1839
0
        while (*pszPtr != '-')
1840
0
        {
1841
0
            pszPtr++;
1842
0
        }
1843
0
        pszPtr++;
1844
1845
0
        char szSymbolName[256] = "";
1846
0
        int i;
1847
0
        for (i = 0;
1848
0
             i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
1849
0
             i++, pszPtr++)
1850
0
        {
1851
0
            szSymbolName[i] = *pszPtr;
1852
0
        }
1853
0
        szSymbolName[i] = '\0';
1854
0
        SetSymbolName(szSymbolName);
1855
0
    }
1856
0
}
1857
1858
/**********************************************************************
1859
 *                   TABCustomPoint::GetStyleString() const
1860
 *
1861
 * Return style string for this feature.
1862
 *
1863
 * Style String is built only once during the first call to GetStyleString().
1864
 **********************************************************************/
1865
const char *TABCustomPoint::GetStyleString() const
1866
0
{
1867
0
    if (m_pszStyleString == nullptr)
1868
0
    {
1869
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1870
0
    }
1871
1872
0
    return m_pszStyleString;
1873
0
}
1874
1875
/*=====================================================================
1876
 *                      class TABPolyline
1877
 *====================================================================*/
1878
1879
/**********************************************************************
1880
 *                   TABPolyline::TABPolyline()
1881
 *
1882
 * Constructor.
1883
 **********************************************************************/
1884
TABPolyline::TABPolyline(OGRFeatureDefn *poDefnIn)
1885
46.7k
    : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
1886
46.7k
      m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
1887
46.7k
{
1888
46.7k
}
1889
1890
/**********************************************************************
1891
 *                   TABPolyline::~TABPolyline()
1892
 *
1893
 * Destructor.
1894
 **********************************************************************/
1895
TABPolyline::~TABPolyline()
1896
46.7k
{
1897
46.7k
}
1898
1899
/**********************************************************************
1900
 *                     TABPolyline::CloneTABFeature()
1901
 *
1902
 * Duplicate feature, including stuff specific to each TABFeature type.
1903
 *
1904
 * This method calls the generic TABFeature::CloneTABFeature() and
1905
 * then copies any members specific to its own type.
1906
 **********************************************************************/
1907
TABFeature *TABPolyline::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1908
0
{
1909
    /*-----------------------------------------------------------------
1910
     * Alloc new feature and copy the base stuff
1911
     *----------------------------------------------------------------*/
1912
0
    TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
1913
1914
0
    CopyTABFeatureBase(poNew);
1915
1916
    /*-----------------------------------------------------------------
1917
     * And members specific to this class
1918
     *----------------------------------------------------------------*/
1919
    // ITABFeaturePen
1920
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
1921
1922
0
    poNew->m_bSmooth = m_bSmooth;
1923
0
    poNew->m_bCenterIsSet = m_bCenterIsSet;
1924
0
    poNew->m_dCenterX = m_dCenterX;
1925
0
    poNew->m_dCenterY = m_dCenterY;
1926
1927
0
    return poNew;
1928
0
}
1929
1930
/**********************************************************************
1931
 *                   TABPolyline::GetNumParts()
1932
 *
1933
 * Return the total number of parts in this object.
1934
 *
1935
 * Returns 0 if the geometry contained in the object is invalid or missing.
1936
 **********************************************************************/
1937
int TABPolyline::GetNumParts()
1938
0
{
1939
0
    int numParts = 0;
1940
1941
0
    OGRGeometry *poGeom = GetGeometryRef();
1942
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1943
0
    {
1944
        /*-------------------------------------------------------------
1945
         * Simple polyline
1946
         *------------------------------------------------------------*/
1947
0
        numParts = 1;
1948
0
    }
1949
0
    else if (poGeom &&
1950
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1951
0
    {
1952
        /*-------------------------------------------------------------
1953
         * Multiple polyline
1954
         *------------------------------------------------------------*/
1955
0
        OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1956
0
        numParts = poMultiLine->getNumGeometries();
1957
0
    }
1958
1959
0
    return numParts;
1960
0
}
1961
1962
/**********************************************************************
1963
 *                   TABPolyline::GetPartRef()
1964
 *
1965
 * Returns a reference to the specified OGRLineString number, hiding the
1966
 * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
1967
 *
1968
 * Returns NULL if the geometry contained in the object is invalid or
1969
 * missing or if the specified part index is invalid.
1970
 **********************************************************************/
1971
OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
1972
0
{
1973
0
    OGRGeometry *poGeom = GetGeometryRef();
1974
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1975
0
        nPartIndex == 0)
1976
0
    {
1977
        /*-------------------------------------------------------------
1978
         * Simple polyline
1979
         *------------------------------------------------------------*/
1980
0
        return poGeom->toLineString();
1981
0
    }
1982
0
    else if (poGeom &&
1983
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1984
0
    {
1985
        /*-------------------------------------------------------------
1986
         * Multiple polyline
1987
         *------------------------------------------------------------*/
1988
0
        OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1989
0
        if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
1990
0
        {
1991
0
            return poMultiLine->getGeometryRef(nPartIndex);
1992
0
        }
1993
0
        else
1994
0
            return nullptr;
1995
0
    }
1996
1997
0
    return nullptr;
1998
0
}
1999
2000
/**********************************************************************
2001
 *                   TABPolyline::ValidateMapInfoType()
2002
 *
2003
 * Check the feature's geometry part and return the corresponding
2004
 * mapinfo object type code.  The m_nMapInfoType member will also
2005
 * be updated for further calls to GetMapInfoType();
2006
 *
2007
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
2008
 * is expected for this object class.
2009
 **********************************************************************/
2010
TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
2011
491
{
2012
    /*-----------------------------------------------------------------
2013
     * Fetch and validate geometry
2014
     *----------------------------------------------------------------*/
2015
491
    OGRGeometry *poGeom = GetGeometryRef();
2016
491
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2017
257
    {
2018
        /*-------------------------------------------------------------
2019
         * Simple polyline
2020
         *------------------------------------------------------------*/
2021
257
        OGRLineString *poLine = poGeom->toLineString();
2022
257
        if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
2023
0
        {
2024
0
            m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2025
0
        }
2026
257
        else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
2027
0
        {
2028
0
            m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2029
0
        }
2030
257
        else if (poLine->getNumPoints() > 2)
2031
25
        {
2032
25
            m_nMapInfoType = TAB_GEOM_PLINE;
2033
25
        }
2034
232
        else if ((poLine->getNumPoints() == 2) &&
2035
232
                 (m_bWriteTwoPointLineAsPolyline == TRUE))
2036
0
        {
2037
0
            m_nMapInfoType = TAB_GEOM_PLINE;
2038
0
        }
2039
232
        else if ((poLine->getNumPoints() == 2) &&
2040
232
                 (m_bWriteTwoPointLineAsPolyline == FALSE))
2041
50
        {
2042
50
            m_nMapInfoType = TAB_GEOM_LINE;
2043
50
        }
2044
182
        else
2045
182
        {
2046
182
            CPLError(CE_Failure, CPLE_AssertionFailed,
2047
182
                     "TABPolyline: Geometry must contain at least 2 points.");
2048
182
            m_nMapInfoType = TAB_GEOM_NONE;
2049
182
        }
2050
257
    }
2051
234
    else if (poGeom &&
2052
234
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2053
234
    {
2054
        /*-------------------------------------------------------------
2055
         * Multiple polyline... validate all components
2056
         *------------------------------------------------------------*/
2057
234
        GInt32 numPointsTotal = 0;
2058
234
        OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2059
234
        int numLines = poMultiLine->getNumGeometries();
2060
2061
234
        m_nMapInfoType = TAB_GEOM_MULTIPLINE;
2062
2063
246
        for (int iLine = 0; iLine < numLines; iLine++)
2064
12
        {
2065
12
            poGeom = poMultiLine->getGeometryRef(iLine);
2066
12
            if (poGeom == nullptr ||
2067
12
                wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2068
0
            {
2069
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2070
0
                         "TABPolyline: Object contains an invalid Geometry!");
2071
0
                m_nMapInfoType = TAB_GEOM_NONE;
2072
0
                numPointsTotal = 0;
2073
0
                break;
2074
0
            }
2075
12
            OGRLineString *poLine = poGeom->toLineString();
2076
12
            numPointsTotal += poLine->getNumPoints();
2077
12
        }
2078
2079
234
        if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
2080
0
            m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2081
234
        else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
2082
0
            m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2083
234
    }
2084
0
    else
2085
0
    {
2086
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
2087
0
                 "TABPolyline: Missing or Invalid Geometry!");
2088
0
        m_nMapInfoType = TAB_GEOM_NONE;
2089
0
    }
2090
2091
    /*-----------------------------------------------------------------
2092
     * Decide if coordinates should be compressed or not.
2093
     *
2094
     * __TODO__ We never write type LINE (2 points line) as compressed
2095
     * for the moment.  If we ever do it, then the decision to write
2096
     * a 2 point line in compressed coordinates or not should take into
2097
     * account the location of the object block MBR, so this would be
2098
     * better handled directly by TABMAPObjLine::WriteObject() since the
2099
     * object block center is not known until it is written to disk.
2100
     *----------------------------------------------------------------*/
2101
491
    if (m_nMapInfoType != TAB_GEOM_LINE)
2102
441
    {
2103
441
        ValidateCoordType(poMapFile);
2104
441
    }
2105
50
    else
2106
50
    {
2107
50
        UpdateMBR(poMapFile);
2108
50
    }
2109
2110
491
    return m_nMapInfoType;
2111
491
}
2112
2113
/**********************************************************************
2114
 *                   TABPolyline::ReadGeometryFromMAPFile()
2115
 *
2116
 * Fill the geometry and representation (color, etc...) part of the
2117
 * feature from the contents of the .MAP object pointed to by poMAPFile.
2118
 *
2119
 * It is assumed that poMAPFile currently points to the beginning of
2120
 * a map object.
2121
 *
2122
 * Returns 0 on success, -1 on error, in which case CPLError() will have
2123
 * been called.
2124
 **********************************************************************/
2125
int TABPolyline::ReadGeometryFromMAPFile(
2126
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2127
    GBool bCoordBlockDataOnly /*=FALSE*/,
2128
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2129
162
{
2130
162
    GInt32 nX = 0;
2131
162
    GInt32 nY = 0;
2132
162
    double dX = 0.0;
2133
162
    double dY = 0.0;
2134
162
    double dXMin = 0.0;
2135
162
    double dYMin = 0.0;
2136
162
    double dXMax = 0.0;
2137
162
    double dYMax = 0.0;
2138
162
    OGRGeometry *poGeometry = nullptr;
2139
162
    GBool bComprCoord = poObjHdr->IsCompressedType();
2140
162
    TABMAPCoordBlock *poCoordBlock = nullptr;
2141
2142
    /*-----------------------------------------------------------------
2143
     * Fetch and validate geometry type
2144
     *----------------------------------------------------------------*/
2145
162
    m_nMapInfoType = poObjHdr->m_nType;
2146
2147
162
    if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
2148
125
    {
2149
        /*=============================================================
2150
         * LINE (2 vertices)
2151
         *============================================================*/
2152
125
        TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2153
2154
125
        m_bSmooth = FALSE;
2155
2156
125
        auto poLine = new OGRLineString();
2157
125
        poGeometry = poLine;
2158
125
        poLine->setNumPoints(2);
2159
2160
125
        poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
2161
125
                                dYMin);
2162
125
        poLine->setPoint(0, dXMin, dYMin);
2163
2164
125
        poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
2165
125
                                dYMax);
2166
125
        poLine->setPoint(1, dXMax, dYMax);
2167
2168
125
        if (!bCoordBlockDataOnly)
2169
125
        {
2170
125
            m_nPenDefIndex = poLineHdr->m_nPenId;  // Pen index
2171
125
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2172
125
        }
2173
125
    }
2174
37
    else if (m_nMapInfoType == TAB_GEOM_PLINE ||
2175
37
             m_nMapInfoType == TAB_GEOM_PLINE_C)
2176
1
    {
2177
        /*=============================================================
2178
         * PLINE ( > 2 vertices)
2179
         *============================================================*/
2180
2181
        /*-------------------------------------------------------------
2182
         * Copy data from poObjHdr
2183
         *------------------------------------------------------------*/
2184
1
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2185
2186
1
        GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2187
1
        const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
2188
1
        if (nCoordDataSize > 1024 * 1024 &&
2189
1
            nCoordDataSize > poMapFile->GetFileSize())
2190
0
        {
2191
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
2192
0
                     nCoordDataSize);
2193
0
            return -1;
2194
0
        }
2195
        // numLineSections = poPLineHdr->m_numLineSections; // Always 1
2196
1
        m_bSmooth = poPLineHdr->m_bSmooth;
2197
2198
        // Centroid/label point
2199
1
        poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2200
1
                                dX, dY);
2201
1
        SetCenter(dX, dY);
2202
2203
        // Compressed coordinate origin (useful only in compressed case!)
2204
1
        m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2205
1
        m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2206
2207
        // MBR
2208
1
        poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2209
1
                                dYMin);
2210
1
        poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2211
1
                                dYMax);
2212
2213
1
        if (!bCoordBlockDataOnly)
2214
1
        {
2215
1
            m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
2216
1
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2217
1
        }
2218
2219
        /*-------------------------------------------------------------
2220
         * Create Geometry and read coordinates
2221
         *------------------------------------------------------------*/
2222
1
        const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
2223
2224
1
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2225
0
            poCoordBlock = *ppoCoordBlock;
2226
1
        else
2227
1
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2228
1
        if (poCoordBlock == nullptr)
2229
1
        {
2230
1
            CPLError(CE_Failure, CPLE_FileIO,
2231
1
                     "Can't access coordinate block at offset %d",
2232
1
                     nCoordBlockPtr);
2233
1
            return -1;
2234
1
        }
2235
2236
0
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2237
2238
0
        auto poLine = new OGRLineString();
2239
0
        poGeometry = poLine;
2240
0
        poLine->setNumPoints(numPoints);
2241
2242
0
        int nStatus = 0;
2243
0
        for (int i = 0; nStatus == 0 && i < numPoints; i++)
2244
0
        {
2245
0
            nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
2246
0
            if (nStatus != 0)
2247
0
                break;
2248
0
            poMapFile->Int2Coordsys(nX, nY, dX, dY);
2249
0
            poLine->setPoint(i, dX, dY);
2250
0
        }
2251
2252
0
        if (nStatus != 0)
2253
0
        {
2254
            // Failed ... error message has already been produced
2255
0
            delete poGeometry;
2256
0
            return nStatus;
2257
0
        }
2258
0
    }
2259
36
    else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2260
36
             m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2261
36
             m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2262
36
             m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2263
36
             m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2264
36
             m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
2265
36
    {
2266
        /*=============================================================
2267
         * PLINE MULTIPLE
2268
         *============================================================*/
2269
36
        const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2270
2271
        /*-------------------------------------------------------------
2272
         * Copy data from poObjHdr
2273
         *------------------------------------------------------------*/
2274
36
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2275
2276
36
        GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2277
        /* GInt32 nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
2278
36
        GInt32 numLineSections = poPLineHdr->m_numLineSections;
2279
36
        m_bSmooth = poPLineHdr->m_bSmooth;
2280
2281
        // Centroid/label point
2282
36
        poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2283
36
                                dX, dY);
2284
36
        SetCenter(dX, dY);
2285
2286
        // Compressed coordinate origin (useful only in compressed case!)
2287
36
        m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2288
36
        m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2289
2290
        // MBR
2291
36
        poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2292
36
                                dYMin);
2293
36
        poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2294
36
                                dYMax);
2295
2296
36
        if (!bCoordBlockDataOnly)
2297
36
        {
2298
36
            m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
2299
36
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2300
36
        }
2301
2302
36
        const int nMinSizeOfSection = 24;
2303
36
        if (numLineSections > INT_MAX / nMinSizeOfSection)
2304
0
        {
2305
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2306
0
            return -1;
2307
0
        }
2308
36
        const GUInt32 nMinimumBytesForSections =
2309
36
            nMinSizeOfSection * numLineSections;
2310
36
        if (nMinimumBytesForSections > 1024 * 1024 &&
2311
36
            nMinimumBytesForSections > poMapFile->GetFileSize())
2312
0
        {
2313
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2314
0
            return -1;
2315
0
        }
2316
2317
        /*-------------------------------------------------------------
2318
         * Read data from the coord. block
2319
         *------------------------------------------------------------*/
2320
36
        TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2321
36
            VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
2322
36
        if (pasSecHdrs == nullptr)
2323
1
            return -1;
2324
2325
35
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2326
1
            poCoordBlock = *ppoCoordBlock;
2327
34
        else
2328
34
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2329
2330
35
        GInt32 numPointsTotal = 0;
2331
35
        if (poCoordBlock == nullptr ||
2332
35
            poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
2333
30
                                           numLineSections, pasSecHdrs,
2334
30
                                           numPointsTotal) != 0)
2335
8
        {
2336
8
            CPLError(CE_Failure, CPLE_FileIO,
2337
8
                     "Failed reading coordinate data at offset %d",
2338
8
                     nCoordBlockPtr);
2339
8
            CPLFree(pasSecHdrs);
2340
8
            return -1;
2341
8
        }
2342
2343
27
        const GUInt32 nMinimumBytesForPoints =
2344
27
            (bComprCoord ? 4 : 8) * numPointsTotal;
2345
27
        if (nMinimumBytesForPoints > 1024 * 1024 &&
2346
27
            nMinimumBytesForPoints > poMapFile->GetFileSize())
2347
0
        {
2348
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
2349
0
            CPLFree(pasSecHdrs);
2350
0
            return -1;
2351
0
        }
2352
2353
27
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2354
2355
27
        GInt32 *panXY = static_cast<GInt32 *>(
2356
27
            VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
2357
27
        if (panXY == nullptr)
2358
0
        {
2359
0
            CPLFree(pasSecHdrs);
2360
0
            return -1;
2361
0
        }
2362
2363
27
        if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
2364
27
            0)
2365
1
        {
2366
1
            CPLError(CE_Failure, CPLE_FileIO,
2367
1
                     "Failed reading coordinate data at offset %d",
2368
1
                     nCoordBlockPtr);
2369
1
            CPLFree(pasSecHdrs);
2370
1
            CPLFree(panXY);
2371
1
            return -1;
2372
1
        }
2373
2374
        /*-------------------------------------------------------------
2375
         * Create a Geometry collection with one line geometry for
2376
         * each coordinates section
2377
         * If object contains only one section, then return a simple LineString
2378
         *------------------------------------------------------------*/
2379
26
        OGRMultiLineString *poMultiLine = nullptr;
2380
26
        if (numLineSections > 1)
2381
25
        {
2382
25
            poMultiLine = new OGRMultiLineString();
2383
25
            poGeometry = poMultiLine;
2384
25
        }
2385
2386
77
        for (int iSection = 0; iSection < numLineSections; iSection++)
2387
51
        {
2388
51
            const int numSectionVertices = pasSecHdrs[iSection].numVertices;
2389
51
            GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
2390
2391
51
            auto poLine = new OGRLineString();
2392
51
            poLine->setNumPoints(numSectionVertices);
2393
2394
151
            for (int i = 0; i < numSectionVertices; i++)
2395
100
            {
2396
100
                poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
2397
100
                poLine->setPoint(i, dX, dY);
2398
100
                pnXYPtr += 2;
2399
100
            }
2400
2401
51
            if (poGeometry == nullptr)
2402
1
                poGeometry = poLine;
2403
50
            else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
2404
0
            {
2405
0
                CPLAssert(false);  // Just in case lower-level lib is modified
2406
0
            }
2407
51
        }
2408
2409
26
        CPLFree(pasSecHdrs);
2410
26
        CPLFree(panXY);
2411
26
    }
2412
0
    else
2413
0
    {
2414
0
        CPLError(
2415
0
            CE_Failure, CPLE_AssertionFailed,
2416
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
2417
0
            m_nMapInfoType, m_nMapInfoType);
2418
0
        return -1;
2419
0
    }
2420
2421
151
    SetGeometryDirectly(poGeometry);
2422
2423
151
    SetMBR(dXMin, dYMin, dXMax, dYMax);
2424
151
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
2425
151
              poObjHdr->m_nMaxY);
2426
2427
    /* Return a ref to coord block so that caller can continue reading
2428
     * after the end of this object (used by TABCollection and index splitting)
2429
     */
2430
151
    if (ppoCoordBlock)
2431
1
        *ppoCoordBlock = poCoordBlock;
2432
2433
151
    return 0;
2434
162
}
2435
2436
/**********************************************************************
2437
 *                   TABPolyline::WriteGeometryToMAPFile()
2438
 *
2439
 * Write the geometry and representation (color, etc...) part of the
2440
 * feature to the .MAP object pointed to by poMAPFile.
2441
 *
2442
 * It is assumed that poMAPFile currently points to a valid map object.
2443
 *
2444
 * Returns 0 on success, -1 on error, in which case CPLError() will have
2445
 * been called.
2446
 **********************************************************************/
2447
int TABPolyline::WriteGeometryToMAPFile(
2448
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2449
    GBool bCoordBlockDataOnly /*=FALSE*/,
2450
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2451
309
{
2452
309
    GInt32 nX = 0;
2453
309
    GInt32 nY = 0;
2454
309
    OGRLineString *poLine = nullptr;
2455
309
    TABMAPCoordBlock *poCoordBlock = nullptr;
2456
2457
    /*-----------------------------------------------------------------
2458
     * We assume that ValidateMapInfoType() was called already and that
2459
     * the type in poObjHdr->m_nType is valid.
2460
     *----------------------------------------------------------------*/
2461
309
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
2462
309
    CPLErrorReset();
2463
2464
    /*-----------------------------------------------------------------
2465
     * Fetch and validate geometry
2466
     *----------------------------------------------------------------*/
2467
309
    OGRGeometry *poGeom = GetGeometryRef();
2468
2469
309
    if ((m_nMapInfoType == TAB_GEOM_LINE ||
2470
309
         m_nMapInfoType == TAB_GEOM_LINE_C) &&
2471
309
        poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
2472
309
        (poLine = poGeom->toLineString())->getNumPoints() == 2)
2473
50
    {
2474
        /*=============================================================
2475
         * LINE (2 vertices)
2476
         *============================================================*/
2477
50
        TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2478
2479
50
        poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
2480
50
                                poLineHdr->m_nX1, poLineHdr->m_nY1);
2481
50
        poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
2482
50
                                poLineHdr->m_nX2, poLineHdr->m_nY2);
2483
50
        poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
2484
50
                          poLineHdr->m_nY2);
2485
2486
50
        if (!bCoordBlockDataOnly)
2487
50
        {
2488
50
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2489
50
            poLineHdr->m_nPenId =
2490
50
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
2491
50
        }
2492
50
    }
2493
259
    else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
2494
259
              m_nMapInfoType == TAB_GEOM_PLINE_C) &&
2495
259
             poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2496
25
    {
2497
        /*=============================================================
2498
         * PLINE ( > 2 vertices and less than 32767 vertices)
2499
         *============================================================*/
2500
25
        GBool bCompressed = poObjHdr->IsCompressedType();
2501
2502
        /*-------------------------------------------------------------
2503
         * Process geometry first...
2504
         *------------------------------------------------------------*/
2505
25
        poLine = poGeom->toLineString();
2506
25
        const int numPoints = poLine->getNumPoints();
2507
25
        CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
2508
2509
25
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2510
0
            poCoordBlock = *ppoCoordBlock;
2511
25
        else
2512
25
            poCoordBlock = poMapFile->GetCurCoordBlock();
2513
25
        poCoordBlock->StartNewFeature();
2514
25
        const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2515
25
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2516
2517
25
        int nStatus = 0;
2518
100
        for (int i = 0; nStatus == 0 && i < numPoints; i++)
2519
75
        {
2520
75
            poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
2521
75
            if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
2522
75
                0)
2523
0
            {
2524
                // Failed ... error message has already been produced
2525
0
                return nStatus;
2526
0
            }
2527
75
        }
2528
2529
25
        const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2530
2531
        /*-------------------------------------------------------------
2532
         * Copy info to poObjHdr
2533
         *------------------------------------------------------------*/
2534
25
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2535
2536
25
        poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2537
25
        poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2538
25
        poPLineHdr->m_numLineSections = 1;
2539
2540
25
        poPLineHdr->m_bSmooth = m_bSmooth;
2541
2542
        // MBR
2543
25
        poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2544
2545
        // Polyline center/label point
2546
25
        double dX = 0.0;
2547
25
        double dY = 0.0;
2548
25
        if (GetCenter(dX, dY) != -1)
2549
25
        {
2550
25
            poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2551
25
                                    poPLineHdr->m_nLabelY);
2552
25
        }
2553
0
        else
2554
0
        {
2555
0
            poPLineHdr->m_nLabelX = m_nComprOrgX;
2556
0
            poPLineHdr->m_nLabelY = m_nComprOrgY;
2557
0
        }
2558
2559
        // Compressed coordinate origin (useful only in compressed case!)
2560
25
        poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2561
25
        poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2562
2563
25
        if (!bCoordBlockDataOnly)
2564
25
        {
2565
25
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2566
25
            poPLineHdr->m_nPenId =
2567
25
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
2568
25
        }
2569
25
    }
2570
234
    else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2571
234
              m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2572
234
              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2573
234
              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2574
234
              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2575
234
              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
2576
234
             poGeom &&
2577
234
             (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
2578
234
              wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
2579
234
    {
2580
        /*=============================================================
2581
         * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
2582
         *============================================================*/
2583
2584
234
        CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2585
234
                  m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2586
234
                  m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2587
234
                  m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2588
234
                  m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2589
234
                  m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
2590
2591
234
        int nStatus = 0;
2592
234
        OGREnvelope sEnvelope;
2593
234
        GBool bCompressed = poObjHdr->IsCompressedType();
2594
2595
        /*-------------------------------------------------------------
2596
         * Process geometry first...
2597
         *------------------------------------------------------------*/
2598
234
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2599
0
            poCoordBlock = *ppoCoordBlock;
2600
234
        else
2601
234
            poCoordBlock = poMapFile->GetCurCoordBlock();
2602
234
        poCoordBlock->StartNewFeature();
2603
234
        const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2604
234
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2605
2606
234
        OGRMultiLineString *poMultiLine = nullptr;
2607
234
        GInt32 numLines = 1;
2608
234
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2609
234
        {
2610
234
            poMultiLine = poGeom->toMultiLineString();
2611
234
            numLines = poMultiLine->getNumGeometries();
2612
234
        }
2613
        // else
2614
        // {
2615
        //     poMultiLine = NULL;
2616
        //     numLines = 1;
2617
        // }
2618
2619
        /*-------------------------------------------------------------
2620
         * Build and write array of coord sections headers
2621
         *------------------------------------------------------------*/
2622
234
        TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2623
234
            VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
2624
234
        if (pasSecHdrs == nullptr)
2625
0
        {
2626
0
            return -1;
2627
0
        }
2628
2629
        /*-------------------------------------------------------------
2630
         * In calculation of nDataOffset, we have to take into account that
2631
         * V450 header section uses int32 instead of int16 for numVertices
2632
         * and we add another 2 bytes to align with a 4 bytes boundary.
2633
         *------------------------------------------------------------*/
2634
234
        int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2635
2636
234
        const int nTotalHdrSizeUncompressed =
2637
234
            (nVersion >= 450 ? 28 : 24) * numLines;
2638
2639
234
        GInt32 numPointsTotal = 0;
2640
246
        for (int iLine = 0; iLine < numLines; iLine++)
2641
12
        {
2642
12
            if (poMultiLine)
2643
12
                poGeom = poMultiLine->getGeometryRef(iLine);
2644
2645
12
            if (poGeom &&
2646
12
                wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2647
12
            {
2648
12
                poLine = poGeom->toLineString();
2649
12
                const GInt32 numPoints = poLine->getNumPoints();
2650
12
                poLine->getEnvelope(&sEnvelope);
2651
2652
12
                pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
2653
12
                pasSecHdrs[iLine].numHoles = 0;  // It is a line!
2654
2655
12
                poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
2656
12
                                        pasSecHdrs[iLine].nXMin,
2657
12
                                        pasSecHdrs[iLine].nYMin);
2658
12
                poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
2659
12
                                        pasSecHdrs[iLine].nXMax,
2660
12
                                        pasSecHdrs[iLine].nYMax);
2661
12
                pasSecHdrs[iLine].nDataOffset =
2662
12
                    nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
2663
12
                pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
2664
2665
12
                numPointsTotal += numPoints;
2666
12
            }
2667
0
            else
2668
0
            {
2669
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2670
0
                         "TABPolyline: Object contains an invalid Geometry!");
2671
0
                nStatus = -1;
2672
0
            }
2673
12
        }
2674
2675
234
        if (nStatus == 0)
2676
234
            nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
2677
234
                                                      pasSecHdrs, bCompressed);
2678
2679
234
        CPLFree(pasSecHdrs);
2680
234
        pasSecHdrs = nullptr;
2681
2682
234
        if (nStatus != 0)
2683
0
            return nStatus;  // Error has already been reported.
2684
2685
        /*-------------------------------------------------------------
2686
         * Then write the coordinates themselves...
2687
         *------------------------------------------------------------*/
2688
246
        for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
2689
12
        {
2690
12
            if (poMultiLine)
2691
12
                poGeom = poMultiLine->getGeometryRef(iLine);
2692
2693
12
            if (poGeom &&
2694
12
                wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2695
12
            {
2696
12
                poLine = poGeom->toLineString();
2697
12
                GInt32 numPoints = poLine->getNumPoints();
2698
2699
14
                for (int i = 0; nStatus == 0 && i < numPoints; i++)
2700
2
                {
2701
2
                    poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
2702
2
                                            nX, nY);
2703
2
                    if ((nStatus = poCoordBlock->WriteIntCoord(
2704
2
                             nX, nY, bCompressed)) != 0)
2705
0
                    {
2706
                        // Failed ... error message has already been produced
2707
0
                        return nStatus;
2708
0
                    }
2709
2
                }
2710
12
            }
2711
0
            else
2712
0
            {
2713
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2714
0
                         "TABPolyline: Object contains an invalid Geometry!");
2715
0
                return -1;
2716
0
            }
2717
12
        }
2718
2719
234
        const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2720
2721
        /*-------------------------------------------------------------
2722
         * ... and finally copy info to poObjHdr
2723
         *------------------------------------------------------------*/
2724
234
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2725
2726
234
        poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2727
234
        poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2728
234
        poPLineHdr->m_numLineSections = numLines;
2729
2730
234
        poPLineHdr->m_bSmooth = m_bSmooth;
2731
2732
        // MBR
2733
234
        poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2734
2735
        // Polyline center/label point
2736
234
        double dX = 0.0;
2737
234
        double dY = 0.0;
2738
234
        if (GetCenter(dX, dY) != -1)
2739
2
        {
2740
2
            poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2741
2
                                    poPLineHdr->m_nLabelY);
2742
2
        }
2743
232
        else
2744
232
        {
2745
232
            poPLineHdr->m_nLabelX = m_nComprOrgX;
2746
232
            poPLineHdr->m_nLabelY = m_nComprOrgY;
2747
232
        }
2748
2749
        // Compressed coordinate origin (useful only in compressed case!)
2750
234
        poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2751
234
        poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2752
2753
234
        if (!bCoordBlockDataOnly)
2754
234
        {
2755
234
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2756
234
            poPLineHdr->m_nPenId =
2757
234
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
2758
234
        }
2759
234
    }
2760
0
    else
2761
0
    {
2762
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
2763
0
                 "TABPolyline: Object contains an invalid Geometry!");
2764
0
        return -1;
2765
0
    }
2766
2767
309
    if (CPLGetLastErrorType() == CE_Failure)
2768
0
        return -1;
2769
2770
    /* Return a ref to coord block so that caller can continue writing
2771
     * after the end of this object (used by index splitting)
2772
     */
2773
309
    if (ppoCoordBlock)
2774
0
        *ppoCoordBlock = poCoordBlock;
2775
2776
309
    return 0;
2777
309
}
2778
2779
/**********************************************************************
2780
 *                   TABPolyline::GetStyleString() const
2781
 *
2782
 * Return style string for this feature.
2783
 *
2784
 * Style String is built only once during the first call to GetStyleString().
2785
 **********************************************************************/
2786
const char *TABPolyline::GetStyleString() const
2787
0
{
2788
0
    if (m_pszStyleString == nullptr)
2789
0
    {
2790
0
        m_pszStyleString = CPLStrdup(GetPenStyleString());
2791
0
    }
2792
2793
0
    return m_pszStyleString;
2794
0
}
2795
2796
/**********************************************************************
2797
 *                   TABPolyline::DumpMIF()
2798
 *
2799
 * Dump feature geometry in a format similar to .MIF PLINEs.
2800
 **********************************************************************/
2801
void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
2802
0
{
2803
0
    OGRMultiLineString *poMultiLine = nullptr;
2804
0
    OGRLineString *poLine = nullptr;
2805
0
    int i, numPoints;
2806
2807
0
    if (fpOut == nullptr)
2808
0
        fpOut = stdout;
2809
2810
    /*-----------------------------------------------------------------
2811
     * Fetch and validate geometry
2812
     *----------------------------------------------------------------*/
2813
0
    OGRGeometry *poGeom = GetGeometryRef();
2814
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2815
0
    {
2816
        /*-------------------------------------------------------------
2817
         * Generate output for simple polyline
2818
         *------------------------------------------------------------*/
2819
0
        poLine = poGeom->toLineString();
2820
0
        numPoints = poLine->getNumPoints();
2821
0
        fprintf(fpOut, "PLINE %d\n", numPoints);
2822
0
        for (i = 0; i < numPoints; i++)
2823
0
            fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
2824
0
    }
2825
0
    else if (poGeom &&
2826
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2827
0
    {
2828
        /*-------------------------------------------------------------
2829
         * Generate output for multiple polyline
2830
         *------------------------------------------------------------*/
2831
0
        int iLine, numLines;
2832
0
        poMultiLine = poGeom->toMultiLineString();
2833
0
        numLines = poMultiLine->getNumGeometries();
2834
0
        fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
2835
0
        for (iLine = 0; iLine < numLines; iLine++)
2836
0
        {
2837
0
            poGeom = poMultiLine->getGeometryRef(iLine);
2838
0
            if (poGeom &&
2839
0
                wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2840
0
            {
2841
0
                poLine = poGeom->toLineString();
2842
0
                numPoints = poLine->getNumPoints();
2843
0
                fprintf(fpOut, " %d\n", numPoints);
2844
0
                for (i = 0; i < numPoints; i++)
2845
0
                    fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
2846
0
                            poLine->getY(i));
2847
0
            }
2848
0
            else
2849
0
            {
2850
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2851
0
                         "TABPolyline: Object contains an invalid Geometry!");
2852
0
                return;
2853
0
            }
2854
0
        }
2855
0
    }
2856
0
    else
2857
0
    {
2858
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
2859
0
                 "TABPolyline: Missing or Invalid Geometry!");
2860
0
        return;
2861
0
    }
2862
2863
0
    if (m_bCenterIsSet)
2864
0
        fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
2865
2866
    // Finish with PEN/BRUSH/etc. clauses
2867
0
    DumpPenDef();
2868
2869
0
    fflush(fpOut);
2870
0
}
2871
2872
/**********************************************************************
2873
 *                   TABPolyline::GetCenter()
2874
 *
2875
 * Returns the center point of the line.  Compute one if it was not
2876
 * explicitly set:
2877
 *
2878
 * In MapInfo, for a simple or multiple polyline (pline), the center point
2879
 * in the object definition is supposed to be either the center point of
2880
 * the pline or the first section of a multiple pline (if an odd number of
2881
 * points in the pline or first section), or the midway point between the
2882
 * two central points (if an even number of points involved).
2883
 *
2884
 * Returns 0 on success, -1 on error.
2885
 **********************************************************************/
2886
int TABPolyline::GetCenter(double &dX, double &dY)
2887
259
{
2888
259
    if (!m_bCenterIsSet)
2889
259
    {
2890
259
        OGRLineString *poLine = nullptr;
2891
2892
259
        OGRGeometry *poGeom = GetGeometryRef();
2893
259
        if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2894
25
        {
2895
25
            poLine = poGeom->toLineString();
2896
25
        }
2897
234
        else if (poGeom &&
2898
234
                 wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2899
234
        {
2900
234
            OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2901
234
            if (poMultiLine->getNumGeometries() > 0)
2902
12
                poLine = poMultiLine->getGeometryRef(0);
2903
234
        }
2904
2905
259
        if (poLine && poLine->getNumPoints() > 0)
2906
27
        {
2907
27
            int i = poLine->getNumPoints() / 2;
2908
27
            if (poLine->getNumPoints() % 2 == 0)
2909
0
            {
2910
                // Return the midway between the 2 center points
2911
0
                m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
2912
0
                m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
2913
0
            }
2914
27
            else
2915
27
            {
2916
                // Return the center point
2917
27
                m_dCenterX = poLine->getX(i);
2918
27
                m_dCenterY = poLine->getY(i);
2919
27
            }
2920
27
            m_bCenterIsSet = TRUE;
2921
27
        }
2922
259
    }
2923
2924
259
    if (!m_bCenterIsSet)
2925
232
        return -1;
2926
2927
27
    dX = m_dCenterX;
2928
27
    dY = m_dCenterY;
2929
27
    return 0;
2930
259
}
2931
2932
/**********************************************************************
2933
 *                   TABPolyline::SetCenter()
2934
 *
2935
 * Set the X,Y coordinates to use as center point for the line.
2936
 **********************************************************************/
2937
void TABPolyline::SetCenter(double dX, double dY)
2938
37
{
2939
37
    m_dCenterX = dX;
2940
37
    m_dCenterY = dY;
2941
37
    m_bCenterIsSet = TRUE;
2942
37
}
2943
2944
/**********************************************************************
2945
 *                   TABPolyline::TwoPointLineAsPolyline()
2946
 *
2947
 * Returns the value of m_bWriteTwoPointLineAsPolyline
2948
 **********************************************************************/
2949
GBool TABPolyline::TwoPointLineAsPolyline()
2950
0
{
2951
0
    return m_bWriteTwoPointLineAsPolyline;
2952
0
}
2953
2954
/**********************************************************************
2955
 *                   TABPolyline::TwoPointLineAsPolyline()
2956
 *
2957
 * Sets the value of m_bWriteTwoPointLineAsPolyline
2958
 **********************************************************************/
2959
void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
2960
0
{
2961
0
    m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
2962
0
}
2963
2964
/*=====================================================================
2965
 *                      class TABRegion
2966
 *====================================================================*/
2967
2968
/**********************************************************************
2969
 *                   TABRegion::TABRegion()
2970
 *
2971
 * Constructor.
2972
 **********************************************************************/
2973
TABRegion::TABRegion(OGRFeatureDefn *poDefnIn)
2974
199k
    : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
2975
199k
      m_dCenterX(0.0), m_dCenterY(0.0)
2976
199k
{
2977
199k
}
2978
2979
/**********************************************************************
2980
 *                   TABRegion::~TABRegion()
2981
 *
2982
 * Destructor.
2983
 **********************************************************************/
2984
TABRegion::~TABRegion()
2985
199k
{
2986
199k
}
2987
2988
/**********************************************************************
2989
 *                     TABRegion::CloneTABFeature()
2990
 *
2991
 * Duplicate feature, including stuff specific to each TABFeature type.
2992
 *
2993
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
2994
 * then copies any members specific to its own type.
2995
 **********************************************************************/
2996
TABFeature *TABRegion::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
2997
0
{
2998
    /*-----------------------------------------------------------------
2999
     * Alloc new feature and copy the base stuff
3000
     *----------------------------------------------------------------*/
3001
0
    TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
3002
3003
0
    CopyTABFeatureBase(poNew);
3004
3005
    /*-----------------------------------------------------------------
3006
     * And members specific to this class
3007
     *----------------------------------------------------------------*/
3008
    // ITABFeaturePen
3009
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
3010
3011
    // ITABFeatureBrush
3012
0
    *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
3013
3014
0
    poNew->m_bSmooth = m_bSmooth;
3015
0
    poNew->m_bCenterIsSet = m_bCenterIsSet;
3016
0
    poNew->m_dCenterX = m_dCenterX;
3017
0
    poNew->m_dCenterY = m_dCenterY;
3018
3019
0
    return poNew;
3020
0
}
3021
3022
/**********************************************************************
3023
 *                   TABRegion::ValidateMapInfoType()
3024
 *
3025
 * Check the feature's geometry part and return the corresponding
3026
 * mapinfo object type code.  The m_nMapInfoType member will also
3027
 * be updated for further calls to GetMapInfoType();
3028
 *
3029
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
3030
 * is expected for this object class.
3031
 **********************************************************************/
3032
TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
3033
175
{
3034
    /*-----------------------------------------------------------------
3035
     * Fetch and validate geometry
3036
     *----------------------------------------------------------------*/
3037
175
    OGRGeometry *poGeom = GetGeometryRef();
3038
175
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3039
175
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3040
175
    {
3041
175
        GInt32 numPointsTotal = 0;
3042
175
        GInt32 numRings = GetNumRings();
3043
323
        for (int i = 0; i < numRings; i++)
3044
148
        {
3045
148
            OGRLinearRing *poRing = GetRingRef(i);
3046
148
            if (poRing)
3047
93
                numPointsTotal += poRing->getNumPoints();
3048
148
        }
3049
175
        if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
3050
0
            m_nMapInfoType = TAB_GEOM_V800_REGION;
3051
175
        else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
3052
0
            m_nMapInfoType = TAB_GEOM_V450_REGION;
3053
175
        else
3054
175
            m_nMapInfoType = TAB_GEOM_REGION;
3055
175
    }
3056
0
    else
3057
0
    {
3058
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
3059
0
                 "TABRegion: Missing or Invalid Geometry!");
3060
0
        m_nMapInfoType = TAB_GEOM_NONE;
3061
0
    }
3062
3063
    /*-----------------------------------------------------------------
3064
     * Decide if coordinates should be compressed or not.
3065
     *----------------------------------------------------------------*/
3066
175
    ValidateCoordType(poMapFile);
3067
3068
175
    return m_nMapInfoType;
3069
175
}
3070
3071
/**********************************************************************
3072
 *                   TABRegion::ReadGeometryFromMAPFile()
3073
 *
3074
 * Fill the geometry and representation (color, etc...) part of the
3075
 * feature from the contents of the .MAP object pointed to by poMAPFile.
3076
 *
3077
 * It is assumed that poMAPFile currently points to the beginning of
3078
 * a map object.
3079
 *
3080
 * Returns 0 on success, -1 on error, in which case CPLError() will have
3081
 * been called.
3082
 **********************************************************************/
3083
int TABRegion::ReadGeometryFromMAPFile(
3084
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3085
    GBool bCoordBlockDataOnly /*=FALSE*/,
3086
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3087
54
{
3088
54
    double dXMin = 0.0;
3089
54
    double dYMin = 0.0;
3090
54
    double dXMax = 0.0;
3091
54
    double dYMax = 0.0;
3092
54
    OGRGeometry *poGeometry = nullptr;
3093
54
    TABMAPCoordBlock *poCoordBlock = nullptr;
3094
3095
    /*-----------------------------------------------------------------
3096
     * Fetch and validate geometry type
3097
     *----------------------------------------------------------------*/
3098
54
    m_nMapInfoType = poObjHdr->m_nType;
3099
3100
54
    if (m_nMapInfoType == TAB_GEOM_REGION ||
3101
54
        m_nMapInfoType == TAB_GEOM_REGION_C ||
3102
54
        m_nMapInfoType == TAB_GEOM_V450_REGION ||
3103
54
        m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3104
54
        m_nMapInfoType == TAB_GEOM_V800_REGION ||
3105
54
        m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3106
54
    {
3107
        /*=============================================================
3108
         * REGION (Similar to PLINE MULTIPLE)
3109
         *============================================================*/
3110
54
        GInt32 /* nCoordDataSize, */ numPointsTotal;
3111
54
        OGRMultiPolygon *poMultiPolygon = nullptr;
3112
54
        OGRPolygon *poPolygon = nullptr;
3113
54
        GBool bComprCoord = poObjHdr->IsCompressedType();
3114
54
        int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3115
3116
        /*-------------------------------------------------------------
3117
         * Copy data from poObjHdr
3118
         *------------------------------------------------------------*/
3119
54
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3120
3121
54
        GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
3122
        /* nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
3123
54
        GInt32 numLineSections = poPLineHdr->m_numLineSections;
3124
54
        m_bSmooth = poPLineHdr->m_bSmooth;
3125
3126
        // Centroid/label point
3127
54
        double dX = 0.0;
3128
54
        double dY = 0.0;
3129
54
        poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
3130
54
                                dX, dY);
3131
54
        SetCenter(dX, dY);
3132
3133
        // Compressed coordinate origin (useful only in compressed case!)
3134
54
        m_nComprOrgX = poPLineHdr->m_nComprOrgX;
3135
54
        m_nComprOrgY = poPLineHdr->m_nComprOrgY;
3136
3137
        // MBR
3138
54
        poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
3139
54
                                dYMin);
3140
54
        poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
3141
54
                                dYMax);
3142
3143
54
        if (!bCoordBlockDataOnly)
3144
54
        {
3145
54
            m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
3146
54
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
3147
54
            m_nBrushDefIndex = poPLineHdr->m_nBrushId;  // Brush index
3148
54
            poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
3149
54
        }
3150
3151
        /*-------------------------------------------------------------
3152
         * Read data from the coord. block
3153
         *------------------------------------------------------------*/
3154
3155
54
        const int nMinSizeOfSection = 24;
3156
54
        if (numLineSections > INT_MAX / nMinSizeOfSection)
3157
0
        {
3158
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3159
0
            return -1;
3160
0
        }
3161
54
        const GUInt32 nMinimumBytesForSections =
3162
54
            nMinSizeOfSection * numLineSections;
3163
54
        if (nMinimumBytesForSections > 1024 * 1024 &&
3164
54
            nMinimumBytesForSections > poMapFile->GetFileSize())
3165
0
        {
3166
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3167
0
            return -1;
3168
0
        }
3169
3170
54
        TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3171
54
            VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
3172
54
        if (pasSecHdrs == nullptr)
3173
0
            return -1;
3174
3175
54
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3176
1
            poCoordBlock = *ppoCoordBlock;
3177
53
        else
3178
53
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
3179
3180
54
        if (poCoordBlock)
3181
50
            poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3182
3183
54
        if (poCoordBlock == nullptr ||
3184
54
            poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
3185
50
                                           numLineSections, pasSecHdrs,
3186
50
                                           numPointsTotal) != 0)
3187
9
        {
3188
9
            CPLError(CE_Failure, CPLE_FileIO,
3189
9
                     "Failed reading coordinate data at offset %d",
3190
9
                     nCoordBlockPtr);
3191
9
            CPLFree(pasSecHdrs);
3192
9
            return -1;
3193
9
        }
3194
3195
45
        const GUInt32 nMinimumBytesForPoints =
3196
45
            (bComprCoord ? 4 : 8) * numPointsTotal;
3197
45
        if (nMinimumBytesForPoints > 1024 * 1024 &&
3198
45
            nMinimumBytesForPoints > poMapFile->GetFileSize())
3199
0
        {
3200
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
3201
0
            CPLFree(pasSecHdrs);
3202
0
            return -1;
3203
0
        }
3204
3205
45
        GInt32 *panXY = static_cast<GInt32 *>(
3206
45
            VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
3207
45
        if (panXY == nullptr)
3208
0
        {
3209
0
            CPLFree(pasSecHdrs);
3210
0
            return -1;
3211
0
        }
3212
3213
45
        if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
3214
45
            0)
3215
2
        {
3216
2
            CPLError(CE_Failure, CPLE_FileIO,
3217
2
                     "Failed reading coordinate data at offset %d",
3218
2
                     nCoordBlockPtr);
3219
2
            CPLFree(pasSecHdrs);
3220
2
            CPLFree(panXY);
3221
2
            return -1;
3222
2
        }
3223
3224
        /*-------------------------------------------------------------
3225
         * Decide if we should return an OGRPolygon or an OGRMultiPolygon
3226
         * depending on the number of outer rings found in CoordSecHdr blocks.
3227
         * The CoodSecHdr block for each outer ring in the region has a flag
3228
         * indicating the number of inner rings that follow.
3229
         * In older versions of the format, the count of inner rings was
3230
         * always zero, so in this case we would always return MultiPolygons.
3231
         *
3232
         * Note: The current implementation assumes that there cannot be
3233
         * holes inside holes (i.e. multiple levels of inner rings)... if
3234
         * that case was encountered then we would return an OGRMultiPolygon
3235
         * in which the topological relationship between the rings would
3236
         * be lost.
3237
         *------------------------------------------------------------*/
3238
43
        int numOuterRings = 0;
3239
86
        for (int iSection = 0; iSection < numLineSections; iSection++)
3240
43
        {
3241
            // Count this as an outer ring.
3242
43
            numOuterRings++;
3243
            // Skip inner rings... so loop continues on an outer ring.
3244
43
            iSection += pasSecHdrs[iSection].numHoles;
3245
43
        }
3246
3247
43
        if (numOuterRings > 1)
3248
0
        {
3249
0
            poMultiPolygon = new OGRMultiPolygon;
3250
0
            poGeometry = poMultiPolygon;
3251
0
        }
3252
43
        else
3253
43
        {
3254
43
            poGeometry = nullptr;  // Will be set later
3255
43
        }
3256
3257
        /*-------------------------------------------------------------
3258
         * OK, build the OGRGeometry object.
3259
         *------------------------------------------------------------*/
3260
43
        int numHolesToRead = 0;
3261
43
        poPolygon = nullptr;
3262
86
        for (int iSection = 0; iSection < numLineSections; iSection++)
3263
43
        {
3264
3265
43
            if (poPolygon == nullptr)
3266
43
                poPolygon = new OGRPolygon();
3267
3268
43
            if (numHolesToRead < 1)
3269
43
                numHolesToRead = pasSecHdrs[iSection].numHoles;
3270
0
            else
3271
0
                numHolesToRead--;
3272
3273
43
            int numSectionVertices = pasSecHdrs[iSection].numVertices;
3274
43
            GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
3275
3276
43
            OGRLinearRing *poRing = new OGRLinearRing();
3277
43
            poRing->setNumPoints(numSectionVertices);
3278
3279
841
            for (int i = 0; i < numSectionVertices; i++)
3280
798
            {
3281
798
                poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
3282
798
                poRing->setPoint(i, dX, dY);
3283
798
                pnXYPtr += 2;
3284
798
            }
3285
3286
43
            poPolygon->addRingDirectly(poRing);
3287
43
            poRing = nullptr;
3288
3289
43
            if (numHolesToRead < 1)
3290
43
            {
3291
43
                if (numOuterRings > 1)
3292
0
                {
3293
0
                    poMultiPolygon->addGeometryDirectly(poPolygon);
3294
0
                }
3295
43
                else
3296
43
                {
3297
43
                    poGeometry = poPolygon;
3298
43
                    CPLAssert(iSection == numLineSections - 1);
3299
43
                }
3300
3301
43
                poPolygon = nullptr;  // We'll alloc a new polygon next loop.
3302
43
            }
3303
43
        }
3304
43
        delete poPolygon;  // should only trigger on corrupted files
3305
3306
43
        CPLFree(pasSecHdrs);
3307
43
        CPLFree(panXY);
3308
43
    }
3309
0
    else
3310
0
    {
3311
0
        CPLError(
3312
0
            CE_Failure, CPLE_AssertionFailed,
3313
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
3314
0
            m_nMapInfoType, m_nMapInfoType);
3315
0
        return -1;
3316
0
    }
3317
3318
43
    SetGeometryDirectly(poGeometry);
3319
3320
43
    SetMBR(dXMin, dYMin, dXMax, dYMax);
3321
43
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
3322
43
              poObjHdr->m_nMaxY);
3323
3324
    /* Return a ref to coord block so that caller can continue reading
3325
     * after the end of this object (used by TABCollection and index splitting)
3326
     */
3327
43
    if (ppoCoordBlock)
3328
1
        *ppoCoordBlock = poCoordBlock;
3329
3330
43
    return 0;
3331
54
}
3332
3333
/**********************************************************************
3334
 *                   TABRegion::WriteGeometryToMAPFile()
3335
 *
3336
 * Write the geometry and representation (color, etc...) part of the
3337
 * feature to the .MAP object pointed to by poMAPFile.
3338
 *
3339
 * It is assumed that poMAPFile currently points to a valid map object.
3340
 *
3341
 * Returns 0 on success, -1 on error, in which case CPLError() will have
3342
 * been called.
3343
 **********************************************************************/
3344
int TABRegion::WriteGeometryToMAPFile(
3345
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3346
    GBool bCoordBlockDataOnly /*=FALSE*/,
3347
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3348
175
{
3349
    /*-----------------------------------------------------------------
3350
     * We assume that ValidateMapInfoType() was called already and that
3351
     * the type in poObjHdr->m_nType is valid.
3352
     *----------------------------------------------------------------*/
3353
175
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
3354
3355
    /*-----------------------------------------------------------------
3356
     * Fetch and validate geometry
3357
     *----------------------------------------------------------------*/
3358
175
    OGRGeometry *poGeom = GetGeometryRef();
3359
175
    TABMAPCoordBlock *poCoordBlock = nullptr;
3360
3361
175
    if ((m_nMapInfoType == TAB_GEOM_REGION ||
3362
175
         m_nMapInfoType == TAB_GEOM_REGION_C ||
3363
175
         m_nMapInfoType == TAB_GEOM_V450_REGION ||
3364
175
         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3365
175
         m_nMapInfoType == TAB_GEOM_V800_REGION ||
3366
175
         m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
3367
175
        poGeom &&
3368
175
        (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3369
175
         wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3370
175
    {
3371
        /*=============================================================
3372
         * REGIONs are similar to PLINE MULTIPLE
3373
         *
3374
         * We accept both OGRPolygons (with one or multiple rings) and
3375
         * OGRMultiPolygons as input.
3376
         *============================================================*/
3377
175
        GBool bCompressed = poObjHdr->IsCompressedType();
3378
3379
        /*-------------------------------------------------------------
3380
         * Process geometry first...
3381
         *------------------------------------------------------------*/
3382
175
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3383
0
            poCoordBlock = *ppoCoordBlock;
3384
175
        else
3385
175
            poCoordBlock = poMapFile->GetCurCoordBlock();
3386
175
        poCoordBlock->StartNewFeature();
3387
175
        GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
3388
175
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3389
3390
#ifdef TABDUMP
3391
        printf(/*ok*/
3392
               "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
3393
               m_nComprOrgX, m_nComprOrgY);
3394
#endif
3395
        /*-------------------------------------------------------------
3396
         * Fetch total number of rings and build array of coord
3397
         * sections headers.
3398
         *------------------------------------------------------------*/
3399
175
        TABMAPCoordSecHdr *pasSecHdrs = nullptr;
3400
175
        int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
3401
175
        int nStatus = numRingsTotal == 0 ? -1 : 0;
3402
3403
        /*-------------------------------------------------------------
3404
         * Write the Coord. Section Header
3405
         *------------------------------------------------------------*/
3406
175
        const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3407
3408
175
        if (nStatus == 0)
3409
74
            nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
3410
74
                                                      pasSecHdrs, bCompressed);
3411
3412
175
        CPLFree(pasSecHdrs);
3413
175
        pasSecHdrs = nullptr;
3414
3415
175
        if (nStatus != 0)
3416
101
            return nStatus;  // Error has already been reported.
3417
3418
        /*-------------------------------------------------------------
3419
         * Go through all the rings in our OGRMultiPolygon or OGRPolygon
3420
         * to write the coordinates themselves...
3421
         *------------------------------------------------------------*/
3422
3423
74
        GInt32 nX = 0, nY = 0;
3424
167
        for (int iRing = 0; iRing < numRingsTotal; iRing++)
3425
93
        {
3426
93
            OGRLinearRing *poRing = GetRingRef(iRing);
3427
93
            if (poRing == nullptr)
3428
0
            {
3429
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
3430
0
                         "TABRegion: Object Geometry contains NULL rings!");
3431
0
                return -1;
3432
0
            }
3433
3434
93
            int numPoints = poRing->getNumPoints();
3435
3436
203
            for (int i = 0; nStatus == 0 && i < numPoints; i++)
3437
110
            {
3438
110
                poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
3439
110
                                        nY);
3440
110
                if ((nStatus =
3441
110
                         poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
3442
0
                {
3443
                    // Failed ... error message has already been produced
3444
0
                    return nStatus;
3445
0
                }
3446
110
            }
3447
93
        } /* for iRing*/
3448
3449
74
        GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
3450
3451
        /*-------------------------------------------------------------
3452
         * ... and finally copy info to poObjHdr
3453
         *------------------------------------------------------------*/
3454
74
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3455
3456
74
        poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
3457
74
        poPLineHdr->m_nCoordDataSize = nCoordDataSize;
3458
74
        poPLineHdr->m_numLineSections = numRingsTotal;
3459
3460
74
        poPLineHdr->m_bSmooth = m_bSmooth;
3461
3462
        // MBR
3463
74
        poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
3464
3465
        // Region center/label point
3466
74
        double dX = 0.0;
3467
74
        double dY = 0.0;
3468
74
        if (GetCenter(dX, dY) != -1)
3469
74
        {
3470
74
            poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
3471
74
                                    poPLineHdr->m_nLabelY);
3472
74
        }
3473
0
        else
3474
0
        {
3475
0
            poPLineHdr->m_nLabelX = m_nComprOrgX;
3476
0
            poPLineHdr->m_nLabelY = m_nComprOrgY;
3477
0
        }
3478
3479
        // Compressed coordinate origin (useful only in compressed case!)
3480
74
        poPLineHdr->m_nComprOrgX = m_nComprOrgX;
3481
74
        poPLineHdr->m_nComprOrgY = m_nComprOrgY;
3482
3483
74
        if (!bCoordBlockDataOnly)
3484
74
        {
3485
74
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
3486
74
            poPLineHdr->m_nPenId =
3487
74
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
3488
3489
74
            m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
3490
74
            poPLineHdr->m_nBrushId =
3491
74
                static_cast<GByte>(m_nBrushDefIndex);  // Brush index
3492
74
        }
3493
74
    }
3494
0
    else
3495
0
    {
3496
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
3497
0
                 "TABRegion: Object contains an invalid Geometry!");
3498
0
        return -1;
3499
0
    }
3500
3501
74
    if (CPLGetLastErrorType() == CE_Failure)
3502
0
        return -1;
3503
3504
    /* Return a ref to coord block so that caller can continue writing
3505
     * after the end of this object (used by index splitting)
3506
     */
3507
74
    if (ppoCoordBlock)
3508
0
        *ppoCoordBlock = poCoordBlock;
3509
3510
74
    return 0;
3511
74
}
3512
3513
/**********************************************************************
3514
 *                   TABRegion::GetNumRings()
3515
 *
3516
 * Return the total number of rings in this object making it look like
3517
 * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3518
 * of rings... hides the complexity of handling OGRMultiPolygons vs
3519
 * OGRPolygons, etc.
3520
 *
3521
 * Returns 0 if the geometry contained in the object is invalid or missing.
3522
 **********************************************************************/
3523
int TABRegion::GetNumRings()
3524
190
{
3525
190
    return ComputeNumRings(nullptr, nullptr);
3526
190
}
3527
3528
int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
3529
                               TABMAPFile *poMapFile)
3530
365
{
3531
365
    int numRingsTotal = 0;
3532
365
    int iLastSect = 0;
3533
3534
365
    if (ppasSecHdrs)
3535
175
        *ppasSecHdrs = nullptr;
3536
3537
365
    OGRGeometry *poGeom = GetGeometryRef();
3538
3539
365
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3540
365
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3541
365
    {
3542
        /*-------------------------------------------------------------
3543
         * Calculate total number of rings...
3544
         *------------------------------------------------------------*/
3545
365
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3546
130
        {
3547
130
            for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
3548
38
            {
3549
38
                numRingsTotal += poPolygon->getNumInteriorRings() + 1;
3550
3551
38
                if (ppasSecHdrs && poMapFile)
3552
19
                {
3553
19
                    if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3554
19
                                      iLastSect) != 0)
3555
0
                        return 0;  // An error happened, return count=0
3556
19
                }
3557
38
            }  // for
3558
130
        }
3559
235
        else
3560
235
        {
3561
235
            OGRPolygon *poPolygon = poGeom->toPolygon();
3562
235
            numRingsTotal = poPolygon->getNumInteriorRings() + 1;
3563
3564
235
            if (ppasSecHdrs && poMapFile)
3565
110
            {
3566
110
                if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3567
110
                                  iLastSect) != 0)
3568
55
                    return 0;  // An error happened, return count=0
3569
110
            }
3570
235
        }
3571
365
    }
3572
3573
    /*-----------------------------------------------------------------
3574
     * If we're generating section header blocks, then init the
3575
     * coordinate offset values.
3576
     *
3577
     * In calculation of nDataOffset, we have to take into account that
3578
     * V450 header section uses int32 instead of int16 for numVertices
3579
     * and we add another 2 bytes to align with a 4 bytes boundary.
3580
     *------------------------------------------------------------*/
3581
310
    const int nTotalHdrSizeUncompressed =
3582
310
        (m_nMapInfoType == TAB_GEOM_V450_REGION ||
3583
310
         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3584
310
         m_nMapInfoType == TAB_GEOM_V800_REGION ||
3585
310
         m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3586
310
            ? 28 * numRingsTotal
3587
310
            : 24 * numRingsTotal;
3588
3589
310
    if (ppasSecHdrs)
3590
120
    {
3591
120
        int numPointsTotal = 0;
3592
120
        CPLAssert(iLastSect == numRingsTotal);
3593
213
        for (int iRing = 0; iRing < numRingsTotal; iRing++)
3594
93
        {
3595
93
            (*ppasSecHdrs)[iRing].nDataOffset =
3596
93
                nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
3597
93
            (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
3598
3599
93
            numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
3600
93
        }
3601
120
    }
3602
3603
310
    return numRingsTotal;
3604
365
}
3605
3606
/**********************************************************************
3607
 *                   TABRegion::AppendSecHdrs()
3608
 *
3609
 * (Private method)
3610
 *
3611
 * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
3612
 **********************************************************************/
3613
int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
3614
                             TABMAPCoordSecHdr *&pasSecHdrs,
3615
                             TABMAPFile *poMapFile, int &iLastRing)
3616
129
{
3617
    /*-------------------------------------------------------------
3618
     * Add a pasSecHdrs[] entry for each ring in this polygon.
3619
     * Note that the structs won't be fully initialized.
3620
     *------------------------------------------------------------*/
3621
129
    int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
3622
3623
129
    pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3624
129
        CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
3625
129
                                   sizeof(TABMAPCoordSecHdr)));
3626
3627
222
    for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
3628
148
    {
3629
148
        OGRLinearRing *poRing = nullptr;
3630
148
        OGREnvelope sEnvelope;
3631
3632
148
        if (iRing == 0)
3633
129
            poRing = poPolygon->getExteriorRing();
3634
19
        else
3635
19
            poRing = poPolygon->getInteriorRing(iRing - 1);
3636
3637
148
        if (poRing == nullptr)
3638
55
        {
3639
55
            CPLError(CE_Failure, CPLE_AssertionFailed,
3640
55
                     "Assertion Failed: Encountered NULL ring in OGRPolygon");
3641
55
            return -1;
3642
55
        }
3643
3644
93
        poRing->getEnvelope(&sEnvelope);
3645
3646
93
        pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
3647
3648
93
        if (iRing == 0)
3649
74
            pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
3650
19
        else
3651
19
            pasSecHdrs[iLastRing].numHoles = 0;
3652
3653
93
        poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
3654
93
                                pasSecHdrs[iLastRing].nXMin,
3655
93
                                pasSecHdrs[iLastRing].nYMin);
3656
93
        poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
3657
93
                                pasSecHdrs[iLastRing].nXMax,
3658
93
                                pasSecHdrs[iLastRing].nYMax);
3659
3660
93
        iLastRing++;
3661
93
    } /* for iRing*/
3662
3663
74
    return 0;
3664
129
}
3665
3666
/**********************************************************************
3667
 *                   TABRegion::GetRingRef()
3668
 *
3669
 * Returns a reference to the specified ring number making it look like
3670
 * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3671
 * of rings... hides the complexity of handling OGRMultiPolygons vs
3672
 * OGRPolygons, etc.
3673
 *
3674
 * Returns NULL if the geometry contained in the object is invalid or
3675
 * missing or if the specified ring index is invalid.
3676
 **********************************************************************/
3677
OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
3678
256
{
3679
256
    OGRLinearRing *poRing = nullptr;
3680
3681
256
    OGRGeometry *poGeom = GetGeometryRef();
3682
3683
256
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3684
256
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3685
256
    {
3686
        /*-------------------------------------------------------------
3687
         * Establish number of polygons based on geometry type
3688
         *------------------------------------------------------------*/
3689
256
        OGRMultiPolygon *poMultiPolygon = nullptr;
3690
256
        int iCurRing = 0;
3691
256
        int numOGRPolygons = 0;
3692
3693
256
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3694
38
        {
3695
38
            poMultiPolygon = poGeom->toMultiPolygon();
3696
38
            numOGRPolygons = poMultiPolygon->getNumGeometries();
3697
38
        }
3698
218
        else
3699
218
        {
3700
218
            numOGRPolygons = 1;
3701
218
        }
3702
3703
        /*-------------------------------------------------------------
3704
         * Loop through polygons until we find the requested ring.
3705
         *------------------------------------------------------------*/
3706
256
        iCurRing = 0;
3707
512
        for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
3708
256
             iPoly++)
3709
256
        {
3710
256
            OGRPolygon *poPolygon = nullptr;
3711
256
            if (poMultiPolygon)
3712
38
                poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3713
218
            else
3714
218
                poPolygon = poGeom->toPolygon();
3715
3716
256
            int numIntRings = poPolygon->getNumInteriorRings();
3717
3718
256
            if (iCurRing == nRequestedRingIndex)
3719
218
            {
3720
218
                poRing = poPolygon->getExteriorRing();
3721
218
            }
3722
38
            else if (nRequestedRingIndex > iCurRing &&
3723
38
                     nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3724
38
            {
3725
38
                poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
3726
38
                                                    (iCurRing + 1));
3727
38
            }
3728
256
            iCurRing += numIntRings + 1;
3729
256
        }
3730
256
    }
3731
3732
256
    return poRing;
3733
256
}
3734
3735
/**********************************************************************
3736
 *                   TABRegion::RingIsHole()
3737
 *
3738
 * Return false if the requested ring index is the first of a polygon
3739
 **********************************************************************/
3740
GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
3741
0
{
3742
0
    OGRGeometry *poGeom = GetGeometryRef();
3743
3744
0
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3745
0
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3746
0
    {
3747
        /*-------------------------------------------------------------
3748
         * Establish number of polygons based on geometry type
3749
         *------------------------------------------------------------*/
3750
0
        OGRMultiPolygon *poMultiPolygon = nullptr;
3751
0
        int iCurRing = 0;
3752
0
        int numOGRPolygons = 0;
3753
3754
0
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3755
0
        {
3756
0
            poMultiPolygon = poGeom->toMultiPolygon();
3757
0
            numOGRPolygons = poMultiPolygon->getNumGeometries();
3758
0
        }
3759
0
        else
3760
0
        {
3761
0
            numOGRPolygons = 1;
3762
0
        }
3763
3764
        /*-------------------------------------------------------------
3765
         * Loop through polygons until we find the requested ring.
3766
         *------------------------------------------------------------*/
3767
0
        iCurRing = 0;
3768
0
        for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
3769
0
        {
3770
0
            OGRPolygon *poPolygon = nullptr;
3771
0
            if (poMultiPolygon)
3772
0
                poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3773
0
            else
3774
0
                poPolygon = poGeom->toPolygon();
3775
3776
0
            int numIntRings = poPolygon->getNumInteriorRings();
3777
3778
0
            if (iCurRing == nRequestedRingIndex)
3779
0
            {
3780
0
                return FALSE;
3781
0
            }
3782
0
            else if (nRequestedRingIndex > iCurRing &&
3783
0
                     nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3784
0
            {
3785
0
                return TRUE;
3786
0
            }
3787
0
            iCurRing += numIntRings + 1;
3788
0
        }
3789
0
    }
3790
3791
0
    return FALSE;
3792
0
}
3793
3794
/**********************************************************************
3795
 *                   TABRegion::GetStyleString() const
3796
 *
3797
 * Return style string for this feature.
3798
 *
3799
 * Style String is built only once during the first call to GetStyleString().
3800
 **********************************************************************/
3801
const char *TABRegion::GetStyleString() const
3802
0
{
3803
0
    if (m_pszStyleString == nullptr)
3804
0
    {
3805
        // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
3806
        // to use temporary buffers
3807
0
        char *pszPen = CPLStrdup(GetPenStyleString());
3808
0
        char *pszBrush = CPLStrdup(GetBrushStyleString());
3809
3810
0
        m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
3811
3812
0
        CPLFree(pszPen);
3813
0
        CPLFree(pszBrush);
3814
0
    }
3815
3816
0
    return m_pszStyleString;
3817
0
}
3818
3819
/**********************************************************************
3820
 *                   TABRegion::DumpMIF()
3821
 *
3822
 * Dump feature geometry in a format similar to .MIF REGIONs.
3823
 **********************************************************************/
3824
void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
3825
0
{
3826
0
    if (fpOut == nullptr)
3827
0
        fpOut = stdout;
3828
3829
    /*-----------------------------------------------------------------
3830
     * Fetch and validate geometry
3831
     *----------------------------------------------------------------*/
3832
0
    OGRGeometry *poGeom = GetGeometryRef();
3833
0
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3834
0
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3835
0
    {
3836
        /*-------------------------------------------------------------
3837
         * Generate output for region
3838
         *
3839
         * Note that we want to handle both OGRPolygons and OGRMultiPolygons
3840
         * that's why we use the GetNumRings()/GetRingRef() interface.
3841
         *------------------------------------------------------------*/
3842
0
        int numRingsTotal = GetNumRings();
3843
3844
0
        fprintf(fpOut, "REGION %d\n", numRingsTotal);
3845
3846
0
        for (int iRing = 0; iRing < numRingsTotal; iRing++)
3847
0
        {
3848
0
            OGRLinearRing *poRing = GetRingRef(iRing);
3849
3850
0
            if (poRing == nullptr)
3851
0
            {
3852
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
3853
0
                         "TABRegion: Object Geometry contains NULL rings!");
3854
0
                return;
3855
0
            }
3856
3857
0
            const int numPoints = poRing->getNumPoints();
3858
0
            fprintf(fpOut, " %d\n", numPoints);
3859
0
            for (int i = 0; i < numPoints; i++)
3860
0
                fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
3861
0
                        poRing->getY(i));
3862
0
        }
3863
0
    }
3864
0
    else
3865
0
    {
3866
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
3867
0
                 "TABRegion: Missing or Invalid Geometry!");
3868
0
        return;
3869
0
    }
3870
3871
0
    if (m_bCenterIsSet)
3872
0
        fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
3873
3874
    // Finish with PEN/BRUSH/etc. clauses
3875
0
    DumpPenDef();
3876
0
    DumpBrushDef();
3877
3878
0
    fflush(fpOut);
3879
0
}
3880
3881
/**********************************************************************
3882
 *                   TABRegion::GetCenter()
3883
 *
3884
 * Returns the center/label point of the region.
3885
 * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
3886
 * before.
3887
 *
3888
 * Returns 0 on success, -1 on error.
3889
 **********************************************************************/
3890
int TABRegion::GetCenter(double &dX, double &dY)
3891
74
{
3892
74
    if (!m_bCenterIsSet)
3893
74
    {
3894
        /*-------------------------------------------------------------
3895
         * Calculate label point.  If we have a multipolygon then we use
3896
         * the first OGRPolygon in the feature to calculate the point.
3897
         *------------------------------------------------------------*/
3898
74
        OGRGeometry *poGeom = GetGeometryRef();
3899
74
        if (poGeom == nullptr)
3900
0
            return -1;
3901
3902
74
        OGRPolygon *poPolygon = nullptr;
3903
3904
74
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3905
19
        {
3906
19
            OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
3907
19
            if (poMultiPolygon->getNumGeometries() > 0)
3908
19
                poPolygon = poMultiPolygon->getGeometryRef(0);
3909
19
        }
3910
55
        else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
3911
55
        {
3912
55
            poPolygon = poGeom->toPolygon();
3913
55
        }
3914
3915
74
        OGRPoint oLabelPoint;
3916
74
        if (poPolygon != nullptr &&
3917
74
            OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
3918
3
        {
3919
3
            m_dCenterX = oLabelPoint.getX();
3920
3
            m_dCenterY = oLabelPoint.getY();
3921
3
        }
3922
71
        else
3923
71
        {
3924
71
            OGREnvelope oEnv;
3925
71
            poGeom->getEnvelope(&oEnv);
3926
71
            m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
3927
71
            m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
3928
71
        }
3929
3930
74
        m_bCenterIsSet = TRUE;
3931
74
    }
3932
3933
74
    if (!m_bCenterIsSet)
3934
0
        return -1;
3935
3936
74
    dX = m_dCenterX;
3937
74
    dY = m_dCenterY;
3938
74
    return 0;
3939
74
}
3940
3941
/**********************************************************************
3942
 *                   TABRegion::SetCenter()
3943
 *
3944
 * Set the X,Y coordinates to use as center/label point for the region.
3945
 **********************************************************************/
3946
void TABRegion::SetCenter(double dX, double dY)
3947
4.69k
{
3948
4.69k
    m_dCenterX = dX;
3949
4.69k
    m_dCenterY = dY;
3950
4.69k
    m_bCenterIsSet = TRUE;
3951
4.69k
}
3952
3953
/*=====================================================================
3954
 *                      class TABRectangle
3955
 *====================================================================*/
3956
3957
/**********************************************************************
3958
 *                   TABRectangle::TABRectangle()
3959
 *
3960
 * Constructor.
3961
 **********************************************************************/
3962
TABRectangle::TABRectangle(OGRFeatureDefn *poDefnIn)
3963
40.3k
    : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
3964
40.3k
      m_dRoundYRadius(0.0)
3965
40.3k
{
3966
40.3k
}
3967
3968
/**********************************************************************
3969
 *                   TABRectangle::~TABRectangle()
3970
 *
3971
 * Destructor.
3972
 **********************************************************************/
3973
TABRectangle::~TABRectangle()
3974
40.3k
{
3975
40.3k
}
3976
3977
/**********************************************************************
3978
 *                     TABRectangle::CloneTABFeature()
3979
 *
3980
 * Duplicate feature, including stuff specific to each TABFeature type.
3981
 *
3982
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
3983
 * then copies any members specific to its own type.
3984
 **********************************************************************/
3985
TABFeature *TABRectangle::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
3986
0
{
3987
    /*-----------------------------------------------------------------
3988
     * Alloc new feature and copy the base stuff
3989
     *----------------------------------------------------------------*/
3990
0
    TABRectangle *poNew =
3991
0
        new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
3992
3993
0
    CopyTABFeatureBase(poNew);
3994
3995
    /*-----------------------------------------------------------------
3996
     * And members specific to this class
3997
     *----------------------------------------------------------------*/
3998
    // ITABFeaturePen
3999
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
4000
4001
    // ITABFeatureBrush
4002
0
    *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4003
4004
0
    poNew->m_bRoundCorners = m_bRoundCorners;
4005
0
    poNew->m_dRoundXRadius = m_dRoundXRadius;
4006
0
    poNew->m_dRoundYRadius = m_dRoundYRadius;
4007
4008
0
    return poNew;
4009
0
}
4010
4011
/**********************************************************************
4012
 *                   TABRectangle::ValidateMapInfoType()
4013
 *
4014
 * Check the feature's geometry part and return the corresponding
4015
 * mapinfo object type code.  The m_nMapInfoType member will also
4016
 * be updated for further calls to GetMapInfoType();
4017
 *
4018
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4019
 * is expected for this object class.
4020
 **********************************************************************/
4021
TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4022
0
{
4023
    /*-----------------------------------------------------------------
4024
     * Fetch and validate geometry
4025
     *----------------------------------------------------------------*/
4026
0
    OGRGeometry *poGeom = GetGeometryRef();
4027
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4028
0
    {
4029
0
        if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4030
0
            m_nMapInfoType = TAB_GEOM_ROUNDRECT;
4031
0
        else
4032
0
            m_nMapInfoType = TAB_GEOM_RECT;
4033
0
    }
4034
0
    else
4035
0
    {
4036
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4037
0
                 "TABRectangle: Missing or Invalid Geometry!");
4038
0
        m_nMapInfoType = TAB_GEOM_NONE;
4039
0
    }
4040
4041
    /*-----------------------------------------------------------------
4042
     * Decide if coordinates should be compressed or not.
4043
     *----------------------------------------------------------------*/
4044
    // __TODO__ For now we always write uncompressed for this class...
4045
    // ValidateCoordType(poMapFile);
4046
0
    UpdateMBR(poMapFile);
4047
4048
0
    return m_nMapInfoType;
4049
0
}
4050
4051
/**********************************************************************
4052
 *                   TABRectangle::UpdateMBR()
4053
 *
4054
 * Update the feature MBR members using the geometry
4055
 *
4056
 * Returns 0 on success, or -1 if there is no geometry in object
4057
 **********************************************************************/
4058
int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4059
0
{
4060
0
    OGREnvelope sEnvelope;
4061
4062
    /*-----------------------------------------------------------------
4063
     * Fetch and validate geometry
4064
     *----------------------------------------------------------------*/
4065
0
    OGRGeometry *poGeom = GetGeometryRef();
4066
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4067
0
        poGeom->getEnvelope(&sEnvelope);
4068
0
    else
4069
0
    {
4070
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4071
0
                 "TABRectangle: Missing or Invalid Geometry!");
4072
0
        return -1;
4073
0
    }
4074
4075
    /*-----------------------------------------------------------------
4076
     * Note that we will simply use the rectangle's MBR and don't really
4077
     * read the polygon geometry... this should be OK unless the
4078
     * polygon geometry was not really a rectangle.
4079
     *----------------------------------------------------------------*/
4080
0
    m_dXMin = sEnvelope.MinX;
4081
0
    m_dYMin = sEnvelope.MinY;
4082
0
    m_dXMax = sEnvelope.MaxX;
4083
0
    m_dYMax = sEnvelope.MaxY;
4084
4085
0
    if (poMapFile)
4086
0
    {
4087
0
        poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4088
0
        poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4089
0
    }
4090
4091
0
    return 0;
4092
0
}
4093
4094
/**********************************************************************
4095
 *                   TABRectangle::ReadGeometryFromMAPFile()
4096
 *
4097
 * Fill the geometry and representation (color, etc...) part of the
4098
 * feature from the contents of the .MAP object pointed to by poMAPFile.
4099
 *
4100
 * It is assumed that poMAPFile currently points to the beginning of
4101
 * a map object.
4102
 *
4103
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4104
 * been called.
4105
 **********************************************************************/
4106
int TABRectangle::ReadGeometryFromMAPFile(
4107
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4108
    GBool bCoordBlockDataOnly /*=FALSE*/,
4109
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4110
78
{
4111
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4112
78
    if (bCoordBlockDataOnly)
4113
0
        return 0;
4114
4115
    /*-----------------------------------------------------------------
4116
     * Fetch and validate geometry type
4117
     *----------------------------------------------------------------*/
4118
78
    m_nMapInfoType = poObjHdr->m_nType;
4119
4120
78
    if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
4121
78
        m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
4122
78
        m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
4123
0
    {
4124
0
        CPLError(
4125
0
            CE_Failure, CPLE_AssertionFailed,
4126
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4127
0
            m_nMapInfoType, m_nMapInfoType);
4128
0
        return -1;
4129
0
    }
4130
4131
    /*-----------------------------------------------------------------
4132
     * Read object information
4133
     *----------------------------------------------------------------*/
4134
78
    TABMAPObjRectEllipse *poRectHdr =
4135
78
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4136
4137
    // Read the corners radius
4138
4139
78
    if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4140
78
        m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4141
46
    {
4142
        // Read the corner's diameters
4143
46
        poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
4144
46
                                    poRectHdr->m_nCornerHeight, m_dRoundXRadius,
4145
46
                                    m_dRoundYRadius);
4146
4147
        // Divide by 2 since we store the corner's radius
4148
46
        m_dRoundXRadius /= 2.0;
4149
46
        m_dRoundYRadius /= 2.0;
4150
4151
46
        m_bRoundCorners = TRUE;
4152
46
    }
4153
32
    else
4154
32
    {
4155
32
        m_bRoundCorners = FALSE;
4156
32
        m_dRoundXRadius = 0.0;
4157
32
        m_dRoundYRadius = 0.0;
4158
32
    }
4159
4160
    // A rectangle is defined by its MBR
4161
4162
78
    double dXMin = 0.0;
4163
78
    double dYMin = 0.0;
4164
78
    double dXMax = 0.0;
4165
78
    double dYMax = 0.0;
4166
78
    poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4167
78
                            dYMin);
4168
78
    poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4169
78
                            dYMax);
4170
4171
78
    m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
4172
78
    poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4173
4174
78
    m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
4175
78
    poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4176
4177
    /*-----------------------------------------------------------------
4178
     * Call SetMBR() and GetMBR() now to make sure that min values are
4179
     * really smaller than max values.
4180
     *----------------------------------------------------------------*/
4181
78
    SetMBR(dXMin, dYMin, dXMax, dYMax);
4182
78
    GetMBR(dXMin, dYMin, dXMax, dYMax);
4183
4184
    /* Copy int MBR to feature class members */
4185
78
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4186
78
              poObjHdr->m_nMaxY);
4187
4188
    /*-----------------------------------------------------------------
4189
     * Create and fill geometry object
4190
     *----------------------------------------------------------------*/
4191
78
    OGRPolygon *poPolygon = new OGRPolygon;
4192
78
    OGRLinearRing *poRing = new OGRLinearRing();
4193
78
    if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4194
35
    {
4195
        /*-------------------------------------------------------------
4196
         * For rounded rectangles, we generate arcs with 45 line
4197
         * segments for each corner.  We start with lower-left corner
4198
         * and proceed counterclockwise
4199
         * We also have to make sure that rounding radius is not too
4200
         * large for the MBR in the generated polygon... however, we
4201
         * always return the true X/Y radius (not adjusted) since this
4202
         * is the way MapInfo seems to do it when a radius bigger than
4203
         * the MBR is passed from TBA to MIF.
4204
         *------------------------------------------------------------*/
4205
35
        const double dXRadius =
4206
35
            std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
4207
35
        const double dYRadius =
4208
35
            std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
4209
35
        TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
4210
35
                       dYRadius, M_PI, 3.0 * M_PI / 2.0);
4211
35
        TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
4212
35
                       dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
4213
35
        TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
4214
35
                       dYRadius, 0.0, M_PI / 2.0);
4215
35
        TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
4216
35
                       dYRadius, M_PI / 2.0, M_PI);
4217
4218
35
        TABCloseRing(poRing);
4219
35
    }
4220
43
    else
4221
43
    {
4222
43
        poRing->addPoint(dXMin, dYMin);
4223
43
        poRing->addPoint(dXMax, dYMin);
4224
43
        poRing->addPoint(dXMax, dYMax);
4225
43
        poRing->addPoint(dXMin, dYMax);
4226
43
        poRing->addPoint(dXMin, dYMin);
4227
43
    }
4228
4229
78
    poPolygon->addRingDirectly(poRing);
4230
78
    SetGeometryDirectly(poPolygon);
4231
4232
78
    return 0;
4233
78
}
4234
4235
/**********************************************************************
4236
 *                   TABRectangle::WriteGeometryToMAPFile()
4237
 *
4238
 * Write the geometry and representation (color, etc...) part of the
4239
 * feature to the .MAP object pointed to by poMAPFile.
4240
 *
4241
 * It is assumed that poMAPFile currently points to a valid map object.
4242
 *
4243
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4244
 * been called.
4245
 **********************************************************************/
4246
int TABRectangle::WriteGeometryToMAPFile(
4247
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4248
    GBool bCoordBlockDataOnly /*=FALSE*/,
4249
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4250
0
{
4251
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4252
0
    if (bCoordBlockDataOnly)
4253
0
        return 0;
4254
4255
    /*-----------------------------------------------------------------
4256
     * We assume that ValidateMapInfoType() was called already and that
4257
     * the type in poObjHdr->m_nType is valid.
4258
     *----------------------------------------------------------------*/
4259
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4260
4261
    /*-----------------------------------------------------------------
4262
     * Fetch and validate geometry and update MBR
4263
     * Note that we will simply use the geometry's MBR and don't really
4264
     * read the polygon geometry... this should be OK unless the
4265
     * polygon geometry was not really a rectangle.
4266
     *----------------------------------------------------------------*/
4267
0
    if (UpdateMBR(poMapFile) != 0)
4268
0
        return -1; /* Error already reported */
4269
4270
    /*-----------------------------------------------------------------
4271
     * Copy object information
4272
     *----------------------------------------------------------------*/
4273
0
    TABMAPObjRectEllipse *poRectHdr =
4274
0
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4275
4276
0
    if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4277
0
        m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4278
0
    {
4279
0
        poMapFile->Coordsys2IntDist(
4280
0
            m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
4281
0
            poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
4282
0
    }
4283
0
    else
4284
0
    {
4285
0
        poRectHdr->m_nCornerWidth = 0;
4286
0
        poRectHdr->m_nCornerHeight = 0;
4287
0
    }
4288
4289
    // A rectangle is defined by its MBR (values were set in UpdateMBR())
4290
0
    poRectHdr->m_nMinX = m_nXMin;
4291
0
    poRectHdr->m_nMinY = m_nYMin;
4292
0
    poRectHdr->m_nMaxX = m_nXMax;
4293
0
    poRectHdr->m_nMaxY = m_nYMax;
4294
4295
0
    m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4296
0
    poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
4297
4298
0
    m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4299
0
    poRectHdr->m_nBrushId =
4300
0
        static_cast<GByte>(m_nBrushDefIndex);  // Brush index
4301
4302
0
    if (CPLGetLastErrorType() == CE_Failure)
4303
0
        return -1;
4304
4305
0
    return 0;
4306
0
}
4307
4308
/**********************************************************************
4309
 *                   TABRectangle::GetStyleString() const
4310
 *
4311
 * Return style string for this feature.
4312
 *
4313
 * Style String is built only once during the first call to GetStyleString().
4314
 **********************************************************************/
4315
const char *TABRectangle::GetStyleString() const
4316
0
{
4317
0
    if (m_pszStyleString == nullptr)
4318
0
    {
4319
        // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4320
        // to use temporary buffers
4321
0
        char *pszPen = CPLStrdup(GetPenStyleString());
4322
0
        char *pszBrush = CPLStrdup(GetBrushStyleString());
4323
4324
0
        m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4325
4326
0
        CPLFree(pszPen);
4327
0
        CPLFree(pszBrush);
4328
0
    }
4329
4330
0
    return m_pszStyleString;
4331
0
}
4332
4333
/**********************************************************************
4334
 *                   TABRectangle::DumpMIF()
4335
 *
4336
 * Dump feature geometry in a format similar to .MIF REGIONs.
4337
 **********************************************************************/
4338
void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
4339
0
{
4340
0
    if (fpOut == nullptr)
4341
0
        fpOut = stdout;
4342
4343
    /*-----------------------------------------------------------------
4344
     * Output RECT or ROUNDRECT parameters
4345
     *----------------------------------------------------------------*/
4346
0
    double dXMin = 0.0;
4347
0
    double dYMin = 0.0;
4348
0
    double dXMax = 0.0;
4349
0
    double dYMax = 0.0;
4350
0
    GetMBR(dXMin, dYMin, dXMax, dYMax);
4351
4352
0
    if (m_bRoundCorners)
4353
0
        fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g    %.15g %.15g)\n",
4354
0
                dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
4355
0
    else
4356
0
        fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4357
0
                dYMax);
4358
4359
    /*-----------------------------------------------------------------
4360
     * Fetch and validate geometry
4361
     *----------------------------------------------------------------*/
4362
0
    OGRGeometry *poGeom = GetGeometryRef();
4363
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4364
0
    {
4365
        /*-------------------------------------------------------------
4366
         * Generate rectangle output as a region
4367
         * We could also output as a RECT or ROUNDRECT in a real MIF generator
4368
         *------------------------------------------------------------*/
4369
0
        OGRPolygon *poPolygon = poGeom->toPolygon();
4370
0
        int numIntRings = poPolygon->getNumInteriorRings();
4371
0
        fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4372
        // In this loop, iRing=-1 for the outer ring.
4373
0
        for (int iRing = -1; iRing < numIntRings; iRing++)
4374
0
        {
4375
0
            OGRLinearRing *poRing = nullptr;
4376
4377
0
            if (iRing == -1)
4378
0
                poRing = poPolygon->getExteriorRing();
4379
0
            else
4380
0
                poRing = poPolygon->getInteriorRing(iRing);
4381
4382
0
            if (poRing == nullptr)
4383
0
            {
4384
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
4385
0
                         "TABRectangle: Object Geometry contains NULL rings!");
4386
0
                return;
4387
0
            }
4388
4389
0
            const int numPoints = poRing->getNumPoints();
4390
0
            fprintf(fpOut, " %d\n", numPoints);
4391
0
            for (int i = 0; i < numPoints; i++)
4392
0
                fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4393
0
                        poRing->getY(i));
4394
0
        }
4395
0
    }
4396
0
    else
4397
0
    {
4398
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4399
0
                 "TABRectangle: Missing or Invalid Geometry!");
4400
0
        return;
4401
0
    }
4402
4403
    // Finish with PEN/BRUSH/etc. clauses
4404
0
    DumpPenDef();
4405
0
    DumpBrushDef();
4406
4407
0
    fflush(fpOut);
4408
0
}
4409
4410
/*=====================================================================
4411
 *                      class TABEllipse
4412
 *====================================================================*/
4413
4414
/**********************************************************************
4415
 *                   TABEllipse::TABEllipse()
4416
 *
4417
 * Constructor.
4418
 **********************************************************************/
4419
TABEllipse::TABEllipse(OGRFeatureDefn *poDefnIn)
4420
15.0k
    : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
4421
15.0k
      m_dYRadius(0.0)
4422
15.0k
{
4423
15.0k
}
4424
4425
/**********************************************************************
4426
 *                   TABEllipse::~TABEllipse()
4427
 *
4428
 * Destructor.
4429
 **********************************************************************/
4430
TABEllipse::~TABEllipse()
4431
15.0k
{
4432
15.0k
}
4433
4434
/**********************************************************************
4435
 *                     TABEllipse::CloneTABFeature()
4436
 *
4437
 * Duplicate feature, including stuff specific to each TABFeature type.
4438
 *
4439
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
4440
 * then copies any members specific to its own type.
4441
 **********************************************************************/
4442
TABFeature *TABEllipse::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4443
0
{
4444
    /*-----------------------------------------------------------------
4445
     * Alloc new feature and copy the base stuff
4446
     *----------------------------------------------------------------*/
4447
0
    TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
4448
4449
0
    CopyTABFeatureBase(poNew);
4450
4451
    /*-----------------------------------------------------------------
4452
     * And members specific to this class
4453
     *----------------------------------------------------------------*/
4454
    // ITABFeaturePen
4455
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
4456
4457
    // ITABFeatureBrush
4458
0
    *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4459
4460
0
    poNew->m_dCenterX = m_dCenterX;
4461
0
    poNew->m_dCenterY = m_dCenterY;
4462
0
    poNew->m_dXRadius = m_dXRadius;
4463
0
    poNew->m_dYRadius = m_dYRadius;
4464
4465
0
    return poNew;
4466
0
}
4467
4468
/**********************************************************************
4469
 *                   TABEllipse::ValidateMapInfoType()
4470
 *
4471
 * Check the feature's geometry part and return the corresponding
4472
 * mapinfo object type code.  The m_nMapInfoType member will also
4473
 * be updated for further calls to GetMapInfoType();
4474
 *
4475
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4476
 * is expected for this object class.
4477
 **********************************************************************/
4478
TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4479
0
{
4480
    /*-----------------------------------------------------------------
4481
     * Fetch and validate geometry
4482
     *----------------------------------------------------------------*/
4483
0
    OGRGeometry *poGeom = GetGeometryRef();
4484
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4485
0
        (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4486
0
    {
4487
0
        m_nMapInfoType = TAB_GEOM_ELLIPSE;
4488
0
    }
4489
0
    else
4490
0
    {
4491
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4492
0
                 "TABEllipse: Missing or Invalid Geometry!");
4493
0
        m_nMapInfoType = TAB_GEOM_NONE;
4494
0
    }
4495
4496
    /*-----------------------------------------------------------------
4497
     * Decide if coordinates should be compressed or not.
4498
     *----------------------------------------------------------------*/
4499
    // __TODO__ For now we always write uncompressed for this class...
4500
    // ValidateCoordType(poMapFile);
4501
0
    UpdateMBR(poMapFile);
4502
4503
0
    return m_nMapInfoType;
4504
0
}
4505
4506
/**********************************************************************
4507
 *                   TABEllipse::UpdateMBR()
4508
 *
4509
 * Update the feature MBR members using the geometry
4510
 *
4511
 * Returns 0 on success, or -1 if there is no geometry in object
4512
 **********************************************************************/
4513
int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4514
0
{
4515
0
    OGREnvelope sEnvelope;
4516
4517
    /*-----------------------------------------------------------------
4518
     * Fetch and validate geometry... Polygon and point are accepted.
4519
     * Note that we will simply use the ellipse's MBR and don't really
4520
     * read the polygon geometry... this should be OK unless the
4521
     * polygon geometry was not really an ellipse.
4522
     *----------------------------------------------------------------*/
4523
0
    OGRGeometry *poGeom = GetGeometryRef();
4524
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4525
0
        (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4526
0
        poGeom->getEnvelope(&sEnvelope);
4527
0
    else
4528
0
    {
4529
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4530
0
                 "TABEllipse: Missing or Invalid Geometry!");
4531
0
        return -1;
4532
0
    }
4533
4534
    /*-----------------------------------------------------------------
4535
     * We use the center of the MBR as the ellipse center, and the
4536
     * X/Y radius to define the MBR size.  If X/Y radius are null then
4537
     * we'll try to use the MBR to recompute them.
4538
     *----------------------------------------------------------------*/
4539
0
    const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
4540
0
    const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
4541
0
    if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
4542
0
    {
4543
0
        m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
4544
0
        m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
4545
0
    }
4546
4547
0
    m_dXMin = dXCenter - m_dXRadius;
4548
0
    m_dYMin = dYCenter - m_dYRadius;
4549
0
    m_dXMax = dXCenter + m_dXRadius;
4550
0
    m_dYMax = dYCenter + m_dYRadius;
4551
4552
0
    if (poMapFile)
4553
0
    {
4554
0
        poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4555
0
        poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4556
0
    }
4557
4558
0
    return 0;
4559
0
}
4560
4561
/**********************************************************************
4562
 *                   TABEllipse::ReadGeometryFromMAPFile()
4563
 *
4564
 * Fill the geometry and representation (color, etc...) part of the
4565
 * feature from the contents of the .MAP object pointed to by poMAPFile.
4566
 *
4567
 * It is assumed that poMAPFile currently points to the beginning of
4568
 * a map object.
4569
 *
4570
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4571
 * been called.
4572
 **********************************************************************/
4573
int TABEllipse::ReadGeometryFromMAPFile(
4574
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4575
    GBool bCoordBlockDataOnly /*=FALSE*/,
4576
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4577
31
{
4578
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4579
31
    if (bCoordBlockDataOnly)
4580
0
        return 0;
4581
4582
    /*-----------------------------------------------------------------
4583
     * Fetch and validate geometry type
4584
     *----------------------------------------------------------------*/
4585
31
    m_nMapInfoType = poObjHdr->m_nType;
4586
4587
31
    if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
4588
31
        m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
4589
0
    {
4590
0
        CPLError(
4591
0
            CE_Failure, CPLE_AssertionFailed,
4592
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4593
0
            m_nMapInfoType, m_nMapInfoType);
4594
0
        return -1;
4595
0
    }
4596
4597
    /*-----------------------------------------------------------------
4598
     * Read object information
4599
     *----------------------------------------------------------------*/
4600
31
    TABMAPObjRectEllipse *poRectHdr =
4601
31
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4602
4603
    // An ellipse is defined by its MBR
4604
4605
31
    double dXMin = 0.0;
4606
31
    double dYMin = 0.0;
4607
31
    double dXMax = 0.0;
4608
31
    double dYMax = 0.0;
4609
31
    poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4610
31
                            dYMin);
4611
31
    poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4612
31
                            dYMax);
4613
4614
31
    m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
4615
31
    poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4616
4617
31
    m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
4618
31
    poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4619
4620
    /*-----------------------------------------------------------------
4621
     * Save info about the ellipse def. inside class members
4622
     *----------------------------------------------------------------*/
4623
31
    m_dCenterX = (dXMin + dXMax) / 2.0;
4624
31
    m_dCenterY = (dYMin + dYMax) / 2.0;
4625
31
    m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
4626
31
    m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
4627
4628
31
    SetMBR(dXMin, dYMin, dXMax, dYMax);
4629
4630
31
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4631
31
              poObjHdr->m_nMaxY);
4632
4633
    /*-----------------------------------------------------------------
4634
     * Create and fill geometry object
4635
     *----------------------------------------------------------------*/
4636
31
    OGRPolygon *poPolygon = new OGRPolygon;
4637
31
    OGRLinearRing *poRing = new OGRLinearRing();
4638
4639
    /*-----------------------------------------------------------------
4640
     * For the OGR geometry, we generate an ellipse with 2 degrees line
4641
     * segments.
4642
     *----------------------------------------------------------------*/
4643
31
    TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
4644
31
                   0.0, 2.0 * M_PI);
4645
31
    TABCloseRing(poRing);
4646
4647
31
    poPolygon->addRingDirectly(poRing);
4648
31
    SetGeometryDirectly(poPolygon);
4649
4650
31
    return 0;
4651
31
}
4652
4653
/**********************************************************************
4654
 *                   TABEllipse::WriteGeometryToMAPFile()
4655
 *
4656
 * Write the geometry and representation (color, etc...) part of the
4657
 * feature to the .MAP object pointed to by poMAPFile.
4658
 *
4659
 * It is assumed that poMAPFile currently points to a valid map object.
4660
 *
4661
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4662
 * been called.
4663
 **********************************************************************/
4664
int TABEllipse::WriteGeometryToMAPFile(
4665
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4666
    GBool bCoordBlockDataOnly /*=FALSE*/,
4667
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4668
0
{
4669
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4670
0
    if (bCoordBlockDataOnly)
4671
0
        return 0;
4672
4673
    /*-----------------------------------------------------------------
4674
     * We assume that ValidateMapInfoType() was called already and that
4675
     * the type in poObjHdr->m_nType is valid.
4676
     *----------------------------------------------------------------*/
4677
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4678
4679
    /*-----------------------------------------------------------------
4680
     * Fetch and validate geometry... Polygon and point are accepted.
4681
     * Note that we will simply use the ellipse's MBR and don't really
4682
     * read the polygon geometry... this should be OK unless the
4683
     * polygon geometry was not really an ellipse.
4684
     *
4685
     * We use the center of the MBR as the ellipse center, and the
4686
     * X/Y radius to define the MBR size.  If X/Y radius are null then
4687
     * we'll try to use the MBR to recompute them.
4688
     *----------------------------------------------------------------*/
4689
0
    if (UpdateMBR(poMapFile) != 0)
4690
0
        return -1; /* Error already reported */
4691
4692
    /*-----------------------------------------------------------------
4693
     * Copy object information
4694
     *----------------------------------------------------------------*/
4695
0
    TABMAPObjRectEllipse *poRectHdr =
4696
0
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4697
4698
    // Reset RoundRect Corner members... just in case (unused for ellipse)
4699
0
    poRectHdr->m_nCornerWidth = 0;
4700
0
    poRectHdr->m_nCornerHeight = 0;
4701
4702
    // An ellipse is defined by its MBR (values were set in UpdateMBR())
4703
0
    poRectHdr->m_nMinX = m_nXMin;
4704
0
    poRectHdr->m_nMinY = m_nYMin;
4705
0
    poRectHdr->m_nMaxX = m_nXMax;
4706
0
    poRectHdr->m_nMaxY = m_nYMax;
4707
4708
0
    m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4709
0
    poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
4710
4711
0
    m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4712
0
    poRectHdr->m_nBrushId =
4713
0
        static_cast<GByte>(m_nBrushDefIndex);  // Brush index
4714
4715
0
    if (CPLGetLastErrorType() == CE_Failure)
4716
0
        return -1;
4717
4718
0
    return 0;
4719
0
}
4720
4721
/**********************************************************************
4722
 *                   TABEllipse::GetStyleString() const
4723
 *
4724
 * Return style string for this feature.
4725
 *
4726
 * Style String is built only once during the first call to GetStyleString().
4727
 **********************************************************************/
4728
const char *TABEllipse::GetStyleString() const
4729
0
{
4730
0
    if (m_pszStyleString == nullptr)
4731
0
    {
4732
        // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4733
        // to use temporary buffers
4734
0
        char *pszPen = CPLStrdup(GetPenStyleString());
4735
0
        char *pszBrush = CPLStrdup(GetBrushStyleString());
4736
4737
0
        m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4738
4739
0
        CPLFree(pszPen);
4740
0
        CPLFree(pszBrush);
4741
0
    }
4742
4743
0
    return m_pszStyleString;
4744
0
}
4745
4746
/**********************************************************************
4747
 *                   TABEllipse::DumpMIF()
4748
 *
4749
 * Dump feature geometry in a format similar to .MIF REGIONs.
4750
 **********************************************************************/
4751
void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
4752
0
{
4753
0
    if (fpOut == nullptr)
4754
0
        fpOut = stdout;
4755
4756
    /*-----------------------------------------------------------------
4757
     * Output ELLIPSE parameters
4758
     *----------------------------------------------------------------*/
4759
0
    double dXMin = 0.0;
4760
0
    double dYMin = 0.0;
4761
0
    double dXMax = 0.0;
4762
0
    double dYMax = 0.0;
4763
0
    GetMBR(dXMin, dYMin, dXMax, dYMax);
4764
0
    fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4765
0
            dYMax);
4766
4767
    /*-----------------------------------------------------------------
4768
     * Fetch and validate geometry
4769
     *----------------------------------------------------------------*/
4770
0
    OGRGeometry *poGeom = GetGeometryRef();
4771
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4772
0
    {
4773
        /*-------------------------------------------------------------
4774
         * Generate ellipse output as a region
4775
         * We could also output as an ELLIPSE in a real MIF generator
4776
         *------------------------------------------------------------*/
4777
0
        OGRPolygon *poPolygon = poGeom->toPolygon();
4778
0
        int numIntRings = poPolygon->getNumInteriorRings();
4779
0
        fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4780
        // In this loop, iRing=-1 for the outer ring.
4781
0
        for (int iRing = -1; iRing < numIntRings; iRing++)
4782
0
        {
4783
0
            OGRLinearRing *poRing = nullptr;
4784
4785
0
            if (iRing == -1)
4786
0
                poRing = poPolygon->getExteriorRing();
4787
0
            else
4788
0
                poRing = poPolygon->getInteriorRing(iRing);
4789
4790
0
            if (poRing == nullptr)
4791
0
            {
4792
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
4793
0
                         "TABEllipse: Object Geometry contains NULL rings!");
4794
0
                return;
4795
0
            }
4796
4797
0
            int numPoints = poRing->getNumPoints();
4798
0
            fprintf(fpOut, " %d\n", numPoints);
4799
0
            for (int i = 0; i < numPoints; i++)
4800
0
                fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4801
0
                        poRing->getY(i));
4802
0
        }
4803
0
    }
4804
0
    else
4805
0
    {
4806
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4807
0
                 "TABEllipse: Missing or Invalid Geometry!");
4808
0
        return;
4809
0
    }
4810
4811
    // Finish with PEN/BRUSH/etc. clauses
4812
0
    DumpPenDef();
4813
0
    DumpBrushDef();
4814
4815
0
    fflush(fpOut);
4816
0
}
4817
4818
/*=====================================================================
4819
 *                      class TABArc
4820
 *====================================================================*/
4821
4822
/**********************************************************************
4823
 *                   TABArc::TABArc()
4824
 *
4825
 * Constructor.
4826
 **********************************************************************/
4827
TABArc::TABArc(OGRFeatureDefn *poDefnIn)
4828
26.4k
    : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
4829
26.4k
      m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
4830
26.4k
{
4831
26.4k
}
4832
4833
/**********************************************************************
4834
 *                   TABArc::~TABArc()
4835
 *
4836
 * Destructor.
4837
 **********************************************************************/
4838
TABArc::~TABArc()
4839
26.4k
{
4840
26.4k
}
4841
4842
/**********************************************************************
4843
 *                     TABArc::CloneTABFeature()
4844
 *
4845
 * Duplicate feature, including stuff specific to each TABFeature type.
4846
 *
4847
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
4848
 * then copies any members specific to its own type.
4849
 **********************************************************************/
4850
TABFeature *TABArc::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4851
0
{
4852
    /*-----------------------------------------------------------------
4853
     * Alloc new feature and copy the base stuff
4854
     *----------------------------------------------------------------*/
4855
0
    TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
4856
4857
0
    CopyTABFeatureBase(poNew);
4858
4859
    /*-----------------------------------------------------------------
4860
     * And members specific to this class
4861
     *----------------------------------------------------------------*/
4862
    // ITABFeaturePen
4863
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
4864
4865
0
    poNew->SetStartAngle(GetStartAngle());
4866
0
    poNew->SetEndAngle(GetEndAngle());
4867
4868
0
    poNew->m_dCenterX = m_dCenterX;
4869
0
    poNew->m_dCenterY = m_dCenterY;
4870
0
    poNew->m_dXRadius = m_dXRadius;
4871
0
    poNew->m_dYRadius = m_dYRadius;
4872
4873
0
    return poNew;
4874
0
}
4875
4876
/**********************************************************************
4877
 *                   TABArc::ValidateMapInfoType()
4878
 *
4879
 * Check the feature's geometry part and return the corresponding
4880
 * mapinfo object type code.  The m_nMapInfoType member will also
4881
 * be updated for further calls to GetMapInfoType();
4882
 *
4883
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4884
 * is expected for this object class.
4885
 **********************************************************************/
4886
TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4887
0
{
4888
    /*-----------------------------------------------------------------
4889
     * Fetch and validate geometry
4890
     *----------------------------------------------------------------*/
4891
0
    OGRGeometry *poGeom = GetGeometryRef();
4892
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
4893
0
        (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4894
0
    {
4895
0
        m_nMapInfoType = TAB_GEOM_ARC;
4896
0
    }
4897
0
    else
4898
0
    {
4899
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4900
0
                 "TABArc: Missing or Invalid Geometry!");
4901
0
        m_nMapInfoType = TAB_GEOM_NONE;
4902
0
    }
4903
4904
    /*-----------------------------------------------------------------
4905
     * Decide if coordinates should be compressed or not.
4906
     *----------------------------------------------------------------*/
4907
    // __TODO__ For now we always write uncompressed for this class...
4908
    // ValidateCoordType(poMapFile);
4909
0
    UpdateMBR(poMapFile);
4910
4911
0
    return m_nMapInfoType;
4912
0
}
4913
4914
/**********************************************************************
4915
 *                   TABArc::UpdateMBR()
4916
 *
4917
 * Update the feature MBR members using the geometry
4918
 *
4919
 * Returns 0 on success, or -1 if there is no geometry in object
4920
 **********************************************************************/
4921
int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4922
0
{
4923
0
    OGREnvelope sEnvelope;
4924
4925
0
    OGRGeometry *poGeom = GetGeometryRef();
4926
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
4927
0
    {
4928
        /*-------------------------------------------------------------
4929
         * POLYGON geometry:
4930
         * Note that we will simply use the ellipse's MBR and don't really
4931
         * read the polygon geometry... this should be OK unless the
4932
         * polygon geometry was not really an ellipse.
4933
         * In the case of a polygon geometry. the m_dCenterX/Y values MUST
4934
         * have been set by the caller.
4935
         *------------------------------------------------------------*/
4936
0
        poGeom->getEnvelope(&sEnvelope);
4937
0
    }
4938
0
    else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4939
0
    {
4940
        /*-------------------------------------------------------------
4941
         * In the case of a POINT GEOMETRY, we will make sure the
4942
         * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
4943
         *
4944
         * In this case we have to reconstruct the arc inside a temporary
4945
         * geometry object in order to find its real MBR.
4946
         *------------------------------------------------------------*/
4947
0
        OGRPoint *poPoint = poGeom->toPoint();
4948
0
        m_dCenterX = poPoint->getX();
4949
0
        m_dCenterY = poPoint->getY();
4950
4951
0
        OGRLineString oTmpLine;
4952
0
        int numPts = 0;
4953
0
        if (m_dEndAngle < m_dStartAngle)
4954
0
            numPts = static_cast<int>(
4955
0
                std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
4956
0
        else
4957
0
            numPts = static_cast<int>(
4958
0
                std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
4959
0
        numPts = std::max(2, numPts);
4960
4961
0
        TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
4962
0
                       m_dYRadius, m_dStartAngle * M_PI / 180.0,
4963
0
                       m_dEndAngle * M_PI / 180.0);
4964
4965
0
        oTmpLine.getEnvelope(&sEnvelope);
4966
0
    }
4967
0
    else
4968
0
    {
4969
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4970
0
                 "TABArc: Missing or Invalid Geometry!");
4971
0
        return -1;
4972
0
    }
4973
4974
    // Update the Arc's MBR
4975
0
    m_dXMin = sEnvelope.MinX;
4976
0
    m_dYMin = sEnvelope.MinY;
4977
0
    m_dXMax = sEnvelope.MaxX;
4978
0
    m_dYMax = sEnvelope.MaxY;
4979
4980
0
    if (poMapFile)
4981
0
    {
4982
0
        poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4983
0
        poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4984
0
    }
4985
4986
0
    return 0;
4987
0
}
4988
4989
/**********************************************************************
4990
 *                   TABArc::ReadGeometryFromMAPFile()
4991
 *
4992
 * Fill the geometry and representation (color, etc...) part of the
4993
 * feature from the contents of the .MAP object pointed to by poMAPFile.
4994
 *
4995
 * It is assumed that poMAPFile currently points to the beginning of
4996
 * a map object.
4997
 *
4998
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4999
 * been called.
5000
 **********************************************************************/
5001
int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5002
                                    TABMAPObjHdr *poObjHdr,
5003
                                    GBool bCoordBlockDataOnly /*=FALSE*/,
5004
                                    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5005
44
{
5006
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5007
44
    if (bCoordBlockDataOnly)
5008
0
        return 0;
5009
5010
    /*-----------------------------------------------------------------
5011
     * Fetch and validate geometry type
5012
     *----------------------------------------------------------------*/
5013
44
    m_nMapInfoType = poObjHdr->m_nType;
5014
5015
44
    if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
5016
0
    {
5017
0
        CPLError(
5018
0
            CE_Failure, CPLE_AssertionFailed,
5019
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5020
0
            m_nMapInfoType, m_nMapInfoType);
5021
0
        return -1;
5022
0
    }
5023
5024
    /*-----------------------------------------------------------------
5025
     * Read object information
5026
     *----------------------------------------------------------------*/
5027
44
    TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5028
5029
    /*-------------------------------------------------------------
5030
     * Start/End angles
5031
     * Since the angles are specified for integer coordinates, and
5032
     * that these coordinates can have the X axis reversed, we have to
5033
     * adjust the angle values for the change in the X axis
5034
     * direction.
5035
     *
5036
     * This should be necessary only when X axis is flipped.
5037
     * __TODO__ Why is order of start/end values reversed as well???
5038
     *------------------------------------------------------------*/
5039
5040
    /*-------------------------------------------------------------
5041
     * OK, Arc angles again!!!!!!!!!!!!
5042
     * After some tests in 1999-11, it appeared that the angle values
5043
     * ALWAYS had to be flipped (read order= end angle followed by
5044
     * start angle), no matter which quadrant the file is in.
5045
     * This does not make any sense, so I suspect that there is something
5046
     * that we are missing here!
5047
     *
5048
     * 2000-01-14.... Again!!!  Based on some sample data files:
5049
     *  File         Ver Quadr  ReflXAxis  Read_Order   Adjust_Angle
5050
     * test_symb.tab 300    2        1      end,start    X=yes Y=no
5051
     * alltypes.tab: 300    1        0      start,end    X=no  Y=no
5052
     * arcs.tab:     300    2        0      end,start    X=yes Y=no
5053
     *
5054
     * Until we prove it wrong, the rule would be:
5055
     *  -> Quadrant 1 and 3, angles order = start, end
5056
     *  -> Quadrant 2 and 4, angles order = end, start
5057
     * + Always adjust angles for x and y axis based on quadrant.
5058
     *
5059
     * This was confirmed using some more files in which the quadrant was
5060
     * manually changed, but whether these are valid results is
5061
     * disputable.
5062
     *
5063
     * The ReflectXAxis flag seems to have no effect here...
5064
     *------------------------------------------------------------*/
5065
5066
    /*-------------------------------------------------------------
5067
     * In version 100 .tab files (version 400 .map), it is possible
5068
     * to have a quadrant value of 0 and it should be treated the
5069
     * same way as quadrant 3
5070
     *------------------------------------------------------------*/
5071
44
    if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
5072
44
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5073
44
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5074
36
    {
5075
        // Quadrants 1 and 3 ... read order = start, end
5076
36
        m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
5077
36
        m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
5078
36
    }
5079
8
    else
5080
8
    {
5081
        // Quadrants 2 and 4 ... read order = end, start
5082
8
        m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
5083
8
        m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
5084
8
    }
5085
5086
44
    if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
5087
44
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5088
44
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5089
19
    {
5090
        // X axis direction is flipped... adjust angle
5091
19
        m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
5092
19
                                                 : (540.0 - m_dStartAngle);
5093
19
        m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
5094
19
                                             : (540.0 - m_dEndAngle);
5095
19
    }
5096
5097
44
    if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
5098
0
    {
5099
0
        CPLError(CE_Failure, CPLE_AppDefined,
5100
0
                 "Wrong start and end angles: %f %f", m_dStartAngle,
5101
0
                 m_dEndAngle);
5102
0
        return -1;
5103
0
    }
5104
5105
44
    if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5106
44
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
5107
44
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5108
14
    {
5109
        // Y axis direction is flipped... this reverses angle direction
5110
        // Unfortunately we never found any file that contains this case,
5111
        // but this should be the behavior to expect!!!
5112
        //
5113
        // 2000-01-14: some files in which quadrant was set to 3 and 4
5114
        // manually seemed to confirm that this is the right thing to do.
5115
14
        m_dStartAngle = 360.0 - m_dStartAngle;
5116
14
        m_dEndAngle = 360.0 - m_dEndAngle;
5117
14
    }
5118
5119
    // An arc is defined by its defining ellipse's MBR:
5120
5121
44
    double dXMin = 0.0;
5122
44
    double dYMin = 0.0;
5123
44
    double dXMax = 0.0;
5124
44
    double dYMax = 0.0;
5125
5126
44
    poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
5127
44
                            poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
5128
44
    poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
5129
44
                            poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
5130
5131
44
    m_dCenterX = (dXMin + dXMax) / 2.0;
5132
44
    m_dCenterY = (dYMin + dYMax) / 2.0;
5133
44
    m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
5134
44
    m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
5135
5136
    // Read the Arc's MBR and use that as this feature's MBR
5137
44
    poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
5138
44
    poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
5139
44
    SetMBR(dXMin, dYMin, dXMax, dYMax);
5140
5141
44
    m_nPenDefIndex = poArcHdr->m_nPenId;  // Pen index
5142
44
    poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5143
5144
    /*-----------------------------------------------------------------
5145
     * Create and fill geometry object
5146
     * For the OGR geometry, we generate an arc with 2 degrees line
5147
     * segments.
5148
     *----------------------------------------------------------------*/
5149
44
    OGRLineString *poLine = new OGRLineString;
5150
5151
44
    const int numPts = std::max(
5152
44
        2,
5153
44
        (m_dEndAngle < m_dStartAngle
5154
44
             ? static_cast<int>(
5155
2
                   std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
5156
44
             : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
5157
42
                                1)));
5158
5159
44
    TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
5160
44
                   m_dYRadius, m_dStartAngle * M_PI / 180.0,
5161
44
                   m_dEndAngle * M_PI / 180.0);
5162
5163
44
    SetGeometryDirectly(poLine);
5164
5165
44
    return 0;
5166
44
}
5167
5168
/**********************************************************************
5169
 *                   TABArc::WriteGeometryToMAPFile()
5170
 *
5171
 * Write the geometry and representation (color, etc...) part of the
5172
 * feature to the .MAP object pointed to by poMAPFile.
5173
 *
5174
 * It is assumed that poMAPFile currently points to a valid map object.
5175
 *
5176
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5177
 * been called.
5178
 **********************************************************************/
5179
int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5180
                                   TABMAPObjHdr *poObjHdr,
5181
                                   GBool bCoordBlockDataOnly /*=FALSE*/,
5182
                                   TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5183
0
{
5184
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5185
0
    if (bCoordBlockDataOnly)
5186
0
        return 0;
5187
5188
    /*-----------------------------------------------------------------
5189
     * We assume that ValidateMapInfoType() was called already and that
5190
     * the type in poObjHdr->m_nType is valid.
5191
     *----------------------------------------------------------------*/
5192
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5193
5194
    /*-----------------------------------------------------------------
5195
     * Fetch and validate geometry
5196
     * In the case of ARCs, this is all done inside UpdateMBR()
5197
     *----------------------------------------------------------------*/
5198
0
    if (UpdateMBR(poMapFile) != 0)
5199
0
        return -1; /* Error already reported */
5200
5201
    /*-----------------------------------------------------------------
5202
     * Copy object information
5203
     *----------------------------------------------------------------*/
5204
0
    TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5205
5206
    /*-------------------------------------------------------------
5207
     * Start/End angles
5208
     * Since we ALWAYS produce files in quadrant 1 then we can
5209
     * ignore the special angle conversion required by flipped axis.
5210
     *
5211
     * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
5212
     *------------------------------------------------------------*/
5213
0
    CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
5214
5215
0
    poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
5216
0
    poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
5217
5218
    // An arc is defined by its defining ellipse's MBR:
5219
0
    poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5220
0
                            poArcHdr->m_nArcEllipseMinX,
5221
0
                            poArcHdr->m_nArcEllipseMinY);
5222
0
    poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5223
0
                            poArcHdr->m_nArcEllipseMaxX,
5224
0
                            poArcHdr->m_nArcEllipseMaxY);
5225
5226
    // Pass the Arc's actual MBR (values were set in UpdateMBR())
5227
0
    poArcHdr->m_nMinX = m_nXMin;
5228
0
    poArcHdr->m_nMinY = m_nYMin;
5229
0
    poArcHdr->m_nMaxX = m_nXMax;
5230
0
    poArcHdr->m_nMaxY = m_nYMax;
5231
5232
0
    m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5233
0
    poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
5234
5235
0
    if (CPLGetLastErrorType() == CE_Failure)
5236
0
        return -1;
5237
5238
0
    return 0;
5239
0
}
5240
5241
/**********************************************************************
5242
 *                   TABArc::SetStart/EndAngle()
5243
 *
5244
 * Set the start/end angle values in degrees, making sure the values are
5245
 * always in the range [0..360]
5246
 **********************************************************************/
5247
void TABArc::SetStartAngle(double dAngle)
5248
0
{
5249
0
    dAngle = fmod(dAngle, 360.0);
5250
0
    if (dAngle < 0.0)
5251
0
        dAngle += 360.0;
5252
5253
0
    m_dStartAngle = dAngle;
5254
0
}
5255
5256
void TABArc::SetEndAngle(double dAngle)
5257
0
{
5258
0
    dAngle = fmod(dAngle, 360.0);
5259
0
    if (dAngle < 0.0)
5260
0
        dAngle += 360.0;
5261
5262
0
    m_dEndAngle = dAngle;
5263
0
}
5264
5265
/**********************************************************************
5266
 *                   TABArc::GetStyleString() const
5267
 *
5268
 * Return style string for this feature.
5269
 *
5270
 * Style String is built only once during the first call to GetStyleString().
5271
 **********************************************************************/
5272
const char *TABArc::GetStyleString() const
5273
0
{
5274
0
    if (m_pszStyleString == nullptr)
5275
0
    {
5276
0
        m_pszStyleString = CPLStrdup(GetPenStyleString());
5277
0
    }
5278
5279
0
    return m_pszStyleString;
5280
0
}
5281
5282
/**********************************************************************
5283
 *                   TABArc::DumpMIF()
5284
 *
5285
 * Dump feature geometry in a format similar to .MIF REGIONs.
5286
 **********************************************************************/
5287
void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
5288
0
{
5289
0
    if (fpOut == nullptr)
5290
0
        fpOut = stdout;
5291
5292
    /*-----------------------------------------------------------------
5293
     * Output ARC parameters
5294
     *----------------------------------------------------------------*/
5295
0
    fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g   %d %d)\n",
5296
0
            m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5297
0
            m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5298
0
            static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
5299
5300
    /*-----------------------------------------------------------------
5301
     * Fetch and validate geometry
5302
     *----------------------------------------------------------------*/
5303
0
    OGRGeometry *poGeom = GetGeometryRef();
5304
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
5305
0
    {
5306
        /*-------------------------------------------------------------
5307
         * Generate arc output as a simple polyline
5308
         * We could also output as an ELLIPSE in a real MIF generator
5309
         *------------------------------------------------------------*/
5310
0
        OGRLineString *poLine = poGeom->toLineString();
5311
0
        const int numPoints = poLine->getNumPoints();
5312
0
        fprintf(fpOut, "PLINE %d\n", numPoints);
5313
0
        for (int i = 0; i < numPoints; i++)
5314
0
            fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
5315
0
    }
5316
0
    else
5317
0
    {
5318
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
5319
0
                 "TABArc: Missing or Invalid Geometry!");
5320
0
        return;
5321
0
    }
5322
5323
    // Finish with PEN/BRUSH/etc. clauses
5324
0
    DumpPenDef();
5325
5326
0
    fflush(fpOut);
5327
0
}
5328
5329
/*=====================================================================
5330
 *                      class TABText
5331
 *====================================================================*/
5332
5333
/**********************************************************************
5334
 *                   TABText::TABText()
5335
 *
5336
 * Constructor.
5337
 **********************************************************************/
5338
TABText::TABText(OGRFeatureDefn *poDefnIn)
5339
79.8k
    : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
5340
79.8k
      m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
5341
79.8k
      m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
5342
79.8k
      m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
5343
79.8k
      m_nFontStyle(0)
5344
79.8k
{
5345
79.8k
}
5346
5347
/**********************************************************************
5348
 *                   TABText::~TABText()
5349
 *
5350
 * Destructor.
5351
 **********************************************************************/
5352
TABText::~TABText()
5353
79.8k
{
5354
79.8k
    CPLFree(m_pszString);
5355
79.8k
}
5356
5357
/**********************************************************************
5358
 *                     TABText::CloneTABFeature()
5359
 *
5360
 * Duplicate feature, including stuff specific to each TABFeature type.
5361
 *
5362
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
5363
 * then copies any members specific to its own type.
5364
 **********************************************************************/
5365
TABFeature *TABText::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
5366
0
{
5367
    /*-----------------------------------------------------------------
5368
     * Alloc new feature and copy the base stuff
5369
     *----------------------------------------------------------------*/
5370
0
    TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
5371
5372
0
    CopyTABFeatureBase(poNew);
5373
5374
    /*-----------------------------------------------------------------
5375
     * And members specific to this class
5376
     *----------------------------------------------------------------*/
5377
    // ITABFeaturePen
5378
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
5379
5380
    // ITABFeatureFont
5381
0
    *(poNew->GetFontDefRef()) = *GetFontDefRef();
5382
5383
0
    poNew->SetTextString(GetTextString());
5384
0
    poNew->SetTextAngle(GetTextAngle());
5385
0
    poNew->SetTextBoxHeight(GetTextBoxHeight());
5386
0
    poNew->SetTextBoxWidth(GetTextBoxWidth());
5387
0
    poNew->SetFontStyleTABValue(GetFontStyleTABValue());
5388
0
    poNew->SetFontBGColor(GetFontBGColor());
5389
0
    poNew->SetFontFGColor(GetFontFGColor());
5390
0
    poNew->SetFontOColor(GetFontOColor());
5391
0
    poNew->SetFontSColor(GetFontSColor());
5392
5393
0
    poNew->SetTextJustification(GetTextJustification());
5394
0
    poNew->SetTextSpacing(GetTextSpacing());
5395
    // Note: Text arrow/line coordinates are not transported... but
5396
    //       we ignore them most of the time anyways.
5397
0
    poNew->SetTextLineType(TABTLNoLine);
5398
5399
0
    return poNew;
5400
0
}
5401
5402
/**********************************************************************
5403
 *                   TABText::ValidateMapInfoType()
5404
 *
5405
 * Check the feature's geometry part and return the corresponding
5406
 * mapinfo object type code.  The m_nMapInfoType member will also
5407
 * be updated for further calls to GetMapInfoType();
5408
 *
5409
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
5410
 * is expected for this object class.
5411
 **********************************************************************/
5412
TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
5413
0
{
5414
    /*-----------------------------------------------------------------
5415
     * Fetch and validate geometry
5416
     *----------------------------------------------------------------*/
5417
0
    OGRGeometry *poGeom = GetGeometryRef();
5418
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5419
0
    {
5420
0
        m_nMapInfoType = TAB_GEOM_TEXT;
5421
0
    }
5422
0
    else
5423
0
    {
5424
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
5425
0
                 "TABText: Missing or Invalid Geometry!");
5426
0
        m_nMapInfoType = TAB_GEOM_NONE;
5427
0
    }
5428
5429
    /*-----------------------------------------------------------------
5430
     * Decide if coordinates should be compressed or not.
5431
     *----------------------------------------------------------------*/
5432
    // __TODO__ For now we always write uncompressed for this class...
5433
    // ValidateCoordType(poMapFile);
5434
0
    UpdateMBR(poMapFile);
5435
5436
0
    return m_nMapInfoType;
5437
0
}
5438
5439
/**********************************************************************
5440
 *                   TABText::ReadGeometryFromMAPFile()
5441
 *
5442
 * Fill the geometry and representation (color, etc...) part of the
5443
 * feature from the contents of the .MAP object pointed to by poMAPFile.
5444
 *
5445
 * It is assumed that poMAPFile currently points to the beginning of
5446
 * a map object.
5447
 *
5448
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5449
 * been called.
5450
 **********************************************************************/
5451
int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5452
                                     TABMAPObjHdr *poObjHdr,
5453
                                     GBool bCoordBlockDataOnly /*=FALSE*/,
5454
                                     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5455
16
{
5456
    /*-----------------------------------------------------------------
5457
     * Fetch and validate geometry type
5458
     *----------------------------------------------------------------*/
5459
16
    m_nMapInfoType = poObjHdr->m_nType;
5460
5461
16
    if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
5462
0
    {
5463
0
        CPLError(
5464
0
            CE_Failure, CPLE_AssertionFailed,
5465
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5466
0
            m_nMapInfoType, m_nMapInfoType);
5467
0
        return -1;
5468
0
    }
5469
5470
    /*=============================================================
5471
     * TEXT
5472
     *============================================================*/
5473
5474
    /*-----------------------------------------------------------------
5475
     * Read object information
5476
     *----------------------------------------------------------------*/
5477
16
    TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5478
5479
16
    const GInt32 nCoordBlockPtr =
5480
16
        poTextHdr->m_nCoordBlockPtr;                     // String position
5481
16
    const int nStringLen = poTextHdr->m_nCoordDataSize;  // String length
5482
16
    m_nTextAlignment = poTextHdr->m_nTextAlignment;      // just./spacing/arrow
5483
5484
    /*-------------------------------------------------------------
5485
     * Text Angle, in tenths of degree.
5486
     * Contrary to arc start/end angles, no conversion based on
5487
     * origin quadrant is required here.
5488
     *------------------------------------------------------------*/
5489
16
    m_dAngle = poTextHdr->m_nAngle / 10.0;
5490
5491
16
    m_nFontStyle = poTextHdr->m_nFontStyle;  // Font style
5492
5493
16
    m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
5494
16
                       poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
5495
16
    m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
5496
16
                       poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
5497
16
    m_rgbOutline = m_rgbBackground;
5498
    // In MapInfo, the shadow color is always gray (128,128,128)
5499
16
    m_rgbShadow = 0x808080;
5500
5501
    // arrow endpoint
5502
16
    poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
5503
16
                            m_dfLineEndX, m_dfLineEndY);
5504
16
    m_bLineEndSet = TRUE;
5505
5506
    // Text Height
5507
16
    double dJunk = 0.0;
5508
16
    poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
5509
5510
16
    if (!bCoordBlockDataOnly)
5511
16
    {
5512
16
        m_nFontDefIndex = poTextHdr->m_nFontId;  // Font name index
5513
16
        poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
5514
16
    }
5515
5516
    // MBR after rotation
5517
16
    double dXMin = 0.0;
5518
16
    double dYMin = 0.0;
5519
16
    double dXMax = 0.0;
5520
16
    double dYMax = 0.0;
5521
16
    poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
5522
16
                            dYMin);
5523
16
    poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
5524
16
                            dYMax);
5525
5526
16
    if (!bCoordBlockDataOnly)
5527
16
    {
5528
16
        m_nPenDefIndex = poTextHdr->m_nPenId;  // Pen index for line
5529
16
        poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5530
16
    }
5531
5532
    /*-------------------------------------------------------------
5533
     * Read text string from the coord. block
5534
     * Note that the string may contain binary '\n' and '\\' chars
5535
     * that we keep to an unescaped form internally. This is to
5536
     * be like OGR drivers. See bug 1107 for details.
5537
     *------------------------------------------------------------*/
5538
16
    char *pszTmpString =
5539
16
        static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
5540
5541
16
    if (nStringLen > 0)
5542
15
    {
5543
15
        TABMAPCoordBlock *poCoordBlock = nullptr;
5544
5545
15
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5546
0
            poCoordBlock = *ppoCoordBlock;
5547
15
        else
5548
15
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
5549
15
        if (poCoordBlock == nullptr ||
5550
15
            poCoordBlock->ReadBytes(
5551
15
                nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
5552
0
        {
5553
0
            CPLError(CE_Failure, CPLE_FileIO,
5554
0
                     "Failed reading text string at offset %d", nCoordBlockPtr);
5555
0
            CPLFree(pszTmpString);
5556
0
            return -1;
5557
0
        }
5558
5559
        /* Return a ref to coord block so that caller can continue reading
5560
         * after the end of this object (used by index splitting)
5561
         */
5562
15
        if (ppoCoordBlock)
5563
0
            *ppoCoordBlock = poCoordBlock;
5564
15
    }
5565
5566
16
    pszTmpString[nStringLen] = '\0';
5567
5568
16
    if (!poMapFile->GetEncoding().empty())
5569
0
    {
5570
0
        char *pszUtf8String =
5571
0
            CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
5572
0
        CPLFree(pszTmpString);
5573
0
        pszTmpString = pszUtf8String;
5574
0
    }
5575
5576
16
    CPLFree(m_pszString);
5577
16
    m_pszString = pszTmpString;  // This string was Escaped before 20050714
5578
5579
    /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
5580
     */
5581
16
    SetMBR(dXMin, dYMin, dXMax, dYMax);
5582
16
    GetMBR(dXMin, dYMin, dXMax, dYMax);
5583
5584
    /* Copy int MBR to feature class members */
5585
16
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
5586
16
              poObjHdr->m_nMaxY);
5587
5588
    /*-----------------------------------------------------------------
5589
     * Create an OGRPoint Geometry...
5590
     * The point X,Y values will be the coords of the lower-left corner before
5591
     * rotation is applied.  (Note that the rotation in MapInfo is done around
5592
     * the upper-left corner)
5593
     * We need to calculate the true lower left corner of the text based
5594
     * on the MBR after rotation, the text height and the rotation angle.
5595
     *----------------------------------------------------------------*/
5596
16
    double dSin = sin(m_dAngle * M_PI / 180.0);
5597
16
    double dCos = cos(m_dAngle * M_PI / 180.0);
5598
16
    double dX = 0.0;
5599
16
    double dY = 0.0;
5600
16
    if (dSin > 0.0 && dCos > 0.0)
5601
15
    {
5602
15
        dX = dXMin + m_dHeight * dSin;
5603
15
        dY = dYMin;
5604
15
    }
5605
1
    else if (dSin > 0.0 && dCos < 0.0)
5606
0
    {
5607
0
        dX = dXMax;
5608
0
        dY = dYMin - m_dHeight * dCos;
5609
0
    }
5610
1
    else if (dSin < 0.0 && dCos < 0.0)
5611
0
    {
5612
0
        dX = dXMax + m_dHeight * dSin;
5613
0
        dY = dYMax;
5614
0
    }
5615
1
    else  // dSin < 0 && dCos > 0
5616
1
    {
5617
1
        dX = dXMin;
5618
1
        dY = dYMax - m_dHeight * dCos;
5619
1
    }
5620
5621
16
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
5622
5623
16
    SetGeometryDirectly(poGeometry);
5624
5625
    /*-----------------------------------------------------------------
5626
     * Compute Text Width: the width of the Text MBR before rotation
5627
     * in ground units... unfortunately this value is not stored in the
5628
     * file, so we have to compute it with the MBR after rotation and
5629
     * the height of the MBR before rotation:
5630
     * With  W = Width of MBR before rotation
5631
     *       H = Height of MBR before rotation
5632
     *       dX = Width of MBR after rotation
5633
     *       dY = Height of MBR after rotation
5634
     *       teta = rotation angle
5635
     *
5636
     *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
5637
     *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
5638
     *
5639
     * and for other teta values, use:
5640
     *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
5641
     *----------------------------------------------------------------*/
5642
16
    dSin = std::abs(dSin);
5643
16
    dCos = std::abs(dCos);
5644
16
    if (m_dHeight == 0.0)
5645
1
        m_dWidth = 0.0;
5646
15
    else if (dCos > dSin)
5647
15
        m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
5648
15
                   (m_dHeight * dCos);
5649
0
    else
5650
0
        m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
5651
0
                   (m_dHeight * dSin);
5652
16
    m_dWidth = std::abs(m_dWidth);
5653
5654
16
    return 0;
5655
16
}
5656
5657
/**********************************************************************
5658
 *                   TABText::WriteGeometryToMAPFile()
5659
 *
5660
 * Write the geometry and representation (color, etc...) part of the
5661
 * feature to the .MAP object pointed to by poMAPFile.
5662
 *
5663
 * It is assumed that poMAPFile currently points to a valid map object.
5664
 *
5665
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5666
 * been called.
5667
 **********************************************************************/
5668
int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5669
                                    TABMAPObjHdr *poObjHdr,
5670
                                    GBool bCoordBlockDataOnly /*=FALSE*/,
5671
                                    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5672
0
{
5673
0
    GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
5674
5675
    /*-----------------------------------------------------------------
5676
     * We assume that ValidateMapInfoType() was called already and that
5677
     * the type in poObjHdr->m_nType is valid.
5678
     *----------------------------------------------------------------*/
5679
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5680
5681
    /*-----------------------------------------------------------------
5682
     * Fetch and validate geometry
5683
     *----------------------------------------------------------------*/
5684
0
    OGRGeometry *poGeom = GetGeometryRef();
5685
0
    OGRPoint *poPoint = nullptr;
5686
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5687
0
        poPoint = poGeom->toPoint();
5688
0
    else
5689
0
    {
5690
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
5691
0
                 "TABText: Missing or Invalid Geometry!");
5692
0
        return -1;
5693
0
    }
5694
5695
0
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
5696
5697
    /*-----------------------------------------------------------------
5698
     * Write string to a coord block first...
5699
     * Note that the string may contain unescaped '\n' and '\\'
5700
     * that we have to keep like that for the MAP file.
5701
     * See MapTools bug 1107 for more details.
5702
     *----------------------------------------------------------------*/
5703
0
    TABMAPCoordBlock *poCoordBlock = nullptr;
5704
0
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5705
0
        poCoordBlock = *ppoCoordBlock;
5706
0
    else
5707
0
        poCoordBlock = poMapFile->GetCurCoordBlock();
5708
0
    poCoordBlock->StartNewFeature();
5709
0
    GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
5710
5711
    // This string was escaped before 20050714
5712
0
    CPLString oTmpString(m_pszString ? m_pszString : "");
5713
0
    if (!poMapFile->GetEncoding().empty())
5714
0
    {
5715
0
        oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
5716
0
    }
5717
5718
0
    int nStringLen = static_cast<int>(oTmpString.length());
5719
5720
0
    if (nStringLen > 0)
5721
0
    {
5722
0
        poCoordBlock->WriteBytes(
5723
0
            nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
5724
0
    }
5725
0
    else
5726
0
    {
5727
0
        nCoordBlockPtr = 0;
5728
0
    }
5729
5730
    /*-----------------------------------------------------------------
5731
     * Copy object information
5732
     *----------------------------------------------------------------*/
5733
0
    TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5734
5735
0
    poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr;    // String position
5736
0
    poTextHdr->m_nCoordDataSize = nStringLen;        // String length
5737
0
    poTextHdr->m_nTextAlignment = m_nTextAlignment;  // just./spacing/arrow
5738
5739
    /*-----------------------------------------------------------------
5740
     * Text Angle, (written in tenths of degrees)
5741
     * Contrary to arc start/end angles, no conversion based on
5742
     * origin quadrant is required here.
5743
     *----------------------------------------------------------------*/
5744
0
    poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
5745
5746
0
    poTextHdr->m_nFontStyle = m_nFontStyle;  // Font style/effect
5747
5748
0
    poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
5749
0
    poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
5750
0
    poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
5751
5752
0
    poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
5753
0
    poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
5754
0
    poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
5755
5756
    /*-----------------------------------------------------------------
5757
     * The OGRPoint's X,Y values were the coords of the lower-left corner
5758
     * before rotation was applied.  (Note that the rotation in MapInfo is
5759
     * done around the upper-left corner)
5760
     * The Feature's MBR is the MBR of the text after rotation... that's
5761
     * what MapInfo uses to define the text location.
5762
     *----------------------------------------------------------------*/
5763
0
    double dXMin = 0.0;
5764
0
    double dYMin = 0.0;
5765
0
    double dXMax = 0.0;
5766
0
    double dYMax = 0.0;
5767
    // Make sure Feature MBR is in sync with other params
5768
5769
0
    UpdateMBR();
5770
0
    GetMBR(dXMin, dYMin, dXMax, dYMax);
5771
5772
0
    poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
5773
0
    poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
5774
5775
    // Label line end point
5776
0
    double dX = 0.0;
5777
0
    double dY = 0.0;
5778
0
    GetTextLineEndPoint(dX, dY);  // Make sure a default line end point is set
5779
0
    poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
5780
0
                            poTextHdr->m_nLineEndY);
5781
5782
    // Text Height
5783
0
    poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
5784
0
    poTextHdr->m_nHeight = nY;
5785
5786
0
    if (!bCoordBlockDataOnly)
5787
0
    {
5788
        // Font name
5789
0
        m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
5790
0
        poTextHdr->m_nFontId =
5791
0
            static_cast<GByte>(m_nFontDefIndex);  // Font name index
5792
0
    }
5793
5794
    // MBR after rotation
5795
0
    poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
5796
5797
0
    if (!bCoordBlockDataOnly)
5798
0
    {
5799
0
        m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5800
0
        poTextHdr->m_nPenId =
5801
0
            static_cast<GByte>(m_nPenDefIndex);  // Pen index for line/arrow
5802
0
    }
5803
5804
0
    if (CPLGetLastErrorType() == CE_Failure)
5805
0
        return -1;
5806
5807
    /* Return a ref to coord block so that caller can continue writing
5808
     * after the end of this object (used by index splitting)
5809
     */
5810
0
    if (ppoCoordBlock)
5811
0
        *ppoCoordBlock = poCoordBlock;
5812
5813
0
    return 0;
5814
0
}
5815
5816
/**********************************************************************
5817
 *                   TABText::GetTextString()
5818
 *
5819
 * Return ref to text string value.
5820
 *
5821
 * Returned string is a reference to the internal string buffer and should
5822
 * not be modified or freed by the caller.
5823
 **********************************************************************/
5824
const char *TABText::GetTextString() const
5825
78.4k
{
5826
78.4k
    if (m_pszString == nullptr)
5827
0
        return "";
5828
5829
78.4k
    return m_pszString;
5830
78.4k
}
5831
5832
/**********************************************************************
5833
 *                   TABText::SetTextString()
5834
 *
5835
 * Set new text string value.
5836
 *
5837
 * Note: The text string may contain "\n" chars or "\\" chars
5838
 * and we expect to receive them in a 2 chars escaped form as
5839
 * described in the MIF format specs.
5840
 **********************************************************************/
5841
void TABText::SetTextString(const char *pszNewStr)
5842
0
{
5843
0
    CPLFree(m_pszString);
5844
0
    m_pszString = CPLStrdup(pszNewStr);
5845
0
}
5846
5847
/**********************************************************************
5848
 *                   TABText::GetTextAngle()
5849
 *
5850
 * Return text angle in degrees.
5851
 **********************************************************************/
5852
double TABText::GetTextAngle() const
5853
0
{
5854
0
    return m_dAngle;
5855
0
}
5856
5857
void TABText::SetTextAngle(double dAngle)
5858
29.5k
{
5859
    // Make sure angle is in the range [0..360]
5860
29.5k
    dAngle = fmod(dAngle, 360.0);
5861
29.5k
    if (dAngle < 0.0)
5862
4.14k
        dAngle += 360.0;
5863
29.5k
    m_dAngle = dAngle;
5864
29.5k
    UpdateMBR();
5865
29.5k
}
5866
5867
/**********************************************************************
5868
 *                   TABText::GetTextBoxHeight()
5869
 *
5870
 * Return text height in Y axis coord. units of the text box before rotation.
5871
 **********************************************************************/
5872
double TABText::GetTextBoxHeight() const
5873
0
{
5874
0
    return m_dHeight;
5875
0
}
5876
5877
void TABText::SetTextBoxHeight(double dHeight)
5878
0
{
5879
0
    m_dHeight = dHeight;
5880
0
    UpdateMBR();
5881
0
}
5882
5883
/**********************************************************************
5884
 *                   TABText::GetTextBoxWidth()
5885
 *
5886
 * Return text width in X axis coord. units. of the text box before rotation.
5887
 *
5888
 * If value has not been set, then we force a default value that assumes
5889
 * that one char's box width is 60% of its height... and we ignore
5890
 * the multiline case.  This should not matter when the user PROPERLY sets
5891
 * the value.
5892
 **********************************************************************/
5893
double TABText::GetTextBoxWidth() const
5894
0
{
5895
0
    if (m_dWidth == 0.0 && m_pszString)
5896
0
    {
5897
0
        m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
5898
0
    }
5899
0
    return m_dWidth;
5900
0
}
5901
5902
void TABText::SetTextBoxWidth(double dWidth)
5903
0
{
5904
0
    m_dWidth = dWidth;
5905
0
    UpdateMBR();
5906
0
}
5907
5908
/**********************************************************************
5909
 *                   TABText::GetTextLineEndPoint()
5910
 *
5911
 * Return X,Y coordinates of the text label line end point.
5912
 * Default is the center of the text MBR.
5913
 **********************************************************************/
5914
void TABText::GetTextLineEndPoint(double &dX, double &dY)
5915
0
{
5916
0
    if (!m_bLineEndSet)
5917
0
    {
5918
        // Set default location at center of text MBR
5919
0
        double dXMin = 0.0;
5920
0
        double dYMin = 0.0;
5921
0
        double dXMax = 0.0;
5922
0
        double dYMax = 0.0;
5923
0
        UpdateMBR();
5924
0
        GetMBR(dXMin, dYMin, dXMax, dYMax);
5925
0
        m_dfLineEndX = (dXMin + dXMax) / 2.0;
5926
0
        m_dfLineEndY = (dYMin + dYMax) / 2.0;
5927
0
        m_bLineEndSet = TRUE;
5928
0
    }
5929
5930
    // Return values
5931
0
    dX = m_dfLineEndX;
5932
0
    dY = m_dfLineEndY;
5933
0
}
5934
5935
void TABText::SetTextLineEndPoint(double dX, double dY)
5936
1.56k
{
5937
1.56k
    m_dfLineEndX = dX;
5938
1.56k
    m_dfLineEndY = dY;
5939
1.56k
    m_bLineEndSet = TRUE;
5940
1.56k
}
5941
5942
/**********************************************************************
5943
 *                   TABText::UpdateMBR()
5944
 *
5945
 * Update the feature MBR using the text origin (OGRPoint geometry), the
5946
 * rotation angle, and the Width/height before rotation.
5947
 *
5948
 * This function cannot perform properly unless all the above have been set.
5949
 *
5950
 * Returns 0 on success, or -1 if there is no geometry in object
5951
 **********************************************************************/
5952
int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
5953
29.5k
{
5954
29.5k
    OGRGeometry *poGeom = GetGeometryRef();
5955
29.5k
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5956
0
    {
5957
0
        OGRPoint *poPoint = poGeom->toPoint();
5958
5959
0
        const double dX0 = poPoint->getX();
5960
0
        const double dY0 = poPoint->getY();
5961
5962
0
        const double dSin = sin(m_dAngle * M_PI / 180.0);
5963
0
        const double dCos = cos(m_dAngle * M_PI / 180.0);
5964
5965
0
        GetTextBoxWidth();  // Force default width value if necessary.
5966
5967
0
        const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
5968
0
        const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
5969
5970
0
        SetMBR(dX0, dY0, dX0, dY0);
5971
0
        for (int i = 0; i < 4; i++)
5972
0
        {
5973
            // Rotate one of the box corners
5974
0
            const double dX1 =
5975
0
                dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
5976
0
            const double dY1 =
5977
0
                dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
5978
5979
            // And update feature MBR with rotated coordinate
5980
0
            if (dX1 < m_dXMin)
5981
0
                m_dXMin = dX1;
5982
0
            if (dX1 > m_dXMax)
5983
0
                m_dXMax = dX1;
5984
0
            if (dY1 < m_dYMin)
5985
0
                m_dYMin = dY1;
5986
0
            if (dY1 > m_dYMax)
5987
0
                m_dYMax = dY1;
5988
0
        }
5989
5990
0
        if (poMapFile)
5991
0
        {
5992
0
            poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
5993
0
            poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
5994
0
        }
5995
5996
0
        return 0;
5997
0
    }
5998
5999
29.5k
    return -1;
6000
29.5k
}
6001
6002
/**********************************************************************
6003
 *                   TABText::GetFontBGColor()
6004
 *
6005
 * Return background color.
6006
 **********************************************************************/
6007
GInt32 TABText::GetFontBGColor() const
6008
0
{
6009
0
    return m_rgbBackground;
6010
0
}
6011
6012
void TABText::SetFontBGColor(GInt32 rgbColor)
6013
5.23k
{
6014
5.23k
    m_rgbBackground = rgbColor;
6015
5.23k
}
6016
6017
/**********************************************************************
6018
 *                   TABText::GetFontOColor()
6019
 *
6020
 * Return outline color.
6021
 **********************************************************************/
6022
GInt32 TABText::GetFontOColor() const
6023
0
{
6024
0
    return m_rgbOutline;
6025
0
}
6026
6027
void TABText::SetFontOColor(GInt32 rgbColor)
6028
0
{
6029
0
    m_rgbOutline = rgbColor;
6030
0
}
6031
6032
/**********************************************************************
6033
 *                   TABText::GetFontSColor()
6034
 *
6035
 * Return shadow color.
6036
 **********************************************************************/
6037
GInt32 TABText::GetFontSColor() const
6038
0
{
6039
0
    return m_rgbShadow;
6040
0
}
6041
6042
void TABText::SetFontSColor(GInt32 rgbColor)
6043
0
{
6044
0
    m_rgbShadow = rgbColor;
6045
0
}
6046
6047
/**********************************************************************
6048
 *                   TABText::GetFontFGColor()
6049
 *
6050
 * Return foreground color.
6051
 **********************************************************************/
6052
GInt32 TABText::GetFontFGColor() const
6053
0
{
6054
0
    return m_rgbForeground;
6055
0
}
6056
6057
void TABText::SetFontFGColor(GInt32 rgbColor)
6058
7.50k
{
6059
7.50k
    m_rgbForeground = rgbColor;
6060
7.50k
}
6061
6062
/**********************************************************************
6063
 *                   TABText::GetTextJustification()
6064
 *
6065
 * Return text justification.  Default is TABTJLeft
6066
 **********************************************************************/
6067
TABTextJust TABText::GetTextJustification() const
6068
0
{
6069
0
    TABTextJust eJust = TABTJLeft;
6070
6071
0
    if (m_nTextAlignment & 0x0200)
6072
0
        eJust = TABTJCenter;
6073
0
    else if (m_nTextAlignment & 0x0400)
6074
0
        eJust = TABTJRight;
6075
6076
0
    return eJust;
6077
0
}
6078
6079
void TABText::SetTextJustification(TABTextJust eJustification)
6080
1.29k
{
6081
    // Flush current value... default is TABTJLeft
6082
1.29k
    m_nTextAlignment &= ~0x0600;
6083
    // ... and set new one.
6084
1.29k
    if (eJustification == TABTJCenter)
6085
1.29k
        m_nTextAlignment |= 0x0200;
6086
0
    else if (eJustification == TABTJRight)
6087
0
        m_nTextAlignment |= 0x0400;
6088
1.29k
}
6089
6090
/**********************************************************************
6091
 *                   TABText::GetTextSpacing()
6092
 *
6093
 * Return text vertical spacing factor.  Default is TABTSSingle
6094
 **********************************************************************/
6095
TABTextSpacing TABText::GetTextSpacing() const
6096
0
{
6097
0
    TABTextSpacing eSpacing = TABTSSingle;
6098
6099
0
    if (m_nTextAlignment & 0x0800)
6100
0
        eSpacing = TABTS1_5;
6101
0
    else if (m_nTextAlignment & 0x1000)
6102
0
        eSpacing = TABTSDouble;
6103
6104
0
    return eSpacing;
6105
0
}
6106
6107
void TABText::SetTextSpacing(TABTextSpacing eSpacing)
6108
12.3k
{
6109
    // Flush current value... default is TABTSSingle
6110
12.3k
    m_nTextAlignment &= ~0x1800;
6111
    // ... and set new one.
6112
12.3k
    if (eSpacing == TABTS1_5)
6113
8.57k
        m_nTextAlignment |= 0x0800;
6114
3.78k
    else if (eSpacing == TABTSDouble)
6115
3.78k
        m_nTextAlignment |= 0x1000;
6116
12.3k
}
6117
6118
/**********************************************************************
6119
 *                   TABText::GetTextLineType()
6120
 *
6121
 * Return text line (arrow) type.  Default is TABTLNoLine
6122
 **********************************************************************/
6123
TABTextLineType TABText::GetTextLineType() const
6124
0
{
6125
0
    TABTextLineType eLine = TABTLNoLine;
6126
6127
0
    if (m_nTextAlignment & 0x2000)
6128
0
        eLine = TABTLSimple;
6129
0
    else if (m_nTextAlignment & 0x4000)
6130
0
        eLine = TABTLArrow;
6131
6132
0
    return eLine;
6133
0
}
6134
6135
void TABText::SetTextLineType(TABTextLineType eLineType)
6136
1.56k
{
6137
    // Flush current value... default is TABTLNoLine
6138
1.56k
    m_nTextAlignment &= ~0x6000;
6139
    // ... and set new one.
6140
1.56k
    if (eLineType == TABTLSimple)
6141
975
        m_nTextAlignment |= 0x2000;
6142
589
    else if (eLineType == TABTLArrow)
6143
589
        m_nTextAlignment |= 0x4000;
6144
1.56k
}
6145
6146
/**********************************************************************
6147
 *                   TABText::QueryFontStyle()
6148
 *
6149
 * Return TRUE if the specified font style attribute is turned ON,
6150
 * or FALSE otherwise.  See enum TABFontStyle for the list of styles
6151
 * that can be queried on.
6152
 **********************************************************************/
6153
GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
6154
5.23k
{
6155
5.23k
    return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
6156
5.23k
}
6157
6158
void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
6159
3.49k
{
6160
3.49k
    if (bStyleOn)
6161
3.49k
        m_nFontStyle |= static_cast<int>(eStyleToToggle);
6162
0
    else
6163
0
        m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
6164
3.49k
}
6165
6166
/**********************************************************************
6167
 *                   TABText::GetFontStyleMIFValue()
6168
 *
6169
 * Return the Font Style value for this object using the style values
6170
 * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
6171
 *
6172
 * The reason why we have to differentiate between the TAB and the MIF font
6173
 * style values is that in TAB, TABFSBox is included in the style value
6174
 * as code 0x100, but in MIF it is not included, instead it is implied by
6175
 * the presence of the BG color in the FONT() clause (the BG color is
6176
 * present only when TABFSBox or TABFSHalo is set).
6177
 * This also has the effect of shifting all the other style values > 0x100
6178
 * by 1 byte.
6179
 **********************************************************************/
6180
int TABText::GetFontStyleMIFValue() const
6181
0
{
6182
    // The conversion is simply to remove bit 0x100 from the value and shift
6183
    // down all values past this bit.
6184
0
    return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
6185
0
}
6186
6187
void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
6188
7.50k
{
6189
7.50k
    m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
6190
    // When BG color is set, then either BOX or HALO should be set.
6191
7.50k
    if (bBGColorSet && !QueryFontStyle(TABFSHalo))
6192
3.49k
        ToggleFontStyle(TABFSBox, TRUE);
6193
7.50k
}
6194
6195
int TABText::IsFontBGColorUsed() const
6196
0
{
6197
    // Font BG color is used only when BOX is set.
6198
0
    return QueryFontStyle(TABFSBox);
6199
0
}
6200
6201
int TABText::IsFontOColorUsed() const
6202
0
{
6203
    // Font outline color is used only when HALO is set.
6204
0
    return QueryFontStyle(TABFSHalo);
6205
0
}
6206
6207
int TABText::IsFontSColorUsed() const
6208
0
{
6209
    // Font shadow color is used only when Shadow is set.
6210
0
    return QueryFontStyle(TABFSShadow);
6211
0
}
6212
6213
int TABText::IsFontBold() const
6214
0
{
6215
    // Font bold is used only when Bold is set.
6216
0
    return QueryFontStyle(TABFSBold);
6217
0
}
6218
6219
int TABText::IsFontItalic() const
6220
0
{
6221
    // Font italic is used only when Italic is set.
6222
0
    return QueryFontStyle(TABFSItalic);
6223
0
}
6224
6225
int TABText::IsFontUnderline() const
6226
0
{
6227
    // Font underline is used only when Underline is set.
6228
0
    return QueryFontStyle(TABFSUnderline);
6229
0
}
6230
6231
/**********************************************************************
6232
 *                   TABText::GetLabelStyleString()
6233
 *
6234
 * This is not the correct location, it should be in ITABFeatureFont,
6235
 * but it is really more easy to put it here.  This fct return a complete
6236
 * string for the representation with the string to display
6237
 **********************************************************************/
6238
const char *TABText::GetLabelStyleString() const
6239
0
{
6240
0
    const char *pszStyle = nullptr;
6241
0
    int nStringLen = static_cast<int>(strlen(GetTextString()));
6242
    // ALL Caps, Extpanded need to modify the string value
6243
0
    char *pszTextString =
6244
0
        static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
6245
    /* char szPattern[20]; */
6246
0
    int nJustification = 1;
6247
6248
0
    strcpy(pszTextString, GetTextString());
6249
    /* szPattern[0] = '\0'; */
6250
6251
0
    switch (GetTextJustification())
6252
0
    {
6253
0
        case TABTJCenter:
6254
0
            nJustification = 2;
6255
0
            break;
6256
0
        case TABTJRight:
6257
0
            nJustification = 3;
6258
0
            break;
6259
0
        case TABTJLeft:
6260
0
        default:
6261
0
            nJustification = 1;
6262
0
            break;
6263
0
    }
6264
6265
    // Compute real font size, taking number of lines ("\\n", "\n") and line
6266
    // spacing into account.
6267
0
    int numLines = 1;
6268
0
    for (int i = 0; pszTextString[i];
6269
0
         numLines +=
6270
0
         ((pszTextString[i] == '\n' ||
6271
0
           (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
6272
0
          pszTextString[i + 1] != '\0'),
6273
0
             ++i)
6274
0
        ;
6275
6276
0
    double dHeight = GetTextBoxHeight() / numLines;
6277
6278
    // In all cases, take out 20% of font height to account for line spacing
6279
0
    if (numLines > 1)
6280
0
    {
6281
0
        switch (GetTextSpacing())
6282
0
        {
6283
0
            case TABTS1_5:
6284
0
                dHeight *= (0.80 * 0.69);
6285
0
                break;
6286
0
            case TABTSDouble:
6287
0
                dHeight *= (0.66 * 0.69);
6288
0
                break;
6289
0
            default:
6290
0
                dHeight *= 0.69;
6291
0
        }
6292
0
    }
6293
0
    else
6294
0
    {
6295
0
        dHeight *= 0.69;
6296
0
    }
6297
6298
0
    if (QueryFontStyle(TABFSAllCaps))
6299
0
        for (int i = 0; pszTextString[i]; ++i)
6300
0
            if (isalpha(static_cast<unsigned char>(pszTextString[i])))
6301
0
                pszTextString[i] = static_cast<char>(
6302
0
                    CPLToupper(static_cast<unsigned char>(pszTextString[i])));
6303
6304
    /* Escape the double quote chars and expand the text */
6305
0
    char *pszTmpTextString = nullptr;
6306
6307
0
    if (QueryFontStyle(TABFSExpanded))
6308
0
        pszTmpTextString = static_cast<char *>(
6309
0
            CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
6310
0
    else
6311
0
        pszTmpTextString = static_cast<char *>(
6312
0
            CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
6313
6314
0
    int j = 0;
6315
0
    for (int i = 0; i < nStringLen; ++i, ++j)
6316
0
    {
6317
0
        if (pszTextString[i] == '"')
6318
0
        {
6319
0
            pszTmpTextString[j] = '\\';
6320
0
            pszTmpTextString[j + 1] = pszTextString[i];
6321
0
            ++j;
6322
0
        }
6323
0
        else
6324
0
            pszTmpTextString[j] = pszTextString[i];
6325
6326
0
        if (QueryFontStyle(TABFSExpanded))
6327
0
        {
6328
0
            pszTmpTextString[j + 1] = ' ';
6329
0
            ++j;
6330
0
        }
6331
0
    }
6332
6333
0
    pszTmpTextString[j] = '\0';
6334
0
    CPLFree(pszTextString);
6335
0
    pszTextString = static_cast<char *>(
6336
0
        CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
6337
0
    strcpy(pszTextString, pszTmpTextString);
6338
0
    CPLFree(pszTmpTextString);
6339
6340
0
    const char *pszBGColor =
6341
0
        IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
6342
0
    const char *pszOColor =
6343
0
        IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
6344
0
    const char *pszSColor =
6345
0
        IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
6346
0
    const char *pszBold = IsFontBold() ? ",bo:1" : "";
6347
0
    const char *pszItalic = IsFontItalic() ? ",it:1" : "";
6348
0
    const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
6349
6350
0
    pszStyle = CPLSPrintf(
6351
0
        "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
6352
0
        pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
6353
0
        pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
6354
0
        GetFontNameRef());
6355
6356
0
    CPLFree(pszTextString);
6357
0
    return pszStyle;
6358
0
}
6359
6360
/**********************************************************************
6361
 *                   TABText::GetStyleString() const
6362
 *
6363
 * Return style string for this feature.
6364
 *
6365
 * Style String is built only once during the first call to GetStyleString().
6366
 **********************************************************************/
6367
const char *TABText::GetStyleString() const
6368
0
{
6369
0
    if (m_pszStyleString == nullptr)
6370
0
    {
6371
0
        m_pszStyleString = CPLStrdup(GetLabelStyleString());
6372
0
    }
6373
6374
0
    return m_pszStyleString;
6375
0
}
6376
6377
void TABText::SetLabelFromStyleString(const char *pszStyleString)
6378
0
{
6379
    // Use the Style Manager to retrieve all the information we need.
6380
0
    auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
6381
0
    std::unique_ptr<OGRStyleTool> poStylePart;
6382
6383
    // Init the StyleMgr with the StyleString.
6384
0
    poStyleMgr->InitStyleString(pszStyleString);
6385
6386
    // Retrieve the Symbol info.
6387
0
    const int numParts = poStyleMgr->GetPartCount();
6388
0
    for (int i = 0; i < numParts; i++)
6389
0
    {
6390
0
        poStylePart.reset(poStyleMgr->GetPart(i));
6391
0
        if (poStylePart == nullptr)
6392
0
        {
6393
0
            continue;
6394
0
        }
6395
6396
0
        if (poStylePart->GetType() == OGRSTCLabel)
6397
0
        {
6398
0
            break;
6399
0
        }
6400
0
        else
6401
0
        {
6402
0
            poStylePart.reset();
6403
0
        }
6404
0
    }
6405
6406
    // If the no Symbol found, do nothing.
6407
0
    if (poStylePart == nullptr)
6408
0
    {
6409
0
        return;
6410
0
    }
6411
6412
0
    auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
6413
6414
0
    GBool bIsNull = 0;
6415
0
    const char *pszText = poLabelStyle->TextString(bIsNull);
6416
0
    if (!bIsNull && pszText)
6417
0
    {
6418
0
        SetTextString(pszText);
6419
6420
0
        poLabelStyle->SetUnit(OGRSTUMM);
6421
0
        double dfSize = poLabelStyle->Size(bIsNull);
6422
0
        if (!bIsNull)
6423
0
        {
6424
0
            dfSize /= 1000;
6425
6426
            // Compute text box height, taking number of lines ("\\n", "\n") and
6427
            // line spacing into account.
6428
0
            int numLines = 1;
6429
0
            for (int i = 0; pszText[i];
6430
0
                 numLines += ((pszText[i] == '\n' ||
6431
0
                               (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
6432
0
                              pszText[i + 1] != '\0'),
6433
0
                     ++i)
6434
0
                ;
6435
6436
            // Cf GetLabelStyleString() for 0.69. We should likely also take
6437
            // into account line spacing if we knew how to compute it.
6438
0
            SetTextBoxHeight(dfSize / 0.69 * numLines);
6439
0
        }
6440
0
    }
6441
6442
0
    if (poLabelStyle->Bold(bIsNull))
6443
0
        ToggleFontStyle(TABFSBold, true);
6444
6445
0
    if (poLabelStyle->Italic(bIsNull))
6446
0
        ToggleFontStyle(TABFSItalic, true);
6447
6448
0
    if (poLabelStyle->Underline(bIsNull))
6449
0
        ToggleFontStyle(TABFSUnderline, true);
6450
6451
0
    const char *pszFontName = poLabelStyle->FontName(bIsNull);
6452
0
    if (!bIsNull && pszFontName)
6453
0
        SetFontName(pszFontName);
6454
6455
    // Set the ForeColor
6456
0
    const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
6457
0
    if (bIsNull)
6458
0
        pszForeColor = nullptr;
6459
0
    if (pszForeColor)
6460
0
    {
6461
0
        if (pszForeColor[0] == '#')
6462
0
            pszForeColor++;
6463
0
        CPLString osForeColor(pszForeColor);
6464
0
        if (strlen(pszForeColor) > 6)
6465
0
            osForeColor.resize(6);
6466
0
        const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
6467
0
        SetFontFGColor(static_cast<GInt32>(nColor));
6468
0
    }
6469
6470
    // Set the BackgroundColor
6471
0
    const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
6472
0
    if (bIsNull)
6473
0
        pszBackColor = nullptr;
6474
0
    if (pszBackColor)
6475
0
    {
6476
0
        if (pszBackColor[0] == '#')
6477
0
            pszBackColor++;
6478
0
        CPLString osBackColor(pszBackColor);
6479
0
        if (strlen(pszBackColor) > 6)
6480
0
            osBackColor.resize(6);
6481
0
        const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
6482
0
        ToggleFontStyle(TABFSBox, true);
6483
0
        SetFontBGColor(static_cast<GInt32>(nColor));
6484
0
    }
6485
6486
    // Set the OutlineColor
6487
0
    const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
6488
0
    if (bIsNull)
6489
0
        pszOutlineColor = nullptr;
6490
0
    if (pszOutlineColor)
6491
0
    {
6492
0
        if (pszOutlineColor[0] == '#')
6493
0
            pszOutlineColor++;
6494
0
        CPLString osOutlineColor(pszOutlineColor);
6495
0
        if (strlen(pszOutlineColor) > 6)
6496
0
            osOutlineColor.resize(6);
6497
0
        const int nColor =
6498
0
            static_cast<int>(strtol(osOutlineColor, nullptr, 16));
6499
0
        ToggleFontStyle(TABFSHalo, true);
6500
0
        SetFontOColor(static_cast<GInt32>(nColor));
6501
0
    }
6502
6503
#if 0
6504
    // Commented out since it is hardcoded to 0x808080.
6505
    // Set the ShadowColor
6506
    const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
6507
    if(bIsNull) pszShadowColor = nullptr;
6508
    if(pszShadowColor)
6509
    {
6510
        if(pszShadowColor[0] == '#')
6511
            pszShadowColor++;
6512
        CPLString osShadowColor(pszShadowColor);
6513
        if( strlen(pszShadowColor) > 6 )
6514
            osShadowColor.resize(6);
6515
        const int nColor =
6516
            static_cast<int>(strtol(osShadowColor, nullptr, 16));
6517
        ToggleFontStyle(TABFSShadow, true);
6518
        SetFontSColor(static_cast<GInt32>(nColor));
6519
    }
6520
#endif
6521
6522
0
    const double dfAngle = poLabelStyle->Angle(bIsNull);
6523
0
    if (!bIsNull)
6524
0
        SetTextAngle(dfAngle);
6525
6526
0
    const int nAnchor = poLabelStyle->Anchor(bIsNull);
6527
0
    if (!bIsNull)
6528
0
    {
6529
0
        switch ((nAnchor - 1) % 3)
6530
0
        {
6531
0
            case 0:
6532
0
                SetTextJustification(TABTJLeft);
6533
0
                break;
6534
0
            case 1:
6535
0
                SetTextJustification(TABTJCenter);
6536
0
                break;
6537
0
            default /* 2 */:
6538
0
                SetTextJustification(TABTJRight);
6539
0
                break;
6540
0
        }
6541
0
    }
6542
0
}
6543
6544
/**********************************************************************
6545
 *                   TABText::DumpMIF()
6546
 *
6547
 * Dump feature geometry in a format similar to .MIF REGIONs.
6548
 **********************************************************************/
6549
void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
6550
0
{
6551
0
    if (fpOut == nullptr)
6552
0
        fpOut = stdout;
6553
6554
    /*-----------------------------------------------------------------
6555
     * Fetch and validate geometry
6556
     *----------------------------------------------------------------*/
6557
0
    OGRGeometry *poGeom = GetGeometryRef();
6558
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6559
0
    {
6560
        /*-------------------------------------------------------------
6561
         * Generate output for text object
6562
         *------------------------------------------------------------*/
6563
0
        OGRPoint *poPoint = poGeom->toPoint();
6564
6565
0
        fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
6566
0
                m_pszString ? m_pszString : "", poPoint->getX(),
6567
0
                poPoint->getY());
6568
6569
0
        fprintf(fpOut, "  m_pszString = '%s'\n", m_pszString);
6570
0
        fprintf(fpOut, "  m_dAngle    = %.15g\n", m_dAngle);
6571
0
        fprintf(fpOut, "  m_dHeight   = %.15g\n", m_dHeight);
6572
0
        fprintf(fpOut, "  m_rgbForeground  = 0x%6.6x (%d)\n", m_rgbForeground,
6573
0
                m_rgbForeground);
6574
0
        fprintf(fpOut, "  m_rgbBackground  = 0x%6.6x (%d)\n", m_rgbBackground,
6575
0
                m_rgbBackground);
6576
0
        fprintf(fpOut, "  m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
6577
0
        fprintf(fpOut, "  m_nFontStyle     = 0x%4.4x\n", m_nFontStyle);
6578
0
    }
6579
0
    else
6580
0
    {
6581
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6582
0
                 "TABText: Missing or Invalid Geometry!");
6583
0
        return;
6584
0
    }
6585
6586
    // Finish with PEN/BRUSH/etc. clauses
6587
0
    DumpPenDef();
6588
0
    DumpFontDef();
6589
6590
0
    fflush(fpOut);
6591
0
}
6592
6593
/*=====================================================================
6594
 *                      class TABMultiPoint
6595
 *====================================================================*/
6596
6597
/**********************************************************************
6598
 *                   TABMultiPoint::TABMultiPoint()
6599
 *
6600
 * Constructor.
6601
 **********************************************************************/
6602
TABMultiPoint::TABMultiPoint(OGRFeatureDefn *poDefnIn)
6603
30.8k
    : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
6604
30.8k
      m_dCenterY(0.0)
6605
30.8k
{
6606
30.8k
}
6607
6608
/**********************************************************************
6609
 *                   TABMultiPoint::~TABMultiPoint()
6610
 *
6611
 * Destructor.
6612
 **********************************************************************/
6613
TABMultiPoint::~TABMultiPoint()
6614
30.8k
{
6615
30.8k
}
6616
6617
/**********************************************************************
6618
 *                     TABMultiPoint::CloneTABFeature()
6619
 *
6620
 * Duplicate feature, including stuff specific to each TABFeature type.
6621
 *
6622
 * This method calls the generic TABFeature::CloneTABFeature() and
6623
 * then copies any members specific to its own type.
6624
 **********************************************************************/
6625
TABFeature *TABMultiPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
6626
0
{
6627
    /*-----------------------------------------------------------------
6628
     * Alloc new feature and copy the base stuff
6629
     *----------------------------------------------------------------*/
6630
0
    TABMultiPoint *poNew =
6631
0
        new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
6632
6633
0
    CopyTABFeatureBase(poNew);
6634
6635
    /*-----------------------------------------------------------------
6636
     * And members specific to this class
6637
     *----------------------------------------------------------------*/
6638
    // ITABFeatureSymbol
6639
0
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
6640
6641
0
    poNew->m_bCenterIsSet = m_bCenterIsSet;
6642
0
    poNew->m_dCenterX = m_dCenterX;
6643
0
    poNew->m_dCenterY = m_dCenterY;
6644
6645
0
    return poNew;
6646
0
}
6647
6648
/**********************************************************************
6649
 *                   TABMultiPoint::ValidateMapInfoType()
6650
 *
6651
 * Check the feature's geometry part and return the corresponding
6652
 * mapinfo object type code.  The m_nMapInfoType member will also
6653
 * be updated for further calls to GetMapInfoType();
6654
 *
6655
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
6656
 * is expected for this object class.
6657
 **********************************************************************/
6658
TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
6659
0
{
6660
    /*-----------------------------------------------------------------
6661
     * Fetch and validate geometry
6662
     *----------------------------------------------------------------*/
6663
0
    OGRGeometry *poGeom = GetGeometryRef();
6664
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6665
0
    {
6666
0
        OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6667
6668
0
        if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
6669
0
            m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
6670
0
        else
6671
0
            m_nMapInfoType = TAB_GEOM_MULTIPOINT;
6672
0
    }
6673
0
    else
6674
0
    {
6675
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6676
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
6677
0
        m_nMapInfoType = TAB_GEOM_NONE;
6678
0
    }
6679
6680
    /*-----------------------------------------------------------------
6681
     * Decide if coordinates should be compressed or not.
6682
     *----------------------------------------------------------------*/
6683
0
    ValidateCoordType(poMapFile);
6684
6685
0
    return m_nMapInfoType;
6686
0
}
6687
6688
/**********************************************************************
6689
 *                   TABMultiPoint::ReadGeometryFromMAPFile()
6690
 *
6691
 * Fill the geometry and representation (color, etc...) part of the
6692
 * feature from the contents of the .MAP object pointed to by poMAPFile.
6693
 *
6694
 * It is assumed that poMAPFile currently points to the beginning of
6695
 * a map object.
6696
 *
6697
 * Returns 0 on success, -1 on error, in which case CPLError() will have
6698
 * been called.
6699
 **********************************************************************/
6700
int TABMultiPoint::ReadGeometryFromMAPFile(
6701
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6702
    GBool bCoordBlockDataOnly /*=FALSE*/,
6703
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6704
18
{
6705
18
    double dXMin = 0.0;
6706
18
    double dYMin = 0.0;
6707
18
    double dXMax = 0.0;
6708
18
    double dYMax = 0.0;
6709
18
    OGRGeometry *poGeometry = nullptr;
6710
18
    GBool bComprCoord = poObjHdr->IsCompressedType();
6711
18
    TABMAPCoordBlock *poCoordBlock = nullptr;
6712
6713
    /*-----------------------------------------------------------------
6714
     * Fetch and validate geometry type
6715
     *----------------------------------------------------------------*/
6716
18
    m_nMapInfoType = poObjHdr->m_nType;
6717
6718
    /*-----------------------------------------------------------------
6719
     * Read object information
6720
     *----------------------------------------------------------------*/
6721
18
    if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
6722
18
        m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
6723
18
        m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
6724
18
        m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
6725
18
    {
6726
        /*-------------------------------------------------------------
6727
         * Copy data from poObjHdr
6728
         *------------------------------------------------------------*/
6729
18
        TABMAPObjMultiPoint *poMPointHdr =
6730
18
            cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6731
6732
18
        const GUInt32 nMinimumBytesForPoints =
6733
18
            (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
6734
18
        if (nMinimumBytesForPoints > 1024 * 1024 &&
6735
18
            nMinimumBytesForPoints > poMapFile->GetFileSize())
6736
0
        {
6737
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
6738
0
            return -1;
6739
0
        }
6740
6741
        // MBR
6742
18
        poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
6743
18
                                dXMin, dYMin);
6744
18
        poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
6745
18
                                dXMax, dYMax);
6746
6747
18
        if (!bCoordBlockDataOnly)
6748
18
        {
6749
18
            m_nSymbolDefIndex = poMPointHdr->m_nSymbolId;  // Symbol index
6750
18
            poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
6751
18
        }
6752
6753
18
        double dX = 0.0;
6754
18
        double dY = 0.0;
6755
        // Centroid/label point
6756
18
        poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
6757
18
                                dX, dY);
6758
18
        SetCenter(dX, dY);
6759
6760
        // Compressed coordinate origin (useful only in compressed case!)
6761
18
        m_nComprOrgX = poMPointHdr->m_nComprOrgX;
6762
18
        m_nComprOrgY = poMPointHdr->m_nComprOrgY;
6763
6764
        /*-------------------------------------------------------------
6765
         * Read Point Coordinates
6766
         *------------------------------------------------------------*/
6767
18
        OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
6768
18
        poGeometry = poMultiPoint;
6769
6770
18
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6771
1
            poCoordBlock = *ppoCoordBlock;
6772
17
        else
6773
17
            poCoordBlock =
6774
17
                poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
6775
18
        if (poCoordBlock == nullptr)
6776
1
        {
6777
1
            delete poGeometry;
6778
1
            return -1;
6779
1
        }
6780
17
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6781
6782
55
        for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6783
39
        {
6784
39
            GInt32 nX = 0;
6785
39
            GInt32 nY = 0;
6786
39
            if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
6787
1
            {
6788
1
                CPLError(CE_Failure, CPLE_FileIO,
6789
1
                         "Failed reading coordinate data at offset %d",
6790
1
                         poMPointHdr->m_nCoordBlockPtr);
6791
1
                delete poGeometry;
6792
1
                return -1;
6793
1
            }
6794
6795
38
            poMapFile->Int2Coordsys(nX, nY, dX, dY);
6796
38
            OGRPoint *poPoint = new OGRPoint(dX, dY);
6797
6798
38
            if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
6799
0
            {
6800
0
                CPLAssert(false);  // Just in case lower-level lib is modified
6801
0
            }
6802
38
        }
6803
17
    }
6804
0
    else
6805
0
    {
6806
0
        CPLError(
6807
0
            CE_Failure, CPLE_AssertionFailed,
6808
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
6809
0
            m_nMapInfoType, m_nMapInfoType);
6810
0
        return -1;
6811
0
    }
6812
6813
16
    SetGeometryDirectly(poGeometry);
6814
6815
16
    SetMBR(dXMin, dYMin, dXMax, dYMax);
6816
6817
    /* Copy int MBR to feature class members */
6818
16
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
6819
16
              poObjHdr->m_nMaxY);
6820
6821
    /* Return a ref to coord block so that caller can continue reading
6822
     * after the end of this object (used by TABCollection and index splitting)
6823
     */
6824
16
    if (ppoCoordBlock)
6825
1
        *ppoCoordBlock = poCoordBlock;
6826
6827
16
    return 0;
6828
18
}
6829
6830
/**********************************************************************
6831
 *                   TABMultiPoint::WriteGeometryToMAPFile()
6832
 *
6833
 * Write the geometry and representation (color, etc...) part of the
6834
 * feature to the .MAP object pointed to by poMAPFile.
6835
 *
6836
 * It is assumed that poMAPFile currently points to a valid map object.
6837
 *
6838
 * Returns 0 on success, -1 on error, in which case CPLError() will have
6839
 * been called.
6840
 **********************************************************************/
6841
int TABMultiPoint::WriteGeometryToMAPFile(
6842
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6843
    GBool bCoordBlockDataOnly /*=FALSE*/,
6844
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6845
0
{
6846
0
    GInt32 nX, nY;
6847
6848
    /*-----------------------------------------------------------------
6849
     * We assume that ValidateMapInfoType() was called already and that
6850
     * the type in poObjHdr->m_nType is valid.
6851
     *----------------------------------------------------------------*/
6852
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
6853
6854
0
    TABMAPObjMultiPoint *poMPointHdr =
6855
0
        cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6856
6857
    /*-----------------------------------------------------------------
6858
     * Fetch and validate geometry
6859
     *----------------------------------------------------------------*/
6860
0
    OGRGeometry *poGeom = GetGeometryRef();
6861
0
    OGRMultiPoint *poMPoint = nullptr;
6862
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6863
0
        poMPoint = poGeom->toMultiPoint();
6864
0
    else
6865
0
    {
6866
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6867
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
6868
0
        return -1;
6869
0
    }
6870
6871
0
    poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
6872
6873
    /*-----------------------------------------------------------------
6874
     * Write data to coordinate block
6875
     *----------------------------------------------------------------*/
6876
0
    const GBool bCompressed = poObjHdr->IsCompressedType();
6877
6878
0
    TABMAPCoordBlock *poCoordBlock = nullptr;
6879
0
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6880
0
        poCoordBlock = *ppoCoordBlock;
6881
0
    else
6882
0
        poCoordBlock = poMapFile->GetCurCoordBlock();
6883
0
    poCoordBlock->StartNewFeature();
6884
0
    poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
6885
0
    poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6886
6887
0
    for (int iPoint = 0, nStatus = 0;
6888
0
         nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6889
0
    {
6890
0
        poGeom = poMPoint->getGeometryRef(iPoint);
6891
6892
0
        if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6893
0
        {
6894
0
            OGRPoint *poPoint = poGeom->toPoint();
6895
6896
0
            poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
6897
0
            if (iPoint == 0)
6898
0
            {
6899
                // Default to the first point, we may use explicit value below
6900
0
                poMPointHdr->m_nLabelX = nX;
6901
0
                poMPointHdr->m_nLabelY = nY;
6902
0
            }
6903
6904
0
            if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
6905
0
                0)
6906
0
            {
6907
                // Failed ... error message has already been produced
6908
0
                return nStatus;
6909
0
            }
6910
0
        }
6911
0
        else
6912
0
        {
6913
0
            CPLError(CE_Failure, CPLE_AssertionFailed,
6914
0
                     "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
6915
0
            return -1;
6916
0
        }
6917
0
    }
6918
6919
    /*-----------------------------------------------------------------
6920
     * Copy object information
6921
     *----------------------------------------------------------------*/
6922
6923
    // Compressed coordinate origin (useful only in compressed case!)
6924
0
    poMPointHdr->m_nComprOrgX = m_nComprOrgX;
6925
0
    poMPointHdr->m_nComprOrgY = m_nComprOrgY;
6926
6927
0
    poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
6928
0
    poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
6929
6930
    // Center/label point (default value already set above)
6931
0
    double dX = 0.0;
6932
0
    double dY = 0.0;
6933
0
    if (GetCenter(dX, dY) != -1)
6934
0
    {
6935
0
        poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
6936
0
                                poMPointHdr->m_nLabelY);
6937
0
    }
6938
6939
0
    if (!bCoordBlockDataOnly)
6940
0
    {
6941
0
        m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
6942
0
        poMPointHdr->m_nSymbolId =
6943
0
            static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
6944
0
    }
6945
6946
0
    if (CPLGetLastErrorType() == CE_Failure)
6947
0
        return -1;
6948
6949
    /* Return a ref to coord block so that caller can continue writing
6950
     * after the end of this object (used by index splitting)
6951
     */
6952
0
    if (ppoCoordBlock)
6953
0
        *ppoCoordBlock = poCoordBlock;
6954
6955
0
    return 0;
6956
0
}
6957
6958
/**********************************************************************
6959
 *                   TABMultiPoint::GetXY()
6960
 *
6961
 * Return this point's X,Y coordinates.
6962
 **********************************************************************/
6963
int TABMultiPoint::GetXY(int i, double &dX, double &dY)
6964
0
{
6965
    /*-----------------------------------------------------------------
6966
     * Fetch and validate geometry
6967
     *----------------------------------------------------------------*/
6968
0
    OGRGeometry *poGeom = GetGeometryRef();
6969
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6970
0
    {
6971
0
        OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6972
6973
0
        if (i >= 0 && i < poMPoint->getNumGeometries() &&
6974
0
            (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
6975
0
            wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6976
0
        {
6977
0
            OGRPoint *poPoint = poGeom->toPoint();
6978
6979
0
            dX = poPoint->getX();
6980
0
            dY = poPoint->getY();
6981
0
        }
6982
0
    }
6983
0
    else
6984
0
    {
6985
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6986
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
6987
0
        dX = 0.0;
6988
0
        dY = 0.0;
6989
0
        return -1;
6990
0
    }
6991
6992
0
    return 0;
6993
0
}
6994
6995
/**********************************************************************
6996
 *                   TABMultiPoint::GetNumPoints()
6997
 *
6998
 * Return the number of points in this multipoint object
6999
 **********************************************************************/
7000
int TABMultiPoint::GetNumPoints()
7001
0
{
7002
    /*-----------------------------------------------------------------
7003
     * Fetch and validate geometry
7004
     *----------------------------------------------------------------*/
7005
0
    OGRGeometry *poGeom = GetGeometryRef();
7006
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7007
0
    {
7008
0
        OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
7009
7010
0
        return poMPoint->getNumGeometries();
7011
0
    }
7012
0
    else
7013
0
    {
7014
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
7015
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
7016
0
        return 0;
7017
0
    }
7018
0
}
7019
7020
/**********************************************************************
7021
 *                   TABMultiPoint::GetStyleString() const
7022
 *
7023
 * Return style string for this feature.
7024
 *
7025
 * Style String is built only once during the first call to GetStyleString().
7026
 **********************************************************************/
7027
const char *TABMultiPoint::GetStyleString() const
7028
0
{
7029
0
    if (m_pszStyleString == nullptr)
7030
0
    {
7031
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
7032
0
    }
7033
7034
0
    return m_pszStyleString;
7035
0
}
7036
7037
/**********************************************************************
7038
 *                   TABMultiPoint::GetCenter()
7039
 *
7040
 * Returns the center point (or label point?) of the object.  Compute one
7041
 * if it was not explicitly set:
7042
 *
7043
 * The default seems to be to use the first point in the collection as
7044
 * the center.. so we'll use that.
7045
 *
7046
 * Returns 0 on success, -1 on error.
7047
 **********************************************************************/
7048
int TABMultiPoint::GetCenter(double &dX, double &dY)
7049
0
{
7050
0
    if (!m_bCenterIsSet && GetNumPoints() > 0)
7051
0
    {
7052
        // The default seems to be to use the first point in the collection
7053
        // as the center... so we'll use that.
7054
0
        if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
7055
0
            m_bCenterIsSet = TRUE;
7056
0
    }
7057
7058
0
    if (!m_bCenterIsSet)
7059
0
        return -1;
7060
7061
0
    dX = m_dCenterX;
7062
0
    dY = m_dCenterY;
7063
0
    return 0;
7064
0
}
7065
7066
/**********************************************************************
7067
 *                   TABMultiPoint::SetCenter()
7068
 *
7069
 * Set the X,Y coordinates to use as center point (or label point?)
7070
 **********************************************************************/
7071
void TABMultiPoint::SetCenter(double dX, double dY)
7072
9.61k
{
7073
9.61k
    m_dCenterX = dX;
7074
9.61k
    m_dCenterY = dY;
7075
9.61k
    m_bCenterIsSet = TRUE;
7076
9.61k
}
7077
7078
/**********************************************************************
7079
 *                   TABMultiPoint::DumpMIF()
7080
 *
7081
 * Dump feature geometry in a format similar to .MIF POINTs.
7082
 **********************************************************************/
7083
void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
7084
0
{
7085
0
    if (fpOut == nullptr)
7086
0
        fpOut = stdout;
7087
7088
    /*-----------------------------------------------------------------
7089
     * Fetch and validate geometry
7090
     *----------------------------------------------------------------*/
7091
0
    OGRGeometry *poGeom = GetGeometryRef();
7092
0
    OGRMultiPoint *poMPoint = nullptr;
7093
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7094
0
        poMPoint = poGeom->toMultiPoint();
7095
0
    else
7096
0
    {
7097
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
7098
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
7099
0
        return;
7100
0
    }
7101
7102
    /*-----------------------------------------------------------------
7103
     * Generate output
7104
     *----------------------------------------------------------------*/
7105
0
    fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
7106
7107
0
    for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
7108
0
    {
7109
0
        poGeom = poMPoint->getGeometryRef(iPoint);
7110
7111
0
        if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
7112
0
        {
7113
0
            OGRPoint *poPoint = poGeom->toPoint();
7114
0
            fprintf(fpOut, "  %.15g %.15g\n", poPoint->getX(), poPoint->getY());
7115
0
        }
7116
0
        else
7117
0
        {
7118
0
            CPLError(CE_Failure, CPLE_AssertionFailed,
7119
0
                     "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
7120
0
            return;
7121
0
        }
7122
0
    }
7123
7124
0
    DumpSymbolDef(fpOut);
7125
7126
0
    if (m_bCenterIsSet)
7127
0
        fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
7128
7129
0
    fflush(fpOut);
7130
0
}
7131
7132
/*=====================================================================
7133
 *                      class TABCollection
7134
 *====================================================================*/
7135
7136
/**********************************************************************
7137
 *                   TABCollection::TABCollection()
7138
 *
7139
 * Constructor.
7140
 **********************************************************************/
7141
TABCollection::TABCollection(OGRFeatureDefn *poDefnIn)
7142
28.4k
    : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
7143
28.4k
      m_poMpoint(nullptr)
7144
28.4k
{
7145
28.4k
}
7146
7147
/**********************************************************************
7148
 *                   TABCollection::~TABCollection()
7149
 *
7150
 * Destructor.
7151
 **********************************************************************/
7152
TABCollection::~TABCollection()
7153
28.4k
{
7154
28.4k
    EmptyCollection();
7155
28.4k
}
7156
7157
/**********************************************************************
7158
 *                   TABCollection::EmptyCollection()
7159
 *
7160
 * Delete/free all collection components.
7161
 **********************************************************************/
7162
void TABCollection::EmptyCollection()
7163
56.9k
{
7164
7165
56.9k
    if (m_poRegion)
7166
15.1k
    {
7167
15.1k
        delete m_poRegion;
7168
15.1k
        m_poRegion = nullptr;
7169
15.1k
    }
7170
7171
56.9k
    if (m_poPline)
7172
8.00k
    {
7173
8.00k
        delete m_poPline;
7174
8.00k
        m_poPline = nullptr;
7175
8.00k
    }
7176
7177
56.9k
    if (m_poMpoint)
7178
11.4k
    {
7179
11.4k
        delete m_poMpoint;
7180
11.4k
        m_poMpoint = nullptr;
7181
11.4k
    }
7182
7183
    // Empty OGR Geometry Collection as well
7184
56.9k
    SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
7185
56.9k
}
7186
7187
/**********************************************************************
7188
 *                     TABCollection::CloneTABFeature()
7189
 *
7190
 * Duplicate feature, including stuff specific to each TABFeature type.
7191
 *
7192
 * This method calls the generic TABFeature::CloneTABFeature() and
7193
 * then copies any members specific to its own type.
7194
 **********************************************************************/
7195
TABFeature *TABCollection::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
7196
0
{
7197
    /*-----------------------------------------------------------------
7198
     * Alloc new feature and copy the base stuff
7199
     *----------------------------------------------------------------*/
7200
0
    TABCollection *poNew =
7201
0
        new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
7202
7203
0
    CopyTABFeatureBase(poNew);
7204
7205
    /*-----------------------------------------------------------------
7206
     * And members specific to this class
7207
     *----------------------------------------------------------------*/
7208
7209
0
    if (m_poRegion)
7210
0
        poNew->SetRegionDirectly(
7211
0
            cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
7212
7213
0
    if (m_poPline)
7214
0
        poNew->SetPolylineDirectly(
7215
0
            cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
7216
7217
0
    if (m_poMpoint)
7218
0
        poNew->SetMultiPointDirectly(
7219
0
            cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
7220
7221
0
    return poNew;
7222
0
}
7223
7224
/**********************************************************************
7225
 *                   TABCollection::ValidateMapInfoType()
7226
 *
7227
 * Check the feature's geometry part and return the corresponding
7228
 * mapinfo object type code.  The m_nMapInfoType member will also
7229
 * be updated for further calls to GetMapInfoType();
7230
 *
7231
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
7232
 * is expected for this object class.
7233
 **********************************************************************/
7234
TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
7235
0
{
7236
0
    int nRegionType = TAB_GEOM_NONE;
7237
0
    int nPLineType = TAB_GEOM_NONE;
7238
0
    int nMPointType = TAB_GEOM_NONE;
7239
0
    int nVersion = 650;
7240
7241
    /*-----------------------------------------------------------------
7242
     * Fetch and validate geometry
7243
     *----------------------------------------------------------------*/
7244
0
    OGRGeometry *poGeom = GetGeometryRef();
7245
0
    if (poGeom &&
7246
0
        wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
7247
0
    {
7248
0
        m_nMapInfoType = TAB_GEOM_COLLECTION;
7249
0
    }
7250
0
    else
7251
0
    {
7252
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
7253
0
                 "TABCollection: Missing or Invalid Geometry!");
7254
0
        m_nMapInfoType = TAB_GEOM_NONE;
7255
0
    }
7256
7257
    /*-----------------------------------------------------------------
7258
     * Decide if coordinates should be compressed or not.
7259
     *----------------------------------------------------------------*/
7260
0
    GBool bComprCoord = ValidateCoordType(poMapFile);
7261
7262
    /*-----------------------------------------------------------------
7263
     * Since all members of the collection share the same compressed coord
7264
     * origin, we should force the compressed origin in all components
7265
     * to be the same.
7266
     * This also implies that ValidateMapInfoType() should *NOT* be called
7267
     * again until the collection components are written by WriteGeom...()
7268
     *----------------------------------------------------------------*/
7269
7270
    // First pass to figure collection type...
7271
0
    if (m_poRegion)
7272
0
    {
7273
0
        m_poRegion->ValidateCoordType(poMapFile);
7274
0
        nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
7275
0
        if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
7276
0
            nVersion = TAB_GEOM_GET_VERSION(nRegionType);
7277
0
    }
7278
7279
0
    if (m_poPline)
7280
0
    {
7281
0
        m_poPline->ValidateCoordType(poMapFile);
7282
0
        nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
7283
0
        if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
7284
0
            nVersion = TAB_GEOM_GET_VERSION(nPLineType);
7285
0
    }
7286
7287
0
    if (m_poMpoint)
7288
0
    {
7289
0
        m_poMpoint->ValidateCoordType(poMapFile);
7290
0
        nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
7291
0
        if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
7292
0
            nVersion = TAB_GEOM_GET_VERSION(nMPointType);
7293
0
    }
7294
7295
    // Need to upgrade native type of collection?
7296
0
    if (nVersion == 800)
7297
0
    {
7298
0
        m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
7299
0
    }
7300
7301
    // Make another pass updating native type and coordinates type and origin
7302
    // of each component
7303
0
    if (m_poRegion && nRegionType != TAB_GEOM_NONE)
7304
0
    {
7305
0
        GInt32 nXMin = 0;
7306
0
        GInt32 nYMin = 0;
7307
0
        GInt32 nXMax = 0;
7308
0
        GInt32 nYMax = 0;
7309
0
        m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7310
0
        m_poRegion->ForceCoordTypeAndOrigin(
7311
0
            (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
7312
0
            bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7313
0
            nYMax);
7314
0
    }
7315
7316
0
    if (m_poPline && nPLineType != TAB_GEOM_NONE)
7317
0
    {
7318
0
        GInt32 nXMin, nYMin, nXMax, nYMax;
7319
0
        m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7320
0
        m_poPline->ForceCoordTypeAndOrigin(
7321
0
            (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
7322
0
                             : TAB_GEOM_V450_MULTIPLINE),
7323
0
            bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7324
0
            nYMax);
7325
0
    }
7326
7327
0
    if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
7328
0
    {
7329
0
        GInt32 nXMin, nYMin, nXMax, nYMax;
7330
0
        m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7331
0
        m_poMpoint->ForceCoordTypeAndOrigin(
7332
0
            (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
7333
0
            bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7334
0
            nYMax);
7335
0
    }
7336
7337
0
    return m_nMapInfoType;
7338
0
}
7339
7340
/**********************************************************************
7341
 *                   TABCollection::ReadLabelAndMBR()
7342
 *
7343
 * Reads the label and MBR elements of the header of a collection component
7344
 *
7345
 * Returns 0 on success, -1 on failure.
7346
 **********************************************************************/
7347
int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7348
                                   GBool bComprCoord, GInt32 nComprOrgX,
7349
                                   GInt32 nComprOrgY, GInt32 &pnMinX,
7350
                                   GInt32 &pnMinY, GInt32 &pnMaxX,
7351
                                   GInt32 &pnMaxY, GInt32 &pnLabelX,
7352
                                   GInt32 &pnLabelY)
7353
3
{
7354
    //
7355
    // The sections in the collection's coord blocks start with center/label
7356
    // point + MBR that are normally found in the object data blocks
7357
    // of regular region/pline/mulitpoint objects.
7358
    //
7359
7360
3
    if (bComprCoord)
7361
3
    {
7362
        // Region center/label point, relative to compr. coord. origin
7363
        // No it is not relative to the Object block center
7364
3
        pnLabelX = poCoordBlock->ReadInt16();
7365
3
        pnLabelY = poCoordBlock->ReadInt16();
7366
7367
3
        TABSaturatedAdd(pnLabelX, nComprOrgX);
7368
3
        TABSaturatedAdd(pnLabelY, nComprOrgY);
7369
7370
3
        pnMinX = poCoordBlock->ReadInt16();  // Read MBR
7371
3
        pnMinY = poCoordBlock->ReadInt16();
7372
3
        pnMaxX = poCoordBlock->ReadInt16();
7373
3
        pnMaxY = poCoordBlock->ReadInt16();
7374
3
        TABSaturatedAdd(pnMinX, nComprOrgX);
7375
3
        TABSaturatedAdd(pnMinY, nComprOrgY);
7376
3
        TABSaturatedAdd(pnMaxX, nComprOrgX);
7377
3
        TABSaturatedAdd(pnMaxY, nComprOrgY);
7378
3
    }
7379
0
    else
7380
0
    {
7381
        // Region center/label point, relative to compr. coord. origin
7382
        // No it is not relative to the Object block center
7383
0
        pnLabelX = poCoordBlock->ReadInt32();
7384
0
        pnLabelY = poCoordBlock->ReadInt32();
7385
7386
0
        pnMinX = poCoordBlock->ReadInt32();  // Read MBR
7387
0
        pnMinY = poCoordBlock->ReadInt32();
7388
0
        pnMaxX = poCoordBlock->ReadInt32();
7389
0
        pnMaxY = poCoordBlock->ReadInt32();
7390
0
    }
7391
7392
3
    return 0;
7393
3
}
7394
7395
/**********************************************************************
7396
 *                   TABCollection::WriteLabelAndMBR()
7397
 *
7398
 * Writes the label and MBR elements of the header of a collection component
7399
 *
7400
 * Returns 0 on success, -1 on failure.
7401
 **********************************************************************/
7402
int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7403
                                    GBool bComprCoord, GInt32 nMinX,
7404
                                    GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
7405
                                    GInt32 nLabelX, GInt32 nLabelY)
7406
0
{
7407
    //
7408
    // The sections in the collection's coord blocks start with center/label
7409
    // point + MBR that are normally found in the object data blocks
7410
    // of regular region/pline/mulitpoint objects.
7411
    //
7412
7413
0
    int nStatus = 0;
7414
0
    if ((nStatus =
7415
0
             poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
7416
0
        (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
7417
0
            0 ||
7418
0
        (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
7419
0
    {
7420
        // Failed ... error message has already been produced
7421
0
        return nStatus;
7422
0
    }
7423
7424
0
    return 0;
7425
0
}
7426
7427
/**********************************************************************
7428
 *                   TABCollection::ReadGeometryFromMAPFile()
7429
 *
7430
 * Fill the geometry and representation (color, etc...) part of the
7431
 * feature from the contents of the .MAP object pointed to by poMAPFile.
7432
 *
7433
 * It is assumed that poMAPFile currently points to the beginning of
7434
 * a map object.
7435
 *
7436
 * Returns 0 on success, -1 on error, in which case CPLError() will have
7437
 * been called.
7438
 **********************************************************************/
7439
int TABCollection::ReadGeometryFromMAPFile(
7440
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7441
    GBool bCoordBlockDataOnly /*=FALSE*/,
7442
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7443
4
{
7444
4
    const GBool bComprCoord = poObjHdr->IsCompressedType();
7445
7446
    /*-----------------------------------------------------------------
7447
     * Fetch and validate geometry type
7448
     *----------------------------------------------------------------*/
7449
4
    m_nMapInfoType = poObjHdr->m_nType;
7450
7451
4
    if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
7452
4
        m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
7453
4
        m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
7454
4
        m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
7455
0
    {
7456
0
        CPLError(
7457
0
            CE_Failure, CPLE_AssertionFailed,
7458
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
7459
0
            m_nMapInfoType, m_nMapInfoType);
7460
0
        return -1;
7461
0
    }
7462
7463
4
    int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7464
7465
    // Make sure collection is empty
7466
4
    EmptyCollection();
7467
7468
    /*-------------------------------------------------------------
7469
     * Copy data from poObjHdr
7470
     *------------------------------------------------------------*/
7471
4
    TABMAPObjCollection *poCollHdr =
7472
4
        cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7473
7474
    // MBR
7475
4
    double dXMin = 0.0;
7476
4
    double dYMin = 0.0;
7477
4
    double dXMax = 0.0;
7478
4
    double dYMax = 0.0;
7479
4
    poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
7480
4
                            dYMin);
7481
4
    poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
7482
4
                            dYMax);
7483
7484
4
    SetMBR(dXMin, dYMin, dXMax, dYMax);
7485
7486
4
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
7487
4
              poObjHdr->m_nMaxY);
7488
7489
4
    int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
7490
4
    TABMAPCoordBlock *poCoordBlock = nullptr;
7491
4
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7492
0
        poCoordBlock = *ppoCoordBlock;
7493
4
    else
7494
4
        poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
7495
7496
    // Compressed coordinate origin (useful only in compressed case!)
7497
4
    m_nComprOrgX = poCollHdr->m_nComprOrgX;
7498
4
    m_nComprOrgY = poCollHdr->m_nComprOrgY;
7499
7500
    /*-----------------------------------------------------------------
7501
     * Region Component
7502
     *----------------------------------------------------------------*/
7503
4
    if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
7504
1
    {
7505
        //
7506
        // Build fake coord section header to pass to TABRegion::ReadGeom...()
7507
        //
7508
1
        TABMAPObjPLine oRegionHdr;
7509
7510
1
        oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7511
1
        oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7512
7513
        //
7514
        // The region section in the coord block starts with center/label
7515
        // point + MBR that are normally found in the object data blocks
7516
        // of regular region objects.
7517
        //
7518
7519
        // In V800 the mini-header starts with a copy of num_parts
7520
1
        if (nVersion >= 800)
7521
0
        {
7522
            // int numParts = poCoordBlock->ReadInt32();
7523
0
            CPLAssert(poCoordBlock->ReadInt32() ==
7524
0
                      poCollHdr->m_nNumRegSections);
7525
0
        }
7526
7527
1
        ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
7528
1
                        oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
7529
1
                        oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
7530
1
                        oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
7531
1
                        oRegionHdr.m_nLabelY);
7532
7533
        // Set CoordBlockPtr so that TABRegion continues reading here
7534
1
        oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7535
7536
1
        if (bComprCoord)
7537
1
            oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
7538
0
        else
7539
0
            oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
7540
1
        if (nVersion == 800)
7541
0
            oRegionHdr.m_nType = static_cast<TABGeomType>(
7542
0
                oRegionHdr.m_nType +
7543
0
                (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
7544
7545
1
        oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
7546
1
        oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
7547
1
        oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
7548
1
        oRegionHdr.m_bSmooth = 0;  // TODO
7549
7550
        //
7551
        // Use a TABRegion to read/store the Region coord data
7552
        //
7553
1
        m_poRegion = new TABRegion(GetDefnRef());
7554
1
        if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
7555
1
                                                bCoordBlockDataOnly,
7556
1
                                                &poCoordBlock) != 0)
7557
0
            return -1;
7558
7559
        // Set new coord block ptr for next object
7560
        /*if (poCoordBlock)
7561
            nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7562
1
    }
7563
7564
    /*-----------------------------------------------------------------
7565
     * PLine Component
7566
     *----------------------------------------------------------------*/
7567
4
    if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
7568
1
    {
7569
        //
7570
        // Build fake coord section header to pass to TABPolyline::ReadGeom..()
7571
        //
7572
1
        TABMAPObjPLine oPLineHdr;
7573
7574
1
        oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7575
1
        oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7576
7577
        //
7578
        // The pline section in the coord block starts with center/label
7579
        // point + MBR that are normally found in the object data blocks
7580
        // of regular pline objects.
7581
        //
7582
7583
        // In V800 the mini-header starts with a copy of num_parts
7584
1
        if (nVersion >= 800)
7585
0
        {
7586
            // int numParts = poCoordBlock->ReadInt32();
7587
0
            CPLAssert(poCoordBlock->ReadInt32() ==
7588
0
                      poCollHdr->m_nNumPLineSections);
7589
0
        }
7590
7591
1
        ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
7592
1
                        oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
7593
1
                        oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
7594
1
                        oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
7595
7596
        // Set CoordBlockPtr so that TABRegion continues reading here
7597
1
        oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7598
7599
1
        if (bComprCoord)
7600
1
            oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
7601
0
        else
7602
0
            oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
7603
1
        if (nVersion == 800)
7604
0
            oPLineHdr.m_nType = static_cast<TABGeomType>(
7605
0
                oPLineHdr.m_nType +
7606
0
                (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
7607
7608
1
        oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
7609
1
        oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
7610
1
        oPLineHdr.m_bSmooth = 0;  // TODO
7611
7612
        //
7613
        // Use a TABPolyline to read/store the Polyline coord data
7614
        //
7615
1
        m_poPline = new TABPolyline(GetDefnRef());
7616
1
        if (m_poPline->ReadGeometryFromMAPFile(
7617
1
                poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7618
0
            return -1;
7619
7620
        // Set new coord block ptr for next object
7621
        /*if (poCoordBlock)
7622
            nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7623
1
    }
7624
7625
    /*-----------------------------------------------------------------
7626
     * MultiPoint Component
7627
     *----------------------------------------------------------------*/
7628
4
    if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
7629
1
    {
7630
        //
7631
        // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
7632
        //
7633
1
        TABMAPObjMultiPoint oMPointHdr;
7634
7635
1
        oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7636
1
        oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7637
7638
        //
7639
        // The pline section in the coord block starts with center/label
7640
        // point + MBR that are normally found in the object data blocks
7641
        // of regular pline objects.
7642
        //
7643
1
        ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
7644
1
                        oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
7645
1
                        oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
7646
1
                        oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
7647
1
                        oMPointHdr.m_nLabelY);
7648
7649
        // Set CoordBlockPtr so that TABRegion continues reading here
7650
1
        oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7651
7652
1
        if (bComprCoord)
7653
1
            oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
7654
0
        else
7655
0
            oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
7656
1
        if (nVersion == 800)
7657
0
            oMPointHdr.m_nType = static_cast<TABGeomType>(
7658
0
                oMPointHdr.m_nType +
7659
0
                (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
7660
7661
1
        oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
7662
1
        oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
7663
7664
        //
7665
        // Use a TABMultiPoint to read/store the coord data
7666
        //
7667
1
        m_poMpoint = new TABMultiPoint(GetDefnRef());
7668
1
        if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
7669
1
                                                bCoordBlockDataOnly,
7670
1
                                                &poCoordBlock) != 0)
7671
0
            return -1;
7672
7673
        // Set new coord block ptr for next object (not really useful here)
7674
        /*if (poCoordBlock)
7675
            nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7676
1
    }
7677
7678
    /*-----------------------------------------------------------------
7679
     * Set the main OGRFeature Geometry
7680
     * (this is actually duplicating geometries from each member)
7681
     *----------------------------------------------------------------*/
7682
4
    if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
7683
0
        return -1;
7684
7685
    /* Return a ref to coord block so that caller can continue reading
7686
     * after the end of this object (used by index splitting)
7687
     */
7688
4
    if (ppoCoordBlock)
7689
0
        *ppoCoordBlock = poCoordBlock;
7690
7691
4
    return 0;
7692
4
}
7693
7694
/**********************************************************************
7695
 *                   TABCollection::WriteGeometryToMAPFile()
7696
 *
7697
 * Write the geometry and representation (color, etc...) part of the
7698
 * feature to the .MAP object pointed to by poMAPFile.
7699
 *
7700
 * It is assumed that poMAPFile currently points to a valid map object.
7701
 *
7702
 * Returns 0 on success, -1 on error, in which case CPLError() will have
7703
 * been called.
7704
 **********************************************************************/
7705
int TABCollection::WriteGeometryToMAPFile(
7706
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7707
    GBool bCoordBlockDataOnly /*=FALSE*/,
7708
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7709
0
{
7710
    /*-----------------------------------------------------------------
7711
     * Note that the current implementation does not allow setting the
7712
     * Geometry via OGRFeature::SetGeometry(). The geometries must be set
7713
     * via the SetRegion/Pline/MpointDirectly() methods which will take
7714
     * care of keeping the OGRFeature's geometry in sync.
7715
     *
7716
     * TODO: If we ever want to support sync'ing changes from the OGRFeature's
7717
     * geometry to the m_poRegion/Pline/Mpoint then a call should be added
7718
     * here, or perhaps in ValidateMapInfoType(), or even better in
7719
     * custom TABCollection::SetGeometry*()... but then this last option
7720
     * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
7721
     *----------------------------------------------------------------*/
7722
7723
    /*-----------------------------------------------------------------
7724
     * We assume that ValidateMapInfoType() was called already and that
7725
     * the type in poObjHdr->m_nType is valid.
7726
     *----------------------------------------------------------------*/
7727
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
7728
7729
0
    TABMAPObjCollection *poCollHdr =
7730
0
        cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7731
7732
    /*-----------------------------------------------------------------
7733
     * Write data to coordinate block for each component...
7734
     *
7735
     * Note that at this point, the caller (TABFile) has called
7736
     * TABCollection::ValidateMapInfoType() which in turn has called
7737
     * each component's respective ValidateMapInfoType() and
7738
     * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
7739
     * their respective WriteGeometryToMapFile() called.
7740
     *----------------------------------------------------------------*/
7741
0
    const GBool bCompressed = poObjHdr->IsCompressedType();
7742
    // TODO: ??? Do we need to track overall collection coord data size???
7743
0
    int nTotalFeatureDataSize = 0;
7744
7745
0
    const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7746
7747
0
    TABMAPCoordBlock *poCoordBlock = nullptr;
7748
0
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7749
0
        poCoordBlock = *ppoCoordBlock;
7750
0
    else
7751
0
        poCoordBlock = poMapFile->GetCurCoordBlock();
7752
0
    poCoordBlock->StartNewFeature();
7753
0
    poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7754
0
    poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
7755
7756
    /*-----------------------------------------------------------------
7757
     * Region component
7758
     *----------------------------------------------------------------*/
7759
0
    if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
7760
0
    {
7761
0
        CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
7762
0
                  m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
7763
0
                  m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
7764
0
                  m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
7765
7766
0
        TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
7767
0
            TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
7768
7769
        // Update count of objects by type in header
7770
0
        if (!bCoordBlockDataOnly)
7771
0
            poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
7772
7773
        // Write a placeholder for centroid/label point and MBR mini-header
7774
        // and we'll come back later to write the real values.
7775
        //
7776
        // Note that the call to WriteGeometryToMAPFile() below will call
7777
        // StartNewFeature() as well, so we need to track the current
7778
        // value before calling it
7779
7780
0
        poCoordBlock->StartNewFeature();
7781
0
        int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7782
7783
        // In V800 the mini-header starts with a copy of num_parts
7784
0
        if (nVersion >= 800)
7785
0
        {
7786
0
            poCoordBlock->WriteInt32(0);
7787
0
        }
7788
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7789
0
        nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7790
7791
0
        if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
7792
0
                                               bCoordBlockDataOnly,
7793
0
                                               &poCoordBlock) != 0)
7794
0
        {
7795
0
            CPLError(CE_Failure, CPLE_FileIO,
7796
0
                     "Failed writing Region part in collection.");
7797
0
            delete poRegionHdr;
7798
0
            return -1;
7799
0
        }
7800
7801
0
        nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
7802
7803
        // Come back to write the real values in the mini-header
7804
0
        int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7805
0
        poCoordBlock->StartNewFeature();
7806
7807
0
        if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7808
0
        {
7809
0
            delete poRegionHdr;
7810
0
            return -1;
7811
0
        }
7812
7813
        // In V800 the mini-header starts with a copy of num_parts
7814
0
        if (nVersion >= 800)
7815
0
        {
7816
0
            poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
7817
0
        }
7818
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
7819
0
                         poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
7820
0
                         poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
7821
0
                         poRegionHdr->m_nLabelY);
7822
7823
        // And finally move the pointer back to the end of this component
7824
0
        if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7825
0
        {
7826
0
            delete poRegionHdr;
7827
0
            return -1;
7828
0
        }
7829
7830
        // Copy other header members to the main collection header
7831
        // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7832
        //       mini-header???
7833
0
        poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
7834
0
        poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
7835
7836
0
        if (!bCoordBlockDataOnly)
7837
0
        {
7838
0
            poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
7839
0
            poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
7840
            // TODO: Smooth flag         = poRegionHdr->m_bSmooth;
7841
0
        }
7842
7843
0
        delete poRegionHdr;
7844
0
    }
7845
0
    else
7846
0
    {
7847
        // No Region component. Set corresponding header fields to 0
7848
7849
0
        poCollHdr->m_nRegionDataSize = 0;
7850
0
        poCollHdr->m_nNumRegSections = 0;
7851
0
        poCollHdr->m_nRegionPenId = 0;
7852
0
        poCollHdr->m_nRegionBrushId = 0;
7853
0
    }
7854
7855
    /*-----------------------------------------------------------------
7856
     * PLine component
7857
     *----------------------------------------------------------------*/
7858
0
    if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
7859
0
    {
7860
0
        CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
7861
0
                  m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
7862
0
                  m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
7863
0
                  m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
7864
7865
0
        TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
7866
0
            TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
7867
7868
        // Update count of objects by type in header
7869
0
        if (!bCoordBlockDataOnly)
7870
0
            poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
7871
7872
        // Write a placeholder for centroid/label point and MBR mini-header
7873
        // and we'll come back later to write the real values.
7874
        //
7875
        // Note that the call to WriteGeometryToMAPFile() below will call
7876
        // StartNewFeature() as well, so we need to track the current
7877
        // value before calling it
7878
7879
0
        poCoordBlock->StartNewFeature();
7880
0
        int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7881
7882
        // In V800 the mini-header starts with a copy of num_parts
7883
0
        if (nVersion >= 800)
7884
0
        {
7885
0
            poCoordBlock->WriteInt32(0);
7886
0
        }
7887
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7888
0
        nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7889
7890
0
        if (m_poPline->WriteGeometryToMAPFile(
7891
0
                poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7892
0
        {
7893
0
            CPLError(CE_Failure, CPLE_FileIO,
7894
0
                     "Failed writing Region part in collection.");
7895
0
            delete poPlineHdr;
7896
0
            return -1;
7897
0
        }
7898
7899
0
        nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
7900
7901
        // Come back to write the real values in the mini-header
7902
0
        int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7903
0
        poCoordBlock->StartNewFeature();
7904
7905
0
        if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7906
0
        {
7907
0
            delete poPlineHdr;
7908
0
            return -1;
7909
0
        }
7910
7911
        // In V800 the mini-header starts with a copy of num_parts
7912
0
        if (nVersion >= 800)
7913
0
        {
7914
0
            poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
7915
0
        }
7916
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
7917
0
                         poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
7918
0
                         poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
7919
0
                         poPlineHdr->m_nLabelY);
7920
7921
        // And finally move the pointer back to the end of this component
7922
0
        if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7923
0
        {
7924
0
            delete poPlineHdr;
7925
0
            return -1;
7926
0
        }
7927
7928
        // Copy other header members to the main collection header
7929
        // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7930
        //       mini-header???
7931
0
        poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
7932
0
        poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
7933
0
        if (!bCoordBlockDataOnly)
7934
0
        {
7935
0
            poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
7936
            // TODO: Smooth flag           = poPlineHdr->m_bSmooth;
7937
0
        }
7938
7939
0
        delete poPlineHdr;
7940
0
    }
7941
0
    else
7942
0
    {
7943
        // No Polyline component. Set corresponding header fields to 0
7944
7945
0
        poCollHdr->m_nPolylineDataSize = 0;
7946
0
        poCollHdr->m_nNumPLineSections = 0;
7947
0
        poCollHdr->m_nPolylinePenId = 0;
7948
0
    }
7949
7950
    /*-----------------------------------------------------------------
7951
     * MultiPoint component
7952
     *----------------------------------------------------------------*/
7953
0
    if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
7954
0
    {
7955
0
        CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
7956
0
                  m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
7957
0
                  m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
7958
0
                  m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
7959
7960
0
        TABMAPObjMultiPoint *poMpointHdr =
7961
0
            cpl::down_cast<TABMAPObjMultiPoint *>(
7962
0
                TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
7963
7964
        // Update count of objects by type in header
7965
0
        if (!bCoordBlockDataOnly)
7966
0
            poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
7967
7968
        // Write a placeholder for centroid/label point and MBR mini-header
7969
        // and we'll come back later to write the real values.
7970
        //
7971
        // Note that the call to WriteGeometryToMAPFile() below will call
7972
        // StartNewFeature() as well, so we need to track the current
7973
        // value before calling it
7974
7975
0
        poCoordBlock->StartNewFeature();
7976
0
        int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7977
7978
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7979
0
        nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7980
7981
0
        if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
7982
0
                                               bCoordBlockDataOnly,
7983
0
                                               &poCoordBlock) != 0)
7984
0
        {
7985
0
            CPLError(CE_Failure, CPLE_FileIO,
7986
0
                     "Failed writing Region part in collection.");
7987
0
            delete poMpointHdr;
7988
0
            return -1;
7989
0
        }
7990
7991
0
        nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
7992
7993
        // Come back to write the real values in the mini-header
7994
0
        int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7995
0
        poCoordBlock->StartNewFeature();
7996
7997
0
        if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7998
0
        {
7999
0
            delete poMpointHdr;
8000
0
            return -1;
8001
0
        }
8002
8003
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
8004
0
                         poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
8005
0
                         poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
8006
0
                         poMpointHdr->m_nLabelY);
8007
8008
        // And finally move the pointer back to the end of this component
8009
0
        if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
8010
0
        {
8011
0
            delete poMpointHdr;
8012
0
            return -1;
8013
0
        }
8014
8015
        // Copy other header members to the main collection header
8016
        // TODO: Does m_nRegionDataSize need to include the centroid+mbr
8017
        //       mini-header???
8018
0
        poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
8019
0
        poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
8020
0
        if (!bCoordBlockDataOnly)
8021
0
        {
8022
0
            poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
8023
0
        }
8024
8025
0
        delete poMpointHdr;
8026
0
    }
8027
0
    else
8028
0
    {
8029
        // No Multipoint component. Set corresponding header fields to 0
8030
8031
0
        poCollHdr->m_nMPointDataSize = 0;
8032
0
        poCollHdr->m_nNumMultiPoints = 0;
8033
0
        poCollHdr->m_nMultiPointSymbolId = 0;
8034
0
    }
8035
8036
    /*-----------------------------------------------------------------
8037
     * Copy object information
8038
     *----------------------------------------------------------------*/
8039
8040
    // Compressed coordinate origin (useful only in compressed case!)
8041
0
    poCollHdr->m_nComprOrgX = m_nComprOrgX;
8042
0
    poCollHdr->m_nComprOrgY = m_nComprOrgY;
8043
8044
0
    poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
8045
8046
0
    poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
8047
8048
0
    if (CPLGetLastErrorType() == CE_Failure)
8049
0
        return -1;
8050
8051
    /* Return a ref to coord block so that caller can continue writing
8052
     * after the end of this object (used by index splitting)
8053
     */
8054
0
    if (ppoCoordBlock)
8055
0
        *ppoCoordBlock = poCoordBlock;
8056
8057
0
    return 0;
8058
0
}
8059
8060
/**********************************************************************
8061
 *                   TABCollection::SyncOGRGeometryCollection()
8062
 *
8063
 * Copy the region/pline/multipoint's geometries to the OGRFeature's
8064
 * geometry.
8065
 **********************************************************************/
8066
int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
8067
                                             GBool bSyncPline,
8068
                                             GBool bSyncMpoint)
8069
56.9k
{
8070
56.9k
    OGRGeometry *poThisGeom = GetGeometryRef();
8071
56.9k
    OGRGeometryCollection *poGeomColl = nullptr;
8072
8073
    // poGeometry is defined in the OGRFeature class
8074
56.9k
    if (poThisGeom == nullptr)
8075
28.4k
    {
8076
28.4k
        poGeomColl = new OGRGeometryCollection();
8077
28.4k
    }
8078
28.4k
    else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
8079
28.4k
    {
8080
28.4k
        poGeomColl = poThisGeom->toGeometryCollection();
8081
28.4k
    }
8082
0
    else
8083
0
    {
8084
0
        CPLError(
8085
0
            CE_Failure, CPLE_AssertionFailed,
8086
0
            "TABCollection: Invalid Geometry. Type must be OGRCollection.");
8087
0
        return -1;
8088
0
    }
8089
8090
    /*-----------------------------------------------------------------
8091
     * Start by removing geometries that need to be replaced
8092
     * In theory there should be a single geometry of each type, but
8093
     * just in case, we'll loop over the whole collection and delete all
8094
     * instances of each type if there are some.
8095
     *----------------------------------------------------------------*/
8096
56.9k
    int numGeometries = poGeomColl->getNumGeometries();
8097
72.8k
    for (int i = 0; i < numGeometries; i++)
8098
15.8k
    {
8099
15.8k
        OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
8100
15.8k
        if (!poGeom)
8101
0
            continue;
8102
8103
15.8k
        if ((bSyncRegion &&
8104
15.8k
             (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
8105
15.8k
              wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
8106
15.8k
            (bSyncPline &&
8107
10.5k
             (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
8108
10.5k
              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
8109
15.8k
            (bSyncMpoint &&
8110
5.25k
             (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
8111
15.8k
        {
8112
            // Remove this geometry
8113
15.8k
            poGeomColl->removeGeometry(i);
8114
8115
            // Unless this was the last geometry, we need to restart
8116
            // scanning the collection since we modified it
8117
15.8k
            if (i != numGeometries - 1)
8118
7.85k
            {
8119
7.85k
                i = 0;
8120
7.85k
                numGeometries = poGeomColl->getNumGeometries();
8121
7.85k
            }
8122
15.8k
        }
8123
15.8k
    }
8124
8125
    /*-----------------------------------------------------------------
8126
     * Copy TAB Feature geometries to OGRGeometryCollection
8127
     *----------------------------------------------------------------*/
8128
56.9k
    if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
8129
1
        poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
8130
8131
56.9k
    if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
8132
1
        poGeomColl->addGeometry(m_poPline->GetGeometryRef());
8133
8134
56.9k
    if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
8135
1
        poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
8136
8137
56.9k
    if (poThisGeom == nullptr)
8138
28.4k
        SetGeometryDirectly(poGeomColl);
8139
8140
56.9k
    return 0;
8141
56.9k
}
8142
8143
/**********************************************************************
8144
 *                   TABCollection::SetRegionDirectly()
8145
 *
8146
 * Set the region component of the collection, deleting the current
8147
 * region component if there is one. The object is then owned by the
8148
 * TABCollection object. Passing NULL just deletes it.
8149
 *
8150
 * Note that an intentional side-effect is that calling this method
8151
 * with the same poRegion pointer that is already owned by this object
8152
 * will force resync'ing the OGR Geometry member.
8153
 **********************************************************************/
8154
int TABCollection::SetRegionDirectly(TABRegion *poRegion)
8155
0
{
8156
0
    if (m_poRegion && m_poRegion != poRegion)
8157
0
        delete m_poRegion;
8158
0
    m_poRegion = poRegion;
8159
8160
    // Update OGRGeometryCollection component as well
8161
0
    return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
8162
0
}
8163
8164
/**********************************************************************
8165
 *                   TABCollection::SetPolylineDirectly()
8166
 *
8167
 * Set the polyline component of the collection, deleting the current
8168
 * polyline component if there is one. The object is then owned by the
8169
 * TABCollection object. Passing NULL just deletes it.
8170
 *
8171
 * Note that an intentional side-effect is that calling this method
8172
 * with the same poPline pointer that is already owned by this object
8173
 * will force resync'ing the OGR Geometry member.
8174
 **********************************************************************/
8175
int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
8176
0
{
8177
0
    if (m_poPline && m_poPline != poPline)
8178
0
        delete m_poPline;
8179
0
    m_poPline = poPline;
8180
8181
    // Update OGRGeometryCollection component as well
8182
0
    return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
8183
0
}
8184
8185
/**********************************************************************
8186
 *                   TABCollection::SetMultiPointDirectly()
8187
 *
8188
 * Set the multipoint component of the collection, deleting the current
8189
 * multipoint component if there is one. The object is then owned by the
8190
 * TABCollection object. Passing NULL just deletes it.
8191
 *
8192
 * Note that an intentional side-effect is that calling this method
8193
 * with the same poMpoint pointer that is already owned by this object
8194
 * will force resync'ing the OGR Geometry member.
8195
 **********************************************************************/
8196
int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
8197
0
{
8198
0
    if (m_poMpoint && m_poMpoint != poMpoint)
8199
0
        delete m_poMpoint;
8200
0
    m_poMpoint = poMpoint;
8201
8202
    // Update OGRGeometryCollection component as well
8203
0
    return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
8204
0
}
8205
8206
/**********************************************************************
8207
 *                   TABCollection::GetStyleString() const
8208
 *
8209
 * Return style string for this feature.
8210
 *
8211
 * Style String is built only once during the first call to GetStyleString().
8212
 **********************************************************************/
8213
const char *TABCollection::GetStyleString() const
8214
0
{
8215
0
    if (m_pszStyleString == nullptr)
8216
0
    {
8217
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
8218
0
    }
8219
8220
0
    return m_pszStyleString;
8221
0
}
8222
8223
/**********************************************************************
8224
 *                   TABCollection::DumpMIF()
8225
 *
8226
 * Dump feature geometry
8227
 **********************************************************************/
8228
void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
8229
0
{
8230
0
    if (fpOut == nullptr)
8231
0
        fpOut = stdout;
8232
8233
    /*-----------------------------------------------------------------
8234
     * Generate output
8235
     *----------------------------------------------------------------*/
8236
0
    int numParts = 0;
8237
0
    if (m_poRegion)
8238
0
        numParts++;
8239
0
    if (m_poPline)
8240
0
        numParts++;
8241
0
    if (m_poMpoint)
8242
0
        numParts++;
8243
8244
0
    fprintf(fpOut, "COLLECTION %d\n", numParts);
8245
8246
0
    if (m_poRegion)
8247
0
        m_poRegion->DumpMIF(fpOut);
8248
8249
0
    if (m_poPline)
8250
0
        m_poPline->DumpMIF(fpOut);
8251
8252
0
    if (m_poMpoint)
8253
0
        m_poMpoint->DumpMIF(fpOut);
8254
8255
0
    DumpSymbolDef(fpOut);
8256
8257
0
    fflush(fpOut);
8258
0
}
8259
8260
/*=====================================================================
8261
 *                      class TABDebugFeature
8262
 *====================================================================*/
8263
8264
/**********************************************************************
8265
 *                   TABDebugFeature::TABDebugFeature()
8266
 *
8267
 * Constructor.
8268
 **********************************************************************/
8269
TABDebugFeature::TABDebugFeature(OGRFeatureDefn *poDefnIn)
8270
0
    : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
8271
0
{
8272
0
    memset(m_abyBuf, 0, sizeof(m_abyBuf));
8273
0
}
8274
8275
/**********************************************************************
8276
 *                   TABDebugFeature::~TABDebugFeature()
8277
 *
8278
 * Destructor.
8279
 **********************************************************************/
8280
TABDebugFeature::~TABDebugFeature()
8281
{
8282
}
8283
8284
/**********************************************************************
8285
 *                   TABDebugFeature::ReadGeometryFromMAPFile()
8286
 *
8287
 * Fill the geometry and representation (color, etc...) part of the
8288
 * feature from the contents of the .MAP object pointed to by poMAPFile.
8289
 *
8290
 * It is assumed that poMAPFile currently points to the beginning of
8291
 * a map object.
8292
 *
8293
 * Returns 0 on success, -1 on error, in which case CPLError() will have
8294
 * been called.
8295
 **********************************************************************/
8296
int TABDebugFeature::ReadGeometryFromMAPFile(
8297
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
8298
    GBool /*bCoordBlockDataOnly=FALSE*/,
8299
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8300
0
{
8301
    /*-----------------------------------------------------------------
8302
     * Fetch geometry type
8303
     *----------------------------------------------------------------*/
8304
0
    m_nMapInfoType = poObjHdr->m_nType;
8305
8306
0
    TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
8307
0
    TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
8308
8309
    /*-----------------------------------------------------------------
8310
     * If object type has coords in a type 3 block, then its position
8311
     * follows
8312
     *----------------------------------------------------------------*/
8313
0
    if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
8314
0
    {
8315
0
        m_nCoordDataPtr = poObjBlock->ReadInt32();
8316
0
        m_nCoordDataSize = poObjBlock->ReadInt32();
8317
0
    }
8318
0
    else
8319
0
    {
8320
0
        m_nCoordDataPtr = -1;
8321
0
        m_nCoordDataSize = 0;
8322
0
    }
8323
8324
0
    m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
8325
0
    if (m_nSize > 0)
8326
0
    {
8327
0
        poObjBlock->GotoByteRel(-5);  // Go back to beginning of header
8328
0
        poObjBlock->ReadBytes(
8329
0
            std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
8330
0
    }
8331
8332
0
    return 0;
8333
0
}
8334
8335
/**********************************************************************
8336
 *                   TABDebugFeature::WriteGeometryToMAPFile()
8337
 *
8338
 * Write the geometry and representation (color, etc...) part of the
8339
 * feature to the .MAP object pointed to by poMAPFile.
8340
 *
8341
 * It is assumed that poMAPFile currently points to a valid map object.
8342
 *
8343
 * Returns 0 on success, -1 on error, in which case CPLError() will have
8344
 * been called.
8345
 **********************************************************************/
8346
int TABDebugFeature::WriteGeometryToMAPFile(
8347
    TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
8348
    GBool /*bCoordBlockDataOnly=FALSE*/,
8349
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8350
0
{
8351
    // Nothing to do here!
8352
8353
0
    CPLError(CE_Failure, CPLE_NotSupported,
8354
0
             "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
8355
8356
0
    return -1;
8357
0
}
8358
8359
/**********************************************************************
8360
 *                   TABDebugFeature::DumpMIF()
8361
 *
8362
 * Dump feature contents... available only in DEBUG mode.
8363
 **********************************************************************/
8364
void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
8365
0
{
8366
0
    if (fpOut == nullptr)
8367
0
        fpOut = stdout;
8368
8369
0
    fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
8370
0
            GetMapInfoType());
8371
0
    fprintf(fpOut, "  Object size: %d bytes\n", m_nSize);
8372
0
    fprintf(fpOut, "  m_nCoordDataPtr  = %d\n", m_nCoordDataPtr);
8373
0
    fprintf(fpOut, "  m_nCoordDataSize = %d\n", m_nCoordDataSize);
8374
0
    fprintf(fpOut, "  ");
8375
8376
0
    for (int i = 0; i < m_nSize; i++)
8377
0
        fprintf(fpOut, " %2.2x", m_abyBuf[i]);
8378
8379
0
    fprintf(fpOut, "  \n");
8380
8381
0
    fflush(fpOut);
8382
0
}
8383
8384
/*=====================================================================
8385
 *                      class ITABFeaturePen
8386
 *====================================================================*/
8387
8388
/**********************************************************************
8389
 *                   ITABFeaturePen::ITABFeaturePen()
8390
 **********************************************************************/
8391
8392
// MI default is PEN(1, 2, 0)
8393
static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
8394
8395
ITABFeaturePen::ITABFeaturePen()
8396
408k
    : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
8397
408k
{
8398
408k
}
8399
8400
/**********************************************************************
8401
 *                   ITABFeaturePen::~ITABFeaturePen()
8402
 **********************************************************************/
8403
8404
408k
ITABFeaturePen::~ITABFeaturePen() = default;
8405
8406
/**********************************************************************
8407
 *                   ITABFeaturePen::GetPenWidthPixel()
8408
 *                   ITABFeaturePen::SetPenWidthPixel()
8409
 *                   ITABFeaturePen::GetPenWidthPoint()
8410
 *                   ITABFeaturePen::SetPenWidthPoint()
8411
 *
8412
 * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
8413
 * in points (value from 0.1 to 203.7 points). The default pen width
8414
 * in MapInfo is 1 pixel.  Pen width in points exist only in file version 450.
8415
 *
8416
 * The following methods hide the way the pen width is stored in the files.
8417
 *
8418
 * In order to establish if a given pen def had its width specified in
8419
 * pixels or in points, one should first call GetPenWidthPoint(), and if
8420
 * it returns 0 then the Pixel width should be used instead:
8421
 *    if (GetPenWidthPoint() == 0)
8422
 *       ... use pen width in points ...
8423
 *    else
8424
 *       ... use Pixel width from GetPenWidthPixel()
8425
 *
8426
 * Note that the reverse is not true: the default pixel width is always 1,
8427
 * even when the pen width was actually set in points.
8428
 **********************************************************************/
8429
8430
GByte ITABFeaturePen::GetPenWidthPixel() const
8431
0
{
8432
0
    return m_sPenDef.nPixelWidth;
8433
0
}
8434
8435
void ITABFeaturePen::SetPenWidthPixel(GByte val)
8436
0
{
8437
0
    const GByte nPixelWidthMin = 1;
8438
0
    const GByte nPixelWidthMax = 7;
8439
0
    m_sPenDef.nPixelWidth =
8440
0
        std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
8441
0
    m_sPenDef.nPointWidth = 0;
8442
0
}
8443
8444
double ITABFeaturePen::GetPenWidthPoint() const
8445
0
{
8446
    // We store point width internally as tenths of points
8447
0
    return m_sPenDef.nPointWidth / 10.0;
8448
0
}
8449
8450
void ITABFeaturePen::SetPenWidthPoint(double val)
8451
0
{
8452
0
    m_sPenDef.nPointWidth =
8453
0
        std::min(std::max(static_cast<int>(val * 10), 1), 2037);
8454
0
    m_sPenDef.nPixelWidth = 1;
8455
0
}
8456
8457
/**********************************************************************
8458
 *                   ITABFeaturePen::GetPenWidthMIF()
8459
 *                   ITABFeaturePen::SetPenWidthMIF()
8460
 *
8461
 * The MIF representation for pen width is either a value from 1 to 7
8462
 * for a pen width in pixels, or a value from 11 to 2047 for a pen
8463
 * width in points = 10 + (point_width*10)
8464
 **********************************************************************/
8465
int ITABFeaturePen::GetPenWidthMIF() const
8466
25
{
8467
25
    return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
8468
25
                                      : m_sPenDef.nPixelWidth);
8469
25
}
8470
8471
void ITABFeaturePen::SetPenWidthMIF(int val)
8472
27.7k
{
8473
27.7k
    if (val > 10)
8474
11.1k
    {
8475
11.1k
        m_sPenDef.nPointWidth = std::min((val - 10), 2037);
8476
11.1k
        m_sPenDef.nPixelWidth = 0;
8477
11.1k
    }
8478
16.5k
    else
8479
16.5k
    {
8480
16.5k
        m_sPenDef.nPixelWidth =
8481
16.5k
            static_cast<GByte>(std::min(std::max(val, 1), 7));
8482
16.5k
        m_sPenDef.nPointWidth = 0;
8483
16.5k
    }
8484
27.7k
}
8485
8486
/**********************************************************************
8487
 *                   ITABFeaturePen::GetPenStyleString()
8488
 *
8489
 *  Return a PEN() string. All representations info for the pen are here.
8490
 **********************************************************************/
8491
const char *ITABFeaturePen::GetPenStyleString() const
8492
0
{
8493
0
    const char *pszStyle = nullptr;
8494
0
    int nOGRStyle = 0;
8495
0
    char szPattern[20];
8496
8497
0
    szPattern[0] = '\0';
8498
8499
    // For now, I only add the 25 first styles
8500
0
    switch (GetPenPattern())
8501
0
    {
8502
0
        case 1:
8503
0
            nOGRStyle = 1;
8504
0
            break;
8505
0
        case 2:
8506
0
            nOGRStyle = 0;
8507
0
            break;
8508
0
        case 3:
8509
0
            nOGRStyle = 3;
8510
0
            strcpy(szPattern, "1 1");
8511
0
            break;
8512
0
        case 4:
8513
0
            nOGRStyle = 3;
8514
0
            strcpy(szPattern, "2 1");
8515
0
            break;
8516
0
        case 5:
8517
0
            nOGRStyle = 3;
8518
0
            strcpy(szPattern, "3 1");
8519
0
            break;
8520
0
        case 6:
8521
0
            nOGRStyle = 3;
8522
0
            strcpy(szPattern, "6 1");
8523
0
            break;
8524
0
        case 7:
8525
0
            nOGRStyle = 4;
8526
0
            strcpy(szPattern, "12 2");
8527
0
            break;
8528
0
        case 8:
8529
0
            nOGRStyle = 4;
8530
0
            strcpy(szPattern, "24 4");
8531
0
            break;
8532
0
        case 9:
8533
0
            nOGRStyle = 3;
8534
0
            strcpy(szPattern, "4 3");
8535
0
            break;
8536
0
        case 10:
8537
0
            nOGRStyle = 5;
8538
0
            strcpy(szPattern, "1 4");
8539
0
            break;
8540
0
        case 11:
8541
0
            nOGRStyle = 3;
8542
0
            strcpy(szPattern, "4 6");
8543
0
            break;
8544
0
        case 12:
8545
0
            nOGRStyle = 3;
8546
0
            strcpy(szPattern, "6 4");
8547
0
            break;
8548
0
        case 13:
8549
0
            nOGRStyle = 4;
8550
0
            strcpy(szPattern, "12 12");
8551
0
            break;
8552
0
        case 14:
8553
0
            nOGRStyle = 6;
8554
0
            strcpy(szPattern, "8 2 1 2");
8555
0
            break;
8556
0
        case 15:
8557
0
            nOGRStyle = 6;
8558
0
            strcpy(szPattern, "12 1 1 1");
8559
0
            break;
8560
0
        case 16:
8561
0
            nOGRStyle = 6;
8562
0
            strcpy(szPattern, "12 1 3 1");
8563
0
            break;
8564
0
        case 17:
8565
0
            nOGRStyle = 6;
8566
0
            strcpy(szPattern, "24 6 4 6");
8567
0
            break;
8568
0
        case 18:
8569
0
            nOGRStyle = 7;
8570
0
            strcpy(szPattern, "24 3 3 3 3 3");
8571
0
            break;
8572
0
        case 19:
8573
0
            nOGRStyle = 7;
8574
0
            strcpy(szPattern, "24 3 3 3 3 3 3 3");
8575
0
            break;
8576
0
        case 20:
8577
0
            nOGRStyle = 7;
8578
0
            strcpy(szPattern, "6 3 1 3 1 3");
8579
0
            break;
8580
0
        case 21:
8581
0
            nOGRStyle = 7;
8582
0
            strcpy(szPattern, "12 2 1 2 1 2");
8583
0
            break;
8584
0
        case 22:
8585
0
            nOGRStyle = 7;
8586
0
            strcpy(szPattern, "12 2 1 2 1 2 1 2");
8587
0
            break;
8588
0
        case 23:
8589
0
            nOGRStyle = 6;
8590
0
            strcpy(szPattern, "4 1 1 1");
8591
0
            break;
8592
0
        case 24:
8593
0
            nOGRStyle = 7;
8594
0
            strcpy(szPattern, "4 1 1 1 1");
8595
0
            break;
8596
0
        case 25:
8597
0
            nOGRStyle = 6;
8598
0
            strcpy(szPattern, "4 1 1 1 2 1 1 1");
8599
0
            break;
8600
8601
0
        default:
8602
0
            nOGRStyle = 0;
8603
0
            break;
8604
0
    }
8605
8606
    // note - MapInfo renders all lines using a round pen cap and round pen join
8607
    // which are not the default values for OGR pen cap/join styles. So we need
8608
    // to explicitly include the cap/j parameters in these strings
8609
0
    if (strlen(szPattern) != 0)
8610
0
    {
8611
0
        if (m_sPenDef.nPointWidth > 0)
8612
0
            pszStyle = CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
8613
0
                                  "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8614
0
                                  static_cast<int>(GetPenWidthPoint()),
8615
0
                                  m_sPenDef.rgbColor, GetPenPattern(),
8616
0
                                  nOGRStyle, szPattern);
8617
0
        else
8618
0
            pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
8619
0
                                  "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8620
0
                                  GetPenWidthPixel(), m_sPenDef.rgbColor,
8621
0
                                  GetPenPattern(), nOGRStyle, szPattern);
8622
0
    }
8623
0
    else
8624
0
    {
8625
0
        if (m_sPenDef.nPointWidth > 0)
8626
0
            pszStyle =
8627
0
                CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\""
8628
0
                           "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8629
0
                           static_cast<int>(GetPenWidthPoint()),
8630
0
                           m_sPenDef.rgbColor, GetPenPattern(), nOGRStyle);
8631
0
        else
8632
0
            pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
8633
0
                                  "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8634
0
                                  GetPenWidthPixel(), m_sPenDef.rgbColor,
8635
0
                                  GetPenPattern(), nOGRStyle);
8636
0
    }
8637
8638
0
    return pszStyle;
8639
0
}
8640
8641
/**********************************************************************
8642
 *                   ITABFeaturePen::SetPenFromStyleString()
8643
 *
8644
 *  Init the Pen properties from a style string.
8645
 **********************************************************************/
8646
void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
8647
0
{
8648
0
    GBool bIsNull = 0;
8649
8650
    // Use the Style Manager to retrieve all the information we need.
8651
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8652
0
    OGRStyleTool *poStylePart = nullptr;
8653
8654
    // Init the StyleMgr with the StyleString.
8655
0
    poStyleMgr->InitStyleString(pszStyleString);
8656
8657
    // Retrieve the Pen info.
8658
0
    const int numParts = poStyleMgr->GetPartCount();
8659
0
    for (int i = 0; i < numParts; i++)
8660
0
    {
8661
0
        poStylePart = poStyleMgr->GetPart(i);
8662
0
        if (poStylePart == nullptr)
8663
0
            continue;
8664
8665
0
        if (poStylePart->GetType() == OGRSTCPen)
8666
0
        {
8667
0
            break;
8668
0
        }
8669
0
        else
8670
0
        {
8671
0
            delete poStylePart;
8672
0
            poStylePart = nullptr;
8673
0
        }
8674
0
    }
8675
8676
    // If the no Pen found, do nothing.
8677
0
    if (poStylePart == nullptr)
8678
0
    {
8679
0
        delete poStyleMgr;
8680
0
        return;
8681
0
    }
8682
8683
0
    OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
8684
8685
    // With Pen, we always want to output points or pixels (which are the same,
8686
    // so just use points).
8687
    //
8688
    // It's very important to set the output unit of the feature.
8689
    // The default value is meter. If we don't do it all numerical values
8690
    // will be assumed to be converted from the input unit to meter when we
8691
    // will get them via GetParam...() functions.
8692
    // See OGRStyleTool::Parse() for more details.
8693
0
    poPenStyle->SetUnit(OGRSTUPoints, 1);
8694
8695
    // Get the Pen Id or pattern
8696
0
    const char *pszPenName = poPenStyle->Id(bIsNull);
8697
0
    if (bIsNull)
8698
0
        pszPenName = nullptr;
8699
8700
    // Set the width
8701
0
    if (poPenStyle->Width(bIsNull) != 0.0)
8702
0
    {
8703
0
        const double nPenWidth = poPenStyle->Width(bIsNull);
8704
        // Width < 10 is a pixel
8705
0
        if (nPenWidth > 10)
8706
0
            SetPenWidthPoint(nPenWidth);
8707
0
        else
8708
0
            SetPenWidthPixel(static_cast<GByte>(nPenWidth));
8709
0
    }
8710
8711
    // Set the color
8712
0
    const char *pszPenColor = poPenStyle->Color(bIsNull);
8713
0
    if (pszPenColor != nullptr)
8714
0
    {
8715
0
        if (pszPenColor[0] == '#')
8716
0
            pszPenColor++;
8717
        // The Pen color is an Hexa string that need to be convert in a int
8718
0
        const GInt32 nPenColor =
8719
0
            static_cast<int>(strtol(pszPenColor, nullptr, 16));
8720
0
        SetPenColor(nPenColor);
8721
0
    }
8722
8723
0
    const char *pszPenPattern = nullptr;
8724
8725
    // Set the Id of the Pen, use Pattern if necessary.
8726
0
    if (pszPenName &&
8727
0
        (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
8728
0
    {
8729
0
        const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
8730
0
        if (pszPenId != nullptr)
8731
0
        {
8732
0
            const int nPenId = atoi(pszPenId + 12);
8733
0
            SetPenPattern(static_cast<GByte>(nPenId));
8734
0
        }
8735
0
        else
8736
0
        {
8737
0
            pszPenId = strstr(pszPenName, "ogr-pen-");
8738
0
            if (pszPenId != nullptr)
8739
0
            {
8740
0
                int nPenId = atoi(pszPenId + 8);
8741
0
                if (nPenId == 0)
8742
0
                    nPenId = 2;
8743
0
                SetPenPattern(static_cast<GByte>(nPenId));
8744
0
            }
8745
0
        }
8746
0
    }
8747
0
    else
8748
0
    {
8749
        // If no Pen Id, use the Pen Pattern to retrieve the Id.
8750
0
        pszPenPattern = poPenStyle->Pattern(bIsNull);
8751
0
        if (bIsNull)
8752
0
            pszPenPattern = nullptr;
8753
0
        else
8754
0
        {
8755
0
            if (strcmp(pszPenPattern, "1 1") == 0)
8756
0
                SetPenPattern(3);
8757
0
            else if (strcmp(pszPenPattern, "2 1") == 0)
8758
0
                SetPenPattern(4);
8759
0
            else if (strcmp(pszPenPattern, "3 1") == 0)
8760
0
                SetPenPattern(5);
8761
0
            else if (strcmp(pszPenPattern, "6 1") == 0)
8762
0
                SetPenPattern(6);
8763
0
            else if (strcmp(pszPenPattern, "12 2") == 0)
8764
0
                SetPenPattern(7);
8765
0
            else if (strcmp(pszPenPattern, "24 4") == 0)
8766
0
                SetPenPattern(8);
8767
0
            else if (strcmp(pszPenPattern, "4 3") == 0)
8768
0
                SetPenPattern(9);
8769
0
            else if (strcmp(pszPenPattern, "1 4") == 0)
8770
0
                SetPenPattern(10);
8771
0
            else if (strcmp(pszPenPattern, "4 6") == 0)
8772
0
                SetPenPattern(11);
8773
0
            else if (strcmp(pszPenPattern, "6 4") == 0)
8774
0
                SetPenPattern(12);
8775
0
            else if (strcmp(pszPenPattern, "12 12") == 0)
8776
0
                SetPenPattern(13);
8777
0
            else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
8778
0
                SetPenPattern(14);
8779
0
            else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
8780
0
                SetPenPattern(15);
8781
0
            else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
8782
0
                SetPenPattern(16);
8783
0
            else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
8784
0
                SetPenPattern(17);
8785
0
            else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
8786
0
                SetPenPattern(18);
8787
0
            else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
8788
0
                SetPenPattern(19);
8789
0
            else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
8790
0
                SetPenPattern(20);
8791
0
            else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
8792
0
                SetPenPattern(21);
8793
0
            else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
8794
0
                SetPenPattern(22);
8795
0
            else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
8796
0
                SetPenPattern(23);
8797
0
            else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
8798
0
                SetPenPattern(24);
8799
0
            else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
8800
0
                SetPenPattern(25);
8801
0
        }
8802
0
    }
8803
8804
0
    delete poStyleMgr;
8805
0
    delete poStylePart;
8806
8807
0
    return;
8808
0
}
8809
8810
/**********************************************************************
8811
 *                   ITABFeaturePen::DumpPenDef()
8812
 *
8813
 * Dump pen definition information.
8814
 **********************************************************************/
8815
void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
8816
0
{
8817
0
    if (fpOut == nullptr)
8818
0
        fpOut = stdout;
8819
8820
0
    fprintf(fpOut, "  m_nPenDefIndex         = %d\n", m_nPenDefIndex);
8821
0
    fprintf(fpOut, "  m_sPenDef.nRefCount    = %d\n", m_sPenDef.nRefCount);
8822
0
    fprintf(fpOut, "  m_sPenDef.nPixelWidth  = %u\n", m_sPenDef.nPixelWidth);
8823
0
    fprintf(fpOut, "  m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
8824
0
    fprintf(fpOut, "  m_sPenDef.nPointWidth  = %d\n", m_sPenDef.nPointWidth);
8825
0
    fprintf(fpOut, "  m_sPenDef.rgbColor     = 0x%6.6x (%d)\n",
8826
0
            m_sPenDef.rgbColor, m_sPenDef.rgbColor);
8827
8828
0
    fflush(fpOut);
8829
0
}
8830
8831
/*=====================================================================
8832
 *                      class ITABFeatureBrush
8833
 *====================================================================*/
8834
8835
/**********************************************************************
8836
 *                   ITABFeatureBrush::ITABFeatureBrush()
8837
 **********************************************************************/
8838
8839
// MI default is BRUSH(2, 16777215, 16777215)
8840
static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
8841
8842
ITABFeatureBrush::ITABFeatureBrush()
8843
255k
    : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
8844
255k
{
8845
255k
}
8846
8847
/**********************************************************************
8848
 *                   ITABFeatureBrush::~ITABFeatureBrush()
8849
 **********************************************************************/
8850
8851
255k
ITABFeatureBrush::~ITABFeatureBrush() = default;
8852
8853
/**********************************************************************
8854
 *                   ITABFeatureBrush::GetBrushStyleString()
8855
 *
8856
 *  Return a Brush() string. All representations info for the Brush are here.
8857
 **********************************************************************/
8858
const char *ITABFeatureBrush::GetBrushStyleString() const
8859
0
{
8860
0
    const char *pszStyle = nullptr;
8861
0
    int nOGRStyle = 0;
8862
    /* char szPattern[20]; */
8863
    //* szPattern[0] = '\0'; */
8864
8865
0
    if (m_sBrushDef.nFillPattern == 1)
8866
0
        nOGRStyle = 1;
8867
0
    else if (m_sBrushDef.nFillPattern == 3)
8868
0
        nOGRStyle = 2;
8869
0
    else if (m_sBrushDef.nFillPattern == 4)
8870
0
        nOGRStyle = 3;
8871
0
    else if (m_sBrushDef.nFillPattern == 5)
8872
0
        nOGRStyle = 5;
8873
0
    else if (m_sBrushDef.nFillPattern == 6)
8874
0
        nOGRStyle = 4;
8875
0
    else if (m_sBrushDef.nFillPattern == 7)
8876
0
        nOGRStyle = 6;
8877
0
    else if (m_sBrushDef.nFillPattern == 8)
8878
0
        nOGRStyle = 7;
8879
8880
0
    if (GetBrushTransparent())
8881
0
    {
8882
        /* Omit BG Color for transparent brushes */
8883
0
        pszStyle = CPLSPrintf(
8884
0
            "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8885
0
            m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
8886
0
    }
8887
0
    else
8888
0
    {
8889
0
        pszStyle = CPLSPrintf(
8890
0
            "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8891
0
            m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
8892
0
            m_sBrushDef.nFillPattern, nOGRStyle);
8893
0
    }
8894
8895
0
    return pszStyle;
8896
0
}
8897
8898
/**********************************************************************
8899
 *                   ITABFeatureBrush::SetBrushFromStyleString()
8900
 *
8901
 *  Set all Brush elements from a StyleString.
8902
 *  Use StyleMgr to do so.
8903
 **********************************************************************/
8904
void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
8905
0
{
8906
0
    GBool bIsNull = 0;
8907
8908
    // Use the Style Manager to retrieve all the information we need.
8909
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8910
0
    OGRStyleTool *poStylePart = nullptr;
8911
8912
    // Init the StyleMgr with the StyleString.
8913
0
    poStyleMgr->InitStyleString(pszStyleString);
8914
8915
    // Retrieve the Brush info.
8916
0
    const int numParts = poStyleMgr->GetPartCount();
8917
0
    for (int i = 0; i < numParts; i++)
8918
0
    {
8919
0
        poStylePart = poStyleMgr->GetPart(i);
8920
0
        if (poStylePart == nullptr)
8921
0
            continue;
8922
8923
0
        if (poStylePart->GetType() == OGRSTCBrush)
8924
0
        {
8925
0
            break;
8926
0
        }
8927
0
        else
8928
0
        {
8929
0
            delete poStylePart;
8930
0
            poStylePart = nullptr;
8931
0
        }
8932
0
    }
8933
8934
    // If the no Brush found, do nothing.
8935
0
    if (poStylePart == nullptr)
8936
0
    {
8937
0
        delete poStyleMgr;
8938
0
        return;
8939
0
    }
8940
8941
0
    OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
8942
8943
    // Set the Brush Id (FillPattern)
8944
0
    const char *pszBrushId = poBrushStyle->Id(bIsNull);
8945
0
    if (bIsNull)
8946
0
        pszBrushId = nullptr;
8947
0
    bool bHasBrushId = false;
8948
8949
0
    if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
8950
0
                       strstr(pszBrushId, "ogr-brush-")))
8951
0
    {
8952
0
        if (strstr(pszBrushId, "mapinfo-brush-"))
8953
0
        {
8954
0
            const int nBrushId = atoi(pszBrushId + 14);
8955
0
            SetBrushPattern(static_cast<GByte>(nBrushId));
8956
0
            bHasBrushId = true;
8957
0
        }
8958
0
        else if (strstr(pszBrushId, "ogr-brush-"))
8959
0
        {
8960
0
            int nBrushId = atoi(pszBrushId + 10);
8961
0
            if (nBrushId > 1)
8962
0
                nBrushId++;
8963
0
            SetBrushPattern(static_cast<GByte>(nBrushId));
8964
0
            bHasBrushId = true;
8965
0
        }
8966
0
    }
8967
8968
    // Set the BackColor, if not set, then it is transparent
8969
0
    const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
8970
0
    if (bIsNull)
8971
0
        pszBrushColor = nullptr;
8972
8973
0
    if (pszBrushColor)
8974
0
    {
8975
0
        if (pszBrushColor[0] == '#')
8976
0
            pszBrushColor++;
8977
0
        if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
8978
0
            pszBrushColor[7] == '0')
8979
0
        {
8980
0
            SetBrushTransparent(1);
8981
0
        }
8982
0
        else
8983
0
        {
8984
0
            CPLString osBrushColor(pszBrushColor);
8985
0
            if (strlen(pszBrushColor) > 6)
8986
0
                osBrushColor.resize(6);
8987
0
            const int nBrushColor =
8988
0
                static_cast<int>(strtol(osBrushColor, nullptr, 16));
8989
0
            SetBrushBGColor(static_cast<GInt32>(nBrushColor));
8990
0
        }
8991
0
    }
8992
0
    else
8993
0
    {
8994
0
        SetBrushTransparent(1);
8995
0
    }
8996
8997
    // Set the ForeColor
8998
0
    pszBrushColor = poBrushStyle->ForeColor(bIsNull);
8999
0
    if (bIsNull)
9000
0
        pszBrushColor = nullptr;
9001
9002
0
    if (pszBrushColor)
9003
0
    {
9004
0
        if (pszBrushColor[0] == '#')
9005
0
            pszBrushColor++;
9006
0
        if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
9007
0
            pszBrushColor[7] == '0')
9008
0
        {
9009
0
            if (!bHasBrushId)
9010
0
                SetBrushPattern(static_cast<GByte>(1));  // No-fill
9011
0
        }
9012
0
        else
9013
0
        {
9014
0
            if (!bHasBrushId)
9015
0
                SetBrushPattern(static_cast<GByte>(2));  // Solid-fill
9016
0
        }
9017
9018
0
        CPLString osBrushColor(pszBrushColor);
9019
0
        if (strlen(pszBrushColor) > 6)
9020
0
            osBrushColor.resize(6);
9021
0
        const int nBrushColor =
9022
0
            static_cast<int>(strtol(osBrushColor, nullptr, 16));
9023
0
        SetBrushFGColor(static_cast<GInt32>(nBrushColor));
9024
0
    }
9025
9026
0
    delete poStyleMgr;
9027
0
    delete poStylePart;
9028
9029
0
    return;
9030
0
}
9031
9032
/**********************************************************************
9033
 *                   ITABFeatureBrush::DumpBrushDef()
9034
 *
9035
 * Dump Brush definition information.
9036
 **********************************************************************/
9037
void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
9038
0
{
9039
0
    if (fpOut == nullptr)
9040
0
        fpOut = stdout;
9041
9042
0
    fprintf(fpOut, "  m_nBrushDefIndex         = %d\n", m_nBrushDefIndex);
9043
0
    fprintf(fpOut, "  m_sBrushDef.nRefCount    = %d\n", m_sBrushDef.nRefCount);
9044
0
    fprintf(fpOut, "  m_sBrushDef.nFillPattern = %d\n",
9045
0
            static_cast<int>(m_sBrushDef.nFillPattern));
9046
0
    fprintf(fpOut, "  m_sBrushDef.bTransparentFill = %d\n",
9047
0
            static_cast<int>(m_sBrushDef.bTransparentFill));
9048
0
    fprintf(fpOut, "  m_sBrushDef.rgbFGColor   = 0x%6.6x (%d)\n",
9049
0
            m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
9050
0
    fprintf(fpOut, "  m_sBrushDef.rgbBGColor   = 0x%6.6x (%d)\n",
9051
0
            m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
9052
9053
0
    fflush(fpOut);
9054
0
}
9055
9056
/*=====================================================================
9057
 *                      class ITABFeatureFont
9058
 *====================================================================*/
9059
9060
/**********************************************************************
9061
 *                   ITABFeatureFont::ITABFeatureFont()
9062
 **********************************************************************/
9063
9064
// MI default is Font("Arial", 0, 0, 0)
9065
static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
9066
9067
ITABFeatureFont::ITABFeatureFont()
9068
94.7k
    : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
9069
94.7k
{
9070
94.7k
}
9071
9072
/**********************************************************************
9073
 *                   ITABFeatureFont::~ITABFeatureFont()
9074
 **********************************************************************/
9075
9076
94.7k
ITABFeatureFont::~ITABFeatureFont() = default;
9077
9078
/**********************************************************************
9079
 *                   ITABFeatureFont::SetFontName()
9080
 **********************************************************************/
9081
void ITABFeatureFont::SetFontName(const char *pszName)
9082
22.3k
{
9083
22.3k
    strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
9084
22.3k
    m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
9085
22.3k
}
9086
9087
/**********************************************************************
9088
 *                   ITABFeatureFont::DumpFontDef()
9089
 *
9090
 * Dump Font definition information.
9091
 **********************************************************************/
9092
void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
9093
0
{
9094
0
    if (fpOut == nullptr)
9095
0
        fpOut = stdout;
9096
9097
0
    fprintf(fpOut, "  m_nFontDefIndex       = %d\n", m_nFontDefIndex);
9098
0
    fprintf(fpOut, "  m_sFontDef.nRefCount  = %d\n", m_sFontDef.nRefCount);
9099
0
    fprintf(fpOut, "  m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
9100
9101
0
    fflush(fpOut);
9102
0
}
9103
9104
/*=====================================================================
9105
 *                      class ITABFeatureSymbol
9106
 *====================================================================*/
9107
9108
/**********************************************************************
9109
 *                   ITABFeatureSymbol::ITABFeatureSymbol()
9110
 **********************************************************************/
9111
9112
// MI default is Symbol(35, 0, 12)
9113
static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
9114
9115
ITABFeatureSymbol::ITABFeatureSymbol()
9116
93.4k
    : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
9117
93.4k
{
9118
93.4k
}
9119
9120
/**********************************************************************
9121
 *                   ITABFeatureSymbol::GetSymbolStyleString()
9122
 *
9123
 *  Return a Symbol() string. All representations info for the Symbol are here.
9124
 **********************************************************************/
9125
const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
9126
0
{
9127
0
    const char *pszStyle = nullptr;
9128
0
    int nOGRStyle = 0;
9129
    /* char szPattern[20]; */
9130
0
    int nAngle = 0;
9131
    /* szPattern[0] = '\0'; */
9132
9133
0
    switch (m_sSymbolDef.nSymbolNo)
9134
0
    {
9135
0
        case 31:
9136
            // this is actually a "null" symbol in MapInfo!
9137
0
            nOGRStyle = 0;
9138
0
            break;
9139
0
        case 32:  // filled square
9140
0
            nOGRStyle = 5;
9141
0
            break;
9142
0
        case 33:  // filled diamond
9143
0
            nAngle = 45;
9144
0
            nOGRStyle = 5;
9145
0
            break;
9146
0
        case 34:  // filled circle
9147
0
            nOGRStyle = 3;
9148
0
            break;
9149
0
        case 35:  // filled star
9150
0
            nOGRStyle = 9;
9151
0
            break;
9152
0
        case 36:  // filled upward pointing triangle
9153
0
            nOGRStyle = 7;
9154
0
            break;
9155
0
        case 37:  // filled downward pointing triangle
9156
0
            nAngle = 180;
9157
0
            nOGRStyle = 7;
9158
0
            break;
9159
0
        case 38:  // hollow square
9160
0
            nOGRStyle = 4;
9161
0
            break;
9162
0
        case 39:  // hollow diamond
9163
0
            nAngle = 45;
9164
0
            nOGRStyle = 4;
9165
0
            break;
9166
0
        case 40:  // hollow circle
9167
0
            nOGRStyle = 2;
9168
0
            break;
9169
0
        case 41:  // hollow star
9170
0
            nOGRStyle = 8;
9171
0
            break;
9172
0
        case 42:  // hollow upward pointing triangle
9173
0
            nOGRStyle = 6;
9174
0
            break;
9175
0
        case 43:  // hollow downward pointing triangle
9176
0
            nAngle = 180;
9177
0
            nOGRStyle = 6;
9178
0
            break;
9179
0
        case 44:  // filled square (with shadow)
9180
0
            nOGRStyle = 5;
9181
0
            break;
9182
0
        case 45:  // filled upward triangle (with shadow)
9183
0
            nOGRStyle = 7;
9184
0
            break;
9185
0
        case 46:  // filled circle (with shadow)
9186
0
            nOGRStyle = 3;
9187
0
            break;
9188
0
        case 49:  // crossed lines
9189
0
            nOGRStyle = 0;
9190
0
            break;
9191
0
        case 50:  // X crossed lines
9192
0
            nOGRStyle = 1;
9193
0
            break;
9194
0
    }
9195
9196
0
    nAngle += static_cast<int>(dfAngle);
9197
9198
0
    pszStyle = CPLSPrintf(
9199
0
        "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
9200
0
        m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
9201
0
        nOGRStyle);
9202
9203
0
    return pszStyle;
9204
0
}
9205
9206
/**********************************************************************
9207
 *                   ITABFeatureSymbol::SetSymbolFromStyleString()
9208
 *
9209
 *  Set all Symbol var from a OGRStyleSymbol.
9210
 **********************************************************************/
9211
void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
9212
0
{
9213
0
    GBool bIsNull = 0;
9214
9215
    // Set the Symbol Id (SymbolNo)
9216
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9217
0
    if (bIsNull)
9218
0
        pszSymbolId = nullptr;
9219
9220
0
    if (pszSymbolId)
9221
0
    {
9222
0
        if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
9223
0
        {
9224
0
            const int nSymbolId = atoi(pszSymbolId + 12);
9225
0
            SetSymbolNo(static_cast<GByte>(nSymbolId));
9226
0
        }
9227
0
        else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
9228
0
        {
9229
0
            const int nSymbolId = atoi(pszSymbolId + 8);
9230
9231
            // The OGR symbol is not the MapInfo one
9232
            // Here's some mapping
9233
0
            switch (nSymbolId)
9234
0
            {
9235
0
                case 0:
9236
0
                    SetSymbolNo(49);
9237
0
                    break;
9238
0
                case 1:
9239
0
                    SetSymbolNo(50);
9240
0
                    break;
9241
0
                case 2:
9242
0
                    SetSymbolNo(40);
9243
0
                    break;
9244
0
                case 3:
9245
0
                    SetSymbolNo(34);
9246
0
                    break;
9247
0
                case 4:
9248
0
                    SetSymbolNo(38);
9249
0
                    break;
9250
0
                case 5:
9251
0
                    SetSymbolNo(32);
9252
0
                    break;
9253
0
                case 6:
9254
0
                    SetSymbolNo(42);
9255
0
                    break;
9256
0
                case 7:
9257
0
                    SetSymbolNo(36);
9258
0
                    break;
9259
0
                case 8:
9260
0
                    SetSymbolNo(41);
9261
0
                    break;
9262
0
                case 9:
9263
0
                    SetSymbolNo(35);
9264
0
                    break;
9265
0
                case 10:  // vertical bar -- no mapinfo equivalent, so use
9266
                          // crosshairs as closest match
9267
0
                    SetSymbolNo(49);
9268
0
                    break;
9269
0
            }
9270
0
        }
9271
0
    }
9272
9273
    // Set SymbolSize
9274
0
    const double dSymbolSize = poSymbolStyle->Size(bIsNull);
9275
0
    if (dSymbolSize != 0.0)
9276
0
    {
9277
0
        SetSymbolSize(static_cast<GInt16>(dSymbolSize));
9278
0
    }
9279
9280
    // Set Symbol Color
9281
0
    const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
9282
0
    if (pszSymbolColor)
9283
0
    {
9284
0
        if (pszSymbolColor[0] == '#')
9285
0
            pszSymbolColor++;
9286
0
        int nSymbolColor =
9287
0
            static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
9288
0
        SetSymbolColor(static_cast<GInt32>(nSymbolColor));
9289
0
    }
9290
0
}
9291
9292
/**********************************************************************
9293
 *                   ITABFeatureSymbol::SetSymbolFromStyleString()
9294
 *
9295
 *  Set all Symbol var from a StyleString. Use StyleMgr to do so.
9296
 **********************************************************************/
9297
void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
9298
0
{
9299
    // Use the Style Manager to retrieve all the information we need.
9300
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9301
0
    OGRStyleTool *poStylePart = nullptr;
9302
9303
    // Init the StyleMgr with the StyleString.
9304
0
    poStyleMgr->InitStyleString(pszStyleString);
9305
9306
    // Retrieve the Symbol info.
9307
0
    const int numParts = poStyleMgr->GetPartCount();
9308
0
    for (int i = 0; i < numParts; i++)
9309
0
    {
9310
0
        poStylePart = poStyleMgr->GetPart(i);
9311
0
        if (poStylePart == nullptr)
9312
0
            continue;
9313
9314
0
        if (poStylePart->GetType() == OGRSTCSymbol)
9315
0
        {
9316
0
            break;
9317
0
        }
9318
0
        else
9319
0
        {
9320
0
            delete poStylePart;
9321
0
            poStylePart = nullptr;
9322
0
        }
9323
0
    }
9324
9325
    // If the no Symbol found, do nothing.
9326
0
    if (poStylePart == nullptr)
9327
0
    {
9328
0
        delete poStyleMgr;
9329
0
        return;
9330
0
    }
9331
9332
0
    OGRStyleSymbol *poSymbolStyle =
9333
0
        cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9334
9335
    // With Symbol, we always want to output points
9336
    //
9337
    // It's very important to set the output unit of the feature.
9338
    // The default value is meter. If we don't do it all numerical values
9339
    // will be assumed to be converted from the input unit to meter when we
9340
    // will get them via GetParam...() functions.
9341
    // See OGRStyleTool::Parse() for more details.
9342
0
    poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
9343
9344
0
    SetSymbolFromStyle(poSymbolStyle);
9345
9346
0
    delete poStyleMgr;
9347
0
    delete poStylePart;
9348
9349
0
    return;
9350
0
}
9351
9352
/**********************************************************************
9353
 *                   ITABFeatureSymbol::GetSymbolFeatureClass()
9354
 *
9355
 *  Return the feature class needed to represent the style string.
9356
 **********************************************************************/
9357
TABFeatureClass
9358
ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
9359
0
{
9360
    // Use the Style Manager to retrieve all the information we need.
9361
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9362
0
    OGRStyleTool *poStylePart = nullptr;
9363
9364
    // Init the StyleMgr with the StyleString.
9365
0
    poStyleMgr->InitStyleString(pszStyleString);
9366
9367
    // Retrieve the Symbol info.
9368
0
    const int numParts = poStyleMgr->GetPartCount();
9369
0
    for (int i = 0; i < numParts; i++)
9370
0
    {
9371
0
        poStylePart = poStyleMgr->GetPart(i);
9372
0
        if (poStylePart == nullptr)
9373
0
        {
9374
0
            continue;
9375
0
        }
9376
9377
0
        if (poStylePart->GetType() == OGRSTCSymbol)
9378
0
        {
9379
0
            break;
9380
0
        }
9381
0
        else
9382
0
        {
9383
0
            delete poStylePart;
9384
0
            poStylePart = nullptr;
9385
0
        }
9386
0
    }
9387
9388
0
    TABFeatureClass result = TABFCPoint;
9389
9390
    // If the no Symbol found, do nothing.
9391
0
    if (poStylePart == nullptr)
9392
0
    {
9393
0
        delete poStyleMgr;
9394
0
        return result;
9395
0
    }
9396
9397
0
    OGRStyleSymbol *poSymbolStyle =
9398
0
        cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9399
9400
0
    GBool bIsNull = 0;
9401
9402
    // Set the Symbol Id (SymbolNo)
9403
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9404
0
    if (bIsNull)
9405
0
        pszSymbolId = nullptr;
9406
9407
0
    if (pszSymbolId)
9408
0
    {
9409
0
        if (STARTS_WITH(pszSymbolId, "font-sym-"))
9410
0
        {
9411
0
            result = TABFCFontPoint;
9412
0
        }
9413
0
        else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
9414
0
        {
9415
0
            result = TABFCCustomPoint;
9416
0
        }
9417
0
    }
9418
9419
0
    delete poStyleMgr;
9420
0
    delete poStylePart;
9421
9422
0
    return result;
9423
0
}
9424
9425
/**********************************************************************
9426
 *                   ITABFeatureSymbol::DumpSymbolDef()
9427
 *
9428
 * Dump Symbol definition information.
9429
 **********************************************************************/
9430
void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
9431
0
{
9432
0
    if (fpOut == nullptr)
9433
0
        fpOut = stdout;
9434
9435
0
    fprintf(fpOut, "  m_nSymbolDefIndex       = %d\n", m_nSymbolDefIndex);
9436
0
    fprintf(fpOut, "  m_sSymbolDef.nRefCount  = %d\n", m_sSymbolDef.nRefCount);
9437
0
    fprintf(fpOut, "  m_sSymbolDef.nSymbolNo  = %d\n", m_sSymbolDef.nSymbolNo);
9438
0
    fprintf(fpOut, "  m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
9439
0
    fprintf(fpOut, "  m_sSymbolDef._unknown_  = %d\n",
9440
0
            static_cast<int>(m_sSymbolDef._nUnknownValue_));
9441
0
    fprintf(fpOut, "  m_sSymbolDef.rgbColor   = 0x%6.6x (%d)\n",
9442
0
            m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
9443
9444
0
    fflush(fpOut);
9445
0
}