Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/mitab/mitab_feature.cpp
Line
Count
Source
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(const OGRFeatureDefn *poDefnIn)
52
817k
    : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
53
817k
      m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
54
817k
      m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
55
817k
{
56
817k
}
57
58
/**********************************************************************
59
 *                   TABFeature::~TABFeature()
60
 *
61
 * Destructor.
62
 **********************************************************************/
63
TABFeature::~TABFeature()
64
817k
{
65
817k
}
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
31.6k
{
77
31.6k
    TABFeature *poFeature = nullptr;
78
79
    /*-----------------------------------------------------------------
80
     * Create new feature object of the right type
81
     *----------------------------------------------------------------*/
82
31.6k
    switch (nMapInfoType)
83
31.6k
    {
84
31.0k
        case TAB_GEOM_NONE:
85
31.0k
            poFeature = new TABFeature(poDefn);
86
31.0k
            break;
87
0
        case TAB_GEOM_SYMBOL_C:
88
67
        case TAB_GEOM_SYMBOL:
89
67
            poFeature = new TABPoint(poDefn);
90
67
            break;
91
0
        case TAB_GEOM_FONTSYMBOL_C:
92
53
        case TAB_GEOM_FONTSYMBOL:
93
53
            poFeature = new TABFontPoint(poDefn);
94
53
            break;
95
0
        case TAB_GEOM_CUSTOMSYMBOL_C:
96
55
        case TAB_GEOM_CUSTOMSYMBOL:
97
55
            poFeature = new TABCustomPoint(poDefn);
98
55
            break;
99
0
        case TAB_GEOM_LINE_C:
100
146
        case TAB_GEOM_LINE:
101
147
        case TAB_GEOM_PLINE_C:
102
147
        case TAB_GEOM_PLINE:
103
182
        case TAB_GEOM_MULTIPLINE_C:
104
182
        case TAB_GEOM_MULTIPLINE:
105
182
        case TAB_GEOM_V450_MULTIPLINE_C:
106
182
        case TAB_GEOM_V450_MULTIPLINE:
107
182
        case TAB_GEOM_V800_MULTIPLINE_C:
108
182
        case TAB_GEOM_V800_MULTIPLINE:
109
182
            poFeature = new TABPolyline(poDefn);
110
182
            break;
111
0
        case TAB_GEOM_ARC_C:
112
61
        case TAB_GEOM_ARC:
113
61
            poFeature = new TABArc(poDefn);
114
61
            break;
115
116
31
        case TAB_GEOM_REGION_C:
117
70
        case TAB_GEOM_REGION:
118
70
        case TAB_GEOM_V450_REGION_C:
119
70
        case TAB_GEOM_V450_REGION:
120
70
        case TAB_GEOM_V800_REGION_C:
121
70
        case TAB_GEOM_V800_REGION:
122
70
            poFeature = new TABRegion(poDefn);
123
70
            break;
124
0
        case TAB_GEOM_RECT_C:
125
34
        case TAB_GEOM_RECT:
126
37
        case TAB_GEOM_ROUNDRECT_C:
127
71
        case TAB_GEOM_ROUNDRECT:
128
71
            poFeature = new TABRectangle(poDefn);
129
71
            break;
130
5
        case TAB_GEOM_ELLIPSE_C:
131
36
        case TAB_GEOM_ELLIPSE:
132
36
            poFeature = new TABEllipse(poDefn);
133
36
            break;
134
0
        case TAB_GEOM_TEXT_C:
135
24
        case TAB_GEOM_TEXT:
136
24
            poFeature = new TABText(poDefn);
137
24
            break;
138
20
        case TAB_GEOM_MULTIPOINT_C:
139
21
        case TAB_GEOM_MULTIPOINT:
140
21
        case TAB_GEOM_V800_MULTIPOINT_C:
141
21
        case TAB_GEOM_V800_MULTIPOINT:
142
21
            poFeature = new TABMultiPoint(poDefn);
143
21
            break;
144
11
        case TAB_GEOM_COLLECTION_C:
145
20
        case TAB_GEOM_COLLECTION:
146
20
        case TAB_GEOM_V800_COLLECTION_C:
147
20
        case TAB_GEOM_V800_COLLECTION:
148
20
            poFeature = new TABCollection(poDefn);
149
20
            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
31.6k
    }
167
168
31.6k
    return poFeature;
169
31.6k
}
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
11.5k
{
186
    /*-----------------------------------------------------------------
187
     * Copy fields only if OGRFeatureDefn is the same
188
     *----------------------------------------------------------------*/
189
11.5k
    const OGRFeatureDefn *poThisDefnRef = GetDefnRef();
190
191
11.5k
    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
11.5k
    poDestFeature->SetGeometry(GetGeometryRef());
203
204
11.5k
    double dXMin = 0.0;
205
11.5k
    double dYMin = 0.0;
206
11.5k
    double dXMax = 0.0;
207
11.5k
    double dYMax = 0.0;
208
11.5k
    GetMBR(dXMin, dYMin, dXMax, dYMax);
209
11.5k
    poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
210
211
11.5k
    GInt32 nXMin = 0;
212
11.5k
    GInt32 nYMin = 0;
213
11.5k
    GInt32 nXMax = 0;
214
11.5k
    GInt32 nYMax = 0;
215
11.5k
    GetIntMBR(nXMin, nYMin, nXMax, nYMax);
216
11.5k
    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
11.5k
}
221
222
/**********************************************************************
223
 *                     TABFeature::CloneTABFeature()
224
 *
225
 * Duplicate feature, including stuff specific to each TABFeature type.
226
 *
227
 * The newly created feature is owned by the caller, and will have its own
228
 * reference to the OGRFeatureDefn.
229
 *
230
 * It is possible to create the clone with a different OGRFeatureDefn,
231
 * in this case, the fields won't be copied of course.
232
 *
233
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
234
 * then copies any members specific to its own type.
235
 **********************************************************************/
236
TABFeature *
237
TABFeature::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
238
11.5k
{
239
    /*-----------------------------------------------------------------
240
     * Alloc new feature and copy the base stuff
241
     *----------------------------------------------------------------*/
242
11.5k
    TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
243
244
11.5k
    CopyTABFeatureBase(poNew);
245
246
    /*-----------------------------------------------------------------
247
     * And members specific to this class
248
     *----------------------------------------------------------------*/
249
    // Nothing to do for this class
250
251
11.5k
    return poNew;
252
11.5k
}
253
254
/**********************************************************************
255
 *                   TABFeature::SetMBR()
256
 *
257
 * Set the values for the MBR corners for this feature.
258
 **********************************************************************/
259
void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
260
345k
{
261
345k
    m_dXMin = std::min(dXMin, dXMax);
262
345k
    m_dYMin = std::min(dYMin, dYMax);
263
345k
    m_dXMax = std::max(dXMin, dXMax);
264
345k
    m_dYMax = std::max(dYMin, dYMax);
265
345k
}
266
267
/**********************************************************************
268
 *                   TABFeature::GetMBR()
269
 *
270
 * Return the values for the MBR corners for this feature.
271
 **********************************************************************/
272
void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
273
                        double &dYMax)
274
119k
{
275
119k
    dXMin = m_dXMin;
276
119k
    dYMin = m_dYMin;
277
119k
    dXMax = m_dXMax;
278
119k
    dYMax = m_dYMax;
279
119k
}
280
281
/**********************************************************************
282
 *                   TABFeature::SetIntMBR()
283
 *
284
 * Set the integer coordinates values of the MBR of this feature.
285
 **********************************************************************/
286
void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
287
                           GInt32 nYMax)
288
12.1k
{
289
12.1k
    m_nXMin = nXMin;
290
12.1k
    m_nYMin = nYMin;
291
12.1k
    m_nXMax = nXMax;
292
12.1k
    m_nYMax = nYMax;
293
12.1k
}
294
295
/**********************************************************************
296
 *                   TABFeature::GetIntMBR()
297
 *
298
 * Return the integer coordinates values of the MBR of this feature.
299
 **********************************************************************/
300
void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
301
                           GInt32 &nYMax)
302
17.5k
{
303
17.5k
    nXMin = m_nXMin;
304
17.5k
    nYMin = m_nYMin;
305
17.5k
    nXMax = m_nXMax;
306
17.5k
    nYMax = m_nYMax;
307
17.5k
}
308
309
/**********************************************************************
310
 *                   TABFeature::ReadRecordFromDATFile()
311
 *
312
 * Fill the fields part of the feature from the contents of the
313
 * table record pointed to by poDATFile.
314
 *
315
 * It is assumed that poDATFile currently points to the beginning of
316
 * the table record and that this feature's OGRFeatureDefn has been
317
 * properly initialized for this table.
318
 **********************************************************************/
319
int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
320
31.6k
{
321
31.6k
    CPLAssert(poDATFile);
322
323
31.6k
    const int numFields = poDATFile->GetNumFields();
324
325
300k
    for (int iField = 0; iField < numFields; iField++)
326
268k
    {
327
268k
        switch (poDATFile->GetFieldType(iField))
328
268k
        {
329
12.3k
            case TABFChar:
330
12.3k
            {
331
12.3k
                int iWidth(poDATFile->GetFieldWidth(iField));
332
12.3k
                CPLString osValue(poDATFile->ReadCharField(iWidth));
333
334
12.3k
                if (!poDATFile->GetEncoding().empty())
335
0
                {
336
0
                    osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
337
0
                }
338
12.3k
                SetField(iField, osValue);
339
12.3k
                break;
340
0
            }
341
293
            case TABFDecimal:
342
293
            {
343
293
                const double dValue = poDATFile->ReadDecimalField(
344
293
                    poDATFile->GetFieldWidth(iField));
345
293
                SetField(iField, dValue);
346
293
                break;
347
0
            }
348
11.5k
            case TABFInteger:
349
11.5k
            {
350
11.5k
                const int nValue = poDATFile->ReadIntegerField(
351
11.5k
                    poDATFile->GetFieldWidth(iField));
352
11.5k
                SetField(iField, nValue);
353
11.5k
                break;
354
0
            }
355
0
            case TABFSmallInt:
356
0
            {
357
0
                const int nValue = poDATFile->ReadSmallIntField(
358
0
                    poDATFile->GetFieldWidth(iField));
359
0
                SetField(iField, nValue);
360
0
                break;
361
0
            }
362
0
            case TABFLargeInt:
363
0
            {
364
0
                const GInt64 nValue = poDATFile->ReadLargeIntField(
365
0
                    poDATFile->GetFieldWidth(iField));
366
0
                SetField(iField, nValue);
367
0
                break;
368
0
            }
369
0
            case TABFFloat:
370
0
            {
371
0
                const double dValue =
372
0
                    poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
373
0
                SetField(iField, dValue);
374
0
                break;
375
0
            }
376
0
            case TABFLogical:
377
0
            {
378
0
                const bool bValue = poDATFile->ReadLogicalField(
379
0
                    poDATFile->GetFieldWidth(iField));
380
0
                SetField(iField, bValue ? 1 : 0);
381
0
                break;
382
0
            }
383
12.1k
            case TABFDate:
384
12.1k
            {
385
12.1k
#ifdef MITAB_USE_OFTDATETIME
386
12.1k
                int nYear = 0;
387
12.1k
                int nMonth = 0;
388
12.1k
                int nDay = 0;
389
12.1k
                const int status = poDATFile->ReadDateField(
390
12.1k
                    poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
391
12.1k
                if (status == 0)
392
11.6k
                {
393
11.6k
                    SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
394
11.6k
                }
395
#else
396
                const char *pszValue =
397
                    poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
398
                SetField(iField, pszValue);
399
#endif
400
12.1k
                break;
401
0
            }
402
7.23k
            case TABFTime:
403
7.23k
            {
404
7.23k
#ifdef MITAB_USE_OFTDATETIME
405
7.23k
                int nHour = 0;
406
7.23k
                int nMin = 0;
407
7.23k
                int nMS = 0;
408
7.23k
                int nSec = 0;
409
7.23k
                const int status =
410
7.23k
                    poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
411
7.23k
                                             &nHour, &nMin, &nSec, &nMS);
412
7.23k
                if (status == 0)
413
154
                {
414
154
                    int nYear = 0;
415
154
                    int nMonth = 0;
416
154
                    int nDay = 0;
417
154
                    SetField(iField, nYear, nMonth, nDay, nHour, nMin,
418
154
                             nSec + nMS / 1000.0f, 0);
419
154
                }
420
#else
421
                const char *pszValue =
422
                    poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
423
                SetField(iField, pszValue);
424
#endif
425
7.23k
                break;
426
0
            }
427
0
            case TABFDateTime:
428
0
            {
429
0
#ifdef MITAB_USE_OFTDATETIME
430
0
                int nYear = 0;
431
0
                int nMonth = 0;
432
0
                int nDay = 0;
433
0
                int nHour = 0;
434
0
                int nMin = 0;
435
0
                int nMS = 0;
436
0
                int nSec = 0;
437
0
                const int status = poDATFile->ReadDateTimeField(
438
0
                    poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
439
0
                    &nHour, &nMin, &nSec, &nMS);
440
0
                if (status == 0)
441
0
                {
442
0
                    SetField(iField, nYear, nMonth, nDay, nHour, nMin,
443
0
                             nSec + nMS / 1000.0f, 0);
444
0
                }
445
#else
446
                const char *pszValue = poDATFile->ReadDateTimeField(
447
                    poDATFile->GetFieldWidth(iField));
448
                SetField(iField, pszValue);
449
#endif
450
0
                break;
451
0
            }
452
225k
            default:
453
                // Other type???  Impossible!
454
225k
                CPLError(CE_Failure, CPLE_AssertionFailed,
455
225k
                         "Unsupported field type for field %d!", iField);
456
268k
        }
457
268k
    }
458
459
31.6k
    return 0;
460
31.6k
}
461
462
/**********************************************************************
463
 *                   TABFeature::WriteRecordToDATFile()
464
 *
465
 * Write the attribute part of the feature to the .DAT file.
466
 *
467
 * It is assumed that poDATFile currently points to the beginning of
468
 * the table record and that this feature's OGRFeatureDefn has been
469
 * properly initialized for this table.
470
 *
471
 * Returns 0 on success, -1 on error.
472
 **********************************************************************/
473
int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
474
                                     TABINDFile *poINDFile, int *panIndexNo)
475
233k
{
476
233k
#ifdef MITAB_USE_OFTDATETIME
477
233k
    int nYear = 0;
478
233k
    int nMon = 0;
479
233k
    int nDay = 0;
480
233k
    int nHour = 0;
481
233k
    int nMin = 0;
482
233k
    int nTZFlag = 0;
483
233k
    float fSec = 0.0f;
484
233k
#endif
485
486
233k
    CPLAssert(poDATFile);
487
488
233k
    const int numFields = poDATFile->GetNumFields();
489
490
233k
    poDATFile->MarkRecordAsExisting();
491
492
233k
    int nStatus = 0;
493
922k
    for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
494
689k
    {
495
        // Hack for "extra" introduced field.
496
689k
        if (iField >= GetDefnRef()->GetFieldCount())
497
3
        {
498
3
            CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
499
3
                      iField == 0);
500
3
            nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
501
3
                                                   poINDFile, 0);
502
3
            continue;
503
3
        }
504
689k
        CPLAssert(panIndexNo != nullptr);
505
506
689k
        switch (poDATFile->GetFieldType(iField))
507
689k
        {
508
678k
            case TABFChar:
509
678k
            {
510
678k
                CPLString osValue(GetFieldAsString(iField));
511
678k
                if (!poDATFile->GetEncoding().empty())
512
0
                {
513
0
                    osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
514
0
                }
515
678k
                nStatus = poDATFile->WriteCharField(
516
678k
                    osValue, poDATFile->GetFieldWidth(iField), poINDFile,
517
678k
                    panIndexNo[iField]);
518
678k
            }
519
678k
            break;
520
0
            case TABFDecimal:
521
0
                nStatus = poDATFile->WriteDecimalField(
522
0
                    GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
523
0
                    poDATFile->GetFieldPrecision(iField), poINDFile,
524
0
                    panIndexNo[iField]);
525
0
                break;
526
5.62k
            case TABFInteger:
527
5.62k
                nStatus = poDATFile->WriteIntegerField(
528
5.62k
                    GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
529
5.62k
                break;
530
0
            case TABFSmallInt:
531
0
                nStatus = poDATFile->WriteSmallIntField(
532
0
                    static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
533
0
                    panIndexNo[iField]);
534
0
                break;
535
0
            case TABFLargeInt:
536
0
                nStatus = poDATFile->WriteLargeIntField(
537
0
                    static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
538
0
                    panIndexNo[iField]);
539
0
                break;
540
5.11k
            case TABFFloat:
541
5.11k
                nStatus = poDATFile->WriteFloatField(
542
5.11k
                    GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
543
5.11k
                break;
544
0
            case TABFLogical:
545
0
                nStatus =
546
0
                    poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
547
0
                                                 poINDFile, panIndexNo[iField]);
548
0
                break;
549
0
            case TABFDate:
550
0
#ifdef MITAB_USE_OFTDATETIME
551
0
                if (IsFieldSetAndNotNull(iField))
552
0
                {
553
0
                    GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
554
0
                                       &nMin, &fSec, &nTZFlag);
555
0
                }
556
0
                else
557
0
                {
558
0
                    nYear = 0;
559
0
                    nMon = 0;
560
0
                    nDay = 0;
561
0
                }
562
563
0
                nStatus = poDATFile->WriteDateField(
564
0
                    nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
565
#else
566
                nStatus = poDATFile->WriteDateField(
567
                    GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
568
#endif
569
0
                break;
570
0
            case TABFTime:
571
0
#ifdef MITAB_USE_OFTDATETIME
572
0
                if (IsFieldSetAndNotNull(iField))
573
0
                {
574
0
                    GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
575
0
                                       &nMin, &fSec, &nTZFlag);
576
0
                }
577
0
                else
578
0
                {
579
                    // Put negative values, so that WriteTimeField() forges
580
                    // a negative value, and ultimately write -1 in the binary
581
                    // field
582
0
                    nHour = -1;
583
0
                    nMin = -1;
584
0
                    fSec = -1;
585
0
                }
586
0
                nStatus = poDATFile->WriteTimeField(
587
0
                    nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
588
0
                    poINDFile, panIndexNo[iField]);
589
590
#else
591
                nStatus = poDATFile->WriteTimeField(
592
                    GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
593
#endif
594
0
                break;
595
0
            case TABFDateTime:
596
0
#ifdef MITAB_USE_OFTDATETIME
597
0
                if (IsFieldSetAndNotNull(iField))
598
0
                {
599
0
                    GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
600
0
                                       &nMin, &fSec, &nTZFlag);
601
0
                }
602
0
                else
603
0
                {
604
0
                    nYear = 0;
605
0
                    nMon = 0;
606
0
                    nDay = 0;
607
0
                    nHour = 0;
608
0
                    nMin = 0;
609
0
                    fSec = 0;
610
0
                }
611
612
0
                nStatus = poDATFile->WriteDateTimeField(
613
0
                    nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
614
0
                    OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
615
#else
616
                nStatus = poDATFile->WriteDateTimeField(
617
                    GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
618
#endif
619
0
                break;
620
0
            default:
621
                // Other type???  Impossible!
622
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
623
0
                         "Unsupported field type!");
624
689k
        }
625
689k
    }
626
627
233k
    if (nStatus != 0)
628
0
        return nStatus;
629
630
233k
    if (poDATFile->CommitRecordToFile() != 0)
631
0
        return -1;
632
633
233k
    return 0;
634
233k
}
635
636
/**********************************************************************
637
 *                   TABFeature::ReadGeometryFromMAPFile()
638
 *
639
 * In derived classes, this method should be reimplemented to
640
 * fill the geometry and representation (color, etc...) part of the
641
 * feature from the contents of the .MAP object pointed to by poMAPFile.
642
 *
643
 * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
644
 * currently points to the beginning of a map object.
645
 *
646
 * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
647
 * the CoordBlock data during splitting of object blocks. In this case we
648
 * need to process only the information related to the CoordBlock. One
649
 * important thing to avoid is reading/writing pen/brush/symbol definitions
650
 * as that would screw up their ref counters.
651
 *
652
 * ppoCoordBlock is used by TABCollection and by index splitting code
653
 * to provide a CoordBlock to use instead of the one from the poMAPFile and
654
 * return the current pointer at the end of the call.
655
 *
656
 * The current implementation does nothing since instances of TABFeature
657
 * objects contain no geometry (i.e. TAB_GEOM_NONE).
658
 *
659
 * Returns 0 on success, -1 on error, in which case CPLError() will have
660
 * been called.
661
 **********************************************************************/
662
int TABFeature::ReadGeometryFromMAPFile(
663
    TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
664
    GBool /*bCoordBlockDataOnly=FALSE*/,
665
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
666
31.0k
{
667
    // Nothing to do. Instances of TABFeature objects contain no geometry.
668
31.0k
    return 0;
669
31.0k
}
670
671
/**********************************************************************
672
 *                   TABFeature::UpdateMBR()
673
 *
674
 * Fetch envelope of poGeom and update MBR.
675
 * Integer coord MBR is updated only if poMapFile is not NULL.
676
 *
677
 * Returns 0 on success, or -1 if there is no geometry in object
678
 **********************************************************************/
679
int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
680
8.25k
{
681
8.25k
    OGRGeometry *poGeom = GetGeometryRef();
682
683
8.25k
    if (poGeom)
684
8.25k
    {
685
8.25k
        OGREnvelope oEnv;
686
8.25k
        poGeom->getEnvelope(&oEnv);
687
688
8.25k
        m_dXMin = oEnv.MinX;
689
8.25k
        m_dYMin = oEnv.MinY;
690
8.25k
        m_dXMax = oEnv.MaxX;
691
8.25k
        m_dYMax = oEnv.MaxY;
692
693
8.25k
        if (poMapFile)
694
8.25k
        {
695
8.25k
            poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
696
8.25k
            poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
697
            // Coordsy2Int can transform a min value to a max one and vice
698
            // versa.
699
8.25k
            if (m_nXMin > m_nXMax)
700
0
            {
701
0
                std::swap(m_nXMin, m_nXMax);
702
0
            }
703
8.25k
            if (m_nYMin > m_nYMax)
704
0
            {
705
0
                std::swap(m_nYMin, m_nYMax);
706
0
            }
707
8.25k
        }
708
709
8.25k
        return 0;
710
8.25k
    }
711
712
0
    return -1;
713
8.25k
}
714
715
/**********************************************************************
716
 *                   TABFeature::ValidateCoordType()
717
 *
718
 * Checks the feature envelope to establish if the feature should be
719
 * written using Compressed coordinates or not and adjust m_nMapInfoType
720
 * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
721
 * m_nXMax, m_nYMax
722
 *
723
 * This function should be used only by the ValidateMapInfoType()
724
 * implementations.
725
 *
726
 * Returns TRUE if coord. should be compressed, FALSE otherwise
727
 **********************************************************************/
728
GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
729
6.35k
{
730
6.35k
    GBool bCompr = FALSE;
731
732
    /*-------------------------------------------------------------
733
     * Decide if coordinates should be compressed or not.
734
     *------------------------------------------------------------*/
735
6.35k
    if (UpdateMBR(poMapFile) == 0)
736
6.35k
    {
737
        /* Test for max range < 65535 here instead of < 65536 to avoid
738
         * compressed coordinate overflows in some boundary situations
739
         */
740
6.35k
        if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
741
5.82k
            (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
742
5.67k
        {
743
5.67k
            bCompr = TRUE;
744
5.67k
        }
745
6.35k
        m_nComprOrgX =
746
6.35k
            static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
747
6.35k
        m_nComprOrgY =
748
6.35k
            static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
749
6.35k
    }
750
751
    /*-------------------------------------------------------------
752
     * Adjust native type
753
     *------------------------------------------------------------*/
754
6.35k
    if (bCompr && ((m_nMapInfoType % 3) == 2))
755
3.41k
        m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
756
3.41k
                                                  1);  // compr = 1, 4, 7, ...
757
2.94k
    else if (!bCompr && ((m_nMapInfoType % 3) == 1))
758
0
        m_nMapInfoType = static_cast<TABGeomType>(
759
0
            m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
760
761
6.35k
    return bCompr;
762
6.35k
}
763
764
/**********************************************************************
765
 *                   TABFeature::ForceCoordTypeAndOrigin()
766
 *
767
 * This function is used by TABCollection::ValidateMapInfoType() to force
768
 * the coord type and compressed origin of all members of a collection
769
 * to be the same. (A replacement for ValidateCoordType() for this
770
 * specific case)
771
 **********************************************************************/
772
void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
773
                                         GInt32 nComprOrgX, GInt32 nComprOrgY,
774
                                         GInt32 nXMin, GInt32 nYMin,
775
                                         GInt32 nXMax, GInt32 nYMax)
776
0
{
777
    /*-------------------------------------------------------------
778
     * Set Compressed Origin and adjust native type
779
     *------------------------------------------------------------*/
780
0
    m_nComprOrgX = nComprOrgX;
781
0
    m_nComprOrgY = nComprOrgY;
782
783
0
    m_nMapInfoType = nMapInfoType;
784
785
0
    if (bCompr && ((m_nMapInfoType % 3) == 2))
786
0
        m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
787
0
                                                  1);  // compr = 1, 4, 7, ...
788
0
    else if (!bCompr && ((m_nMapInfoType % 3) == 1))
789
0
        m_nMapInfoType = static_cast<TABGeomType>(
790
0
            m_nMapInfoType + 1);  // non-compr = 2, 5, 8, ...
791
792
0
    m_nXMin = nXMin;
793
0
    m_nYMin = nYMin;
794
0
    m_nXMax = nXMax;
795
0
    m_nYMax = nYMax;
796
0
}
797
798
/**********************************************************************
799
 *                   TABFeature::WriteGeometryToMAPFile()
800
 *
801
 *
802
 * In derived classes, this method should be reimplemented to
803
 * write the geometry and representation (color, etc...) part of the
804
 * feature to the .MAP object pointed to by poMAPFile.
805
 *
806
 * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
807
 * currently points to a valid map object.
808
 *
809
 * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
810
 * the CoordBlock data during splitting of object blocks. In this case we
811
 * need to process only the information related to the CoordBlock. One
812
 * important thing to avoid is reading/writing pen/brush/symbol definitions
813
 * as that would screw up their ref counters.
814
 *
815
 * ppoCoordBlock is used by TABCollection and by index splitting code
816
 * to provide a CoordBlock to use instead of the one from the poMAPFile and
817
 * return the current pointer at the end of the call.
818
 *
819
 * The current implementation does nothing since instances of TABFeature
820
 * objects contain no geometry (i.e. TAB_GEOM_NONE).
821
 *
822
 * Returns 0 on success, -1 on error, in which case CPLError() will have
823
 * been called.
824
 **********************************************************************/
825
int TABFeature::WriteGeometryToMAPFile(
826
    TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
827
    GBool /*bCoordBlockDataOnly=FALSE*/,
828
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
829
225k
{
830
    /*-----------------------------------------------------------------
831
     * Nothing to do... instances of TABFeature objects contain no geometry.
832
     *----------------------------------------------------------------*/
833
834
225k
    return 0;
835
225k
}
836
837
/**********************************************************************
838
 *                   TABFeature::DumpMID()
839
 *
840
 * Dump feature attributes in a format similar to .MID data records.
841
 **********************************************************************/
842
void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
843
0
{
844
0
    const OGRFeatureDefn *l_poDefn = GetDefnRef();
845
846
0
    if (fpOut == nullptr)
847
0
        fpOut = stdout;
848
849
0
    for (int iField = 0; iField < l_poDefn->GetFieldCount(); iField++)
850
0
    {
851
0
        const OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
852
853
0
        fprintf(fpOut, "  %s (%s) = %s\n", poFDefn->GetNameRef(),
854
0
                OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
855
0
                GetFieldAsString(iField));
856
0
    }
857
858
0
    fflush(fpOut);
859
0
}
860
861
/**********************************************************************
862
 *                   TABFeature::DumpMIF()
863
 *
864
 * Dump feature geometry in a format similar to .MIF files.
865
 **********************************************************************/
866
void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
867
0
{
868
0
    if (fpOut == nullptr)
869
0
        fpOut = stdout;
870
871
    /*-----------------------------------------------------------------
872
     * Generate output... not much to do, feature contains no geometry.
873
     *----------------------------------------------------------------*/
874
0
    fprintf(fpOut, "NONE\n");
875
876
0
    fflush(fpOut);
877
0
}
878
879
/*=====================================================================
880
 *                      class TABPoint
881
 *====================================================================*/
882
883
/**********************************************************************
884
 *                   TABPoint::TABPoint()
885
 *
886
 * Constructor.
887
 **********************************************************************/
888
34.9k
TABPoint::TABPoint(const OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
889
34.9k
{
890
34.9k
}
891
892
/**********************************************************************
893
 *                   TABPoint::~TABPoint()
894
 *
895
 * Destructor.
896
 **********************************************************************/
897
TABPoint::~TABPoint()
898
34.9k
{
899
34.9k
}
900
901
/**********************************************************************
902
 *                     TABPoint::CloneTABFeature()
903
 *
904
 * Duplicate feature, including stuff specific to each TABFeature type.
905
 *
906
 * This method calls the generic TABFeature::CloneTABFeature() and
907
 * then copies any members specific to its own type.
908
 **********************************************************************/
909
TABFeature *TABPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
910
3
{
911
    /*-----------------------------------------------------------------
912
     * Alloc new feature and copy the base stuff
913
     *----------------------------------------------------------------*/
914
3
    TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
915
916
3
    CopyTABFeatureBase(poNew);
917
918
    /*-----------------------------------------------------------------
919
     * And members specific to this class
920
     *----------------------------------------------------------------*/
921
    // ITABFeatureSymbol
922
3
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
923
924
3
    return poNew;
925
3
}
926
927
/**********************************************************************
928
 *                   TABPoint::ValidateMapInfoType()
929
 *
930
 * Check the feature's geometry part and return the corresponding
931
 * mapinfo object type code.  The m_nMapInfoType member will also
932
 * be updated for further calls to GetMapInfoType();
933
 *
934
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
935
 * is expected for this object class.
936
 **********************************************************************/
937
TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
938
1.79k
{
939
    /*-----------------------------------------------------------------
940
     * Fetch and validate geometry
941
     * __TODO__ For now we always write in uncompressed format (until we
942
     * find that this is not correct... note that at this point the
943
     * decision to use compressed/uncompressed will likely be based on
944
     * the distance between the point and the object block center in
945
     * integer coordinates being > 32767 or not... remains to be verified)
946
     *----------------------------------------------------------------*/
947
1.79k
    OGRGeometry *poGeom = GetGeometryRef();
948
1.79k
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
949
1.79k
    {
950
1.79k
        switch (GetFeatureClass())
951
1.79k
        {
952
0
            case TABFCFontPoint:
953
0
                m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
954
0
                break;
955
0
            case TABFCCustomPoint:
956
0
                m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
957
0
                break;
958
1.79k
            case TABFCPoint:
959
1.79k
            default:
960
1.79k
                m_nMapInfoType = TAB_GEOM_SYMBOL;
961
1.79k
                break;
962
1.79k
        }
963
1.79k
    }
964
0
    else
965
0
    {
966
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
967
0
                 "TABPoint: Missing or Invalid Geometry!");
968
0
        m_nMapInfoType = TAB_GEOM_NONE;
969
0
    }
970
971
1.79k
    UpdateMBR(poMapFile);
972
973
1.79k
    return m_nMapInfoType;
974
1.79k
}
975
976
/**********************************************************************
977
 *                   TABPoint::ReadGeometryFromMAPFile()
978
 *
979
 * Fill the geometry and representation (color, etc...) part of the
980
 * feature from the contents of the .MAP object pointed to by poMAPFile.
981
 *
982
 * It is assumed that poMAPFile currently points to the beginning of
983
 * a map object.
984
 *
985
 * Returns 0 on success, -1 on error, in which case CPLError() will have
986
 * been called.
987
 **********************************************************************/
988
int TABPoint::ReadGeometryFromMAPFile(
989
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
990
    GBool bCoordBlockDataOnly /*=FALSE*/,
991
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
992
67
{
993
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
994
67
    if (bCoordBlockDataOnly)
995
0
        return 0;
996
997
    /*-----------------------------------------------------------------
998
     * Fetch and validate geometry type
999
     *----------------------------------------------------------------*/
1000
67
    m_nMapInfoType = poObjHdr->m_nType;
1001
1002
67
    if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
1003
0
        m_nMapInfoType != TAB_GEOM_SYMBOL_C)
1004
0
    {
1005
0
        CPLError(
1006
0
            CE_Failure, CPLE_AssertionFailed,
1007
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1008
0
            m_nMapInfoType, m_nMapInfoType);
1009
0
        return -1;
1010
0
    }
1011
1012
    /*-----------------------------------------------------------------
1013
     * Read object information
1014
     *----------------------------------------------------------------*/
1015
67
    TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1016
1017
67
    m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
1018
1019
67
    poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1020
1021
    /*-----------------------------------------------------------------
1022
     * Create and fill geometry object
1023
     *----------------------------------------------------------------*/
1024
67
    double dX = 0.0;
1025
67
    double dY = 0.0;
1026
1027
67
    poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1028
67
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1029
1030
67
    SetGeometryDirectly(poGeometry);
1031
1032
67
    SetMBR(dX, dY, dX, dY);
1033
67
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1034
67
              poObjHdr->m_nMaxY);
1035
1036
67
    return 0;
1037
67
}
1038
1039
/**********************************************************************
1040
 *                   TABPoint::WriteGeometryToMAPFile()
1041
 *
1042
 * Write the geometry and representation (color, etc...) part of the
1043
 * feature to the .MAP object pointed to by poMAPFile.
1044
 *
1045
 * It is assumed that poMAPFile currently points to a valid map object.
1046
 *
1047
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1048
 * been called.
1049
 **********************************************************************/
1050
int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
1051
                                     TABMAPObjHdr *poObjHdr,
1052
                                     GBool bCoordBlockDataOnly /*=FALSE*/,
1053
                                     TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1054
1.79k
{
1055
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1056
1.79k
    if (bCoordBlockDataOnly)
1057
0
        return 0;
1058
1059
    /*-----------------------------------------------------------------
1060
     * We assume that ValidateMapInfoType() was called already and that
1061
     * the type in poObjHdr->m_nType is valid.
1062
     *----------------------------------------------------------------*/
1063
1.79k
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1064
1065
    /*-----------------------------------------------------------------
1066
     * Fetch and validate geometry
1067
     *----------------------------------------------------------------*/
1068
1.79k
    OGRGeometry *poGeom = GetGeometryRef();
1069
1.79k
    OGRPoint *poPoint = nullptr;
1070
1.79k
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1071
1.79k
        poPoint = poGeom->toPoint();
1072
0
    else
1073
0
    {
1074
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1075
0
                 "TABPoint: Missing or Invalid Geometry!");
1076
0
        return -1;
1077
0
    }
1078
1079
1.79k
    GInt32 nX = 0;
1080
1.79k
    GInt32 nY = 0;
1081
1.79k
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1082
1083
    /*-----------------------------------------------------------------
1084
     * Copy object information
1085
     *----------------------------------------------------------------*/
1086
1.79k
    TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1087
1088
1.79k
    poPointHdr->m_nX = nX;
1089
1.79k
    poPointHdr->m_nY = nY;
1090
1.79k
    poPointHdr->SetMBR(nX, nY, nX, nY);
1091
1092
1.79k
    m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1093
1.79k
    poPointHdr->m_nSymbolId =
1094
1.79k
        static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
1095
1096
1.79k
    if (CPLGetLastErrorType() == CE_Failure)
1097
0
        return -1;
1098
1099
1.79k
    return 0;
1100
1.79k
}
1101
1102
/**********************************************************************
1103
 *                   TABPoint::GetX()
1104
 *
1105
 * Return this point's X coordinate.
1106
 **********************************************************************/
1107
double TABPoint::GetX()
1108
0
{
1109
1110
    /*-----------------------------------------------------------------
1111
     * Fetch and validate geometry
1112
     *----------------------------------------------------------------*/
1113
0
    OGRGeometry *poGeom = GetGeometryRef();
1114
0
    OGRPoint *poPoint = nullptr;
1115
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1116
0
        poPoint = poGeom->toPoint();
1117
0
    else
1118
0
    {
1119
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1120
0
                 "TABPoint: Missing or Invalid Geometry!");
1121
0
        return 0.0;
1122
0
    }
1123
1124
0
    return poPoint->getX();
1125
0
}
1126
1127
/**********************************************************************
1128
 *                   TABPoint::GetY()
1129
 *
1130
 * Return this point's Y coordinate.
1131
 **********************************************************************/
1132
double TABPoint::GetY()
1133
0
{
1134
    /*-----------------------------------------------------------------
1135
     * Fetch and validate geometry
1136
     *----------------------------------------------------------------*/
1137
0
    OGRGeometry *poGeom = GetGeometryRef();
1138
0
    OGRPoint *poPoint = nullptr;
1139
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1140
0
        poPoint = poGeom->toPoint();
1141
0
    else
1142
0
    {
1143
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1144
0
                 "TABPoint: Missing or Invalid Geometry!");
1145
0
        return 0.0;
1146
0
    }
1147
1148
0
    return poPoint->getY();
1149
0
}
1150
1151
/**********************************************************************
1152
 *                   TABPoint::GetStyleString() const
1153
 *
1154
 * Return style string for this feature.
1155
 *
1156
 * Style String is built only once during the first call to GetStyleString().
1157
 **********************************************************************/
1158
const char *TABPoint::GetStyleString() const
1159
0
{
1160
0
    if (m_pszStyleString == nullptr)
1161
0
    {
1162
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1163
0
    }
1164
1165
0
    return m_pszStyleString;
1166
0
}
1167
1168
/**********************************************************************
1169
 *                   TABPoint::DumpMIF()
1170
 *
1171
 * Dump feature geometry in a format similar to .MIF POINTs.
1172
 **********************************************************************/
1173
void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
1174
0
{
1175
0
    if (fpOut == nullptr)
1176
0
        fpOut = stdout;
1177
1178
    /*-----------------------------------------------------------------
1179
     * Fetch and validate geometry
1180
     *----------------------------------------------------------------*/
1181
0
    OGRGeometry *poGeom = GetGeometryRef();
1182
0
    OGRPoint *poPoint = nullptr;
1183
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1184
0
        poPoint = poGeom->toPoint();
1185
0
    else
1186
0
    {
1187
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1188
0
                 "TABPoint: Missing or Invalid Geometry!");
1189
0
        return;
1190
0
    }
1191
1192
    /*-----------------------------------------------------------------
1193
     * Generate output
1194
     *----------------------------------------------------------------*/
1195
0
    fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
1196
1197
0
    DumpSymbolDef(fpOut);
1198
1199
    /*-----------------------------------------------------------------
1200
     * Handle stuff specific to derived classes
1201
     *----------------------------------------------------------------*/
1202
    // cppcheck-suppress knownConditionTrueFalse
1203
0
    if (GetFeatureClass() == TABFCFontPoint)
1204
0
    {
1205
0
        TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
1206
0
        fprintf(fpOut, "  m_nFontStyle     = 0x%2.2x (%d)\n",
1207
0
                poFeature->GetFontStyleTABValue(),
1208
0
                poFeature->GetFontStyleTABValue());
1209
1210
0
        poFeature->DumpFontDef(fpOut);
1211
0
    }
1212
    // cppcheck-suppress knownConditionTrueFalse
1213
0
    if (GetFeatureClass() == TABFCCustomPoint)
1214
0
    {
1215
0
        TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
1216
1217
0
        fprintf(fpOut, "  m_nUnknown_      = 0x%2.2x (%d)\n",
1218
0
                poFeature->m_nUnknown_, poFeature->m_nUnknown_);
1219
0
        fprintf(fpOut, "  m_nCustomStyle   = 0x%2.2x (%d)\n",
1220
0
                poFeature->GetCustomSymbolStyle(),
1221
0
                poFeature->GetCustomSymbolStyle());
1222
1223
0
        poFeature->DumpFontDef(fpOut);
1224
0
    }
1225
1226
0
    fflush(fpOut);
1227
0
}
1228
1229
/*=====================================================================
1230
 *                      class TABFontPoint
1231
 *====================================================================*/
1232
1233
/**********************************************************************
1234
 *                   TABFontPoint::TABFontPoint()
1235
 *
1236
 * Constructor.
1237
 **********************************************************************/
1238
TABFontPoint::TABFontPoint(const OGRFeatureDefn *poDefnIn)
1239
8.67k
    : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
1240
8.67k
{
1241
8.67k
}
1242
1243
/**********************************************************************
1244
 *                   TABFontPoint::~TABFontPoint()
1245
 *
1246
 * Destructor.
1247
 **********************************************************************/
1248
TABFontPoint::~TABFontPoint()
1249
8.67k
{
1250
8.67k
}
1251
1252
/**********************************************************************
1253
 *                     TABFontPoint::CloneTABFeature()
1254
 *
1255
 * Duplicate feature, including stuff specific to each TABFeature type.
1256
 *
1257
 * This method calls the generic TABFeature::CloneTABFeature() and
1258
 * then copies any members specific to its own type.
1259
 **********************************************************************/
1260
TABFeature *
1261
TABFontPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
1262
0
{
1263
    /*-----------------------------------------------------------------
1264
     * Alloc new feature and copy the base stuff
1265
     *----------------------------------------------------------------*/
1266
0
    TABFontPoint *poNew =
1267
0
        new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
1268
1269
0
    CopyTABFeatureBase(poNew);
1270
1271
    /*-----------------------------------------------------------------
1272
     * And members specific to this class
1273
     *----------------------------------------------------------------*/
1274
    // ITABFeatureSymbol
1275
0
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1276
1277
    // ITABFeatureFont
1278
0
    *(poNew->GetFontDefRef()) = *GetFontDefRef();
1279
1280
0
    poNew->SetSymbolAngle(GetSymbolAngle());
1281
0
    poNew->SetFontStyleTABValue(GetFontStyleTABValue());
1282
1283
0
    return poNew;
1284
0
}
1285
1286
/**********************************************************************
1287
 *                   TABFontPoint::ReadGeometryFromMAPFile()
1288
 *
1289
 * Fill the geometry and representation (color, etc...) part of the
1290
 * feature from the contents of the .MAP object pointed to by poMAPFile.
1291
 *
1292
 * It is assumed that poMAPFile currently points to the beginning of
1293
 * a map object.
1294
 *
1295
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1296
 * been called.
1297
 **********************************************************************/
1298
int TABFontPoint::ReadGeometryFromMAPFile(
1299
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1300
    GBool bCoordBlockDataOnly /*=FALSE*/,
1301
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1302
53
{
1303
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1304
53
    if (bCoordBlockDataOnly)
1305
0
        return 0;
1306
1307
    /*-----------------------------------------------------------------
1308
     * Fetch and validate geometry type
1309
     *----------------------------------------------------------------*/
1310
53
    m_nMapInfoType = poObjHdr->m_nType;
1311
1312
53
    if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
1313
0
        m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
1314
0
    {
1315
0
        CPLError(
1316
0
            CE_Failure, CPLE_AssertionFailed,
1317
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1318
0
            m_nMapInfoType, m_nMapInfoType);
1319
0
        return -1;
1320
0
    }
1321
1322
    /*-----------------------------------------------------------------
1323
     * Read object information
1324
     * NOTE: This symbol type does not contain a reference to a
1325
     * SymbolDef block in the file, but we still use the m_sSymbolDef
1326
     * structure to store the information inside the class so that the
1327
     * ITABFeatureSymbol methods work properly for the class user.
1328
     *----------------------------------------------------------------*/
1329
53
    TABMAPObjFontPoint *poPointHdr =
1330
53
        cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1331
1332
53
    m_nSymbolDefIndex = -1;
1333
53
    m_sSymbolDef.nRefCount = 0;
1334
1335
53
    m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId;    // shape
1336
53
    m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize;  // point size
1337
1338
53
    m_nFontStyle = poPointHdr->m_nFontStyle;  // font style
1339
1340
53
    m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
1341
53
                            poPointHdr->m_nG * 256 + poPointHdr->m_nB;
1342
1343
    /*-------------------------------------------------------------
1344
     * Symbol Angle, in tenths of degree.
1345
     * Contrary to arc start/end angles, no conversion based on
1346
     * origin quadrant is required here.
1347
     *------------------------------------------------------------*/
1348
53
    m_dAngle = poPointHdr->m_nAngle / 10.0;
1349
1350
53
    m_nFontDefIndex = poPointHdr->m_nFontId;  // Font name index
1351
1352
53
    poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1353
1354
    /*-----------------------------------------------------------------
1355
     * Create and fill geometry object
1356
     *----------------------------------------------------------------*/
1357
53
    double dX = 0.0;
1358
53
    double dY = 0.0;
1359
53
    poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1360
53
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1361
1362
53
    SetGeometryDirectly(poGeometry);
1363
1364
53
    SetMBR(dX, dY, dX, dY);
1365
53
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1366
53
              poObjHdr->m_nMaxY);
1367
1368
53
    return 0;
1369
53
}
1370
1371
/**********************************************************************
1372
 *                   TABFontPoint::WriteGeometryToMAPFile()
1373
 *
1374
 * Write the geometry and representation (color, etc...) part of the
1375
 * feature to the .MAP object pointed to by poMAPFile.
1376
 *
1377
 * It is assumed that poMAPFile currently points to a valid map object.
1378
 *
1379
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1380
 * been called.
1381
 **********************************************************************/
1382
int TABFontPoint::WriteGeometryToMAPFile(
1383
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1384
    GBool bCoordBlockDataOnly /*=FALSE*/,
1385
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1386
0
{
1387
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1388
0
    if (bCoordBlockDataOnly)
1389
0
        return 0;
1390
1391
    /*-----------------------------------------------------------------
1392
     * We assume that ValidateMapInfoType() was called already and that
1393
     * the type in poObjHdr->m_nType is valid.
1394
     *----------------------------------------------------------------*/
1395
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1396
1397
    /*-----------------------------------------------------------------
1398
     * Fetch and validate geometry
1399
     *----------------------------------------------------------------*/
1400
0
    OGRGeometry *poGeom = GetGeometryRef();
1401
0
    OGRPoint *poPoint = nullptr;
1402
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1403
0
        poPoint = poGeom->toPoint();
1404
0
    else
1405
0
    {
1406
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1407
0
                 "TABFontPoint: Missing or Invalid Geometry!");
1408
0
        return -1;
1409
0
    }
1410
1411
0
    GInt32 nX = 0;
1412
0
    GInt32 nY = 0;
1413
0
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1414
1415
    /*-----------------------------------------------------------------
1416
     * Copy object information
1417
     * NOTE: This symbol type does not contain a reference to a
1418
     * SymbolDef block in the file, but we still use the m_sSymbolDef
1419
     * structure to store the information inside the class so that the
1420
     * ITABFeatureSymbol methods work properly for the class user.
1421
     *----------------------------------------------------------------*/
1422
0
    TABMAPObjFontPoint *poPointHdr =
1423
0
        cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1424
1425
0
    poPointHdr->m_nX = nX;
1426
0
    poPointHdr->m_nY = nY;
1427
0
    poPointHdr->SetMBR(nX, nY, nX, nY);
1428
1429
0
    poPointHdr->m_nSymbolId =
1430
0
        static_cast<GByte>(m_sSymbolDef.nSymbolNo);  // shape
1431
0
    poPointHdr->m_nPointSize =
1432
0
        static_cast<GByte>(m_sSymbolDef.nPointSize);  // point size
1433
0
    poPointHdr->m_nFontStyle = m_nFontStyle;          // font style
1434
1435
0
    poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
1436
0
    poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
1437
0
    poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
1438
1439
    /*-------------------------------------------------------------
1440
     * Symbol Angle, in tenths of degree.
1441
     * Contrary to arc start/end angles, no conversion based on
1442
     * origin quadrant is required here.
1443
     *------------------------------------------------------------*/
1444
0
    poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
1445
1446
    // Write Font Def
1447
0
    m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1448
0
    poPointHdr->m_nFontId =
1449
0
        static_cast<GByte>(m_nFontDefIndex);  // Font name index
1450
1451
0
    if (CPLGetLastErrorType() == CE_Failure)
1452
0
        return -1;
1453
1454
0
    return 0;
1455
0
}
1456
1457
/**********************************************************************
1458
 *                   TABFontPoint::QueryFontStyle()
1459
 *
1460
 * Return TRUE if the specified font style attribute is turned ON,
1461
 * or FALSE otherwise.  See enum TABFontStyle for the list of styles
1462
 * that can be queried on.
1463
 **********************************************************************/
1464
GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
1465
0
{
1466
0
    return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
1467
0
}
1468
1469
void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
1470
0
{
1471
0
    if (bStyleOn)
1472
0
        m_nFontStyle |= static_cast<int>(eStyleToToggle);
1473
0
    else
1474
0
        m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
1475
0
}
1476
1477
/**********************************************************************
1478
 *                   TABFontPoint::GetFontStyleMIFValue()
1479
 *
1480
 * Return the Font Style value for this object using the style values
1481
 * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
1482
 *
1483
 * The reason why we have to differentiate between the TAB and the MIF font
1484
 * style values is that in TAB, TABFSBox is included in the style value
1485
 * as code 0x100, but in MIF it is not included, instead it is implied by
1486
 * the presence of the BG color in the FONT() clause (the BG color is
1487
 * present only when TABFSBox or TABFSHalo is set).
1488
 * This also has the effect of shifting all the other style values > 0x100
1489
 * by 1 byte.
1490
 *
1491
 * NOTE: Even if there is no BG color for font symbols, we inherit this
1492
 * problem because Font Point styles use the same codes as Text Font styles.
1493
 **********************************************************************/
1494
int TABFontPoint::GetFontStyleMIFValue()
1495
0
{
1496
    // The conversion is simply to remove bit 0x100 from the value and shift
1497
    // down all values past this bit.
1498
0
    return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
1499
0
}
1500
1501
void TABFontPoint::SetFontStyleMIFValue(int nStyle)
1502
8.61k
{
1503
8.61k
    m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
1504
8.61k
}
1505
1506
/**********************************************************************
1507
 *                   TABFontPoint::SetSymbolAngle()
1508
 *
1509
 * Set the symbol angle value in degrees, making sure the value is
1510
 * always in the range [0..360]
1511
 **********************************************************************/
1512
void TABFontPoint::SetSymbolAngle(double dAngle)
1513
8.61k
{
1514
8.61k
    dAngle = fmod(dAngle, 360.0);
1515
8.61k
    if (dAngle < 0.0)
1516
1.10k
        dAngle += 360.0;
1517
1518
8.61k
    m_dAngle = dAngle;
1519
8.61k
}
1520
1521
/**********************************************************************
1522
 *                   TABFontPoint::GetSymbolStyleString()
1523
 *
1524
 *  Return a Symbol() string. All representations info for the Symbol are here.
1525
 **********************************************************************/
1526
const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
1527
0
{
1528
    /* Get the SymbolStyleString, and add the outline Color
1529
       (halo/border in MapInfo Symbol terminology) */
1530
0
    const char *outlineColor = nullptr;
1531
0
    if (m_nFontStyle & 16)
1532
0
        outlineColor = ",o:#000000";
1533
0
    else if (m_nFontStyle & 512)
1534
0
        outlineColor = ",o:#ffffff";
1535
0
    else
1536
0
        outlineColor = "";
1537
1538
0
    int nAngle = static_cast<int>(dfAngle);
1539
0
    const char *pszStyle;
1540
1541
0
    pszStyle = CPLSPrintf(
1542
0
        "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
1543
0
        nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
1544
0
        m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
1545
0
    return pszStyle;
1546
0
}
1547
1548
/**********************************************************************
1549
 *                   TABFontPoint::GetStyleString() const
1550
 *
1551
 * Return style string for this feature.
1552
 *
1553
 * Style String is built only once during the first call to GetStyleString().
1554
 **********************************************************************/
1555
const char *TABFontPoint::GetStyleString() const
1556
0
{
1557
0
    if (m_pszStyleString == nullptr)
1558
0
    {
1559
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
1560
0
    }
1561
1562
0
    return m_pszStyleString;
1563
0
}
1564
1565
/**********************************************************************
1566
 *                   TABFontPoint::SetSymbolFromStyle()
1567
 *
1568
 *  Set all Symbol var from a OGRStyleSymbol.
1569
 **********************************************************************/
1570
void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1571
0
{
1572
0
    ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1573
1574
0
    GBool bIsNull = 0;
1575
1576
    // Try to set font glyph number
1577
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1578
0
    if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
1579
0
    {
1580
0
        const int nSymbolId = atoi(pszSymbolId + 9);
1581
0
        SetSymbolNo(static_cast<GInt16>(nSymbolId));
1582
0
    }
1583
1584
0
    const char *pszFontName = poSymbolStyle->FontName(bIsNull);
1585
0
    if ((!bIsNull) && pszFontName)
1586
0
    {
1587
0
        SetFontName(pszFontName);
1588
0
    }
1589
0
}
1590
1591
/*=====================================================================
1592
 *                      class TABCustomPoint
1593
 *====================================================================*/
1594
1595
/**********************************************************************
1596
 *                   TABCustomPoint::TABCustomPoint()
1597
 *
1598
 * Constructor.
1599
 **********************************************************************/
1600
TABCustomPoint::TABCustomPoint(const OGRFeatureDefn *poDefnIn)
1601
6.12k
    : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
1602
6.12k
{
1603
6.12k
}
1604
1605
/**********************************************************************
1606
 *                   TABCustomPoint::~TABCustomPoint()
1607
 *
1608
 * Destructor.
1609
 **********************************************************************/
1610
TABCustomPoint::~TABCustomPoint()
1611
6.12k
{
1612
6.12k
}
1613
1614
/**********************************************************************
1615
 *                     TABCustomPoint::CloneTABFeature()
1616
 *
1617
 * Duplicate feature, including stuff specific to each TABFeature type.
1618
 *
1619
 * This method calls the generic TABFeature::CloneTABFeature() and
1620
 * then copies any members specific to its own type.
1621
 **********************************************************************/
1622
TABFeature *
1623
TABCustomPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
1624
0
{
1625
    /*-----------------------------------------------------------------
1626
     * Alloc new feature and copy the base stuff
1627
     *----------------------------------------------------------------*/
1628
0
    TABCustomPoint *poNew =
1629
0
        new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
1630
1631
0
    CopyTABFeatureBase(poNew);
1632
1633
    /*-----------------------------------------------------------------
1634
     * And members specific to this class
1635
     *----------------------------------------------------------------*/
1636
    // ITABFeatureSymbol
1637
0
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1638
1639
    // ITABFeatureFont
1640
0
    *(poNew->GetFontDefRef()) = *GetFontDefRef();
1641
1642
0
    poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
1643
1644
0
    return poNew;
1645
0
}
1646
1647
/**********************************************************************
1648
 *                   TABCustomPoint::ReadGeometryFromMAPFile()
1649
 *
1650
 * Fill the geometry and representation (color, etc...) part of the
1651
 * feature from the contents of the .MAP object pointed to by poMAPFile.
1652
 *
1653
 * It is assumed that poMAPFile currently points to the beginning of
1654
 * a map object.
1655
 *
1656
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1657
 * been called.
1658
 **********************************************************************/
1659
int TABCustomPoint::ReadGeometryFromMAPFile(
1660
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1661
    GBool bCoordBlockDataOnly /*=FALSE*/,
1662
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1663
55
{
1664
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1665
55
    if (bCoordBlockDataOnly)
1666
0
        return 0;
1667
1668
    /*-----------------------------------------------------------------
1669
     * Fetch and validate geometry type
1670
     *----------------------------------------------------------------*/
1671
55
    m_nMapInfoType = poObjHdr->m_nType;
1672
1673
55
    if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
1674
0
        m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
1675
0
    {
1676
0
        CPLError(
1677
0
            CE_Failure, CPLE_AssertionFailed,
1678
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1679
0
            m_nMapInfoType, m_nMapInfoType);
1680
0
        return -1;
1681
0
    }
1682
1683
    /*-----------------------------------------------------------------
1684
     * Read object information
1685
     *----------------------------------------------------------------*/
1686
55
    TABMAPObjCustomPoint *poPointHdr =
1687
55
        cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1688
1689
55
    m_nUnknown_ = poPointHdr->m_nUnknown_;        // ???
1690
55
    m_nCustomStyle = poPointHdr->m_nCustomStyle;  // 0x01=Show BG,
1691
                                                  // 0x02=Apply Color
1692
1693
55
    m_nSymbolDefIndex = poPointHdr->m_nSymbolId;  // Symbol index
1694
55
    poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1695
1696
55
    m_nFontDefIndex = poPointHdr->m_nFontId;  // Font index
1697
55
    poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1698
1699
    /*-----------------------------------------------------------------
1700
     * Create and fill geometry object
1701
     *----------------------------------------------------------------*/
1702
55
    double dX = 0.0;
1703
55
    double dY = 0.0;
1704
55
    poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1705
55
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1706
1707
55
    SetGeometryDirectly(poGeometry);
1708
1709
55
    SetMBR(dX, dY, dX, dY);
1710
55
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1711
55
              poObjHdr->m_nMaxY);
1712
1713
55
    return 0;
1714
55
}
1715
1716
/**********************************************************************
1717
 *                   TABCustomPoint::WriteGeometryToMAPFile()
1718
 *
1719
 * Write the geometry and representation (color, etc...) part of the
1720
 * feature to the .MAP object pointed to by poMAPFile.
1721
 *
1722
 * It is assumed that poMAPFile currently points to a valid map object.
1723
 *
1724
 * Returns 0 on success, -1 on error, in which case CPLError() will have
1725
 * been called.
1726
 **********************************************************************/
1727
int TABCustomPoint::WriteGeometryToMAPFile(
1728
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1729
    GBool bCoordBlockDataOnly /*=FALSE*/,
1730
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1731
0
{
1732
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1733
0
    if (bCoordBlockDataOnly)
1734
0
        return 0;
1735
1736
    /*-----------------------------------------------------------------
1737
     * We assume that ValidateMapInfoType() was called already and that
1738
     * the type in poObjHdr->m_nType is valid.
1739
     *----------------------------------------------------------------*/
1740
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1741
1742
    /*-----------------------------------------------------------------
1743
     * Fetch and validate geometry
1744
     *----------------------------------------------------------------*/
1745
0
    OGRGeometry *poGeom = GetGeometryRef();
1746
0
    OGRPoint *poPoint = nullptr;
1747
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1748
0
        poPoint = poGeom->toPoint();
1749
0
    else
1750
0
    {
1751
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1752
0
                 "TABCustomPoint: Missing or Invalid Geometry!");
1753
0
        return -1;
1754
0
    }
1755
1756
0
    GInt32 nX = 0;
1757
0
    GInt32 nY = 0;
1758
0
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1759
1760
    /*-----------------------------------------------------------------
1761
     * Copy object information
1762
     *----------------------------------------------------------------*/
1763
0
    TABMAPObjCustomPoint *poPointHdr =
1764
0
        cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1765
1766
0
    poPointHdr->m_nX = nX;
1767
0
    poPointHdr->m_nY = nY;
1768
0
    poPointHdr->SetMBR(nX, nY, nX, nY);
1769
0
    poPointHdr->m_nUnknown_ = m_nUnknown_;
1770
0
    poPointHdr->m_nCustomStyle = m_nCustomStyle;  // 0x01=Show BG,
1771
                                                  // 0x02=Apply Color
1772
1773
0
    m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1774
0
    poPointHdr->m_nSymbolId =
1775
0
        static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
1776
1777
0
    m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1778
0
    poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex);  // Font index
1779
1780
0
    if (CPLGetLastErrorType() == CE_Failure)
1781
0
        return -1;
1782
1783
0
    return 0;
1784
0
}
1785
1786
/**********************************************************************
1787
 *                   TABCustomPoint::GetSymbolStyleString()
1788
 *
1789
 *  Return a Symbol() string. All representations info for the Symbol are here.
1790
 **********************************************************************/
1791
const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
1792
0
{
1793
    /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
1794
     * "apply color". */
1795
0
    const char *color = nullptr;
1796
0
    if (m_nCustomStyle & 0x02)
1797
0
        color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
1798
0
    else
1799
0
        color = "";
1800
1801
0
    int nAngle = static_cast<int>(dfAngle);
1802
0
    const char *pszStyle;
1803
0
    const std::string osExt = CPLGetExtensionSafe(GetSymbolNameRef());
1804
0
    char szLowerExt[8] = "";
1805
0
    const char *pszPtr = osExt.c_str();
1806
0
    int i;
1807
1808
0
    for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
1809
0
    {
1810
0
        szLowerExt[i] =
1811
0
            static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
1812
0
    }
1813
0
    szLowerExt[i] = '\0';
1814
1815
0
    pszStyle = CPLSPrintf(
1816
0
        "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
1817
0
        nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
1818
0
        GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
1819
0
    return pszStyle;
1820
0
}
1821
1822
/**********************************************************************
1823
 *                   TABCustomPoint::SetSymbolFromStyle()
1824
 *
1825
 *  Set all Symbol var from a OGRStyleSymbol.
1826
 **********************************************************************/
1827
void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1828
0
{
1829
0
    ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1830
1831
0
    GBool bIsNull = 0;
1832
1833
    // Try to set font glyph number
1834
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1835
0
    if ((!bIsNull) && pszSymbolId &&
1836
0
        STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
1837
0
    {
1838
0
        const int nSymbolStyle = atoi(pszSymbolId + 19);
1839
0
        SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
1840
1841
0
        const char *pszPtr = pszSymbolId + 19;
1842
0
        while (*pszPtr != '-')
1843
0
        {
1844
0
            pszPtr++;
1845
0
        }
1846
0
        pszPtr++;
1847
1848
0
        char szSymbolName[256] = "";
1849
0
        int i;
1850
0
        for (i = 0;
1851
0
             i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
1852
0
             i++, pszPtr++)
1853
0
        {
1854
0
            szSymbolName[i] = *pszPtr;
1855
0
        }
1856
0
        szSymbolName[i] = '\0';
1857
0
        SetSymbolName(szSymbolName);
1858
0
    }
1859
0
}
1860
1861
/**********************************************************************
1862
 *                   TABCustomPoint::GetStyleString() const
1863
 *
1864
 * Return style string for this feature.
1865
 *
1866
 * Style String is built only once during the first call to GetStyleString().
1867
 **********************************************************************/
1868
const char *TABCustomPoint::GetStyleString() const
1869
0
{
1870
0
    if (m_pszStyleString == nullptr)
1871
0
    {
1872
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1873
0
    }
1874
1875
0
    return m_pszStyleString;
1876
0
}
1877
1878
/*=====================================================================
1879
 *                      class TABPolyline
1880
 *====================================================================*/
1881
1882
/**********************************************************************
1883
 *                   TABPolyline::TABPolyline()
1884
 *
1885
 * Constructor.
1886
 **********************************************************************/
1887
TABPolyline::TABPolyline(const OGRFeatureDefn *poDefnIn)
1888
48.9k
    : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
1889
48.9k
      m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
1890
48.9k
{
1891
48.9k
}
1892
1893
/**********************************************************************
1894
 *                   TABPolyline::~TABPolyline()
1895
 *
1896
 * Destructor.
1897
 **********************************************************************/
1898
TABPolyline::~TABPolyline()
1899
48.9k
{
1900
48.9k
}
1901
1902
/**********************************************************************
1903
 *                     TABPolyline::CloneTABFeature()
1904
 *
1905
 * Duplicate feature, including stuff specific to each TABFeature type.
1906
 *
1907
 * This method calls the generic TABFeature::CloneTABFeature() and
1908
 * then copies any members specific to its own type.
1909
 **********************************************************************/
1910
TABFeature *
1911
TABPolyline::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
1912
0
{
1913
    /*-----------------------------------------------------------------
1914
     * Alloc new feature and copy the base stuff
1915
     *----------------------------------------------------------------*/
1916
0
    TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
1917
1918
0
    CopyTABFeatureBase(poNew);
1919
1920
    /*-----------------------------------------------------------------
1921
     * And members specific to this class
1922
     *----------------------------------------------------------------*/
1923
    // ITABFeaturePen
1924
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
1925
1926
0
    poNew->m_bSmooth = m_bSmooth;
1927
0
    poNew->m_bCenterIsSet = m_bCenterIsSet;
1928
0
    poNew->m_dCenterX = m_dCenterX;
1929
0
    poNew->m_dCenterY = m_dCenterY;
1930
1931
0
    return poNew;
1932
0
}
1933
1934
/**********************************************************************
1935
 *                   TABPolyline::GetNumParts()
1936
 *
1937
 * Return the total number of parts in this object.
1938
 *
1939
 * Returns 0 if the geometry contained in the object is invalid or missing.
1940
 **********************************************************************/
1941
int TABPolyline::GetNumParts()
1942
0
{
1943
0
    int numParts = 0;
1944
1945
0
    OGRGeometry *poGeom = GetGeometryRef();
1946
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1947
0
    {
1948
        /*-------------------------------------------------------------
1949
         * Simple polyline
1950
         *------------------------------------------------------------*/
1951
0
        numParts = 1;
1952
0
    }
1953
0
    else if (poGeom &&
1954
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1955
0
    {
1956
        /*-------------------------------------------------------------
1957
         * Multiple polyline
1958
         *------------------------------------------------------------*/
1959
0
        OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1960
0
        numParts = poMultiLine->getNumGeometries();
1961
0
    }
1962
1963
0
    return numParts;
1964
0
}
1965
1966
/**********************************************************************
1967
 *                   TABPolyline::GetPartRef()
1968
 *
1969
 * Returns a reference to the specified OGRLineString number, hiding the
1970
 * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
1971
 *
1972
 * Returns NULL if the geometry contained in the object is invalid or
1973
 * missing or if the specified part index is invalid.
1974
 **********************************************************************/
1975
OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
1976
0
{
1977
0
    OGRGeometry *poGeom = GetGeometryRef();
1978
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1979
0
        nPartIndex == 0)
1980
0
    {
1981
        /*-------------------------------------------------------------
1982
         * Simple polyline
1983
         *------------------------------------------------------------*/
1984
0
        return poGeom->toLineString();
1985
0
    }
1986
0
    else if (poGeom &&
1987
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1988
0
    {
1989
        /*-------------------------------------------------------------
1990
         * Multiple polyline
1991
         *------------------------------------------------------------*/
1992
0
        OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1993
0
        if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
1994
0
        {
1995
0
            return poMultiLine->getGeometryRef(nPartIndex);
1996
0
        }
1997
0
        else
1998
0
            return nullptr;
1999
0
    }
2000
2001
0
    return nullptr;
2002
0
}
2003
2004
/**********************************************************************
2005
 *                   TABPolyline::ValidateMapInfoType()
2006
 *
2007
 * Check the feature's geometry part and return the corresponding
2008
 * mapinfo object type code.  The m_nMapInfoType member will also
2009
 * be updated for further calls to GetMapInfoType();
2010
 *
2011
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
2012
 * is expected for this object class.
2013
 **********************************************************************/
2014
TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
2015
4.99k
{
2016
    /*-----------------------------------------------------------------
2017
     * Fetch and validate geometry
2018
     *----------------------------------------------------------------*/
2019
4.99k
    OGRGeometry *poGeom = GetGeometryRef();
2020
4.99k
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2021
3.20k
    {
2022
        /*-------------------------------------------------------------
2023
         * Simple polyline
2024
         *------------------------------------------------------------*/
2025
3.20k
        OGRLineString *poLine = poGeom->toLineString();
2026
3.20k
        if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
2027
0
        {
2028
0
            m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2029
0
        }
2030
3.20k
        else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
2031
0
        {
2032
0
            m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2033
0
        }
2034
3.20k
        else if (poLine->getNumPoints() > 2)
2035
827
        {
2036
827
            m_nMapInfoType = TAB_GEOM_PLINE;
2037
827
        }
2038
2.37k
        else if ((poLine->getNumPoints() == 2) &&
2039
110
                 (m_bWriteTwoPointLineAsPolyline == TRUE))
2040
0
        {
2041
0
            m_nMapInfoType = TAB_GEOM_PLINE;
2042
0
        }
2043
2.37k
        else if ((poLine->getNumPoints() == 2) &&
2044
110
                 (m_bWriteTwoPointLineAsPolyline == FALSE))
2045
110
        {
2046
110
            m_nMapInfoType = TAB_GEOM_LINE;
2047
110
        }
2048
2.26k
        else
2049
2.26k
        {
2050
2.26k
            CPLError(CE_Failure, CPLE_AssertionFailed,
2051
2.26k
                     "TABPolyline: Geometry must contain at least 2 points.");
2052
2.26k
            m_nMapInfoType = TAB_GEOM_NONE;
2053
2.26k
        }
2054
3.20k
    }
2055
1.78k
    else if (poGeom &&
2056
1.78k
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2057
1.78k
    {
2058
        /*-------------------------------------------------------------
2059
         * Multiple polyline... validate all components
2060
         *------------------------------------------------------------*/
2061
1.78k
        GInt32 numPointsTotal = 0;
2062
1.78k
        OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2063
1.78k
        int numLines = poMultiLine->getNumGeometries();
2064
2065
1.78k
        m_nMapInfoType = TAB_GEOM_MULTIPLINE;
2066
2067
1.82k
        for (int iLine = 0; iLine < numLines; iLine++)
2068
36
        {
2069
36
            poGeom = poMultiLine->getGeometryRef(iLine);
2070
36
            if (poGeom == nullptr ||
2071
36
                wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2072
0
            {
2073
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2074
0
                         "TABPolyline: Object contains an invalid Geometry!");
2075
0
                m_nMapInfoType = TAB_GEOM_NONE;
2076
0
                numPointsTotal = 0;
2077
0
                break;
2078
0
            }
2079
36
            OGRLineString *poLine = poGeom->toLineString();
2080
36
            numPointsTotal += poLine->getNumPoints();
2081
36
        }
2082
2083
1.78k
        if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
2084
0
            m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2085
1.78k
        else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
2086
0
            m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2087
1.78k
    }
2088
0
    else
2089
0
    {
2090
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
2091
0
                 "TABPolyline: Missing or Invalid Geometry!");
2092
0
        m_nMapInfoType = TAB_GEOM_NONE;
2093
0
    }
2094
2095
    /*-----------------------------------------------------------------
2096
     * Decide if coordinates should be compressed or not.
2097
     *
2098
     * __TODO__ We never write type LINE (2 points line) as compressed
2099
     * for the moment.  If we ever do it, then the decision to write
2100
     * a 2 point line in compressed coordinates or not should take into
2101
     * account the location of the object block MBR, so this would be
2102
     * better handled directly by TABMAPObjLine::WriteObject() since the
2103
     * object block center is not known until it is written to disk.
2104
     *----------------------------------------------------------------*/
2105
4.99k
    if (m_nMapInfoType != TAB_GEOM_LINE)
2106
4.88k
    {
2107
4.88k
        ValidateCoordType(poMapFile);
2108
4.88k
    }
2109
110
    else
2110
110
    {
2111
110
        UpdateMBR(poMapFile);
2112
110
    }
2113
2114
4.99k
    return m_nMapInfoType;
2115
4.99k
}
2116
2117
/**********************************************************************
2118
 *                   TABPolyline::ReadGeometryFromMAPFile()
2119
 *
2120
 * Fill the geometry and representation (color, etc...) part of the
2121
 * feature from the contents of the .MAP object pointed to by poMAPFile.
2122
 *
2123
 * It is assumed that poMAPFile currently points to the beginning of
2124
 * a map object.
2125
 *
2126
 * Returns 0 on success, -1 on error, in which case CPLError() will have
2127
 * been called.
2128
 **********************************************************************/
2129
int TABPolyline::ReadGeometryFromMAPFile(
2130
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2131
    GBool bCoordBlockDataOnly /*=FALSE*/,
2132
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2133
187
{
2134
187
    GInt32 nX = 0;
2135
187
    GInt32 nY = 0;
2136
187
    double dX = 0.0;
2137
187
    double dY = 0.0;
2138
187
    double dXMin = 0.0;
2139
187
    double dYMin = 0.0;
2140
187
    double dXMax = 0.0;
2141
187
    double dYMax = 0.0;
2142
187
    OGRGeometry *poGeometry = nullptr;
2143
187
    GBool bComprCoord = poObjHdr->IsCompressedType();
2144
187
    TABMAPCoordBlock *poCoordBlock = nullptr;
2145
2146
    /*-----------------------------------------------------------------
2147
     * Fetch and validate geometry type
2148
     *----------------------------------------------------------------*/
2149
187
    m_nMapInfoType = poObjHdr->m_nType;
2150
2151
187
    if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
2152
145
    {
2153
        /*=============================================================
2154
         * LINE (2 vertices)
2155
         *============================================================*/
2156
145
        TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2157
2158
145
        m_bSmooth = FALSE;
2159
2160
145
        auto poLine = new OGRLineString();
2161
145
        poGeometry = poLine;
2162
145
        poLine->setNumPoints(2);
2163
2164
145
        poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
2165
145
                                dYMin);
2166
145
        poLine->setPoint(0, dXMin, dYMin);
2167
2168
145
        poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
2169
145
                                dYMax);
2170
145
        poLine->setPoint(1, dXMax, dYMax);
2171
2172
145
        if (!bCoordBlockDataOnly)
2173
145
        {
2174
145
            m_nPenDefIndex = poLineHdr->m_nPenId;  // Pen index
2175
145
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2176
145
        }
2177
145
    }
2178
42
    else if (m_nMapInfoType == TAB_GEOM_PLINE ||
2179
42
             m_nMapInfoType == TAB_GEOM_PLINE_C)
2180
1
    {
2181
        /*=============================================================
2182
         * PLINE ( > 2 vertices)
2183
         *============================================================*/
2184
2185
        /*-------------------------------------------------------------
2186
         * Copy data from poObjHdr
2187
         *------------------------------------------------------------*/
2188
1
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2189
2190
1
        GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2191
1
        const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
2192
1
        if (nCoordDataSize > 1024 * 1024 &&
2193
0
            nCoordDataSize > poMapFile->GetFileSize())
2194
0
        {
2195
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
2196
0
                     nCoordDataSize);
2197
0
            return -1;
2198
0
        }
2199
        // numLineSections = poPLineHdr->m_numLineSections; // Always 1
2200
1
        m_bSmooth = poPLineHdr->m_bSmooth;
2201
2202
        // Centroid/label point
2203
1
        poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2204
1
                                dX, dY);
2205
1
        SetCenter(dX, dY);
2206
2207
        // Compressed coordinate origin (useful only in compressed case!)
2208
1
        m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2209
1
        m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2210
2211
        // MBR
2212
1
        poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2213
1
                                dYMin);
2214
1
        poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2215
1
                                dYMax);
2216
2217
1
        if (!bCoordBlockDataOnly)
2218
1
        {
2219
1
            m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
2220
1
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2221
1
        }
2222
2223
        /*-------------------------------------------------------------
2224
         * Create Geometry and read coordinates
2225
         *------------------------------------------------------------*/
2226
1
        const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
2227
2228
1
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2229
0
            poCoordBlock = *ppoCoordBlock;
2230
1
        else
2231
1
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2232
1
        if (poCoordBlock == nullptr)
2233
1
        {
2234
1
            CPLError(CE_Failure, CPLE_FileIO,
2235
1
                     "Can't access coordinate block at offset %d",
2236
1
                     nCoordBlockPtr);
2237
1
            return -1;
2238
1
        }
2239
2240
0
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2241
2242
0
        auto poLine = new OGRLineString();
2243
0
        poGeometry = poLine;
2244
0
        poLine->setNumPoints(numPoints);
2245
2246
0
        int nStatus = 0;
2247
0
        for (int i = 0; nStatus == 0 && i < numPoints; i++)
2248
0
        {
2249
0
            nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
2250
0
            if (nStatus != 0)
2251
0
                break;
2252
0
            poMapFile->Int2Coordsys(nX, nY, dX, dY);
2253
0
            poLine->setPoint(i, dX, dY);
2254
0
        }
2255
2256
0
        if (nStatus != 0)
2257
0
        {
2258
            // Failed ... error message has already been produced
2259
0
            delete poGeometry;
2260
0
            return nStatus;
2261
0
        }
2262
0
    }
2263
41
    else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2264
41
             m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2265
6
             m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2266
6
             m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2267
0
             m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2268
0
             m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
2269
41
    {
2270
        /*=============================================================
2271
         * PLINE MULTIPLE
2272
         *============================================================*/
2273
41
        const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2274
2275
        /*-------------------------------------------------------------
2276
         * Copy data from poObjHdr
2277
         *------------------------------------------------------------*/
2278
41
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2279
2280
41
        GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2281
        /* GInt32 nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
2282
41
        GInt32 numLineSections = poPLineHdr->m_numLineSections;
2283
41
        m_bSmooth = poPLineHdr->m_bSmooth;
2284
2285
        // Centroid/label point
2286
41
        poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2287
41
                                dX, dY);
2288
41
        SetCenter(dX, dY);
2289
2290
        // Compressed coordinate origin (useful only in compressed case!)
2291
41
        m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2292
41
        m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2293
2294
        // MBR
2295
41
        poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2296
41
                                dYMin);
2297
41
        poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2298
41
                                dYMax);
2299
2300
41
        if (!bCoordBlockDataOnly)
2301
41
        {
2302
41
            m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
2303
41
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2304
41
        }
2305
2306
41
        const int nMinSizeOfSection = 24;
2307
41
        if (numLineSections > INT_MAX / nMinSizeOfSection)
2308
0
        {
2309
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2310
0
            return -1;
2311
0
        }
2312
41
        const GUInt32 nMinimumBytesForSections =
2313
41
            nMinSizeOfSection * numLineSections;
2314
41
        if (nMinimumBytesForSections > 1024 * 1024 &&
2315
0
            nMinimumBytesForSections > poMapFile->GetFileSize())
2316
0
        {
2317
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2318
0
            return -1;
2319
0
        }
2320
2321
        /*-------------------------------------------------------------
2322
         * Read data from the coord. block
2323
         *------------------------------------------------------------*/
2324
41
        TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2325
41
            VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
2326
41
        if (pasSecHdrs == nullptr)
2327
0
            return -1;
2328
2329
41
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2330
6
            poCoordBlock = *ppoCoordBlock;
2331
35
        else
2332
35
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2333
2334
41
        GInt32 numPointsTotal = 0;
2335
41
        if (poCoordBlock == nullptr ||
2336
38
            poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
2337
38
                                           numLineSections, pasSecHdrs,
2338
38
                                           numPointsTotal) != 0)
2339
4
        {
2340
4
            CPLError(CE_Failure, CPLE_FileIO,
2341
4
                     "Failed reading coordinate data at offset %d",
2342
4
                     nCoordBlockPtr);
2343
4
            CPLFree(pasSecHdrs);
2344
4
            return -1;
2345
4
        }
2346
2347
37
        const GUInt32 nMinimumBytesForPoints =
2348
37
            (bComprCoord ? 4 : 8) * numPointsTotal;
2349
37
        if (nMinimumBytesForPoints > 1024 * 1024 &&
2350
0
            nMinimumBytesForPoints > poMapFile->GetFileSize())
2351
0
        {
2352
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
2353
0
            CPLFree(pasSecHdrs);
2354
0
            return -1;
2355
0
        }
2356
2357
37
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2358
2359
37
        GInt32 *panXY = static_cast<GInt32 *>(
2360
37
            VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
2361
37
        if (panXY == nullptr)
2362
1
        {
2363
1
            CPLFree(pasSecHdrs);
2364
1
            return -1;
2365
1
        }
2366
2367
36
        if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
2368
36
            0)
2369
1
        {
2370
1
            CPLError(CE_Failure, CPLE_FileIO,
2371
1
                     "Failed reading coordinate data at offset %d",
2372
1
                     nCoordBlockPtr);
2373
1
            CPLFree(pasSecHdrs);
2374
1
            CPLFree(panXY);
2375
1
            return -1;
2376
1
        }
2377
2378
        /*-------------------------------------------------------------
2379
         * Create a Geometry collection with one line geometry for
2380
         * each coordinates section
2381
         * If object contains only one section, then return a simple LineString
2382
         *------------------------------------------------------------*/
2383
35
        OGRMultiLineString *poMultiLine = nullptr;
2384
35
        if (numLineSections > 1)
2385
30
        {
2386
30
            poMultiLine = new OGRMultiLineString();
2387
30
            poGeometry = poMultiLine;
2388
30
        }
2389
2390
100
        for (int iSection = 0; iSection < numLineSections; iSection++)
2391
65
        {
2392
65
            const int numSectionVertices = pasSecHdrs[iSection].numVertices;
2393
65
            GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
2394
2395
65
            auto poLine = new OGRLineString();
2396
65
            poLine->setNumPoints(numSectionVertices);
2397
2398
193
            for (int i = 0; i < numSectionVertices; i++)
2399
128
            {
2400
128
                poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
2401
128
                poLine->setPoint(i, dX, dY);
2402
128
                pnXYPtr += 2;
2403
128
            }
2404
2405
65
            if (poGeometry == nullptr)
2406
5
                poGeometry = poLine;
2407
60
            else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
2408
0
            {
2409
0
                CPLAssert(false);  // Just in case lower-level lib is modified
2410
0
            }
2411
65
        }
2412
2413
35
        CPLFree(pasSecHdrs);
2414
35
        CPLFree(panXY);
2415
35
    }
2416
0
    else
2417
0
    {
2418
0
        CPLError(
2419
0
            CE_Failure, CPLE_AssertionFailed,
2420
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
2421
0
            m_nMapInfoType, m_nMapInfoType);
2422
0
        return -1;
2423
0
    }
2424
2425
180
    SetGeometryDirectly(poGeometry);
2426
2427
180
    SetMBR(dXMin, dYMin, dXMax, dYMax);
2428
180
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
2429
180
              poObjHdr->m_nMaxY);
2430
2431
    /* Return a ref to coord block so that caller can continue reading
2432
     * after the end of this object (used by TABCollection and index splitting)
2433
     */
2434
180
    if (ppoCoordBlock)
2435
5
        *ppoCoordBlock = poCoordBlock;
2436
2437
180
    return 0;
2438
187
}
2439
2440
/**********************************************************************
2441
 *                   TABPolyline::WriteGeometryToMAPFile()
2442
 *
2443
 * Write the geometry and representation (color, etc...) part of the
2444
 * feature to the .MAP object pointed to by poMAPFile.
2445
 *
2446
 * It is assumed that poMAPFile currently points to a valid map object.
2447
 *
2448
 * Returns 0 on success, -1 on error, in which case CPLError() will have
2449
 * been called.
2450
 **********************************************************************/
2451
int TABPolyline::WriteGeometryToMAPFile(
2452
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2453
    GBool bCoordBlockDataOnly /*=FALSE*/,
2454
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2455
2.72k
{
2456
2.72k
    GInt32 nX = 0;
2457
2.72k
    GInt32 nY = 0;
2458
2.72k
    OGRLineString *poLine = nullptr;
2459
2.72k
    TABMAPCoordBlock *poCoordBlock = nullptr;
2460
2461
    /*-----------------------------------------------------------------
2462
     * We assume that ValidateMapInfoType() was called already and that
2463
     * the type in poObjHdr->m_nType is valid.
2464
     *----------------------------------------------------------------*/
2465
2.72k
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
2466
2.72k
    CPLErrorReset();
2467
2468
    /*-----------------------------------------------------------------
2469
     * Fetch and validate geometry
2470
     *----------------------------------------------------------------*/
2471
2.72k
    OGRGeometry *poGeom = GetGeometryRef();
2472
2473
2.72k
    if ((m_nMapInfoType == TAB_GEOM_LINE ||
2474
2.61k
         m_nMapInfoType == TAB_GEOM_LINE_C) &&
2475
110
        poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
2476
110
        (poLine = poGeom->toLineString())->getNumPoints() == 2)
2477
110
    {
2478
        /*=============================================================
2479
         * LINE (2 vertices)
2480
         *============================================================*/
2481
110
        TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2482
2483
110
        poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
2484
110
                                poLineHdr->m_nX1, poLineHdr->m_nY1);
2485
110
        poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
2486
110
                                poLineHdr->m_nX2, poLineHdr->m_nY2);
2487
110
        poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
2488
110
                          poLineHdr->m_nY2);
2489
2490
110
        if (!bCoordBlockDataOnly)
2491
110
        {
2492
110
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2493
110
            poLineHdr->m_nPenId =
2494
110
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
2495
110
        }
2496
110
    }
2497
2.61k
    else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
2498
2.06k
              m_nMapInfoType == TAB_GEOM_PLINE_C) &&
2499
827
             poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2500
827
    {
2501
        /*=============================================================
2502
         * PLINE ( > 2 vertices and less than 32767 vertices)
2503
         *============================================================*/
2504
827
        GBool bCompressed = poObjHdr->IsCompressedType();
2505
2506
        /*-------------------------------------------------------------
2507
         * Process geometry first...
2508
         *------------------------------------------------------------*/
2509
827
        poLine = poGeom->toLineString();
2510
827
        const int numPoints = poLine->getNumPoints();
2511
827
        CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
2512
2513
827
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2514
0
            poCoordBlock = *ppoCoordBlock;
2515
827
        else
2516
827
            poCoordBlock = poMapFile->GetCurCoordBlock();
2517
827
        poCoordBlock->StartNewFeature();
2518
827
        const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2519
827
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2520
2521
827
        int nStatus = 0;
2522
10.0k
        for (int i = 0; nStatus == 0 && i < numPoints; i++)
2523
9.18k
        {
2524
9.18k
            poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
2525
9.18k
            if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
2526
9.18k
                0)
2527
0
            {
2528
                // Failed ... error message has already been produced
2529
0
                return nStatus;
2530
0
            }
2531
9.18k
        }
2532
2533
827
        const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2534
2535
        /*-------------------------------------------------------------
2536
         * Copy info to poObjHdr
2537
         *------------------------------------------------------------*/
2538
827
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2539
2540
827
        poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2541
827
        poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2542
827
        poPLineHdr->m_numLineSections = 1;
2543
2544
827
        poPLineHdr->m_bSmooth = m_bSmooth;
2545
2546
        // MBR
2547
827
        poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2548
2549
        // Polyline center/label point
2550
827
        double dX = 0.0;
2551
827
        double dY = 0.0;
2552
827
        if (GetCenter(dX, dY) != -1)
2553
827
        {
2554
827
            poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2555
827
                                    poPLineHdr->m_nLabelY);
2556
827
        }
2557
0
        else
2558
0
        {
2559
0
            poPLineHdr->m_nLabelX = m_nComprOrgX;
2560
0
            poPLineHdr->m_nLabelY = m_nComprOrgY;
2561
0
        }
2562
2563
        // Compressed coordinate origin (useful only in compressed case!)
2564
827
        poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2565
827
        poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2566
2567
827
        if (!bCoordBlockDataOnly)
2568
827
        {
2569
827
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2570
827
            poPLineHdr->m_nPenId =
2571
827
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
2572
827
        }
2573
827
    }
2574
1.78k
    else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2575
1.75k
              m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2576
0
              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2577
0
              m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2578
0
              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2579
0
              m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
2580
1.78k
             poGeom &&
2581
1.78k
             (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
2582
0
              wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
2583
1.78k
    {
2584
        /*=============================================================
2585
         * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
2586
         *============================================================*/
2587
2588
1.78k
        CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2589
1.78k
                  m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2590
1.78k
                  m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2591
1.78k
                  m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2592
1.78k
                  m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2593
1.78k
                  m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
2594
2595
1.78k
        int nStatus = 0;
2596
1.78k
        OGREnvelope sEnvelope;
2597
1.78k
        GBool bCompressed = poObjHdr->IsCompressedType();
2598
2599
        /*-------------------------------------------------------------
2600
         * Process geometry first...
2601
         *------------------------------------------------------------*/
2602
1.78k
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2603
0
            poCoordBlock = *ppoCoordBlock;
2604
1.78k
        else
2605
1.78k
            poCoordBlock = poMapFile->GetCurCoordBlock();
2606
1.78k
        poCoordBlock->StartNewFeature();
2607
1.78k
        const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2608
1.78k
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2609
2610
1.78k
        OGRMultiLineString *poMultiLine = nullptr;
2611
1.78k
        GInt32 numLines = 1;
2612
1.78k
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2613
1.78k
        {
2614
1.78k
            poMultiLine = poGeom->toMultiLineString();
2615
1.78k
            numLines = poMultiLine->getNumGeometries();
2616
1.78k
        }
2617
        // else
2618
        // {
2619
        //     poMultiLine = NULL;
2620
        //     numLines = 1;
2621
        // }
2622
2623
        /*-------------------------------------------------------------
2624
         * Build and write array of coord sections headers
2625
         *------------------------------------------------------------*/
2626
1.78k
        TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2627
1.78k
            VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
2628
1.78k
        if (pasSecHdrs == nullptr)
2629
0
        {
2630
0
            return -1;
2631
0
        }
2632
2633
        /*-------------------------------------------------------------
2634
         * In calculation of nDataOffset, we have to take into account that
2635
         * V450 header section uses int32 instead of int16 for numVertices
2636
         * and we add another 2 bytes to align with a 4 bytes boundary.
2637
         *------------------------------------------------------------*/
2638
1.78k
        int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2639
2640
1.78k
        const int nTotalHdrSizeUncompressed =
2641
1.78k
            (nVersion >= 450 ? 28 : 24) * numLines;
2642
2643
1.78k
        GInt32 numPointsTotal = 0;
2644
1.82k
        for (int iLine = 0; iLine < numLines; iLine++)
2645
36
        {
2646
36
            if (poMultiLine)
2647
36
                poGeom = poMultiLine->getGeometryRef(iLine);
2648
2649
36
            if (poGeom &&
2650
36
                wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2651
36
            {
2652
36
                poLine = poGeom->toLineString();
2653
36
                const GInt32 numPoints = poLine->getNumPoints();
2654
36
                poLine->getEnvelope(&sEnvelope);
2655
2656
36
                pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
2657
36
                pasSecHdrs[iLine].numHoles = 0;  // It is a line!
2658
2659
36
                poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
2660
36
                                        pasSecHdrs[iLine].nXMin,
2661
36
                                        pasSecHdrs[iLine].nYMin);
2662
36
                poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
2663
36
                                        pasSecHdrs[iLine].nXMax,
2664
36
                                        pasSecHdrs[iLine].nYMax);
2665
36
                pasSecHdrs[iLine].nDataOffset =
2666
36
                    nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
2667
36
                pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
2668
2669
36
                numPointsTotal += numPoints;
2670
36
            }
2671
0
            else
2672
0
            {
2673
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2674
0
                         "TABPolyline: Object contains an invalid Geometry!");
2675
0
                nStatus = -1;
2676
0
            }
2677
36
        }
2678
2679
1.78k
        if (nStatus == 0)
2680
1.78k
            nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
2681
1.78k
                                                      pasSecHdrs, bCompressed);
2682
2683
1.78k
        CPLFree(pasSecHdrs);
2684
1.78k
        pasSecHdrs = nullptr;
2685
2686
1.78k
        if (nStatus != 0)
2687
0
            return nStatus;  // Error has already been reported.
2688
2689
        /*-------------------------------------------------------------
2690
         * Then write the coordinates themselves...
2691
         *------------------------------------------------------------*/
2692
1.82k
        for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
2693
36
        {
2694
36
            if (poMultiLine)
2695
36
                poGeom = poMultiLine->getGeometryRef(iLine);
2696
2697
36
            if (poGeom &&
2698
36
                wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2699
36
            {
2700
36
                poLine = poGeom->toLineString();
2701
36
                GInt32 numPoints = poLine->getNumPoints();
2702
2703
217
                for (int i = 0; nStatus == 0 && i < numPoints; i++)
2704
181
                {
2705
181
                    poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
2706
181
                                            nX, nY);
2707
181
                    if ((nStatus = poCoordBlock->WriteIntCoord(
2708
181
                             nX, nY, bCompressed)) != 0)
2709
0
                    {
2710
                        // Failed ... error message has already been produced
2711
0
                        return nStatus;
2712
0
                    }
2713
181
                }
2714
36
            }
2715
0
            else
2716
0
            {
2717
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2718
0
                         "TABPolyline: Object contains an invalid Geometry!");
2719
0
                return -1;
2720
0
            }
2721
36
        }
2722
2723
1.78k
        const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2724
2725
        /*-------------------------------------------------------------
2726
         * ... and finally copy info to poObjHdr
2727
         *------------------------------------------------------------*/
2728
1.78k
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2729
2730
1.78k
        poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2731
1.78k
        poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2732
1.78k
        poPLineHdr->m_numLineSections = numLines;
2733
2734
1.78k
        poPLineHdr->m_bSmooth = m_bSmooth;
2735
2736
        // MBR
2737
1.78k
        poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2738
2739
        // Polyline center/label point
2740
1.78k
        double dX = 0.0;
2741
1.78k
        double dY = 0.0;
2742
1.78k
        if (GetCenter(dX, dY) != -1)
2743
29
        {
2744
29
            poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2745
29
                                    poPLineHdr->m_nLabelY);
2746
29
        }
2747
1.75k
        else
2748
1.75k
        {
2749
1.75k
            poPLineHdr->m_nLabelX = m_nComprOrgX;
2750
1.75k
            poPLineHdr->m_nLabelY = m_nComprOrgY;
2751
1.75k
        }
2752
2753
        // Compressed coordinate origin (useful only in compressed case!)
2754
1.78k
        poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2755
1.78k
        poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2756
2757
1.78k
        if (!bCoordBlockDataOnly)
2758
1.78k
        {
2759
1.78k
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2760
1.78k
            poPLineHdr->m_nPenId =
2761
1.78k
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
2762
1.78k
        }
2763
1.78k
    }
2764
0
    else
2765
0
    {
2766
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
2767
0
                 "TABPolyline: Object contains an invalid Geometry!");
2768
0
        return -1;
2769
0
    }
2770
2771
2.72k
    if (CPLGetLastErrorType() == CE_Failure)
2772
0
        return -1;
2773
2774
    /* Return a ref to coord block so that caller can continue writing
2775
     * after the end of this object (used by index splitting)
2776
     */
2777
2.72k
    if (ppoCoordBlock)
2778
0
        *ppoCoordBlock = poCoordBlock;
2779
2780
2.72k
    return 0;
2781
2.72k
}
2782
2783
/**********************************************************************
2784
 *                   TABPolyline::GetStyleString() const
2785
 *
2786
 * Return style string for this feature.
2787
 *
2788
 * Style String is built only once during the first call to GetStyleString().
2789
 **********************************************************************/
2790
const char *TABPolyline::GetStyleString() const
2791
0
{
2792
0
    if (m_pszStyleString == nullptr)
2793
0
    {
2794
0
        m_pszStyleString = CPLStrdup(GetPenStyleString());
2795
0
    }
2796
2797
0
    return m_pszStyleString;
2798
0
}
2799
2800
/**********************************************************************
2801
 *                   TABPolyline::DumpMIF()
2802
 *
2803
 * Dump feature geometry in a format similar to .MIF PLINEs.
2804
 **********************************************************************/
2805
void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
2806
0
{
2807
0
    OGRMultiLineString *poMultiLine = nullptr;
2808
0
    OGRLineString *poLine = nullptr;
2809
0
    int i, numPoints;
2810
2811
0
    if (fpOut == nullptr)
2812
0
        fpOut = stdout;
2813
2814
    /*-----------------------------------------------------------------
2815
     * Fetch and validate geometry
2816
     *----------------------------------------------------------------*/
2817
0
    OGRGeometry *poGeom = GetGeometryRef();
2818
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2819
0
    {
2820
        /*-------------------------------------------------------------
2821
         * Generate output for simple polyline
2822
         *------------------------------------------------------------*/
2823
0
        poLine = poGeom->toLineString();
2824
0
        numPoints = poLine->getNumPoints();
2825
0
        fprintf(fpOut, "PLINE %d\n", numPoints);
2826
0
        for (i = 0; i < numPoints; i++)
2827
0
            fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
2828
0
    }
2829
0
    else if (poGeom &&
2830
0
             wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2831
0
    {
2832
        /*-------------------------------------------------------------
2833
         * Generate output for multiple polyline
2834
         *------------------------------------------------------------*/
2835
0
        int iLine, numLines;
2836
0
        poMultiLine = poGeom->toMultiLineString();
2837
0
        numLines = poMultiLine->getNumGeometries();
2838
0
        fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
2839
0
        for (iLine = 0; iLine < numLines; iLine++)
2840
0
        {
2841
0
            poGeom = poMultiLine->getGeometryRef(iLine);
2842
0
            if (poGeom &&
2843
0
                wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2844
0
            {
2845
0
                poLine = poGeom->toLineString();
2846
0
                numPoints = poLine->getNumPoints();
2847
0
                fprintf(fpOut, " %d\n", numPoints);
2848
0
                for (i = 0; i < numPoints; i++)
2849
0
                    fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
2850
0
                            poLine->getY(i));
2851
0
            }
2852
0
            else
2853
0
            {
2854
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
2855
0
                         "TABPolyline: Object contains an invalid Geometry!");
2856
0
                return;
2857
0
            }
2858
0
        }
2859
0
    }
2860
0
    else
2861
0
    {
2862
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
2863
0
                 "TABPolyline: Missing or Invalid Geometry!");
2864
0
        return;
2865
0
    }
2866
2867
0
    if (m_bCenterIsSet)
2868
0
        fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
2869
2870
    // Finish with PEN/BRUSH/etc. clauses
2871
0
    DumpPenDef();
2872
2873
0
    fflush(fpOut);
2874
0
}
2875
2876
/**********************************************************************
2877
 *                   TABPolyline::GetCenter()
2878
 *
2879
 * Returns the center point of the line.  Compute one if it was not
2880
 * explicitly set:
2881
 *
2882
 * In MapInfo, for a simple or multiple polyline (pline), the center point
2883
 * in the object definition is supposed to be either the center point of
2884
 * the pline or the first section of a multiple pline (if an odd number of
2885
 * points in the pline or first section), or the midway point between the
2886
 * two central points (if an even number of points involved).
2887
 *
2888
 * Returns 0 on success, -1 on error.
2889
 **********************************************************************/
2890
int TABPolyline::GetCenter(double &dX, double &dY)
2891
2.61k
{
2892
2.61k
    if (!m_bCenterIsSet)
2893
2.61k
    {
2894
2.61k
        OGRLineString *poLine = nullptr;
2895
2896
2.61k
        OGRGeometry *poGeom = GetGeometryRef();
2897
2.61k
        if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2898
827
        {
2899
827
            poLine = poGeom->toLineString();
2900
827
        }
2901
1.78k
        else if (poGeom &&
2902
1.78k
                 wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2903
1.78k
        {
2904
1.78k
            OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2905
1.78k
            if (poMultiLine->getNumGeometries() > 0)
2906
36
                poLine = poMultiLine->getGeometryRef(0);
2907
1.78k
        }
2908
2909
2.61k
        if (poLine && poLine->getNumPoints() > 0)
2910
856
        {
2911
856
            int i = poLine->getNumPoints() / 2;
2912
856
            if (poLine->getNumPoints() % 2 == 0)
2913
146
            {
2914
                // Return the midway between the 2 center points
2915
146
                m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
2916
146
                m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
2917
146
            }
2918
710
            else
2919
710
            {
2920
                // Return the center point
2921
710
                m_dCenterX = poLine->getX(i);
2922
710
                m_dCenterY = poLine->getY(i);
2923
710
            }
2924
856
            m_bCenterIsSet = TRUE;
2925
856
        }
2926
2.61k
    }
2927
2928
2.61k
    if (!m_bCenterIsSet)
2929
1.75k
        return -1;
2930
2931
856
    dX = m_dCenterX;
2932
856
    dY = m_dCenterY;
2933
856
    return 0;
2934
2.61k
}
2935
2936
/**********************************************************************
2937
 *                   TABPolyline::SetCenter()
2938
 *
2939
 * Set the X,Y coordinates to use as center point for the line.
2940
 **********************************************************************/
2941
void TABPolyline::SetCenter(double dX, double dY)
2942
42
{
2943
42
    m_dCenterX = dX;
2944
42
    m_dCenterY = dY;
2945
42
    m_bCenterIsSet = TRUE;
2946
42
}
2947
2948
/**********************************************************************
2949
 *                   TABPolyline::TwoPointLineAsPolyline()
2950
 *
2951
 * Returns the value of m_bWriteTwoPointLineAsPolyline
2952
 **********************************************************************/
2953
GBool TABPolyline::TwoPointLineAsPolyline()
2954
0
{
2955
0
    return m_bWriteTwoPointLineAsPolyline;
2956
0
}
2957
2958
/**********************************************************************
2959
 *                   TABPolyline::TwoPointLineAsPolyline()
2960
 *
2961
 * Sets the value of m_bWriteTwoPointLineAsPolyline
2962
 **********************************************************************/
2963
void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
2964
0
{
2965
0
    m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
2966
0
}
2967
2968
/*=====================================================================
2969
 *                      class TABRegion
2970
 *====================================================================*/
2971
2972
/**********************************************************************
2973
 *                   TABRegion::TABRegion()
2974
 *
2975
 * Constructor.
2976
 **********************************************************************/
2977
TABRegion::TABRegion(const OGRFeatureDefn *poDefnIn)
2978
195k
    : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
2979
195k
      m_dCenterX(0.0), m_dCenterY(0.0)
2980
195k
{
2981
195k
}
2982
2983
/**********************************************************************
2984
 *                   TABRegion::~TABRegion()
2985
 *
2986
 * Destructor.
2987
 **********************************************************************/
2988
TABRegion::~TABRegion()
2989
195k
{
2990
195k
}
2991
2992
/**********************************************************************
2993
 *                     TABRegion::CloneTABFeature()
2994
 *
2995
 * Duplicate feature, including stuff specific to each TABFeature type.
2996
 *
2997
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
2998
 * then copies any members specific to its own type.
2999
 **********************************************************************/
3000
TABFeature *
3001
TABRegion::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
3002
0
{
3003
    /*-----------------------------------------------------------------
3004
     * Alloc new feature and copy the base stuff
3005
     *----------------------------------------------------------------*/
3006
0
    TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
3007
3008
0
    CopyTABFeatureBase(poNew);
3009
3010
    /*-----------------------------------------------------------------
3011
     * And members specific to this class
3012
     *----------------------------------------------------------------*/
3013
    // ITABFeaturePen
3014
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
3015
3016
    // ITABFeatureBrush
3017
0
    *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
3018
3019
0
    poNew->m_bSmooth = m_bSmooth;
3020
0
    poNew->m_bCenterIsSet = m_bCenterIsSet;
3021
0
    poNew->m_dCenterX = m_dCenterX;
3022
0
    poNew->m_dCenterY = m_dCenterY;
3023
3024
0
    return poNew;
3025
0
}
3026
3027
/**********************************************************************
3028
 *                   TABRegion::ValidateMapInfoType()
3029
 *
3030
 * Check the feature's geometry part and return the corresponding
3031
 * mapinfo object type code.  The m_nMapInfoType member will also
3032
 * be updated for further calls to GetMapInfoType();
3033
 *
3034
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
3035
 * is expected for this object class.
3036
 **********************************************************************/
3037
TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
3038
1.47k
{
3039
    /*-----------------------------------------------------------------
3040
     * Fetch and validate geometry
3041
     *----------------------------------------------------------------*/
3042
1.47k
    OGRGeometry *poGeom = GetGeometryRef();
3043
1.47k
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3044
300
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3045
1.47k
    {
3046
1.47k
        GInt32 numPointsTotal = 0;
3047
1.47k
        GInt32 numRings = GetNumRings();
3048
3.01k
        for (int i = 0; i < numRings; i++)
3049
1.54k
        {
3050
1.54k
            OGRLinearRing *poRing = GetRingRef(i);
3051
1.54k
            if (poRing)
3052
1.31k
                numPointsTotal += poRing->getNumPoints();
3053
1.54k
        }
3054
1.47k
        if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
3055
0
            m_nMapInfoType = TAB_GEOM_V800_REGION;
3056
1.47k
        else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
3057
0
            m_nMapInfoType = TAB_GEOM_V450_REGION;
3058
1.47k
        else
3059
1.47k
            m_nMapInfoType = TAB_GEOM_REGION;
3060
1.47k
    }
3061
0
    else
3062
0
    {
3063
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
3064
0
                 "TABRegion: Missing or Invalid Geometry!");
3065
0
        m_nMapInfoType = TAB_GEOM_NONE;
3066
0
    }
3067
3068
    /*-----------------------------------------------------------------
3069
     * Decide if coordinates should be compressed or not.
3070
     *----------------------------------------------------------------*/
3071
1.47k
    ValidateCoordType(poMapFile);
3072
3073
1.47k
    return m_nMapInfoType;
3074
1.47k
}
3075
3076
/**********************************************************************
3077
 *                   TABRegion::ReadGeometryFromMAPFile()
3078
 *
3079
 * Fill the geometry and representation (color, etc...) part of the
3080
 * feature from the contents of the .MAP object pointed to by poMAPFile.
3081
 *
3082
 * It is assumed that poMAPFile currently points to the beginning of
3083
 * a map object.
3084
 *
3085
 * Returns 0 on success, -1 on error, in which case CPLError() will have
3086
 * been called.
3087
 **********************************************************************/
3088
int TABRegion::ReadGeometryFromMAPFile(
3089
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3090
    GBool bCoordBlockDataOnly /*=FALSE*/,
3091
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3092
76
{
3093
76
    double dXMin = 0.0;
3094
76
    double dYMin = 0.0;
3095
76
    double dXMax = 0.0;
3096
76
    double dYMax = 0.0;
3097
76
    OGRGeometry *poGeometry = nullptr;
3098
76
    TABMAPCoordBlock *poCoordBlock = nullptr;
3099
3100
    /*-----------------------------------------------------------------
3101
     * Fetch and validate geometry type
3102
     *----------------------------------------------------------------*/
3103
76
    m_nMapInfoType = poObjHdr->m_nType;
3104
3105
76
    if (m_nMapInfoType == TAB_GEOM_REGION ||
3106
38
        m_nMapInfoType == TAB_GEOM_REGION_C ||
3107
7
        m_nMapInfoType == TAB_GEOM_V450_REGION ||
3108
7
        m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3109
0
        m_nMapInfoType == TAB_GEOM_V800_REGION ||
3110
0
        m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3111
76
    {
3112
        /*=============================================================
3113
         * REGION (Similar to PLINE MULTIPLE)
3114
         *============================================================*/
3115
76
        GInt32 /* nCoordDataSize, */ numPointsTotal;
3116
76
        OGRMultiPolygon *poMultiPolygon = nullptr;
3117
76
        OGRPolygon *poPolygon = nullptr;
3118
76
        GBool bComprCoord = poObjHdr->IsCompressedType();
3119
76
        int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3120
3121
        /*-------------------------------------------------------------
3122
         * Copy data from poObjHdr
3123
         *------------------------------------------------------------*/
3124
76
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3125
3126
76
        GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
3127
        /* nCoordDataSize  = poPLineHdr->m_nCoordDataSize; */
3128
76
        GInt32 numLineSections = poPLineHdr->m_numLineSections;
3129
76
        m_bSmooth = poPLineHdr->m_bSmooth;
3130
3131
        // Centroid/label point
3132
76
        double dX = 0.0;
3133
76
        double dY = 0.0;
3134
76
        poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
3135
76
                                dX, dY);
3136
76
        SetCenter(dX, dY);
3137
3138
        // Compressed coordinate origin (useful only in compressed case!)
3139
76
        m_nComprOrgX = poPLineHdr->m_nComprOrgX;
3140
76
        m_nComprOrgY = poPLineHdr->m_nComprOrgY;
3141
3142
        // MBR
3143
76
        poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
3144
76
                                dYMin);
3145
76
        poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
3146
76
                                dYMax);
3147
3148
76
        if (!bCoordBlockDataOnly)
3149
76
        {
3150
76
            m_nPenDefIndex = poPLineHdr->m_nPenId;  // Pen index
3151
76
            poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
3152
76
            m_nBrushDefIndex = poPLineHdr->m_nBrushId;  // Brush index
3153
76
            poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
3154
76
        }
3155
3156
        /*-------------------------------------------------------------
3157
         * Read data from the coord. block
3158
         *------------------------------------------------------------*/
3159
3160
76
        const int nMinSizeOfSection = 24;
3161
76
        if (numLineSections > INT_MAX / nMinSizeOfSection)
3162
0
        {
3163
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3164
0
            return -1;
3165
0
        }
3166
76
        const GUInt32 nMinimumBytesForSections =
3167
76
            nMinSizeOfSection * numLineSections;
3168
76
        if (nMinimumBytesForSections > 1024 * 1024 &&
3169
0
            nMinimumBytesForSections > poMapFile->GetFileSize())
3170
0
        {
3171
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3172
0
            return -1;
3173
0
        }
3174
3175
76
        TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3176
76
            VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
3177
76
        if (pasSecHdrs == nullptr)
3178
0
            return -1;
3179
3180
76
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3181
7
            poCoordBlock = *ppoCoordBlock;
3182
69
        else
3183
69
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
3184
3185
76
        if (poCoordBlock)
3186
72
            poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3187
3188
76
        if (poCoordBlock == nullptr ||
3189
72
            poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
3190
72
                                           numLineSections, pasSecHdrs,
3191
72
                                           numPointsTotal) != 0)
3192
10
        {
3193
10
            CPLError(CE_Failure, CPLE_FileIO,
3194
10
                     "Failed reading coordinate data at offset %d",
3195
10
                     nCoordBlockPtr);
3196
10
            CPLFree(pasSecHdrs);
3197
10
            return -1;
3198
10
        }
3199
3200
66
        const GUInt32 nMinimumBytesForPoints =
3201
66
            (bComprCoord ? 4 : 8) * numPointsTotal;
3202
66
        if (nMinimumBytesForPoints > 1024 * 1024 &&
3203
0
            nMinimumBytesForPoints > poMapFile->GetFileSize())
3204
0
        {
3205
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
3206
0
            CPLFree(pasSecHdrs);
3207
0
            return -1;
3208
0
        }
3209
3210
66
        GInt32 *panXY = static_cast<GInt32 *>(
3211
66
            VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
3212
66
        if (panXY == nullptr)
3213
0
        {
3214
0
            CPLFree(pasSecHdrs);
3215
0
            return -1;
3216
0
        }
3217
3218
66
        if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
3219
66
            0)
3220
2
        {
3221
2
            CPLError(CE_Failure, CPLE_FileIO,
3222
2
                     "Failed reading coordinate data at offset %d",
3223
2
                     nCoordBlockPtr);
3224
2
            CPLFree(pasSecHdrs);
3225
2
            CPLFree(panXY);
3226
2
            return -1;
3227
2
        }
3228
3229
        /*-------------------------------------------------------------
3230
         * Decide if we should return an OGRPolygon or an OGRMultiPolygon
3231
         * depending on the number of outer rings found in CoordSecHdr blocks.
3232
         * The CoodSecHdr block for each outer ring in the region has a flag
3233
         * indicating the number of inner rings that follow.
3234
         * In older versions of the format, the count of inner rings was
3235
         * always zero, so in this case we would always return MultiPolygons.
3236
         *
3237
         * Note: The current implementation assumes that there cannot be
3238
         * holes inside holes (i.e. multiple levels of inner rings)... if
3239
         * that case was encountered then we would return an OGRMultiPolygon
3240
         * in which the topological relationship between the rings would
3241
         * be lost.
3242
         *------------------------------------------------------------*/
3243
64
        int numOuterRings = 0;
3244
131
        for (int iSection = 0; iSection < numLineSections; iSection++)
3245
67
        {
3246
            // Count this as an outer ring.
3247
67
            numOuterRings++;
3248
            // Skip inner rings... so loop continues on an outer ring.
3249
67
            iSection += pasSecHdrs[iSection].numHoles;
3250
67
        }
3251
3252
64
        if (numOuterRings > 1)
3253
3
        {
3254
3
            poMultiPolygon = new OGRMultiPolygon;
3255
3
            poGeometry = poMultiPolygon;
3256
3
        }
3257
61
        else
3258
61
        {
3259
61
            poGeometry = nullptr;  // Will be set later
3260
61
        }
3261
3262
        /*-------------------------------------------------------------
3263
         * OK, build the OGRGeometry object.
3264
         *------------------------------------------------------------*/
3265
64
        int numHolesToRead = 0;
3266
64
        poPolygon = nullptr;
3267
131
        for (int iSection = 0; iSection < numLineSections; iSection++)
3268
67
        {
3269
3270
67
            if (poPolygon == nullptr)
3271
67
                poPolygon = new OGRPolygon();
3272
3273
67
            if (numHolesToRead < 1)
3274
67
                numHolesToRead = pasSecHdrs[iSection].numHoles;
3275
0
            else
3276
0
                numHolesToRead--;
3277
3278
67
            int numSectionVertices = pasSecHdrs[iSection].numVertices;
3279
67
            GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
3280
3281
67
            OGRLinearRing *poRing = new OGRLinearRing();
3282
67
            poRing->setNumPoints(numSectionVertices);
3283
3284
940
            for (int i = 0; i < numSectionVertices; i++)
3285
873
            {
3286
873
                poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
3287
873
                poRing->setPoint(i, dX, dY);
3288
873
                pnXYPtr += 2;
3289
873
            }
3290
3291
67
            poPolygon->addRingDirectly(poRing);
3292
67
            poRing = nullptr;
3293
3294
67
            if (numHolesToRead < 1)
3295
64
            {
3296
64
                if (numOuterRings > 1)
3297
3
                {
3298
3
                    poMultiPolygon->addGeometryDirectly(poPolygon);
3299
3
                }
3300
61
                else
3301
61
                {
3302
61
                    poGeometry = poPolygon;
3303
61
                    CPLAssert(iSection == numLineSections - 1);
3304
61
                }
3305
3306
64
                poPolygon = nullptr;  // We'll alloc a new polygon next loop.
3307
64
            }
3308
67
        }
3309
64
        delete poPolygon;  // should only trigger on corrupted files
3310
3311
64
        CPLFree(pasSecHdrs);
3312
64
        CPLFree(panXY);
3313
64
    }
3314
0
    else
3315
0
    {
3316
0
        CPLError(
3317
0
            CE_Failure, CPLE_AssertionFailed,
3318
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
3319
0
            m_nMapInfoType, m_nMapInfoType);
3320
0
        return -1;
3321
0
    }
3322
3323
64
    SetGeometryDirectly(poGeometry);
3324
3325
64
    SetMBR(dXMin, dYMin, dXMax, dYMax);
3326
64
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
3327
64
              poObjHdr->m_nMaxY);
3328
3329
    /* Return a ref to coord block so that caller can continue reading
3330
     * after the end of this object (used by TABCollection and index splitting)
3331
     */
3332
64
    if (ppoCoordBlock)
3333
6
        *ppoCoordBlock = poCoordBlock;
3334
3335
64
    return 0;
3336
76
}
3337
3338
/**********************************************************************
3339
 *                   TABRegion::WriteGeometryToMAPFile()
3340
 *
3341
 * Write the geometry and representation (color, etc...) part of the
3342
 * feature to the .MAP object pointed to by poMAPFile.
3343
 *
3344
 * It is assumed that poMAPFile currently points to a valid map object.
3345
 *
3346
 * Returns 0 on success, -1 on error, in which case CPLError() will have
3347
 * been called.
3348
 **********************************************************************/
3349
int TABRegion::WriteGeometryToMAPFile(
3350
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3351
    GBool bCoordBlockDataOnly /*=FALSE*/,
3352
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3353
1.47k
{
3354
    /*-----------------------------------------------------------------
3355
     * We assume that ValidateMapInfoType() was called already and that
3356
     * the type in poObjHdr->m_nType is valid.
3357
     *----------------------------------------------------------------*/
3358
1.47k
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
3359
3360
    /*-----------------------------------------------------------------
3361
     * Fetch and validate geometry
3362
     *----------------------------------------------------------------*/
3363
1.47k
    OGRGeometry *poGeom = GetGeometryRef();
3364
1.47k
    TABMAPCoordBlock *poCoordBlock = nullptr;
3365
3366
1.47k
    if ((m_nMapInfoType == TAB_GEOM_REGION ||
3367
1.37k
         m_nMapInfoType == TAB_GEOM_REGION_C ||
3368
0
         m_nMapInfoType == TAB_GEOM_V450_REGION ||
3369
0
         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3370
0
         m_nMapInfoType == TAB_GEOM_V800_REGION ||
3371
0
         m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
3372
1.47k
        poGeom &&
3373
1.47k
        (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3374
300
         wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3375
1.47k
    {
3376
        /*=============================================================
3377
         * REGIONs are similar to PLINE MULTIPLE
3378
         *
3379
         * We accept both OGRPolygons (with one or multiple rings) and
3380
         * OGRMultiPolygons as input.
3381
         *============================================================*/
3382
1.47k
        GBool bCompressed = poObjHdr->IsCompressedType();
3383
3384
        /*-------------------------------------------------------------
3385
         * Process geometry first...
3386
         *------------------------------------------------------------*/
3387
1.47k
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3388
0
            poCoordBlock = *ppoCoordBlock;
3389
1.47k
        else
3390
1.47k
            poCoordBlock = poMapFile->GetCurCoordBlock();
3391
1.47k
        poCoordBlock->StartNewFeature();
3392
1.47k
        GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
3393
1.47k
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3394
3395
#ifdef TABDUMP
3396
        printf(/*ok*/
3397
               "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
3398
               m_nComprOrgX, m_nComprOrgY);
3399
#endif
3400
        /*-------------------------------------------------------------
3401
         * Fetch total number of rings and build array of coord
3402
         * sections headers.
3403
         *------------------------------------------------------------*/
3404
1.47k
        TABMAPCoordSecHdr *pasSecHdrs = nullptr;
3405
1.47k
        int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
3406
1.47k
        int nStatus = numRingsTotal == 0 ? -1 : 0;
3407
3408
        /*-------------------------------------------------------------
3409
         * Write the Coord. Section Header
3410
         *------------------------------------------------------------*/
3411
1.47k
        const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3412
3413
1.47k
        if (nStatus == 0)
3414
1.13k
            nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
3415
1.13k
                                                      pasSecHdrs, bCompressed);
3416
3417
1.47k
        CPLFree(pasSecHdrs);
3418
1.47k
        pasSecHdrs = nullptr;
3419
3420
1.47k
        if (nStatus != 0)
3421
332
            return nStatus;  // Error has already been reported.
3422
3423
        /*-------------------------------------------------------------
3424
         * Go through all the rings in our OGRMultiPolygon or OGRPolygon
3425
         * to write the coordinates themselves...
3426
         *------------------------------------------------------------*/
3427
3428
1.13k
        GInt32 nX = 0, nY = 0;
3429
2.45k
        for (int iRing = 0; iRing < numRingsTotal; iRing++)
3430
1.31k
        {
3431
1.31k
            OGRLinearRing *poRing = GetRingRef(iRing);
3432
1.31k
            if (poRing == nullptr)
3433
0
            {
3434
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
3435
0
                         "TABRegion: Object Geometry contains NULL rings!");
3436
0
                return -1;
3437
0
            }
3438
3439
1.31k
            int numPoints = poRing->getNumPoints();
3440
3441
1.86k
            for (int i = 0; nStatus == 0 && i < numPoints; i++)
3442
550
            {
3443
550
                poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
3444
550
                                        nY);
3445
550
                if ((nStatus =
3446
550
                         poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
3447
0
                {
3448
                    // Failed ... error message has already been produced
3449
0
                    return nStatus;
3450
0
                }
3451
550
            }
3452
1.31k
        } /* for iRing*/
3453
3454
1.13k
        GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
3455
3456
        /*-------------------------------------------------------------
3457
         * ... and finally copy info to poObjHdr
3458
         *------------------------------------------------------------*/
3459
1.13k
        TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3460
3461
1.13k
        poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
3462
1.13k
        poPLineHdr->m_nCoordDataSize = nCoordDataSize;
3463
1.13k
        poPLineHdr->m_numLineSections = numRingsTotal;
3464
3465
1.13k
        poPLineHdr->m_bSmooth = m_bSmooth;
3466
3467
        // MBR
3468
1.13k
        poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
3469
3470
        // Region center/label point
3471
1.13k
        double dX = 0.0;
3472
1.13k
        double dY = 0.0;
3473
1.13k
        if (GetCenter(dX, dY) != -1)
3474
1.13k
        {
3475
1.13k
            poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
3476
1.13k
                                    poPLineHdr->m_nLabelY);
3477
1.13k
        }
3478
0
        else
3479
0
        {
3480
0
            poPLineHdr->m_nLabelX = m_nComprOrgX;
3481
0
            poPLineHdr->m_nLabelY = m_nComprOrgY;
3482
0
        }
3483
3484
        // Compressed coordinate origin (useful only in compressed case!)
3485
1.13k
        poPLineHdr->m_nComprOrgX = m_nComprOrgX;
3486
1.13k
        poPLineHdr->m_nComprOrgY = m_nComprOrgY;
3487
3488
1.13k
        if (!bCoordBlockDataOnly)
3489
1.13k
        {
3490
1.13k
            m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
3491
1.13k
            poPLineHdr->m_nPenId =
3492
1.13k
                static_cast<GByte>(m_nPenDefIndex);  // Pen index
3493
3494
1.13k
            m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
3495
1.13k
            poPLineHdr->m_nBrushId =
3496
1.13k
                static_cast<GByte>(m_nBrushDefIndex);  // Brush index
3497
1.13k
        }
3498
1.13k
    }
3499
0
    else
3500
0
    {
3501
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
3502
0
                 "TABRegion: Object contains an invalid Geometry!");
3503
0
        return -1;
3504
0
    }
3505
3506
1.13k
    if (CPLGetLastErrorType() == CE_Failure)
3507
0
        return -1;
3508
3509
    /* Return a ref to coord block so that caller can continue writing
3510
     * after the end of this object (used by index splitting)
3511
     */
3512
1.13k
    if (ppoCoordBlock)
3513
0
        *ppoCoordBlock = poCoordBlock;
3514
3515
1.13k
    return 0;
3516
1.13k
}
3517
3518
/**********************************************************************
3519
 *                   TABRegion::GetNumRings()
3520
 *
3521
 * Return the total number of rings in this object making it look like
3522
 * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3523
 * of rings... hides the complexity of handling OGRMultiPolygons vs
3524
 * OGRPolygons, etc.
3525
 *
3526
 * Returns 0 if the geometry contained in the object is invalid or missing.
3527
 **********************************************************************/
3528
int TABRegion::GetNumRings()
3529
1.51k
{
3530
1.51k
    return ComputeNumRings(nullptr, nullptr);
3531
1.51k
}
3532
3533
int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
3534
                               TABMAPFile *poMapFile)
3535
2.98k
{
3536
2.98k
    int numRingsTotal = 0;
3537
2.98k
    int iLastSect = 0;
3538
3539
2.98k
    if (ppasSecHdrs)
3540
1.47k
        *ppasSecHdrs = nullptr;
3541
3542
2.98k
    OGRGeometry *poGeom = GetGeometryRef();
3543
3544
2.98k
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3545
607
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3546
2.98k
    {
3547
        /*-------------------------------------------------------------
3548
         * Calculate total number of rings...
3549
         *------------------------------------------------------------*/
3550
2.98k
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3551
607
        {
3552
607
            for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
3553
390
            {
3554
390
                numRingsTotal += poPolygon->getNumInteriorRings() + 1;
3555
3556
390
                if (ppasSecHdrs && poMapFile)
3557
195
                {
3558
195
                    if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3559
195
                                      iLastSect) != 0)
3560
22
                        return 0;  // An error happened, return count=0
3561
195
                }
3562
390
            }  // for
3563
607
        }
3564
2.37k
        else
3565
2.37k
        {
3566
2.37k
            OGRPolygon *poPolygon = poGeom->toPolygon();
3567
2.37k
            numRingsTotal = poPolygon->getNumInteriorRings() + 1;
3568
3569
2.37k
            if (ppasSecHdrs && poMapFile)
3570
1.17k
            {
3571
1.17k
                if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3572
1.17k
                                  iLastSect) != 0)
3573
205
                    return 0;  // An error happened, return count=0
3574
1.17k
            }
3575
2.37k
        }
3576
2.98k
    }
3577
3578
    /*-----------------------------------------------------------------
3579
     * If we're generating section header blocks, then init the
3580
     * coordinate offset values.
3581
     *
3582
     * In calculation of nDataOffset, we have to take into account that
3583
     * V450 header section uses int32 instead of int16 for numVertices
3584
     * and we add another 2 bytes to align with a 4 bytes boundary.
3585
     *------------------------------------------------------------*/
3586
2.75k
    const int nTotalHdrSizeUncompressed =
3587
2.75k
        (m_nMapInfoType == TAB_GEOM_V450_REGION ||
3588
2.75k
         m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3589
2.75k
         m_nMapInfoType == TAB_GEOM_V800_REGION ||
3590
2.75k
         m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3591
2.75k
            ? 28 * numRingsTotal
3592
2.75k
            : 24 * numRingsTotal;
3593
3594
2.75k
    if (ppasSecHdrs)
3595
1.24k
    {
3596
1.24k
        int numPointsTotal = 0;
3597
1.24k
        CPLAssert(iLastSect == numRingsTotal);
3598
2.56k
        for (int iRing = 0; iRing < numRingsTotal; iRing++)
3599
1.31k
        {
3600
1.31k
            (*ppasSecHdrs)[iRing].nDataOffset =
3601
1.31k
                nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
3602
1.31k
            (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
3603
3604
1.31k
            numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
3605
1.31k
        }
3606
1.24k
    }
3607
3608
2.75k
    return numRingsTotal;
3609
2.98k
}
3610
3611
/**********************************************************************
3612
 *                   TABRegion::AppendSecHdrs()
3613
 *
3614
 * (Private method)
3615
 *
3616
 * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
3617
 **********************************************************************/
3618
int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
3619
                             TABMAPCoordSecHdr *&pasSecHdrs,
3620
                             TABMAPFile *poMapFile, int &iLastRing)
3621
1.36k
{
3622
    /*-------------------------------------------------------------
3623
     * Add a pasSecHdrs[] entry for each ring in this polygon.
3624
     * Note that the structs won't be fully initialized.
3625
     *------------------------------------------------------------*/
3626
1.36k
    int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
3627
3628
1.36k
    pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3629
1.36k
        CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
3630
1.36k
                                   sizeof(TABMAPCoordSecHdr)));
3631
3632
2.68k
    for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
3633
1.54k
    {
3634
1.54k
        OGRLinearRing *poRing = nullptr;
3635
1.54k
        OGREnvelope sEnvelope;
3636
3637
1.54k
        if (iRing == 0)
3638
1.36k
            poRing = poPolygon->getExteriorRing();
3639
179
        else
3640
179
            poRing = poPolygon->getInteriorRing(iRing - 1);
3641
3642
1.54k
        if (poRing == nullptr)
3643
227
        {
3644
227
            CPLError(CE_Failure, CPLE_AssertionFailed,
3645
227
                     "Assertion Failed: Encountered NULL ring in OGRPolygon");
3646
227
            return -1;
3647
227
        }
3648
3649
1.31k
        poRing->getEnvelope(&sEnvelope);
3650
3651
1.31k
        pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
3652
3653
1.31k
        if (iRing == 0)
3654
1.13k
            pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
3655
179
        else
3656
179
            pasSecHdrs[iLastRing].numHoles = 0;
3657
3658
1.31k
        poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
3659
1.31k
                                pasSecHdrs[iLastRing].nXMin,
3660
1.31k
                                pasSecHdrs[iLastRing].nYMin);
3661
1.31k
        poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
3662
1.31k
                                pasSecHdrs[iLastRing].nXMax,
3663
1.31k
                                pasSecHdrs[iLastRing].nYMax);
3664
3665
1.31k
        iLastRing++;
3666
1.31k
    } /* for iRing*/
3667
3668
1.13k
    return 0;
3669
1.36k
}
3670
3671
/**********************************************************************
3672
 *                   TABRegion::GetRingRef()
3673
 *
3674
 * Returns a reference to the specified ring number making it look like
3675
 * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3676
 * of rings... hides the complexity of handling OGRMultiPolygons vs
3677
 * OGRPolygons, etc.
3678
 *
3679
 * Returns NULL if the geometry contained in the object is invalid or
3680
 * missing or if the specified ring index is invalid.
3681
 **********************************************************************/
3682
OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
3683
2.92k
{
3684
2.92k
    OGRLinearRing *poRing = nullptr;
3685
3686
2.92k
    OGRGeometry *poGeom = GetGeometryRef();
3687
3688
2.92k
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3689
368
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3690
2.92k
    {
3691
        /*-------------------------------------------------------------
3692
         * Establish number of polygons based on geometry type
3693
         *------------------------------------------------------------*/
3694
2.92k
        OGRMultiPolygon *poMultiPolygon = nullptr;
3695
2.92k
        int iCurRing = 0;
3696
2.92k
        int numOGRPolygons = 0;
3697
3698
2.92k
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3699
368
        {
3700
368
            poMultiPolygon = poGeom->toMultiPolygon();
3701
368
            numOGRPolygons = poMultiPolygon->getNumGeometries();
3702
368
        }
3703
2.55k
        else
3704
2.55k
        {
3705
2.55k
            numOGRPolygons = 1;
3706
2.55k
        }
3707
3708
        /*-------------------------------------------------------------
3709
         * Loop through polygons until we find the requested ring.
3710
         *------------------------------------------------------------*/
3711
2.92k
        iCurRing = 0;
3712
5.84k
        for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
3713
2.92k
             iPoly++)
3714
2.92k
        {
3715
2.92k
            OGRPolygon *poPolygon = nullptr;
3716
2.92k
            if (poMultiPolygon)
3717
368
                poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3718
2.55k
            else
3719
2.55k
                poPolygon = poGeom->toPolygon();
3720
3721
2.92k
            int numIntRings = poPolygon->getNumInteriorRings();
3722
3723
2.92k
            if (iCurRing == nRequestedRingIndex)
3724
2.53k
            {
3725
2.53k
                poRing = poPolygon->getExteriorRing();
3726
2.53k
            }
3727
385
            else if (nRequestedRingIndex > iCurRing &&
3728
385
                     nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3729
385
            {
3730
385
                poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
3731
385
                                                    (iCurRing + 1));
3732
385
            }
3733
2.92k
            iCurRing += numIntRings + 1;
3734
2.92k
        }
3735
2.92k
    }
3736
3737
2.92k
    return poRing;
3738
2.92k
}
3739
3740
/**********************************************************************
3741
 *                   TABRegion::RingIsHole()
3742
 *
3743
 * Return false if the requested ring index is the first of a polygon
3744
 **********************************************************************/
3745
GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
3746
0
{
3747
0
    OGRGeometry *poGeom = GetGeometryRef();
3748
3749
0
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3750
0
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3751
0
    {
3752
        /*-------------------------------------------------------------
3753
         * Establish number of polygons based on geometry type
3754
         *------------------------------------------------------------*/
3755
0
        OGRMultiPolygon *poMultiPolygon = nullptr;
3756
0
        int iCurRing = 0;
3757
0
        int numOGRPolygons = 0;
3758
3759
0
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3760
0
        {
3761
0
            poMultiPolygon = poGeom->toMultiPolygon();
3762
0
            numOGRPolygons = poMultiPolygon->getNumGeometries();
3763
0
        }
3764
0
        else
3765
0
        {
3766
0
            numOGRPolygons = 1;
3767
0
        }
3768
3769
        /*-------------------------------------------------------------
3770
         * Loop through polygons until we find the requested ring.
3771
         *------------------------------------------------------------*/
3772
0
        iCurRing = 0;
3773
0
        for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
3774
0
        {
3775
0
            OGRPolygon *poPolygon = nullptr;
3776
0
            if (poMultiPolygon)
3777
0
                poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3778
0
            else
3779
0
                poPolygon = poGeom->toPolygon();
3780
3781
0
            int numIntRings = poPolygon->getNumInteriorRings();
3782
3783
0
            if (iCurRing == nRequestedRingIndex)
3784
0
            {
3785
0
                return FALSE;
3786
0
            }
3787
0
            else if (nRequestedRingIndex > iCurRing &&
3788
0
                     nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3789
0
            {
3790
0
                return TRUE;
3791
0
            }
3792
0
            iCurRing += numIntRings + 1;
3793
0
        }
3794
0
    }
3795
3796
0
    return FALSE;
3797
0
}
3798
3799
/**********************************************************************
3800
 *                   TABRegion::GetStyleString() const
3801
 *
3802
 * Return style string for this feature.
3803
 *
3804
 * Style String is built only once during the first call to GetStyleString().
3805
 **********************************************************************/
3806
const char *TABRegion::GetStyleString() const
3807
0
{
3808
0
    if (m_pszStyleString == nullptr)
3809
0
    {
3810
        // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
3811
        // to use temporary buffers
3812
0
        char *pszPen = CPLStrdup(GetPenStyleString());
3813
0
        char *pszBrush = CPLStrdup(GetBrushStyleString());
3814
3815
0
        m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
3816
3817
0
        CPLFree(pszPen);
3818
0
        CPLFree(pszBrush);
3819
0
    }
3820
3821
0
    return m_pszStyleString;
3822
0
}
3823
3824
/**********************************************************************
3825
 *                   TABRegion::DumpMIF()
3826
 *
3827
 * Dump feature geometry in a format similar to .MIF REGIONs.
3828
 **********************************************************************/
3829
void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
3830
0
{
3831
0
    if (fpOut == nullptr)
3832
0
        fpOut = stdout;
3833
3834
    /*-----------------------------------------------------------------
3835
     * Fetch and validate geometry
3836
     *----------------------------------------------------------------*/
3837
0
    OGRGeometry *poGeom = GetGeometryRef();
3838
0
    if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3839
0
                   wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3840
0
    {
3841
        /*-------------------------------------------------------------
3842
         * Generate output for region
3843
         *
3844
         * Note that we want to handle both OGRPolygons and OGRMultiPolygons
3845
         * that's why we use the GetNumRings()/GetRingRef() interface.
3846
         *------------------------------------------------------------*/
3847
0
        int numRingsTotal = GetNumRings();
3848
3849
0
        fprintf(fpOut, "REGION %d\n", numRingsTotal);
3850
3851
0
        for (int iRing = 0; iRing < numRingsTotal; iRing++)
3852
0
        {
3853
0
            OGRLinearRing *poRing = GetRingRef(iRing);
3854
3855
0
            if (poRing == nullptr)
3856
0
            {
3857
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
3858
0
                         "TABRegion: Object Geometry contains NULL rings!");
3859
0
                return;
3860
0
            }
3861
3862
0
            const int numPoints = poRing->getNumPoints();
3863
0
            fprintf(fpOut, " %d\n", numPoints);
3864
0
            for (int i = 0; i < numPoints; i++)
3865
0
                fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
3866
0
                        poRing->getY(i));
3867
0
        }
3868
0
    }
3869
0
    else
3870
0
    {
3871
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
3872
0
                 "TABRegion: Missing or Invalid Geometry!");
3873
0
        return;
3874
0
    }
3875
3876
0
    if (m_bCenterIsSet)
3877
0
        fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
3878
3879
    // Finish with PEN/BRUSH/etc. clauses
3880
0
    DumpPenDef();
3881
0
    DumpBrushDef();
3882
3883
0
    fflush(fpOut);
3884
0
}
3885
3886
/**********************************************************************
3887
 *                   TABRegion::GetCenter()
3888
 *
3889
 * Returns the center/label point of the region.
3890
 * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
3891
 * before.
3892
 *
3893
 * Returns 0 on success, -1 on error.
3894
 **********************************************************************/
3895
int TABRegion::GetCenter(double &dX, double &dY)
3896
1.13k
{
3897
1.13k
    if (!m_bCenterIsSet)
3898
1.13k
    {
3899
        /*-------------------------------------------------------------
3900
         * Calculate label point.  If we have a multipolygon then we use
3901
         * the first OGRPolygon in the feature to calculate the point.
3902
         *------------------------------------------------------------*/
3903
1.13k
        OGRGeometry *poGeom = GetGeometryRef();
3904
1.13k
        if (poGeom == nullptr)
3905
0
            return -1;
3906
3907
1.13k
        OGRPolygon *poPolygon = nullptr;
3908
3909
1.13k
        if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3910
173
        {
3911
173
            OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
3912
173
            if (poMultiPolygon->getNumGeometries() > 0)
3913
173
                poPolygon = poMultiPolygon->getGeometryRef(0);
3914
173
        }
3915
965
        else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
3916
965
        {
3917
965
            poPolygon = poGeom->toPolygon();
3918
965
        }
3919
3920
1.13k
        OGRPoint oLabelPoint;
3921
1.13k
        if (poPolygon != nullptr &&
3922
1.13k
            OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
3923
64
        {
3924
64
            m_dCenterX = oLabelPoint.getX();
3925
64
            m_dCenterY = oLabelPoint.getY();
3926
64
        }
3927
1.07k
        else
3928
1.07k
        {
3929
1.07k
            OGREnvelope oEnv;
3930
1.07k
            poGeom->getEnvelope(&oEnv);
3931
1.07k
            m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
3932
1.07k
            m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
3933
1.07k
        }
3934
3935
1.13k
        m_bCenterIsSet = TRUE;
3936
1.13k
    }
3937
3938
1.13k
    if (!m_bCenterIsSet)
3939
0
        return -1;
3940
3941
1.13k
    dX = m_dCenterX;
3942
1.13k
    dY = m_dCenterY;
3943
1.13k
    return 0;
3944
1.13k
}
3945
3946
/**********************************************************************
3947
 *                   TABRegion::SetCenter()
3948
 *
3949
 * Set the X,Y coordinates to use as center/label point for the region.
3950
 **********************************************************************/
3951
void TABRegion::SetCenter(double dX, double dY)
3952
2.39k
{
3953
2.39k
    m_dCenterX = dX;
3954
2.39k
    m_dCenterY = dY;
3955
2.39k
    m_bCenterIsSet = TRUE;
3956
2.39k
}
3957
3958
/*=====================================================================
3959
 *                      class TABRectangle
3960
 *====================================================================*/
3961
3962
/**********************************************************************
3963
 *                   TABRectangle::TABRectangle()
3964
 *
3965
 * Constructor.
3966
 **********************************************************************/
3967
TABRectangle::TABRectangle(const OGRFeatureDefn *poDefnIn)
3968
39.9k
    : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
3969
39.9k
      m_dRoundYRadius(0.0)
3970
39.9k
{
3971
39.9k
}
3972
3973
/**********************************************************************
3974
 *                   TABRectangle::~TABRectangle()
3975
 *
3976
 * Destructor.
3977
 **********************************************************************/
3978
TABRectangle::~TABRectangle()
3979
39.9k
{
3980
39.9k
}
3981
3982
/**********************************************************************
3983
 *                     TABRectangle::CloneTABFeature()
3984
 *
3985
 * Duplicate feature, including stuff specific to each TABFeature type.
3986
 *
3987
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
3988
 * then copies any members specific to its own type.
3989
 **********************************************************************/
3990
TABFeature *
3991
TABRectangle::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
3992
0
{
3993
    /*-----------------------------------------------------------------
3994
     * Alloc new feature and copy the base stuff
3995
     *----------------------------------------------------------------*/
3996
0
    TABRectangle *poNew =
3997
0
        new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
3998
3999
0
    CopyTABFeatureBase(poNew);
4000
4001
    /*-----------------------------------------------------------------
4002
     * And members specific to this class
4003
     *----------------------------------------------------------------*/
4004
    // ITABFeaturePen
4005
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
4006
4007
    // ITABFeatureBrush
4008
0
    *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4009
4010
0
    poNew->m_bRoundCorners = m_bRoundCorners;
4011
0
    poNew->m_dRoundXRadius = m_dRoundXRadius;
4012
0
    poNew->m_dRoundYRadius = m_dRoundYRadius;
4013
4014
0
    return poNew;
4015
0
}
4016
4017
/**********************************************************************
4018
 *                   TABRectangle::ValidateMapInfoType()
4019
 *
4020
 * Check the feature's geometry part and return the corresponding
4021
 * mapinfo object type code.  The m_nMapInfoType member will also
4022
 * be updated for further calls to GetMapInfoType();
4023
 *
4024
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4025
 * is expected for this object class.
4026
 **********************************************************************/
4027
TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4028
0
{
4029
    /*-----------------------------------------------------------------
4030
     * Fetch and validate geometry
4031
     *----------------------------------------------------------------*/
4032
0
    OGRGeometry *poGeom = GetGeometryRef();
4033
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4034
0
    {
4035
0
        if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4036
0
            m_nMapInfoType = TAB_GEOM_ROUNDRECT;
4037
0
        else
4038
0
            m_nMapInfoType = TAB_GEOM_RECT;
4039
0
    }
4040
0
    else
4041
0
    {
4042
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4043
0
                 "TABRectangle: Missing or Invalid Geometry!");
4044
0
        m_nMapInfoType = TAB_GEOM_NONE;
4045
0
    }
4046
4047
    /*-----------------------------------------------------------------
4048
     * Decide if coordinates should be compressed or not.
4049
     *----------------------------------------------------------------*/
4050
    // __TODO__ For now we always write uncompressed for this class...
4051
    // ValidateCoordType(poMapFile);
4052
0
    UpdateMBR(poMapFile);
4053
4054
0
    return m_nMapInfoType;
4055
0
}
4056
4057
/**********************************************************************
4058
 *                   TABRectangle::UpdateMBR()
4059
 *
4060
 * Update the feature MBR members using the geometry
4061
 *
4062
 * Returns 0 on success, or -1 if there is no geometry in object
4063
 **********************************************************************/
4064
int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4065
0
{
4066
0
    OGREnvelope sEnvelope;
4067
4068
    /*-----------------------------------------------------------------
4069
     * Fetch and validate geometry
4070
     *----------------------------------------------------------------*/
4071
0
    OGRGeometry *poGeom = GetGeometryRef();
4072
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4073
0
        poGeom->getEnvelope(&sEnvelope);
4074
0
    else
4075
0
    {
4076
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4077
0
                 "TABRectangle: Missing or Invalid Geometry!");
4078
0
        return -1;
4079
0
    }
4080
4081
    /*-----------------------------------------------------------------
4082
     * Note that we will simply use the rectangle's MBR and don't really
4083
     * read the polygon geometry... this should be OK unless the
4084
     * polygon geometry was not really a rectangle.
4085
     *----------------------------------------------------------------*/
4086
0
    m_dXMin = sEnvelope.MinX;
4087
0
    m_dYMin = sEnvelope.MinY;
4088
0
    m_dXMax = sEnvelope.MaxX;
4089
0
    m_dYMax = sEnvelope.MaxY;
4090
4091
0
    if (poMapFile)
4092
0
    {
4093
0
        poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4094
0
        poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4095
0
    }
4096
4097
0
    return 0;
4098
0
}
4099
4100
/**********************************************************************
4101
 *                   TABRectangle::ReadGeometryFromMAPFile()
4102
 *
4103
 * Fill the geometry and representation (color, etc...) part of the
4104
 * feature from the contents of the .MAP object pointed to by poMAPFile.
4105
 *
4106
 * It is assumed that poMAPFile currently points to the beginning of
4107
 * a map object.
4108
 *
4109
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4110
 * been called.
4111
 **********************************************************************/
4112
int TABRectangle::ReadGeometryFromMAPFile(
4113
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4114
    GBool bCoordBlockDataOnly /*=FALSE*/,
4115
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4116
71
{
4117
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4118
71
    if (bCoordBlockDataOnly)
4119
0
        return 0;
4120
4121
    /*-----------------------------------------------------------------
4122
     * Fetch and validate geometry type
4123
     *----------------------------------------------------------------*/
4124
71
    m_nMapInfoType = poObjHdr->m_nType;
4125
4126
71
    if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
4127
37
        m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
4128
3
        m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
4129
0
    {
4130
0
        CPLError(
4131
0
            CE_Failure, CPLE_AssertionFailed,
4132
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4133
0
            m_nMapInfoType, m_nMapInfoType);
4134
0
        return -1;
4135
0
    }
4136
4137
    /*-----------------------------------------------------------------
4138
     * Read object information
4139
     *----------------------------------------------------------------*/
4140
71
    TABMAPObjRectEllipse *poRectHdr =
4141
71
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4142
4143
    // Read the corners radius
4144
4145
71
    if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4146
37
        m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4147
37
    {
4148
        // Read the corner's diameters
4149
37
        poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
4150
37
                                    poRectHdr->m_nCornerHeight, m_dRoundXRadius,
4151
37
                                    m_dRoundYRadius);
4152
4153
        // Divide by 2 since we store the corner's radius
4154
37
        m_dRoundXRadius /= 2.0;
4155
37
        m_dRoundYRadius /= 2.0;
4156
4157
37
        m_bRoundCorners = TRUE;
4158
37
    }
4159
34
    else
4160
34
    {
4161
34
        m_bRoundCorners = FALSE;
4162
34
        m_dRoundXRadius = 0.0;
4163
34
        m_dRoundYRadius = 0.0;
4164
34
    }
4165
4166
    // A rectangle is defined by its MBR
4167
4168
71
    double dXMin = 0.0;
4169
71
    double dYMin = 0.0;
4170
71
    double dXMax = 0.0;
4171
71
    double dYMax = 0.0;
4172
71
    poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4173
71
                            dYMin);
4174
71
    poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4175
71
                            dYMax);
4176
4177
71
    m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
4178
71
    poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4179
4180
71
    m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
4181
71
    poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4182
4183
    /*-----------------------------------------------------------------
4184
     * Call SetMBR() and GetMBR() now to make sure that min values are
4185
     * really smaller than max values.
4186
     *----------------------------------------------------------------*/
4187
71
    SetMBR(dXMin, dYMin, dXMax, dYMax);
4188
71
    GetMBR(dXMin, dYMin, dXMax, dYMax);
4189
4190
    /* Copy int MBR to feature class members */
4191
71
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4192
71
              poObjHdr->m_nMaxY);
4193
4194
    /*-----------------------------------------------------------------
4195
     * Create and fill geometry object
4196
     *----------------------------------------------------------------*/
4197
71
    OGRPolygon *poPolygon = new OGRPolygon;
4198
71
    OGRLinearRing *poRing = new OGRLinearRing();
4199
71
    if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4200
31
    {
4201
        /*-------------------------------------------------------------
4202
         * For rounded rectangles, we generate arcs with 45 line
4203
         * segments for each corner.  We start with lower-left corner
4204
         * and proceed counterclockwise
4205
         * We also have to make sure that rounding radius is not too
4206
         * large for the MBR in the generated polygon... however, we
4207
         * always return the true X/Y radius (not adjusted) since this
4208
         * is the way MapInfo seems to do it when a radius bigger than
4209
         * the MBR is passed from TBA to MIF.
4210
         *------------------------------------------------------------*/
4211
31
        const double dXRadius =
4212
31
            std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
4213
31
        const double dYRadius =
4214
31
            std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
4215
31
        TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
4216
31
                       dYRadius, M_PI, 3.0 * M_PI / 2.0);
4217
31
        TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
4218
31
                       dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
4219
31
        TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
4220
31
                       dYRadius, 0.0, M_PI / 2.0);
4221
31
        TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
4222
31
                       dYRadius, M_PI / 2.0, M_PI);
4223
4224
31
        TABCloseRing(poRing);
4225
31
    }
4226
40
    else
4227
40
    {
4228
40
        poRing->addPoint(dXMin, dYMin);
4229
40
        poRing->addPoint(dXMax, dYMin);
4230
40
        poRing->addPoint(dXMax, dYMax);
4231
40
        poRing->addPoint(dXMin, dYMax);
4232
40
        poRing->addPoint(dXMin, dYMin);
4233
40
    }
4234
4235
71
    poPolygon->addRingDirectly(poRing);
4236
71
    SetGeometryDirectly(poPolygon);
4237
4238
71
    return 0;
4239
71
}
4240
4241
/**********************************************************************
4242
 *                   TABRectangle::WriteGeometryToMAPFile()
4243
 *
4244
 * Write the geometry and representation (color, etc...) part of the
4245
 * feature to the .MAP object pointed to by poMAPFile.
4246
 *
4247
 * It is assumed that poMAPFile currently points to a valid map object.
4248
 *
4249
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4250
 * been called.
4251
 **********************************************************************/
4252
int TABRectangle::WriteGeometryToMAPFile(
4253
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4254
    GBool bCoordBlockDataOnly /*=FALSE*/,
4255
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4256
0
{
4257
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4258
0
    if (bCoordBlockDataOnly)
4259
0
        return 0;
4260
4261
    /*-----------------------------------------------------------------
4262
     * We assume that ValidateMapInfoType() was called already and that
4263
     * the type in poObjHdr->m_nType is valid.
4264
     *----------------------------------------------------------------*/
4265
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4266
4267
    /*-----------------------------------------------------------------
4268
     * Fetch and validate geometry and update MBR
4269
     * Note that we will simply use the geometry's MBR and don't really
4270
     * read the polygon geometry... this should be OK unless the
4271
     * polygon geometry was not really a rectangle.
4272
     *----------------------------------------------------------------*/
4273
0
    if (UpdateMBR(poMapFile) != 0)
4274
0
        return -1; /* Error already reported */
4275
4276
    /*-----------------------------------------------------------------
4277
     * Copy object information
4278
     *----------------------------------------------------------------*/
4279
0
    TABMAPObjRectEllipse *poRectHdr =
4280
0
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4281
4282
0
    if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4283
0
        m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4284
0
    {
4285
0
        poMapFile->Coordsys2IntDist(
4286
0
            m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
4287
0
            poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
4288
0
    }
4289
0
    else
4290
0
    {
4291
0
        poRectHdr->m_nCornerWidth = 0;
4292
0
        poRectHdr->m_nCornerHeight = 0;
4293
0
    }
4294
4295
    // A rectangle is defined by its MBR (values were set in UpdateMBR())
4296
0
    poRectHdr->m_nMinX = m_nXMin;
4297
0
    poRectHdr->m_nMinY = m_nYMin;
4298
0
    poRectHdr->m_nMaxX = m_nXMax;
4299
0
    poRectHdr->m_nMaxY = m_nYMax;
4300
4301
0
    m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4302
0
    poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
4303
4304
0
    m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4305
0
    poRectHdr->m_nBrushId =
4306
0
        static_cast<GByte>(m_nBrushDefIndex);  // Brush index
4307
4308
0
    if (CPLGetLastErrorType() == CE_Failure)
4309
0
        return -1;
4310
4311
0
    return 0;
4312
0
}
4313
4314
/**********************************************************************
4315
 *                   TABRectangle::GetStyleString() const
4316
 *
4317
 * Return style string for this feature.
4318
 *
4319
 * Style String is built only once during the first call to GetStyleString().
4320
 **********************************************************************/
4321
const char *TABRectangle::GetStyleString() const
4322
0
{
4323
0
    if (m_pszStyleString == nullptr)
4324
0
    {
4325
        // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4326
        // to use temporary buffers
4327
0
        char *pszPen = CPLStrdup(GetPenStyleString());
4328
0
        char *pszBrush = CPLStrdup(GetBrushStyleString());
4329
4330
0
        m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4331
4332
0
        CPLFree(pszPen);
4333
0
        CPLFree(pszBrush);
4334
0
    }
4335
4336
0
    return m_pszStyleString;
4337
0
}
4338
4339
/**********************************************************************
4340
 *                   TABRectangle::DumpMIF()
4341
 *
4342
 * Dump feature geometry in a format similar to .MIF REGIONs.
4343
 **********************************************************************/
4344
void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
4345
0
{
4346
0
    if (fpOut == nullptr)
4347
0
        fpOut = stdout;
4348
4349
    /*-----------------------------------------------------------------
4350
     * Output RECT or ROUNDRECT parameters
4351
     *----------------------------------------------------------------*/
4352
0
    double dXMin = 0.0;
4353
0
    double dYMin = 0.0;
4354
0
    double dXMax = 0.0;
4355
0
    double dYMax = 0.0;
4356
0
    GetMBR(dXMin, dYMin, dXMax, dYMax);
4357
4358
0
    if (m_bRoundCorners)
4359
0
        fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g    %.15g %.15g)\n",
4360
0
                dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
4361
0
    else
4362
0
        fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4363
0
                dYMax);
4364
4365
    /*-----------------------------------------------------------------
4366
     * Fetch and validate geometry
4367
     *----------------------------------------------------------------*/
4368
0
    OGRGeometry *poGeom = GetGeometryRef();
4369
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4370
0
    {
4371
        /*-------------------------------------------------------------
4372
         * Generate rectangle output as a region
4373
         * We could also output as a RECT or ROUNDRECT in a real MIF generator
4374
         *------------------------------------------------------------*/
4375
0
        OGRPolygon *poPolygon = poGeom->toPolygon();
4376
0
        int numIntRings = poPolygon->getNumInteriorRings();
4377
0
        fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4378
        // In this loop, iRing=-1 for the outer ring.
4379
0
        for (int iRing = -1; iRing < numIntRings; iRing++)
4380
0
        {
4381
0
            OGRLinearRing *poRing = nullptr;
4382
4383
0
            if (iRing == -1)
4384
0
                poRing = poPolygon->getExteriorRing();
4385
0
            else
4386
0
                poRing = poPolygon->getInteriorRing(iRing);
4387
4388
0
            if (poRing == nullptr)
4389
0
            {
4390
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
4391
0
                         "TABRectangle: Object Geometry contains NULL rings!");
4392
0
                return;
4393
0
            }
4394
4395
0
            const int numPoints = poRing->getNumPoints();
4396
0
            fprintf(fpOut, " %d\n", numPoints);
4397
0
            for (int i = 0; i < numPoints; i++)
4398
0
                fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4399
0
                        poRing->getY(i));
4400
0
        }
4401
0
    }
4402
0
    else
4403
0
    {
4404
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4405
0
                 "TABRectangle: Missing or Invalid Geometry!");
4406
0
        return;
4407
0
    }
4408
4409
    // Finish with PEN/BRUSH/etc. clauses
4410
0
    DumpPenDef();
4411
0
    DumpBrushDef();
4412
4413
0
    fflush(fpOut);
4414
0
}
4415
4416
/*=====================================================================
4417
 *                      class TABEllipse
4418
 *====================================================================*/
4419
4420
/**********************************************************************
4421
 *                   TABEllipse::TABEllipse()
4422
 *
4423
 * Constructor.
4424
 **********************************************************************/
4425
TABEllipse::TABEllipse(const OGRFeatureDefn *poDefnIn)
4426
20.8k
    : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
4427
20.8k
      m_dYRadius(0.0)
4428
20.8k
{
4429
20.8k
}
4430
4431
/**********************************************************************
4432
 *                   TABEllipse::~TABEllipse()
4433
 *
4434
 * Destructor.
4435
 **********************************************************************/
4436
TABEllipse::~TABEllipse()
4437
20.8k
{
4438
20.8k
}
4439
4440
/**********************************************************************
4441
 *                     TABEllipse::CloneTABFeature()
4442
 *
4443
 * Duplicate feature, including stuff specific to each TABFeature type.
4444
 *
4445
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
4446
 * then copies any members specific to its own type.
4447
 **********************************************************************/
4448
TABFeature *
4449
TABEllipse::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
4450
0
{
4451
    /*-----------------------------------------------------------------
4452
     * Alloc new feature and copy the base stuff
4453
     *----------------------------------------------------------------*/
4454
0
    TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
4455
4456
0
    CopyTABFeatureBase(poNew);
4457
4458
    /*-----------------------------------------------------------------
4459
     * And members specific to this class
4460
     *----------------------------------------------------------------*/
4461
    // ITABFeaturePen
4462
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
4463
4464
    // ITABFeatureBrush
4465
0
    *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4466
4467
0
    poNew->m_dCenterX = m_dCenterX;
4468
0
    poNew->m_dCenterY = m_dCenterY;
4469
0
    poNew->m_dXRadius = m_dXRadius;
4470
0
    poNew->m_dYRadius = m_dYRadius;
4471
4472
0
    return poNew;
4473
0
}
4474
4475
/**********************************************************************
4476
 *                   TABEllipse::ValidateMapInfoType()
4477
 *
4478
 * Check the feature's geometry part and return the corresponding
4479
 * mapinfo object type code.  The m_nMapInfoType member will also
4480
 * be updated for further calls to GetMapInfoType();
4481
 *
4482
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4483
 * is expected for this object class.
4484
 **********************************************************************/
4485
TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4486
0
{
4487
    /*-----------------------------------------------------------------
4488
     * Fetch and validate geometry
4489
     *----------------------------------------------------------------*/
4490
0
    OGRGeometry *poGeom = GetGeometryRef();
4491
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4492
0
        (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4493
0
    {
4494
0
        m_nMapInfoType = TAB_GEOM_ELLIPSE;
4495
0
    }
4496
0
    else
4497
0
    {
4498
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4499
0
                 "TABEllipse: Missing or Invalid Geometry!");
4500
0
        m_nMapInfoType = TAB_GEOM_NONE;
4501
0
    }
4502
4503
    /*-----------------------------------------------------------------
4504
     * Decide if coordinates should be compressed or not.
4505
     *----------------------------------------------------------------*/
4506
    // __TODO__ For now we always write uncompressed for this class...
4507
    // ValidateCoordType(poMapFile);
4508
0
    UpdateMBR(poMapFile);
4509
4510
0
    return m_nMapInfoType;
4511
0
}
4512
4513
/**********************************************************************
4514
 *                   TABEllipse::UpdateMBR()
4515
 *
4516
 * Update the feature MBR members using the geometry
4517
 *
4518
 * Returns 0 on success, or -1 if there is no geometry in object
4519
 **********************************************************************/
4520
int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4521
0
{
4522
0
    OGREnvelope sEnvelope;
4523
4524
    /*-----------------------------------------------------------------
4525
     * Fetch and validate geometry... Polygon and point are accepted.
4526
     * Note that we will simply use the ellipse's MBR and don't really
4527
     * read the polygon geometry... this should be OK unless the
4528
     * polygon geometry was not really an ellipse.
4529
     *----------------------------------------------------------------*/
4530
0
    OGRGeometry *poGeom = GetGeometryRef();
4531
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4532
0
        (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4533
0
        poGeom->getEnvelope(&sEnvelope);
4534
0
    else
4535
0
    {
4536
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4537
0
                 "TABEllipse: Missing or Invalid Geometry!");
4538
0
        return -1;
4539
0
    }
4540
4541
    /*-----------------------------------------------------------------
4542
     * We use the center of the MBR as the ellipse center, and the
4543
     * X/Y radius to define the MBR size.  If X/Y radius are null then
4544
     * we'll try to use the MBR to recompute them.
4545
     *----------------------------------------------------------------*/
4546
0
    const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
4547
0
    const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
4548
0
    if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
4549
0
    {
4550
0
        m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
4551
0
        m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
4552
0
    }
4553
4554
0
    m_dXMin = dXCenter - m_dXRadius;
4555
0
    m_dYMin = dYCenter - m_dYRadius;
4556
0
    m_dXMax = dXCenter + m_dXRadius;
4557
0
    m_dYMax = dYCenter + m_dYRadius;
4558
4559
0
    if (poMapFile)
4560
0
    {
4561
0
        poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4562
0
        poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4563
0
    }
4564
4565
0
    return 0;
4566
0
}
4567
4568
/**********************************************************************
4569
 *                   TABEllipse::ReadGeometryFromMAPFile()
4570
 *
4571
 * Fill the geometry and representation (color, etc...) part of the
4572
 * feature from the contents of the .MAP object pointed to by poMAPFile.
4573
 *
4574
 * It is assumed that poMAPFile currently points to the beginning of
4575
 * a map object.
4576
 *
4577
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4578
 * been called.
4579
 **********************************************************************/
4580
int TABEllipse::ReadGeometryFromMAPFile(
4581
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4582
    GBool bCoordBlockDataOnly /*=FALSE*/,
4583
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4584
36
{
4585
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4586
36
    if (bCoordBlockDataOnly)
4587
0
        return 0;
4588
4589
    /*-----------------------------------------------------------------
4590
     * Fetch and validate geometry type
4591
     *----------------------------------------------------------------*/
4592
36
    m_nMapInfoType = poObjHdr->m_nType;
4593
4594
36
    if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
4595
5
        m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
4596
0
    {
4597
0
        CPLError(
4598
0
            CE_Failure, CPLE_AssertionFailed,
4599
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4600
0
            m_nMapInfoType, m_nMapInfoType);
4601
0
        return -1;
4602
0
    }
4603
4604
    /*-----------------------------------------------------------------
4605
     * Read object information
4606
     *----------------------------------------------------------------*/
4607
36
    TABMAPObjRectEllipse *poRectHdr =
4608
36
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4609
4610
    // An ellipse is defined by its MBR
4611
4612
36
    double dXMin = 0.0;
4613
36
    double dYMin = 0.0;
4614
36
    double dXMax = 0.0;
4615
36
    double dYMax = 0.0;
4616
36
    poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4617
36
                            dYMin);
4618
36
    poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4619
36
                            dYMax);
4620
4621
36
    m_nPenDefIndex = poRectHdr->m_nPenId;  // Pen index
4622
36
    poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4623
4624
36
    m_nBrushDefIndex = poRectHdr->m_nBrushId;  // Brush index
4625
36
    poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4626
4627
    /*-----------------------------------------------------------------
4628
     * Save info about the ellipse def. inside class members
4629
     *----------------------------------------------------------------*/
4630
36
    m_dCenterX = (dXMin + dXMax) / 2.0;
4631
36
    m_dCenterY = (dYMin + dYMax) / 2.0;
4632
36
    m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
4633
36
    m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
4634
4635
36
    SetMBR(dXMin, dYMin, dXMax, dYMax);
4636
4637
36
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4638
36
              poObjHdr->m_nMaxY);
4639
4640
    /*-----------------------------------------------------------------
4641
     * Create and fill geometry object
4642
     *----------------------------------------------------------------*/
4643
36
    OGRPolygon *poPolygon = new OGRPolygon;
4644
36
    OGRLinearRing *poRing = new OGRLinearRing();
4645
4646
    /*-----------------------------------------------------------------
4647
     * For the OGR geometry, we generate an ellipse with 2 degrees line
4648
     * segments.
4649
     *----------------------------------------------------------------*/
4650
36
    TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
4651
36
                   0.0, 2.0 * M_PI);
4652
36
    TABCloseRing(poRing);
4653
4654
36
    poPolygon->addRingDirectly(poRing);
4655
36
    SetGeometryDirectly(poPolygon);
4656
4657
36
    return 0;
4658
36
}
4659
4660
/**********************************************************************
4661
 *                   TABEllipse::WriteGeometryToMAPFile()
4662
 *
4663
 * Write the geometry and representation (color, etc...) part of the
4664
 * feature to the .MAP object pointed to by poMAPFile.
4665
 *
4666
 * It is assumed that poMAPFile currently points to a valid map object.
4667
 *
4668
 * Returns 0 on success, -1 on error, in which case CPLError() will have
4669
 * been called.
4670
 **********************************************************************/
4671
int TABEllipse::WriteGeometryToMAPFile(
4672
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4673
    GBool bCoordBlockDataOnly /*=FALSE*/,
4674
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4675
0
{
4676
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4677
0
    if (bCoordBlockDataOnly)
4678
0
        return 0;
4679
4680
    /*-----------------------------------------------------------------
4681
     * We assume that ValidateMapInfoType() was called already and that
4682
     * the type in poObjHdr->m_nType is valid.
4683
     *----------------------------------------------------------------*/
4684
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4685
4686
    /*-----------------------------------------------------------------
4687
     * Fetch and validate geometry... Polygon and point are accepted.
4688
     * Note that we will simply use the ellipse's MBR and don't really
4689
     * read the polygon geometry... this should be OK unless the
4690
     * polygon geometry was not really an ellipse.
4691
     *
4692
     * We use the center of the MBR as the ellipse center, and the
4693
     * X/Y radius to define the MBR size.  If X/Y radius are null then
4694
     * we'll try to use the MBR to recompute them.
4695
     *----------------------------------------------------------------*/
4696
0
    if (UpdateMBR(poMapFile) != 0)
4697
0
        return -1; /* Error already reported */
4698
4699
    /*-----------------------------------------------------------------
4700
     * Copy object information
4701
     *----------------------------------------------------------------*/
4702
0
    TABMAPObjRectEllipse *poRectHdr =
4703
0
        cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4704
4705
    // Reset RoundRect Corner members... just in case (unused for ellipse)
4706
0
    poRectHdr->m_nCornerWidth = 0;
4707
0
    poRectHdr->m_nCornerHeight = 0;
4708
4709
    // An ellipse is defined by its MBR (values were set in UpdateMBR())
4710
0
    poRectHdr->m_nMinX = m_nXMin;
4711
0
    poRectHdr->m_nMinY = m_nYMin;
4712
0
    poRectHdr->m_nMaxX = m_nXMax;
4713
0
    poRectHdr->m_nMaxY = m_nYMax;
4714
4715
0
    m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4716
0
    poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
4717
4718
0
    m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4719
0
    poRectHdr->m_nBrushId =
4720
0
        static_cast<GByte>(m_nBrushDefIndex);  // Brush index
4721
4722
0
    if (CPLGetLastErrorType() == CE_Failure)
4723
0
        return -1;
4724
4725
0
    return 0;
4726
0
}
4727
4728
/**********************************************************************
4729
 *                   TABEllipse::GetStyleString() const
4730
 *
4731
 * Return style string for this feature.
4732
 *
4733
 * Style String is built only once during the first call to GetStyleString().
4734
 **********************************************************************/
4735
const char *TABEllipse::GetStyleString() const
4736
0
{
4737
0
    if (m_pszStyleString == nullptr)
4738
0
    {
4739
        // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4740
        // to use temporary buffers
4741
0
        char *pszPen = CPLStrdup(GetPenStyleString());
4742
0
        char *pszBrush = CPLStrdup(GetBrushStyleString());
4743
4744
0
        m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4745
4746
0
        CPLFree(pszPen);
4747
0
        CPLFree(pszBrush);
4748
0
    }
4749
4750
0
    return m_pszStyleString;
4751
0
}
4752
4753
/**********************************************************************
4754
 *                   TABEllipse::DumpMIF()
4755
 *
4756
 * Dump feature geometry in a format similar to .MIF REGIONs.
4757
 **********************************************************************/
4758
void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
4759
0
{
4760
0
    if (fpOut == nullptr)
4761
0
        fpOut = stdout;
4762
4763
    /*-----------------------------------------------------------------
4764
     * Output ELLIPSE parameters
4765
     *----------------------------------------------------------------*/
4766
0
    double dXMin = 0.0;
4767
0
    double dYMin = 0.0;
4768
0
    double dXMax = 0.0;
4769
0
    double dYMax = 0.0;
4770
0
    GetMBR(dXMin, dYMin, dXMax, dYMax);
4771
0
    fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4772
0
            dYMax);
4773
4774
    /*-----------------------------------------------------------------
4775
     * Fetch and validate geometry
4776
     *----------------------------------------------------------------*/
4777
0
    OGRGeometry *poGeom = GetGeometryRef();
4778
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4779
0
    {
4780
        /*-------------------------------------------------------------
4781
         * Generate ellipse output as a region
4782
         * We could also output as an ELLIPSE in a real MIF generator
4783
         *------------------------------------------------------------*/
4784
0
        OGRPolygon *poPolygon = poGeom->toPolygon();
4785
0
        int numIntRings = poPolygon->getNumInteriorRings();
4786
0
        fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4787
        // In this loop, iRing=-1 for the outer ring.
4788
0
        for (int iRing = -1; iRing < numIntRings; iRing++)
4789
0
        {
4790
0
            OGRLinearRing *poRing = nullptr;
4791
4792
0
            if (iRing == -1)
4793
0
                poRing = poPolygon->getExteriorRing();
4794
0
            else
4795
0
                poRing = poPolygon->getInteriorRing(iRing);
4796
4797
0
            if (poRing == nullptr)
4798
0
            {
4799
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
4800
0
                         "TABEllipse: Object Geometry contains NULL rings!");
4801
0
                return;
4802
0
            }
4803
4804
0
            int numPoints = poRing->getNumPoints();
4805
0
            fprintf(fpOut, " %d\n", numPoints);
4806
0
            for (int i = 0; i < numPoints; i++)
4807
0
                fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4808
0
                        poRing->getY(i));
4809
0
        }
4810
0
    }
4811
0
    else
4812
0
    {
4813
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4814
0
                 "TABEllipse: Missing or Invalid Geometry!");
4815
0
        return;
4816
0
    }
4817
4818
    // Finish with PEN/BRUSH/etc. clauses
4819
0
    DumpPenDef();
4820
0
    DumpBrushDef();
4821
4822
0
    fflush(fpOut);
4823
0
}
4824
4825
/*=====================================================================
4826
 *                      class TABArc
4827
 *====================================================================*/
4828
4829
/**********************************************************************
4830
 *                   TABArc::TABArc()
4831
 *
4832
 * Constructor.
4833
 **********************************************************************/
4834
TABArc::TABArc(const OGRFeatureDefn *poDefnIn)
4835
21.1k
    : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
4836
21.1k
      m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
4837
21.1k
{
4838
21.1k
}
4839
4840
/**********************************************************************
4841
 *                   TABArc::~TABArc()
4842
 *
4843
 * Destructor.
4844
 **********************************************************************/
4845
TABArc::~TABArc()
4846
21.1k
{
4847
21.1k
}
4848
4849
/**********************************************************************
4850
 *                     TABArc::CloneTABFeature()
4851
 *
4852
 * Duplicate feature, including stuff specific to each TABFeature type.
4853
 *
4854
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
4855
 * then copies any members specific to its own type.
4856
 **********************************************************************/
4857
TABFeature *TABArc::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
4858
0
{
4859
    /*-----------------------------------------------------------------
4860
     * Alloc new feature and copy the base stuff
4861
     *----------------------------------------------------------------*/
4862
0
    TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
4863
4864
0
    CopyTABFeatureBase(poNew);
4865
4866
    /*-----------------------------------------------------------------
4867
     * And members specific to this class
4868
     *----------------------------------------------------------------*/
4869
    // ITABFeaturePen
4870
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
4871
4872
0
    poNew->SetStartAngle(GetStartAngle());
4873
0
    poNew->SetEndAngle(GetEndAngle());
4874
4875
0
    poNew->m_dCenterX = m_dCenterX;
4876
0
    poNew->m_dCenterY = m_dCenterY;
4877
0
    poNew->m_dXRadius = m_dXRadius;
4878
0
    poNew->m_dYRadius = m_dYRadius;
4879
4880
0
    return poNew;
4881
0
}
4882
4883
/**********************************************************************
4884
 *                   TABArc::ValidateMapInfoType()
4885
 *
4886
 * Check the feature's geometry part and return the corresponding
4887
 * mapinfo object type code.  The m_nMapInfoType member will also
4888
 * be updated for further calls to GetMapInfoType();
4889
 *
4890
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4891
 * is expected for this object class.
4892
 **********************************************************************/
4893
TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4894
0
{
4895
    /*-----------------------------------------------------------------
4896
     * Fetch and validate geometry
4897
     *----------------------------------------------------------------*/
4898
0
    OGRGeometry *poGeom = GetGeometryRef();
4899
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
4900
0
        (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4901
0
    {
4902
0
        m_nMapInfoType = TAB_GEOM_ARC;
4903
0
    }
4904
0
    else
4905
0
    {
4906
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4907
0
                 "TABArc: Missing or Invalid Geometry!");
4908
0
        m_nMapInfoType = TAB_GEOM_NONE;
4909
0
    }
4910
4911
    /*-----------------------------------------------------------------
4912
     * Decide if coordinates should be compressed or not.
4913
     *----------------------------------------------------------------*/
4914
    // __TODO__ For now we always write uncompressed for this class...
4915
    // ValidateCoordType(poMapFile);
4916
0
    UpdateMBR(poMapFile);
4917
4918
0
    return m_nMapInfoType;
4919
0
}
4920
4921
/**********************************************************************
4922
 *                   TABArc::UpdateMBR()
4923
 *
4924
 * Update the feature MBR members using the geometry
4925
 *
4926
 * Returns 0 on success, or -1 if there is no geometry in object
4927
 **********************************************************************/
4928
int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4929
0
{
4930
0
    OGREnvelope sEnvelope;
4931
4932
0
    OGRGeometry *poGeom = GetGeometryRef();
4933
0
    if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
4934
0
    {
4935
        /*-------------------------------------------------------------
4936
         * POLYGON geometry:
4937
         * Note that we will simply use the ellipse's MBR and don't really
4938
         * read the polygon geometry... this should be OK unless the
4939
         * polygon geometry was not really an ellipse.
4940
         * In the case of a polygon geometry. the m_dCenterX/Y values MUST
4941
         * have been set by the caller.
4942
         *------------------------------------------------------------*/
4943
0
        poGeom->getEnvelope(&sEnvelope);
4944
0
    }
4945
0
    else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4946
0
    {
4947
        /*-------------------------------------------------------------
4948
         * In the case of a POINT GEOMETRY, we will make sure the
4949
         * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
4950
         *
4951
         * In this case we have to reconstruct the arc inside a temporary
4952
         * geometry object in order to find its real MBR.
4953
         *------------------------------------------------------------*/
4954
0
        OGRPoint *poPoint = poGeom->toPoint();
4955
0
        m_dCenterX = poPoint->getX();
4956
0
        m_dCenterY = poPoint->getY();
4957
4958
0
        OGRLineString oTmpLine;
4959
0
        int numPts = 0;
4960
0
        if (m_dEndAngle < m_dStartAngle)
4961
0
            numPts = static_cast<int>(
4962
0
                std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
4963
0
        else
4964
0
            numPts = static_cast<int>(
4965
0
                std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
4966
0
        numPts = std::max(2, numPts);
4967
4968
0
        TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
4969
0
                       m_dYRadius, m_dStartAngle * M_PI / 180.0,
4970
0
                       m_dEndAngle * M_PI / 180.0);
4971
4972
0
        oTmpLine.getEnvelope(&sEnvelope);
4973
0
    }
4974
0
    else
4975
0
    {
4976
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
4977
0
                 "TABArc: Missing or Invalid Geometry!");
4978
0
        return -1;
4979
0
    }
4980
4981
    // Update the Arc's MBR
4982
0
    m_dXMin = sEnvelope.MinX;
4983
0
    m_dYMin = sEnvelope.MinY;
4984
0
    m_dXMax = sEnvelope.MaxX;
4985
0
    m_dYMax = sEnvelope.MaxY;
4986
4987
0
    if (poMapFile)
4988
0
    {
4989
0
        poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4990
0
        poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4991
0
    }
4992
4993
0
    return 0;
4994
0
}
4995
4996
/**********************************************************************
4997
 *                   TABArc::ReadGeometryFromMAPFile()
4998
 *
4999
 * Fill the geometry and representation (color, etc...) part of the
5000
 * feature from the contents of the .MAP object pointed to by poMAPFile.
5001
 *
5002
 * It is assumed that poMAPFile currently points to the beginning of
5003
 * a map object.
5004
 *
5005
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5006
 * been called.
5007
 **********************************************************************/
5008
int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5009
                                    TABMAPObjHdr *poObjHdr,
5010
                                    GBool bCoordBlockDataOnly /*=FALSE*/,
5011
                                    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5012
61
{
5013
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5014
61
    if (bCoordBlockDataOnly)
5015
0
        return 0;
5016
5017
    /*-----------------------------------------------------------------
5018
     * Fetch and validate geometry type
5019
     *----------------------------------------------------------------*/
5020
61
    m_nMapInfoType = poObjHdr->m_nType;
5021
5022
61
    if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
5023
0
    {
5024
0
        CPLError(
5025
0
            CE_Failure, CPLE_AssertionFailed,
5026
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5027
0
            m_nMapInfoType, m_nMapInfoType);
5028
0
        return -1;
5029
0
    }
5030
5031
    /*-----------------------------------------------------------------
5032
     * Read object information
5033
     *----------------------------------------------------------------*/
5034
61
    TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5035
5036
    /*-------------------------------------------------------------
5037
     * Start/End angles
5038
     * Since the angles are specified for integer coordinates, and
5039
     * that these coordinates can have the X axis reversed, we have to
5040
     * adjust the angle values for the change in the X axis
5041
     * direction.
5042
     *
5043
     * This should be necessary only when X axis is flipped.
5044
     * __TODO__ Why is order of start/end values reversed as well???
5045
     *------------------------------------------------------------*/
5046
5047
    /*-------------------------------------------------------------
5048
     * OK, Arc angles again!!!!!!!!!!!!
5049
     * After some tests in 1999-11, it appeared that the angle values
5050
     * ALWAYS had to be flipped (read order= end angle followed by
5051
     * start angle), no matter which quadrant the file is in.
5052
     * This does not make any sense, so I suspect that there is something
5053
     * that we are missing here!
5054
     *
5055
     * 2000-01-14.... Again!!!  Based on some sample data files:
5056
     *  File         Ver Quadr  ReflXAxis  Read_Order   Adjust_Angle
5057
     * test_symb.tab 300    2        1      end,start    X=yes Y=no
5058
     * alltypes.tab: 300    1        0      start,end    X=no  Y=no
5059
     * arcs.tab:     300    2        0      end,start    X=yes Y=no
5060
     *
5061
     * Until we prove it wrong, the rule would be:
5062
     *  -> Quadrant 1 and 3, angles order = start, end
5063
     *  -> Quadrant 2 and 4, angles order = end, start
5064
     * + Always adjust angles for x and y axis based on quadrant.
5065
     *
5066
     * This was confirmed using some more files in which the quadrant was
5067
     * manually changed, but whether these are valid results is
5068
     * disputable.
5069
     *
5070
     * The ReflectXAxis flag seems to have no effect here...
5071
     *------------------------------------------------------------*/
5072
5073
    /*-------------------------------------------------------------
5074
     * In version 100 .tab files (version 400 .map), it is possible
5075
     * to have a quadrant value of 0 and it should be treated the
5076
     * same way as quadrant 3
5077
     *------------------------------------------------------------*/
5078
61
    if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
5079
11
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5080
7
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5081
60
    {
5082
        // Quadrants 1 and 3 ... read order = start, end
5083
60
        m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
5084
60
        m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
5085
60
    }
5086
1
    else
5087
1
    {
5088
        // Quadrants 2 and 4 ... read order = end, start
5089
1
        m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
5090
1
        m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
5091
1
    }
5092
5093
61
    if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
5094
61
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5095
57
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5096
10
    {
5097
        // X axis direction is flipped... adjust angle
5098
10
        m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
5099
10
                                                 : (540.0 - m_dStartAngle);
5100
10
        m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
5101
10
                                             : (540.0 - m_dEndAngle);
5102
10
    }
5103
5104
61
    if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
5105
0
    {
5106
0
        CPLError(CE_Failure, CPLE_AppDefined,
5107
0
                 "Wrong start and end angles: %f %f", m_dStartAngle,
5108
0
                 m_dEndAngle);
5109
0
        return -1;
5110
0
    }
5111
5112
61
    if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5113
57
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
5114
57
        poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5115
10
    {
5116
        // Y axis direction is flipped... this reverses angle direction
5117
        // Unfortunately we never found any file that contains this case,
5118
        // but this should be the behavior to expect!!!
5119
        //
5120
        // 2000-01-14: some files in which quadrant was set to 3 and 4
5121
        // manually seemed to confirm that this is the right thing to do.
5122
10
        m_dStartAngle = 360.0 - m_dStartAngle;
5123
10
        m_dEndAngle = 360.0 - m_dEndAngle;
5124
10
    }
5125
5126
    // An arc is defined by its defining ellipse's MBR:
5127
5128
61
    double dXMin = 0.0;
5129
61
    double dYMin = 0.0;
5130
61
    double dXMax = 0.0;
5131
61
    double dYMax = 0.0;
5132
5133
61
    poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
5134
61
                            poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
5135
61
    poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
5136
61
                            poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
5137
5138
61
    m_dCenterX = (dXMin + dXMax) / 2.0;
5139
61
    m_dCenterY = (dYMin + dYMax) / 2.0;
5140
61
    m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
5141
61
    m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
5142
5143
    // Read the Arc's MBR and use that as this feature's MBR
5144
61
    poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
5145
61
    poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
5146
61
    SetMBR(dXMin, dYMin, dXMax, dYMax);
5147
5148
61
    m_nPenDefIndex = poArcHdr->m_nPenId;  // Pen index
5149
61
    poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5150
5151
    /*-----------------------------------------------------------------
5152
     * Create and fill geometry object
5153
     * For the OGR geometry, we generate an arc with 2 degrees line
5154
     * segments.
5155
     *----------------------------------------------------------------*/
5156
61
    OGRLineString *poLine = new OGRLineString;
5157
5158
61
    const int numPts = std::max(
5159
61
        2,
5160
61
        (m_dEndAngle < m_dStartAngle
5161
61
             ? static_cast<int>(
5162
1
                   std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
5163
61
             : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
5164
60
                                1)));
5165
5166
61
    TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
5167
61
                   m_dYRadius, m_dStartAngle * M_PI / 180.0,
5168
61
                   m_dEndAngle * M_PI / 180.0);
5169
5170
61
    SetGeometryDirectly(poLine);
5171
5172
61
    return 0;
5173
61
}
5174
5175
/**********************************************************************
5176
 *                   TABArc::WriteGeometryToMAPFile()
5177
 *
5178
 * Write the geometry and representation (color, etc...) part of the
5179
 * feature to the .MAP object pointed to by poMAPFile.
5180
 *
5181
 * It is assumed that poMAPFile currently points to a valid map object.
5182
 *
5183
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5184
 * been called.
5185
 **********************************************************************/
5186
int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5187
                                   TABMAPObjHdr *poObjHdr,
5188
                                   GBool bCoordBlockDataOnly /*=FALSE*/,
5189
                                   TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5190
0
{
5191
    /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5192
0
    if (bCoordBlockDataOnly)
5193
0
        return 0;
5194
5195
    /*-----------------------------------------------------------------
5196
     * We assume that ValidateMapInfoType() was called already and that
5197
     * the type in poObjHdr->m_nType is valid.
5198
     *----------------------------------------------------------------*/
5199
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5200
5201
    /*-----------------------------------------------------------------
5202
     * Fetch and validate geometry
5203
     * In the case of ARCs, this is all done inside UpdateMBR()
5204
     *----------------------------------------------------------------*/
5205
0
    if (UpdateMBR(poMapFile) != 0)
5206
0
        return -1; /* Error already reported */
5207
5208
    /*-----------------------------------------------------------------
5209
     * Copy object information
5210
     *----------------------------------------------------------------*/
5211
0
    TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5212
5213
    /*-------------------------------------------------------------
5214
     * Start/End angles
5215
     * Since we ALWAYS produce files in quadrant 1 then we can
5216
     * ignore the special angle conversion required by flipped axis.
5217
     *
5218
     * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
5219
     *------------------------------------------------------------*/
5220
0
    CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
5221
5222
0
    poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
5223
0
    poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
5224
5225
    // An arc is defined by its defining ellipse's MBR:
5226
0
    poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5227
0
                            poArcHdr->m_nArcEllipseMinX,
5228
0
                            poArcHdr->m_nArcEllipseMinY);
5229
0
    poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5230
0
                            poArcHdr->m_nArcEllipseMaxX,
5231
0
                            poArcHdr->m_nArcEllipseMaxY);
5232
5233
    // Pass the Arc's actual MBR (values were set in UpdateMBR())
5234
0
    poArcHdr->m_nMinX = m_nXMin;
5235
0
    poArcHdr->m_nMinY = m_nYMin;
5236
0
    poArcHdr->m_nMaxX = m_nXMax;
5237
0
    poArcHdr->m_nMaxY = m_nYMax;
5238
5239
0
    m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5240
0
    poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex);  // Pen index
5241
5242
0
    if (CPLGetLastErrorType() == CE_Failure)
5243
0
        return -1;
5244
5245
0
    return 0;
5246
0
}
5247
5248
/**********************************************************************
5249
 *                   TABArc::SetStart/EndAngle()
5250
 *
5251
 * Set the start/end angle values in degrees, making sure the values are
5252
 * always in the range [0..360]
5253
 **********************************************************************/
5254
void TABArc::SetStartAngle(double dAngle)
5255
0
{
5256
0
    dAngle = fmod(dAngle, 360.0);
5257
0
    if (dAngle < 0.0)
5258
0
        dAngle += 360.0;
5259
5260
0
    m_dStartAngle = dAngle;
5261
0
}
5262
5263
void TABArc::SetEndAngle(double dAngle)
5264
0
{
5265
0
    dAngle = fmod(dAngle, 360.0);
5266
0
    if (dAngle < 0.0)
5267
0
        dAngle += 360.0;
5268
5269
0
    m_dEndAngle = dAngle;
5270
0
}
5271
5272
/**********************************************************************
5273
 *                   TABArc::GetStyleString() const
5274
 *
5275
 * Return style string for this feature.
5276
 *
5277
 * Style String is built only once during the first call to GetStyleString().
5278
 **********************************************************************/
5279
const char *TABArc::GetStyleString() const
5280
0
{
5281
0
    if (m_pszStyleString == nullptr)
5282
0
    {
5283
0
        m_pszStyleString = CPLStrdup(GetPenStyleString());
5284
0
    }
5285
5286
0
    return m_pszStyleString;
5287
0
}
5288
5289
/**********************************************************************
5290
 *                   TABArc::DumpMIF()
5291
 *
5292
 * Dump feature geometry in a format similar to .MIF REGIONs.
5293
 **********************************************************************/
5294
void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
5295
0
{
5296
0
    if (fpOut == nullptr)
5297
0
        fpOut = stdout;
5298
5299
    /*-----------------------------------------------------------------
5300
     * Output ARC parameters
5301
     *----------------------------------------------------------------*/
5302
0
    fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g   %d %d)\n",
5303
0
            m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5304
0
            m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5305
0
            static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
5306
5307
    /*-----------------------------------------------------------------
5308
     * Fetch and validate geometry
5309
     *----------------------------------------------------------------*/
5310
0
    OGRGeometry *poGeom = GetGeometryRef();
5311
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
5312
0
    {
5313
        /*-------------------------------------------------------------
5314
         * Generate arc output as a simple polyline
5315
         * We could also output as an ELLIPSE in a real MIF generator
5316
         *------------------------------------------------------------*/
5317
0
        OGRLineString *poLine = poGeom->toLineString();
5318
0
        const int numPoints = poLine->getNumPoints();
5319
0
        fprintf(fpOut, "PLINE %d\n", numPoints);
5320
0
        for (int i = 0; i < numPoints; i++)
5321
0
            fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
5322
0
    }
5323
0
    else
5324
0
    {
5325
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
5326
0
                 "TABArc: Missing or Invalid Geometry!");
5327
0
        return;
5328
0
    }
5329
5330
    // Finish with PEN/BRUSH/etc. clauses
5331
0
    DumpPenDef();
5332
5333
0
    fflush(fpOut);
5334
0
}
5335
5336
/*=====================================================================
5337
 *                      class TABText
5338
 *====================================================================*/
5339
5340
/**********************************************************************
5341
 *                   TABText::TABText()
5342
 *
5343
 * Constructor.
5344
 **********************************************************************/
5345
TABText::TABText(const OGRFeatureDefn *poDefnIn)
5346
69.5k
    : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
5347
69.5k
      m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
5348
69.5k
      m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
5349
69.5k
      m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
5350
69.5k
      m_nFontStyle(0)
5351
69.5k
{
5352
69.5k
}
5353
5354
/**********************************************************************
5355
 *                   TABText::~TABText()
5356
 *
5357
 * Destructor.
5358
 **********************************************************************/
5359
TABText::~TABText()
5360
69.5k
{
5361
69.5k
    CPLFree(m_pszString);
5362
69.5k
}
5363
5364
/**********************************************************************
5365
 *                     TABText::CloneTABFeature()
5366
 *
5367
 * Duplicate feature, including stuff specific to each TABFeature type.
5368
 *
5369
 * This method calls the generic TABFeature::CopyTABFeatureBase() and
5370
 * then copies any members specific to its own type.
5371
 **********************************************************************/
5372
TABFeature *TABText::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
5373
0
{
5374
    /*-----------------------------------------------------------------
5375
     * Alloc new feature and copy the base stuff
5376
     *----------------------------------------------------------------*/
5377
0
    TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
5378
5379
0
    CopyTABFeatureBase(poNew);
5380
5381
    /*-----------------------------------------------------------------
5382
     * And members specific to this class
5383
     *----------------------------------------------------------------*/
5384
    // ITABFeaturePen
5385
0
    *(poNew->GetPenDefRef()) = *GetPenDefRef();
5386
5387
    // ITABFeatureFont
5388
0
    *(poNew->GetFontDefRef()) = *GetFontDefRef();
5389
5390
0
    poNew->SetTextString(GetTextString());
5391
0
    poNew->SetTextAngle(GetTextAngle());
5392
0
    poNew->SetTextBoxHeight(GetTextBoxHeight());
5393
0
    poNew->SetTextBoxWidth(GetTextBoxWidth());
5394
0
    poNew->SetFontStyleTABValue(GetFontStyleTABValue());
5395
0
    poNew->SetFontBGColor(GetFontBGColor());
5396
0
    poNew->SetFontFGColor(GetFontFGColor());
5397
0
    poNew->SetFontOColor(GetFontOColor());
5398
0
    poNew->SetFontSColor(GetFontSColor());
5399
5400
0
    poNew->SetTextJustification(GetTextJustification());
5401
0
    poNew->SetTextSpacing(GetTextSpacing());
5402
    // Note: Text arrow/line coordinates are not transported... but
5403
    //       we ignore them most of the time anyways.
5404
0
    poNew->SetTextLineType(TABTLNoLine);
5405
5406
0
    return poNew;
5407
0
}
5408
5409
/**********************************************************************
5410
 *                   TABText::ValidateMapInfoType()
5411
 *
5412
 * Check the feature's geometry part and return the corresponding
5413
 * mapinfo object type code.  The m_nMapInfoType member will also
5414
 * be updated for further calls to GetMapInfoType();
5415
 *
5416
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
5417
 * is expected for this object class.
5418
 **********************************************************************/
5419
TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
5420
0
{
5421
    /*-----------------------------------------------------------------
5422
     * Fetch and validate geometry
5423
     *----------------------------------------------------------------*/
5424
0
    OGRGeometry *poGeom = GetGeometryRef();
5425
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5426
0
    {
5427
0
        m_nMapInfoType = TAB_GEOM_TEXT;
5428
0
    }
5429
0
    else
5430
0
    {
5431
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
5432
0
                 "TABText: Missing or Invalid Geometry!");
5433
0
        m_nMapInfoType = TAB_GEOM_NONE;
5434
0
    }
5435
5436
    /*-----------------------------------------------------------------
5437
     * Decide if coordinates should be compressed or not.
5438
     *----------------------------------------------------------------*/
5439
    // __TODO__ For now we always write uncompressed for this class...
5440
    // ValidateCoordType(poMapFile);
5441
0
    UpdateMBR(poMapFile);
5442
5443
0
    return m_nMapInfoType;
5444
0
}
5445
5446
/**********************************************************************
5447
 *                   TABText::ReadGeometryFromMAPFile()
5448
 *
5449
 * Fill the geometry and representation (color, etc...) part of the
5450
 * feature from the contents of the .MAP object pointed to by poMAPFile.
5451
 *
5452
 * It is assumed that poMAPFile currently points to the beginning of
5453
 * a map object.
5454
 *
5455
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5456
 * been called.
5457
 **********************************************************************/
5458
int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5459
                                     TABMAPObjHdr *poObjHdr,
5460
                                     GBool bCoordBlockDataOnly /*=FALSE*/,
5461
                                     TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5462
24
{
5463
    /*-----------------------------------------------------------------
5464
     * Fetch and validate geometry type
5465
     *----------------------------------------------------------------*/
5466
24
    m_nMapInfoType = poObjHdr->m_nType;
5467
5468
24
    if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
5469
0
    {
5470
0
        CPLError(
5471
0
            CE_Failure, CPLE_AssertionFailed,
5472
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5473
0
            m_nMapInfoType, m_nMapInfoType);
5474
0
        return -1;
5475
0
    }
5476
5477
    /*=============================================================
5478
     * TEXT
5479
     *============================================================*/
5480
5481
    /*-----------------------------------------------------------------
5482
     * Read object information
5483
     *----------------------------------------------------------------*/
5484
24
    TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5485
5486
24
    const GInt32 nCoordBlockPtr =
5487
24
        poTextHdr->m_nCoordBlockPtr;                     // String position
5488
24
    const int nStringLen = poTextHdr->m_nCoordDataSize;  // String length
5489
24
    m_nTextAlignment = poTextHdr->m_nTextAlignment;      // just./spacing/arrow
5490
5491
    /*-------------------------------------------------------------
5492
     * Text Angle, in tenths of degree.
5493
     * Contrary to arc start/end angles, no conversion based on
5494
     * origin quadrant is required here.
5495
     *------------------------------------------------------------*/
5496
24
    m_dAngle = poTextHdr->m_nAngle / 10.0;
5497
5498
24
    m_nFontStyle = poTextHdr->m_nFontStyle;  // Font style
5499
5500
24
    m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
5501
24
                       poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
5502
24
    m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
5503
24
                       poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
5504
24
    m_rgbOutline = m_rgbBackground;
5505
    // In MapInfo, the shadow color is always gray (128,128,128)
5506
24
    m_rgbShadow = 0x808080;
5507
5508
    // arrow endpoint
5509
24
    poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
5510
24
                            m_dfLineEndX, m_dfLineEndY);
5511
24
    m_bLineEndSet = TRUE;
5512
5513
    // Text Height
5514
24
    double dJunk = 0.0;
5515
24
    poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
5516
5517
24
    if (!bCoordBlockDataOnly)
5518
24
    {
5519
24
        m_nFontDefIndex = poTextHdr->m_nFontId;  // Font name index
5520
24
        poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
5521
24
    }
5522
5523
    // MBR after rotation
5524
24
    double dXMin = 0.0;
5525
24
    double dYMin = 0.0;
5526
24
    double dXMax = 0.0;
5527
24
    double dYMax = 0.0;
5528
24
    poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
5529
24
                            dYMin);
5530
24
    poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
5531
24
                            dYMax);
5532
5533
24
    if (!bCoordBlockDataOnly)
5534
24
    {
5535
24
        m_nPenDefIndex = poTextHdr->m_nPenId;  // Pen index for line
5536
24
        poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5537
24
    }
5538
5539
    /*-------------------------------------------------------------
5540
     * Read text string from the coord. block
5541
     * Note that the string may contain binary '\n' and '\\' chars
5542
     * that we keep to an unescaped form internally. This is to
5543
     * be like OGR drivers. See bug 1107 for details.
5544
     *------------------------------------------------------------*/
5545
24
    char *pszTmpString =
5546
24
        static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
5547
5548
24
    if (nStringLen > 0)
5549
24
    {
5550
24
        TABMAPCoordBlock *poCoordBlock = nullptr;
5551
5552
24
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5553
0
            poCoordBlock = *ppoCoordBlock;
5554
24
        else
5555
24
            poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
5556
24
        if (poCoordBlock == nullptr ||
5557
24
            poCoordBlock->ReadBytes(
5558
24
                nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
5559
0
        {
5560
0
            CPLError(CE_Failure, CPLE_FileIO,
5561
0
                     "Failed reading text string at offset %d", nCoordBlockPtr);
5562
0
            CPLFree(pszTmpString);
5563
0
            return -1;
5564
0
        }
5565
5566
        /* Return a ref to coord block so that caller can continue reading
5567
         * after the end of this object (used by index splitting)
5568
         */
5569
24
        if (ppoCoordBlock)
5570
0
            *ppoCoordBlock = poCoordBlock;
5571
24
    }
5572
5573
24
    pszTmpString[nStringLen] = '\0';
5574
5575
24
    if (!poMapFile->GetEncoding().empty())
5576
0
    {
5577
0
        char *pszUtf8String =
5578
0
            CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
5579
0
        CPLFree(pszTmpString);
5580
0
        pszTmpString = pszUtf8String;
5581
0
    }
5582
5583
24
    CPLFree(m_pszString);
5584
24
    m_pszString = pszTmpString;  // This string was Escaped before 20050714
5585
5586
    /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
5587
     */
5588
24
    SetMBR(dXMin, dYMin, dXMax, dYMax);
5589
24
    GetMBR(dXMin, dYMin, dXMax, dYMax);
5590
5591
    /* Copy int MBR to feature class members */
5592
24
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
5593
24
              poObjHdr->m_nMaxY);
5594
5595
    /*-----------------------------------------------------------------
5596
     * Create an OGRPoint Geometry...
5597
     * The point X,Y values will be the coords of the lower-left corner before
5598
     * rotation is applied.  (Note that the rotation in MapInfo is done around
5599
     * the upper-left corner)
5600
     * We need to calculate the true lower left corner of the text based
5601
     * on the MBR after rotation, the text height and the rotation angle.
5602
     *----------------------------------------------------------------*/
5603
24
    double dSin = sin(m_dAngle * M_PI / 180.0);
5604
24
    double dCos = cos(m_dAngle * M_PI / 180.0);
5605
24
    double dX = 0.0;
5606
24
    double dY = 0.0;
5607
24
    if (dSin > 0.0 && dCos > 0.0)
5608
24
    {
5609
24
        dX = dXMin + m_dHeight * dSin;
5610
24
        dY = dYMin;
5611
24
    }
5612
0
    else if (dSin > 0.0 && dCos < 0.0)
5613
0
    {
5614
0
        dX = dXMax;
5615
0
        dY = dYMin - m_dHeight * dCos;
5616
0
    }
5617
0
    else if (dSin < 0.0 && dCos < 0.0)
5618
0
    {
5619
0
        dX = dXMax + m_dHeight * dSin;
5620
0
        dY = dYMax;
5621
0
    }
5622
0
    else  // dSin < 0 && dCos > 0
5623
0
    {
5624
0
        dX = dXMin;
5625
0
        dY = dYMax - m_dHeight * dCos;
5626
0
    }
5627
5628
24
    OGRGeometry *poGeometry = new OGRPoint(dX, dY);
5629
5630
24
    SetGeometryDirectly(poGeometry);
5631
5632
    /*-----------------------------------------------------------------
5633
     * Compute Text Width: the width of the Text MBR before rotation
5634
     * in ground units... unfortunately this value is not stored in the
5635
     * file, so we have to compute it with the MBR after rotation and
5636
     * the height of the MBR before rotation:
5637
     * With  W = Width of MBR before rotation
5638
     *       H = Height of MBR before rotation
5639
     *       dX = Width of MBR after rotation
5640
     *       dY = Height of MBR after rotation
5641
     *       teta = rotation angle
5642
     *
5643
     *  For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
5644
     *   W = H * (dX - H * sin(teta)) / (H * cos(teta))
5645
     *
5646
     * and for other teta values, use:
5647
     *   W = H * (dY - H * cos(teta)) / (H * sin(teta))
5648
     *----------------------------------------------------------------*/
5649
24
    dSin = std::abs(dSin);
5650
24
    dCos = std::abs(dCos);
5651
24
    if (m_dHeight == 0.0)
5652
0
        m_dWidth = 0.0;
5653
24
    else if (dCos > dSin)
5654
24
        m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
5655
24
                   (m_dHeight * dCos);
5656
0
    else
5657
0
        m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
5658
0
                   (m_dHeight * dSin);
5659
24
    m_dWidth = std::abs(m_dWidth);
5660
5661
24
    return 0;
5662
24
}
5663
5664
/**********************************************************************
5665
 *                   TABText::WriteGeometryToMAPFile()
5666
 *
5667
 * Write the geometry and representation (color, etc...) part of the
5668
 * feature to the .MAP object pointed to by poMAPFile.
5669
 *
5670
 * It is assumed that poMAPFile currently points to a valid map object.
5671
 *
5672
 * Returns 0 on success, -1 on error, in which case CPLError() will have
5673
 * been called.
5674
 **********************************************************************/
5675
int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5676
                                    TABMAPObjHdr *poObjHdr,
5677
                                    GBool bCoordBlockDataOnly /*=FALSE*/,
5678
                                    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5679
0
{
5680
0
    GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
5681
5682
    /*-----------------------------------------------------------------
5683
     * We assume that ValidateMapInfoType() was called already and that
5684
     * the type in poObjHdr->m_nType is valid.
5685
     *----------------------------------------------------------------*/
5686
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5687
5688
    /*-----------------------------------------------------------------
5689
     * Fetch and validate geometry
5690
     *----------------------------------------------------------------*/
5691
0
    OGRGeometry *poGeom = GetGeometryRef();
5692
0
    OGRPoint *poPoint = nullptr;
5693
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5694
0
        poPoint = poGeom->toPoint();
5695
0
    else
5696
0
    {
5697
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
5698
0
                 "TABText: Missing or Invalid Geometry!");
5699
0
        return -1;
5700
0
    }
5701
5702
0
    poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
5703
5704
    /*-----------------------------------------------------------------
5705
     * Write string to a coord block first...
5706
     * Note that the string may contain unescaped '\n' and '\\'
5707
     * that we have to keep like that for the MAP file.
5708
     * See MapTools bug 1107 for more details.
5709
     *----------------------------------------------------------------*/
5710
0
    TABMAPCoordBlock *poCoordBlock = nullptr;
5711
0
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5712
0
        poCoordBlock = *ppoCoordBlock;
5713
0
    else
5714
0
        poCoordBlock = poMapFile->GetCurCoordBlock();
5715
0
    poCoordBlock->StartNewFeature();
5716
0
    GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
5717
5718
    // This string was escaped before 20050714
5719
0
    CPLString oTmpString(m_pszString ? m_pszString : "");
5720
0
    if (!poMapFile->GetEncoding().empty())
5721
0
    {
5722
0
        oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
5723
0
    }
5724
5725
0
    int nStringLen = static_cast<int>(oTmpString.length());
5726
5727
0
    if (nStringLen > 0)
5728
0
    {
5729
0
        poCoordBlock->WriteBytes(
5730
0
            nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
5731
0
    }
5732
0
    else
5733
0
    {
5734
0
        nCoordBlockPtr = 0;
5735
0
    }
5736
5737
    /*-----------------------------------------------------------------
5738
     * Copy object information
5739
     *----------------------------------------------------------------*/
5740
0
    TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5741
5742
0
    poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr;    // String position
5743
0
    poTextHdr->m_nCoordDataSize = nStringLen;        // String length
5744
0
    poTextHdr->m_nTextAlignment = m_nTextAlignment;  // just./spacing/arrow
5745
5746
    /*-----------------------------------------------------------------
5747
     * Text Angle, (written in tenths of degrees)
5748
     * Contrary to arc start/end angles, no conversion based on
5749
     * origin quadrant is required here.
5750
     *----------------------------------------------------------------*/
5751
0
    poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
5752
5753
0
    poTextHdr->m_nFontStyle = m_nFontStyle;  // Font style/effect
5754
5755
0
    poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
5756
0
    poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
5757
0
    poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
5758
5759
0
    poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
5760
0
    poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
5761
0
    poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
5762
5763
    /*-----------------------------------------------------------------
5764
     * The OGRPoint's X,Y values were the coords of the lower-left corner
5765
     * before rotation was applied.  (Note that the rotation in MapInfo is
5766
     * done around the upper-left corner)
5767
     * The Feature's MBR is the MBR of the text after rotation... that's
5768
     * what MapInfo uses to define the text location.
5769
     *----------------------------------------------------------------*/
5770
0
    double dXMin = 0.0;
5771
0
    double dYMin = 0.0;
5772
0
    double dXMax = 0.0;
5773
0
    double dYMax = 0.0;
5774
    // Make sure Feature MBR is in sync with other params
5775
5776
0
    UpdateMBR();
5777
0
    GetMBR(dXMin, dYMin, dXMax, dYMax);
5778
5779
0
    poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
5780
0
    poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
5781
5782
    // Label line end point
5783
0
    double dX = 0.0;
5784
0
    double dY = 0.0;
5785
0
    GetTextLineEndPoint(dX, dY);  // Make sure a default line end point is set
5786
0
    poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
5787
0
                            poTextHdr->m_nLineEndY);
5788
5789
    // Text Height
5790
0
    poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
5791
0
    poTextHdr->m_nHeight = nY;
5792
5793
0
    if (!bCoordBlockDataOnly)
5794
0
    {
5795
        // Font name
5796
0
        m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
5797
0
        poTextHdr->m_nFontId =
5798
0
            static_cast<GByte>(m_nFontDefIndex);  // Font name index
5799
0
    }
5800
5801
    // MBR after rotation
5802
0
    poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
5803
5804
0
    if (!bCoordBlockDataOnly)
5805
0
    {
5806
0
        m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5807
0
        poTextHdr->m_nPenId =
5808
0
            static_cast<GByte>(m_nPenDefIndex);  // Pen index for line/arrow
5809
0
    }
5810
5811
0
    if (CPLGetLastErrorType() == CE_Failure)
5812
0
        return -1;
5813
5814
    /* Return a ref to coord block so that caller can continue writing
5815
     * after the end of this object (used by index splitting)
5816
     */
5817
0
    if (ppoCoordBlock)
5818
0
        *ppoCoordBlock = poCoordBlock;
5819
5820
0
    return 0;
5821
0
}
5822
5823
/**********************************************************************
5824
 *                   TABText::GetTextString()
5825
 *
5826
 * Return ref to text string value.
5827
 *
5828
 * Returned string is a reference to the internal string buffer and should
5829
 * not be modified or freed by the caller.
5830
 **********************************************************************/
5831
const char *TABText::GetTextString() const
5832
68.2k
{
5833
68.2k
    if (m_pszString == nullptr)
5834
0
        return "";
5835
5836
68.2k
    return m_pszString;
5837
68.2k
}
5838
5839
/**********************************************************************
5840
 *                   TABText::SetTextString()
5841
 *
5842
 * Set new text string value.
5843
 *
5844
 * Note: The text string may contain "\n" chars or "\\" chars
5845
 * and we expect to receive them in a 2 chars escaped form as
5846
 * described in the MIF format specs.
5847
 **********************************************************************/
5848
void TABText::SetTextString(const char *pszNewStr)
5849
0
{
5850
0
    CPLFree(m_pszString);
5851
0
    m_pszString = CPLStrdup(pszNewStr);
5852
0
}
5853
5854
/**********************************************************************
5855
 *                   TABText::GetTextAngle()
5856
 *
5857
 * Return text angle in degrees.
5858
 **********************************************************************/
5859
double TABText::GetTextAngle() const
5860
0
{
5861
0
    return m_dAngle;
5862
0
}
5863
5864
void TABText::SetTextAngle(double dAngle)
5865
35.2k
{
5866
    // Make sure angle is in the range [0..360]
5867
35.2k
    dAngle = fmod(dAngle, 360.0);
5868
35.2k
    if (dAngle < 0.0)
5869
8.46k
        dAngle += 360.0;
5870
35.2k
    m_dAngle = dAngle;
5871
35.2k
    UpdateMBR();
5872
35.2k
}
5873
5874
/**********************************************************************
5875
 *                   TABText::GetTextBoxHeight()
5876
 *
5877
 * Return text height in Y axis coord. units of the text box before rotation.
5878
 **********************************************************************/
5879
double TABText::GetTextBoxHeight() const
5880
0
{
5881
0
    return m_dHeight;
5882
0
}
5883
5884
void TABText::SetTextBoxHeight(double dHeight)
5885
0
{
5886
0
    m_dHeight = dHeight;
5887
0
    UpdateMBR();
5888
0
}
5889
5890
/**********************************************************************
5891
 *                   TABText::GetTextBoxWidth()
5892
 *
5893
 * Return text width in X axis coord. units. of the text box before rotation.
5894
 *
5895
 * If value has not been set, then we force a default value that assumes
5896
 * that one char's box width is 60% of its height... and we ignore
5897
 * the multiline case.  This should not matter when the user PROPERLY sets
5898
 * the value.
5899
 **********************************************************************/
5900
double TABText::GetTextBoxWidth() const
5901
0
{
5902
0
    if (m_dWidth == 0.0 && m_pszString)
5903
0
    {
5904
0
        m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
5905
0
    }
5906
0
    return m_dWidth;
5907
0
}
5908
5909
void TABText::SetTextBoxWidth(double dWidth)
5910
0
{
5911
0
    m_dWidth = dWidth;
5912
0
    UpdateMBR();
5913
0
}
5914
5915
/**********************************************************************
5916
 *                   TABText::GetTextLineEndPoint()
5917
 *
5918
 * Return X,Y coordinates of the text label line end point.
5919
 * Default is the center of the text MBR.
5920
 **********************************************************************/
5921
void TABText::GetTextLineEndPoint(double &dX, double &dY)
5922
0
{
5923
0
    if (!m_bLineEndSet)
5924
0
    {
5925
        // Set default location at center of text MBR
5926
0
        double dXMin = 0.0;
5927
0
        double dYMin = 0.0;
5928
0
        double dXMax = 0.0;
5929
0
        double dYMax = 0.0;
5930
0
        UpdateMBR();
5931
0
        GetMBR(dXMin, dYMin, dXMax, dYMax);
5932
0
        m_dfLineEndX = (dXMin + dXMax) / 2.0;
5933
0
        m_dfLineEndY = (dYMin + dYMax) / 2.0;
5934
0
        m_bLineEndSet = TRUE;
5935
0
    }
5936
5937
    // Return values
5938
0
    dX = m_dfLineEndX;
5939
0
    dY = m_dfLineEndY;
5940
0
}
5941
5942
void TABText::SetTextLineEndPoint(double dX, double dY)
5943
1.80k
{
5944
1.80k
    m_dfLineEndX = dX;
5945
1.80k
    m_dfLineEndY = dY;
5946
1.80k
    m_bLineEndSet = TRUE;
5947
1.80k
}
5948
5949
/**********************************************************************
5950
 *                   TABText::UpdateMBR()
5951
 *
5952
 * Update the feature MBR using the text origin (OGRPoint geometry), the
5953
 * rotation angle, and the Width/height before rotation.
5954
 *
5955
 * This function cannot perform properly unless all the above have been set.
5956
 *
5957
 * Returns 0 on success, or -1 if there is no geometry in object
5958
 **********************************************************************/
5959
int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
5960
35.2k
{
5961
35.2k
    OGRGeometry *poGeom = GetGeometryRef();
5962
35.2k
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5963
0
    {
5964
0
        OGRPoint *poPoint = poGeom->toPoint();
5965
5966
0
        const double dX0 = poPoint->getX();
5967
0
        const double dY0 = poPoint->getY();
5968
5969
0
        const double dSin = sin(m_dAngle * M_PI / 180.0);
5970
0
        const double dCos = cos(m_dAngle * M_PI / 180.0);
5971
5972
0
        GetTextBoxWidth();  // Force default width value if necessary.
5973
5974
0
        const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
5975
0
        const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
5976
5977
0
        SetMBR(dX0, dY0, dX0, dY0);
5978
0
        for (int i = 0; i < 4; i++)
5979
0
        {
5980
            // Rotate one of the box corners
5981
0
            const double dX1 =
5982
0
                dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
5983
0
            const double dY1 =
5984
0
                dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
5985
5986
            // And update feature MBR with rotated coordinate
5987
0
            if (dX1 < m_dXMin)
5988
0
                m_dXMin = dX1;
5989
0
            if (dX1 > m_dXMax)
5990
0
                m_dXMax = dX1;
5991
0
            if (dY1 < m_dYMin)
5992
0
                m_dYMin = dY1;
5993
0
            if (dY1 > m_dYMax)
5994
0
                m_dYMax = dY1;
5995
0
        }
5996
5997
0
        if (poMapFile)
5998
0
        {
5999
0
            poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
6000
0
            poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
6001
0
        }
6002
6003
0
        return 0;
6004
0
    }
6005
6006
35.2k
    return -1;
6007
35.2k
}
6008
6009
/**********************************************************************
6010
 *                   TABText::GetFontBGColor()
6011
 *
6012
 * Return background color.
6013
 **********************************************************************/
6014
GInt32 TABText::GetFontBGColor() const
6015
0
{
6016
0
    return m_rgbBackground;
6017
0
}
6018
6019
void TABText::SetFontBGColor(GInt32 rgbColor)
6020
3.97k
{
6021
3.97k
    m_rgbBackground = rgbColor;
6022
3.97k
}
6023
6024
/**********************************************************************
6025
 *                   TABText::GetFontOColor()
6026
 *
6027
 * Return outline color.
6028
 **********************************************************************/
6029
GInt32 TABText::GetFontOColor() const
6030
0
{
6031
0
    return m_rgbOutline;
6032
0
}
6033
6034
void TABText::SetFontOColor(GInt32 rgbColor)
6035
0
{
6036
0
    m_rgbOutline = rgbColor;
6037
0
}
6038
6039
/**********************************************************************
6040
 *                   TABText::GetFontSColor()
6041
 *
6042
 * Return shadow color.
6043
 **********************************************************************/
6044
GInt32 TABText::GetFontSColor() const
6045
0
{
6046
0
    return m_rgbShadow;
6047
0
}
6048
6049
void TABText::SetFontSColor(GInt32 rgbColor)
6050
0
{
6051
0
    m_rgbShadow = rgbColor;
6052
0
}
6053
6054
/**********************************************************************
6055
 *                   TABText::GetFontFGColor()
6056
 *
6057
 * Return foreground color.
6058
 **********************************************************************/
6059
GInt32 TABText::GetFontFGColor() const
6060
0
{
6061
0
    return m_rgbForeground;
6062
0
}
6063
6064
void TABText::SetFontFGColor(GInt32 rgbColor)
6065
6.55k
{
6066
6.55k
    m_rgbForeground = rgbColor;
6067
6.55k
}
6068
6069
/**********************************************************************
6070
 *                   TABText::GetTextJustification()
6071
 *
6072
 * Return text justification.  Default is TABTJLeft
6073
 **********************************************************************/
6074
TABTextJust TABText::GetTextJustification() const
6075
0
{
6076
0
    TABTextJust eJust = TABTJLeft;
6077
6078
0
    if (m_nTextAlignment & 0x0200)
6079
0
        eJust = TABTJCenter;
6080
0
    else if (m_nTextAlignment & 0x0400)
6081
0
        eJust = TABTJRight;
6082
6083
0
    return eJust;
6084
0
}
6085
6086
void TABText::SetTextJustification(TABTextJust eJustification)
6087
2.84k
{
6088
    // Flush current value... default is TABTJLeft
6089
2.84k
    m_nTextAlignment &= ~0x0600;
6090
    // ... and set new one.
6091
2.84k
    if (eJustification == TABTJCenter)
6092
2.16k
        m_nTextAlignment |= 0x0200;
6093
674
    else if (eJustification == TABTJRight)
6094
674
        m_nTextAlignment |= 0x0400;
6095
2.84k
}
6096
6097
/**********************************************************************
6098
 *                   TABText::GetTextSpacing()
6099
 *
6100
 * Return text vertical spacing factor.  Default is TABTSSingle
6101
 **********************************************************************/
6102
TABTextSpacing TABText::GetTextSpacing() const
6103
0
{
6104
0
    TABTextSpacing eSpacing = TABTSSingle;
6105
6106
0
    if (m_nTextAlignment & 0x0800)
6107
0
        eSpacing = TABTS1_5;
6108
0
    else if (m_nTextAlignment & 0x1000)
6109
0
        eSpacing = TABTSDouble;
6110
6111
0
    return eSpacing;
6112
0
}
6113
6114
void TABText::SetTextSpacing(TABTextSpacing eSpacing)
6115
6.77k
{
6116
    // Flush current value... default is TABTSSingle
6117
6.77k
    m_nTextAlignment &= ~0x1800;
6118
    // ... and set new one.
6119
6.77k
    if (eSpacing == TABTS1_5)
6120
2.65k
        m_nTextAlignment |= 0x0800;
6121
4.12k
    else if (eSpacing == TABTSDouble)
6122
4.12k
        m_nTextAlignment |= 0x1000;
6123
6.77k
}
6124
6125
/**********************************************************************
6126
 *                   TABText::GetTextLineType()
6127
 *
6128
 * Return text line (arrow) type.  Default is TABTLNoLine
6129
 **********************************************************************/
6130
TABTextLineType TABText::GetTextLineType() const
6131
0
{
6132
0
    TABTextLineType eLine = TABTLNoLine;
6133
6134
0
    if (m_nTextAlignment & 0x2000)
6135
0
        eLine = TABTLSimple;
6136
0
    else if (m_nTextAlignment & 0x4000)
6137
0
        eLine = TABTLArrow;
6138
6139
0
    return eLine;
6140
0
}
6141
6142
void TABText::SetTextLineType(TABTextLineType eLineType)
6143
1.80k
{
6144
    // Flush current value... default is TABTLNoLine
6145
1.80k
    m_nTextAlignment &= ~0x6000;
6146
    // ... and set new one.
6147
1.80k
    if (eLineType == TABTLSimple)
6148
1.48k
        m_nTextAlignment |= 0x2000;
6149
328
    else if (eLineType == TABTLArrow)
6150
328
        m_nTextAlignment |= 0x4000;
6151
1.80k
}
6152
6153
/**********************************************************************
6154
 *                   TABText::QueryFontStyle()
6155
 *
6156
 * Return TRUE if the specified font style attribute is turned ON,
6157
 * or FALSE otherwise.  See enum TABFontStyle for the list of styles
6158
 * that can be queried on.
6159
 **********************************************************************/
6160
GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
6161
3.97k
{
6162
3.97k
    return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
6163
3.97k
}
6164
6165
void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
6166
3.36k
{
6167
3.36k
    if (bStyleOn)
6168
3.36k
        m_nFontStyle |= static_cast<int>(eStyleToToggle);
6169
0
    else
6170
0
        m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
6171
3.36k
}
6172
6173
/**********************************************************************
6174
 *                   TABText::GetFontStyleMIFValue()
6175
 *
6176
 * Return the Font Style value for this object using the style values
6177
 * that are used in a MIF FONT() clause.  See MIF specs (appendix A).
6178
 *
6179
 * The reason why we have to differentiate between the TAB and the MIF font
6180
 * style values is that in TAB, TABFSBox is included in the style value
6181
 * as code 0x100, but in MIF it is not included, instead it is implied by
6182
 * the presence of the BG color in the FONT() clause (the BG color is
6183
 * present only when TABFSBox or TABFSHalo is set).
6184
 * This also has the effect of shifting all the other style values > 0x100
6185
 * by 1 byte.
6186
 **********************************************************************/
6187
int TABText::GetFontStyleMIFValue() const
6188
0
{
6189
    // The conversion is simply to remove bit 0x100 from the value and shift
6190
    // down all values past this bit.
6191
0
    return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
6192
0
}
6193
6194
void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
6195
6.55k
{
6196
6.55k
    m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
6197
    // When BG color is set, then either BOX or HALO should be set.
6198
6.55k
    if (bBGColorSet && !QueryFontStyle(TABFSHalo))
6199
3.36k
        ToggleFontStyle(TABFSBox, TRUE);
6200
6.55k
}
6201
6202
int TABText::IsFontBGColorUsed() const
6203
0
{
6204
    // Font BG color is used only when BOX is set.
6205
0
    return QueryFontStyle(TABFSBox);
6206
0
}
6207
6208
int TABText::IsFontOColorUsed() const
6209
0
{
6210
    // Font outline color is used only when HALO is set.
6211
0
    return QueryFontStyle(TABFSHalo);
6212
0
}
6213
6214
int TABText::IsFontSColorUsed() const
6215
0
{
6216
    // Font shadow color is used only when Shadow is set.
6217
0
    return QueryFontStyle(TABFSShadow);
6218
0
}
6219
6220
int TABText::IsFontBold() const
6221
0
{
6222
    // Font bold is used only when Bold is set.
6223
0
    return QueryFontStyle(TABFSBold);
6224
0
}
6225
6226
int TABText::IsFontItalic() const
6227
0
{
6228
    // Font italic is used only when Italic is set.
6229
0
    return QueryFontStyle(TABFSItalic);
6230
0
}
6231
6232
int TABText::IsFontUnderline() const
6233
0
{
6234
    // Font underline is used only when Underline is set.
6235
0
    return QueryFontStyle(TABFSUnderline);
6236
0
}
6237
6238
/**********************************************************************
6239
 *                   TABText::GetLabelStyleString()
6240
 *
6241
 * This is not the correct location, it should be in ITABFeatureFont,
6242
 * but it is really more easy to put it here.  This fct return a complete
6243
 * string for the representation with the string to display
6244
 **********************************************************************/
6245
const char *TABText::GetLabelStyleString() const
6246
0
{
6247
0
    const char *pszStyle = nullptr;
6248
0
    int nStringLen = static_cast<int>(strlen(GetTextString()));
6249
    // ALL Caps, Extpanded need to modify the string value
6250
0
    char *pszTextString =
6251
0
        static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
6252
    /* char szPattern[20]; */
6253
0
    int nJustification = 1;
6254
6255
0
    strcpy(pszTextString, GetTextString());
6256
    /* szPattern[0] = '\0'; */
6257
6258
0
    switch (GetTextJustification())
6259
0
    {
6260
0
        case TABTJCenter:
6261
0
            nJustification = 2;
6262
0
            break;
6263
0
        case TABTJRight:
6264
0
            nJustification = 3;
6265
0
            break;
6266
0
        case TABTJLeft:
6267
0
        default:
6268
0
            nJustification = 1;
6269
0
            break;
6270
0
    }
6271
6272
    // Compute real font size, taking number of lines ("\\n", "\n") and line
6273
    // spacing into account.
6274
0
    int numLines = 1;
6275
0
    for (int i = 0; pszTextString[i];
6276
0
         numLines +=
6277
0
         ((pszTextString[i] == '\n' ||
6278
0
           (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
6279
0
          pszTextString[i + 1] != '\0'),
6280
0
             ++i)
6281
0
        ;
6282
6283
0
    double dHeight = GetTextBoxHeight() / numLines;
6284
6285
    // In all cases, take out 20% of font height to account for line spacing
6286
0
    if (numLines > 1)
6287
0
    {
6288
0
        switch (GetTextSpacing())
6289
0
        {
6290
0
            case TABTS1_5:
6291
0
                dHeight *= (0.80 * 0.69);
6292
0
                break;
6293
0
            case TABTSDouble:
6294
0
                dHeight *= (0.66 * 0.69);
6295
0
                break;
6296
0
            default:
6297
0
                dHeight *= 0.69;
6298
0
        }
6299
0
    }
6300
0
    else
6301
0
    {
6302
0
        dHeight *= 0.69;
6303
0
    }
6304
6305
0
    if (QueryFontStyle(TABFSAllCaps))
6306
0
        for (int i = 0; pszTextString[i]; ++i)
6307
0
            if (isalpha(static_cast<unsigned char>(pszTextString[i])))
6308
0
                pszTextString[i] = static_cast<char>(
6309
0
                    CPLToupper(static_cast<unsigned char>(pszTextString[i])));
6310
6311
    /* Escape the double quote chars and expand the text */
6312
0
    char *pszTmpTextString = nullptr;
6313
6314
0
    if (QueryFontStyle(TABFSExpanded))
6315
0
        pszTmpTextString = static_cast<char *>(
6316
0
            CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
6317
0
    else
6318
0
        pszTmpTextString = static_cast<char *>(
6319
0
            CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
6320
6321
0
    int j = 0;
6322
0
    for (int i = 0; i < nStringLen; ++i, ++j)
6323
0
    {
6324
0
        if (pszTextString[i] == '"')
6325
0
        {
6326
0
            pszTmpTextString[j] = '\\';
6327
0
            pszTmpTextString[j + 1] = pszTextString[i];
6328
0
            ++j;
6329
0
        }
6330
0
        else
6331
0
            pszTmpTextString[j] = pszTextString[i];
6332
6333
0
        if (QueryFontStyle(TABFSExpanded))
6334
0
        {
6335
0
            pszTmpTextString[j + 1] = ' ';
6336
0
            ++j;
6337
0
        }
6338
0
    }
6339
6340
0
    pszTmpTextString[j] = '\0';
6341
0
    CPLFree(pszTextString);
6342
0
    pszTextString = static_cast<char *>(
6343
0
        CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
6344
0
    strcpy(pszTextString, pszTmpTextString);
6345
0
    CPLFree(pszTmpTextString);
6346
6347
0
    const char *pszBGColor =
6348
0
        IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
6349
0
    const char *pszOColor =
6350
0
        IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
6351
0
    const char *pszSColor =
6352
0
        IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
6353
0
    const char *pszBold = IsFontBold() ? ",bo:1" : "";
6354
0
    const char *pszItalic = IsFontItalic() ? ",it:1" : "";
6355
0
    const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
6356
6357
0
    pszStyle = CPLSPrintf(
6358
0
        "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
6359
0
        pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
6360
0
        pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
6361
0
        GetFontNameRef());
6362
6363
0
    CPLFree(pszTextString);
6364
0
    return pszStyle;
6365
0
}
6366
6367
/**********************************************************************
6368
 *                   TABText::GetStyleString() const
6369
 *
6370
 * Return style string for this feature.
6371
 *
6372
 * Style String is built only once during the first call to GetStyleString().
6373
 **********************************************************************/
6374
const char *TABText::GetStyleString() const
6375
0
{
6376
0
    if (m_pszStyleString == nullptr)
6377
0
    {
6378
0
        m_pszStyleString = CPLStrdup(GetLabelStyleString());
6379
0
    }
6380
6381
0
    return m_pszStyleString;
6382
0
}
6383
6384
void TABText::SetLabelFromStyleString(const char *pszStyleString)
6385
0
{
6386
    // Use the Style Manager to retrieve all the information we need.
6387
0
    auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
6388
0
    std::unique_ptr<OGRStyleTool> poStylePart;
6389
6390
    // Init the StyleMgr with the StyleString.
6391
0
    poStyleMgr->InitStyleString(pszStyleString);
6392
6393
    // Retrieve the Symbol info.
6394
0
    const int numParts = poStyleMgr->GetPartCount();
6395
0
    for (int i = 0; i < numParts; i++)
6396
0
    {
6397
0
        poStylePart.reset(poStyleMgr->GetPart(i));
6398
0
        if (poStylePart == nullptr)
6399
0
        {
6400
0
            continue;
6401
0
        }
6402
6403
0
        if (poStylePart->GetType() == OGRSTCLabel)
6404
0
        {
6405
0
            break;
6406
0
        }
6407
0
        else
6408
0
        {
6409
0
            poStylePart.reset();
6410
0
        }
6411
0
    }
6412
6413
    // If the no Symbol found, do nothing.
6414
0
    if (poStylePart == nullptr)
6415
0
    {
6416
0
        return;
6417
0
    }
6418
6419
0
    auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
6420
6421
0
    GBool bIsNull = 0;
6422
0
    const char *pszText = poLabelStyle->TextString(bIsNull);
6423
0
    if (!bIsNull && pszText)
6424
0
    {
6425
0
        SetTextString(pszText);
6426
6427
0
        poLabelStyle->SetUnit(OGRSTUMM);
6428
0
        double dfSize = poLabelStyle->Size(bIsNull);
6429
0
        if (!bIsNull)
6430
0
        {
6431
0
            dfSize /= 1000;
6432
6433
            // Compute text box height, taking number of lines ("\\n", "\n") and
6434
            // line spacing into account.
6435
0
            int numLines = 1;
6436
0
            for (int i = 0; pszText[i];
6437
0
                 numLines += ((pszText[i] == '\n' ||
6438
0
                               (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
6439
0
                              pszText[i + 1] != '\0'),
6440
0
                     ++i)
6441
0
                ;
6442
6443
            // Cf GetLabelStyleString() for 0.69. We should likely also take
6444
            // into account line spacing if we knew how to compute it.
6445
0
            SetTextBoxHeight(dfSize / 0.69 * numLines);
6446
0
        }
6447
0
    }
6448
6449
0
    if (poLabelStyle->Bold(bIsNull))
6450
0
        ToggleFontStyle(TABFSBold, true);
6451
6452
0
    if (poLabelStyle->Italic(bIsNull))
6453
0
        ToggleFontStyle(TABFSItalic, true);
6454
6455
0
    if (poLabelStyle->Underline(bIsNull))
6456
0
        ToggleFontStyle(TABFSUnderline, true);
6457
6458
0
    const char *pszFontName = poLabelStyle->FontName(bIsNull);
6459
0
    if (!bIsNull && pszFontName)
6460
0
        SetFontName(pszFontName);
6461
6462
    // Set the ForeColor
6463
0
    const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
6464
0
    if (bIsNull)
6465
0
        pszForeColor = nullptr;
6466
0
    if (pszForeColor)
6467
0
    {
6468
0
        if (pszForeColor[0] == '#')
6469
0
            pszForeColor++;
6470
0
        CPLString osForeColor(pszForeColor);
6471
0
        if (strlen(pszForeColor) > 6)
6472
0
            osForeColor.resize(6);
6473
0
        const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
6474
0
        SetFontFGColor(static_cast<GInt32>(nColor));
6475
0
    }
6476
6477
    // Set the BackgroundColor
6478
0
    const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
6479
0
    if (bIsNull)
6480
0
        pszBackColor = nullptr;
6481
0
    if (pszBackColor)
6482
0
    {
6483
0
        if (pszBackColor[0] == '#')
6484
0
            pszBackColor++;
6485
0
        CPLString osBackColor(pszBackColor);
6486
0
        if (strlen(pszBackColor) > 6)
6487
0
            osBackColor.resize(6);
6488
0
        const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
6489
0
        ToggleFontStyle(TABFSBox, true);
6490
0
        SetFontBGColor(static_cast<GInt32>(nColor));
6491
0
    }
6492
6493
    // Set the OutlineColor
6494
0
    const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
6495
0
    if (bIsNull)
6496
0
        pszOutlineColor = nullptr;
6497
0
    if (pszOutlineColor)
6498
0
    {
6499
0
        if (pszOutlineColor[0] == '#')
6500
0
            pszOutlineColor++;
6501
0
        CPLString osOutlineColor(pszOutlineColor);
6502
0
        if (strlen(pszOutlineColor) > 6)
6503
0
            osOutlineColor.resize(6);
6504
0
        const int nColor =
6505
0
            static_cast<int>(strtol(osOutlineColor, nullptr, 16));
6506
0
        ToggleFontStyle(TABFSHalo, true);
6507
0
        SetFontOColor(static_cast<GInt32>(nColor));
6508
0
    }
6509
6510
#if 0
6511
    // Commented out since it is hardcoded to 0x808080.
6512
    // Set the ShadowColor
6513
    const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
6514
    if(bIsNull) pszShadowColor = nullptr;
6515
    if(pszShadowColor)
6516
    {
6517
        if(pszShadowColor[0] == '#')
6518
            pszShadowColor++;
6519
        CPLString osShadowColor(pszShadowColor);
6520
        if( strlen(pszShadowColor) > 6 )
6521
            osShadowColor.resize(6);
6522
        const int nColor =
6523
            static_cast<int>(strtol(osShadowColor, nullptr, 16));
6524
        ToggleFontStyle(TABFSShadow, true);
6525
        SetFontSColor(static_cast<GInt32>(nColor));
6526
    }
6527
#endif
6528
6529
0
    const double dfAngle = poLabelStyle->Angle(bIsNull);
6530
0
    if (!bIsNull)
6531
0
        SetTextAngle(dfAngle);
6532
6533
0
    const int nAnchor = poLabelStyle->Anchor(bIsNull);
6534
0
    if (!bIsNull)
6535
0
    {
6536
0
        switch ((nAnchor - 1) % 3)
6537
0
        {
6538
0
            case 0:
6539
0
                SetTextJustification(TABTJLeft);
6540
0
                break;
6541
0
            case 1:
6542
0
                SetTextJustification(TABTJCenter);
6543
0
                break;
6544
0
            default /* 2 */:
6545
0
                SetTextJustification(TABTJRight);
6546
0
                break;
6547
0
        }
6548
0
    }
6549
0
}
6550
6551
/**********************************************************************
6552
 *                   TABText::DumpMIF()
6553
 *
6554
 * Dump feature geometry in a format similar to .MIF REGIONs.
6555
 **********************************************************************/
6556
void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
6557
0
{
6558
0
    if (fpOut == nullptr)
6559
0
        fpOut = stdout;
6560
6561
    /*-----------------------------------------------------------------
6562
     * Fetch and validate geometry
6563
     *----------------------------------------------------------------*/
6564
0
    OGRGeometry *poGeom = GetGeometryRef();
6565
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6566
0
    {
6567
        /*-------------------------------------------------------------
6568
         * Generate output for text object
6569
         *------------------------------------------------------------*/
6570
0
        OGRPoint *poPoint = poGeom->toPoint();
6571
6572
0
        fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
6573
0
                m_pszString ? m_pszString : "", poPoint->getX(),
6574
0
                poPoint->getY());
6575
6576
0
        fprintf(fpOut, "  m_pszString = '%s'\n", m_pszString);
6577
0
        fprintf(fpOut, "  m_dAngle    = %.15g\n", m_dAngle);
6578
0
        fprintf(fpOut, "  m_dHeight   = %.15g\n", m_dHeight);
6579
0
        fprintf(fpOut, "  m_rgbForeground  = 0x%6.6x (%d)\n", m_rgbForeground,
6580
0
                m_rgbForeground);
6581
0
        fprintf(fpOut, "  m_rgbBackground  = 0x%6.6x (%d)\n", m_rgbBackground,
6582
0
                m_rgbBackground);
6583
0
        fprintf(fpOut, "  m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
6584
0
        fprintf(fpOut, "  m_nFontStyle     = 0x%4.4x\n", m_nFontStyle);
6585
0
    }
6586
0
    else
6587
0
    {
6588
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6589
0
                 "TABText: Missing or Invalid Geometry!");
6590
0
        return;
6591
0
    }
6592
6593
    // Finish with PEN/BRUSH/etc. clauses
6594
0
    DumpPenDef();
6595
0
    DumpFontDef();
6596
6597
0
    fflush(fpOut);
6598
0
}
6599
6600
/*=====================================================================
6601
 *                      class TABMultiPoint
6602
 *====================================================================*/
6603
6604
/**********************************************************************
6605
 *                   TABMultiPoint::TABMultiPoint()
6606
 *
6607
 * Constructor.
6608
 **********************************************************************/
6609
TABMultiPoint::TABMultiPoint(const OGRFeatureDefn *poDefnIn)
6610
30.6k
    : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
6611
30.6k
      m_dCenterY(0.0)
6612
30.6k
{
6613
30.6k
}
6614
6615
/**********************************************************************
6616
 *                   TABMultiPoint::~TABMultiPoint()
6617
 *
6618
 * Destructor.
6619
 **********************************************************************/
6620
TABMultiPoint::~TABMultiPoint()
6621
30.6k
{
6622
30.6k
}
6623
6624
/**********************************************************************
6625
 *                     TABMultiPoint::CloneTABFeature()
6626
 *
6627
 * Duplicate feature, including stuff specific to each TABFeature type.
6628
 *
6629
 * This method calls the generic TABFeature::CloneTABFeature() and
6630
 * then copies any members specific to its own type.
6631
 **********************************************************************/
6632
TABFeature *
6633
TABMultiPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
6634
0
{
6635
    /*-----------------------------------------------------------------
6636
     * Alloc new feature and copy the base stuff
6637
     *----------------------------------------------------------------*/
6638
0
    TABMultiPoint *poNew =
6639
0
        new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
6640
6641
0
    CopyTABFeatureBase(poNew);
6642
6643
    /*-----------------------------------------------------------------
6644
     * And members specific to this class
6645
     *----------------------------------------------------------------*/
6646
    // ITABFeatureSymbol
6647
0
    *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
6648
6649
0
    poNew->m_bCenterIsSet = m_bCenterIsSet;
6650
0
    poNew->m_dCenterX = m_dCenterX;
6651
0
    poNew->m_dCenterY = m_dCenterY;
6652
6653
0
    return poNew;
6654
0
}
6655
6656
/**********************************************************************
6657
 *                   TABMultiPoint::ValidateMapInfoType()
6658
 *
6659
 * Check the feature's geometry part and return the corresponding
6660
 * mapinfo object type code.  The m_nMapInfoType member will also
6661
 * be updated for further calls to GetMapInfoType();
6662
 *
6663
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
6664
 * is expected for this object class.
6665
 **********************************************************************/
6666
TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
6667
0
{
6668
    /*-----------------------------------------------------------------
6669
     * Fetch and validate geometry
6670
     *----------------------------------------------------------------*/
6671
0
    OGRGeometry *poGeom = GetGeometryRef();
6672
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6673
0
    {
6674
0
        OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6675
6676
0
        if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
6677
0
            m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
6678
0
        else
6679
0
            m_nMapInfoType = TAB_GEOM_MULTIPOINT;
6680
0
    }
6681
0
    else
6682
0
    {
6683
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6684
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
6685
0
        m_nMapInfoType = TAB_GEOM_NONE;
6686
0
    }
6687
6688
    /*-----------------------------------------------------------------
6689
     * Decide if coordinates should be compressed or not.
6690
     *----------------------------------------------------------------*/
6691
0
    ValidateCoordType(poMapFile);
6692
6693
0
    return m_nMapInfoType;
6694
0
}
6695
6696
/**********************************************************************
6697
 *                   TABMultiPoint::ReadGeometryFromMAPFile()
6698
 *
6699
 * Fill the geometry and representation (color, etc...) part of the
6700
 * feature from the contents of the .MAP object pointed to by poMAPFile.
6701
 *
6702
 * It is assumed that poMAPFile currently points to the beginning of
6703
 * a map object.
6704
 *
6705
 * Returns 0 on success, -1 on error, in which case CPLError() will have
6706
 * been called.
6707
 **********************************************************************/
6708
int TABMultiPoint::ReadGeometryFromMAPFile(
6709
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6710
    GBool bCoordBlockDataOnly /*=FALSE*/,
6711
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6712
31
{
6713
31
    double dXMin = 0.0;
6714
31
    double dYMin = 0.0;
6715
31
    double dXMax = 0.0;
6716
31
    double dYMax = 0.0;
6717
31
    OGRGeometry *poGeometry = nullptr;
6718
31
    GBool bComprCoord = poObjHdr->IsCompressedType();
6719
31
    TABMAPCoordBlock *poCoordBlock = nullptr;
6720
6721
    /*-----------------------------------------------------------------
6722
     * Fetch and validate geometry type
6723
     *----------------------------------------------------------------*/
6724
31
    m_nMapInfoType = poObjHdr->m_nType;
6725
6726
    /*-----------------------------------------------------------------
6727
     * Read object information
6728
     *----------------------------------------------------------------*/
6729
31
    if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
6730
24
        m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
6731
0
        m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
6732
0
        m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
6733
31
    {
6734
        /*-------------------------------------------------------------
6735
         * Copy data from poObjHdr
6736
         *------------------------------------------------------------*/
6737
31
        TABMAPObjMultiPoint *poMPointHdr =
6738
31
            cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6739
6740
31
        const GUInt32 nMinimumBytesForPoints =
6741
31
            (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
6742
31
        if (nMinimumBytesForPoints > 1024 * 1024 &&
6743
1
            nMinimumBytesForPoints > poMapFile->GetFileSize())
6744
1
        {
6745
1
            CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
6746
1
            return -1;
6747
1
        }
6748
6749
        // MBR
6750
30
        poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
6751
30
                                dXMin, dYMin);
6752
30
        poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
6753
30
                                dXMax, dYMax);
6754
6755
30
        if (!bCoordBlockDataOnly)
6756
30
        {
6757
30
            m_nSymbolDefIndex = poMPointHdr->m_nSymbolId;  // Symbol index
6758
30
            poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
6759
30
        }
6760
6761
30
        double dX = 0.0;
6762
30
        double dY = 0.0;
6763
        // Centroid/label point
6764
30
        poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
6765
30
                                dX, dY);
6766
30
        SetCenter(dX, dY);
6767
6768
        // Compressed coordinate origin (useful only in compressed case!)
6769
30
        m_nComprOrgX = poMPointHdr->m_nComprOrgX;
6770
30
        m_nComprOrgY = poMPointHdr->m_nComprOrgY;
6771
6772
        /*-------------------------------------------------------------
6773
         * Read Point Coordinates
6774
         *------------------------------------------------------------*/
6775
30
        OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
6776
30
        poGeometry = poMultiPoint;
6777
6778
30
        if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6779
11
            poCoordBlock = *ppoCoordBlock;
6780
19
        else
6781
19
            poCoordBlock =
6782
19
                poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
6783
30
        if (poCoordBlock == nullptr)
6784
0
        {
6785
0
            delete poGeometry;
6786
0
            return -1;
6787
0
        }
6788
30
        poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6789
6790
94
        for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6791
66
        {
6792
66
            GInt32 nX = 0;
6793
66
            GInt32 nY = 0;
6794
66
            if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
6795
2
            {
6796
2
                CPLError(CE_Failure, CPLE_FileIO,
6797
2
                         "Failed reading coordinate data at offset %d",
6798
2
                         poMPointHdr->m_nCoordBlockPtr);
6799
2
                delete poGeometry;
6800
2
                return -1;
6801
2
            }
6802
6803
64
            poMapFile->Int2Coordsys(nX, nY, dX, dY);
6804
64
            OGRPoint *poPoint = new OGRPoint(dX, dY);
6805
6806
64
            if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
6807
0
            {
6808
0
                CPLAssert(false);  // Just in case lower-level lib is modified
6809
0
            }
6810
64
        }
6811
30
    }
6812
0
    else
6813
0
    {
6814
0
        CPLError(
6815
0
            CE_Failure, CPLE_AssertionFailed,
6816
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
6817
0
            m_nMapInfoType, m_nMapInfoType);
6818
0
        return -1;
6819
0
    }
6820
6821
28
    SetGeometryDirectly(poGeometry);
6822
6823
28
    SetMBR(dXMin, dYMin, dXMax, dYMax);
6824
6825
    /* Copy int MBR to feature class members */
6826
28
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
6827
28
              poObjHdr->m_nMaxY);
6828
6829
    /* Return a ref to coord block so that caller can continue reading
6830
     * after the end of this object (used by TABCollection and index splitting)
6831
     */
6832
28
    if (ppoCoordBlock)
6833
11
        *ppoCoordBlock = poCoordBlock;
6834
6835
28
    return 0;
6836
31
}
6837
6838
/**********************************************************************
6839
 *                   TABMultiPoint::WriteGeometryToMAPFile()
6840
 *
6841
 * Write the geometry and representation (color, etc...) part of the
6842
 * feature to the .MAP object pointed to by poMAPFile.
6843
 *
6844
 * It is assumed that poMAPFile currently points to a valid map object.
6845
 *
6846
 * Returns 0 on success, -1 on error, in which case CPLError() will have
6847
 * been called.
6848
 **********************************************************************/
6849
int TABMultiPoint::WriteGeometryToMAPFile(
6850
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6851
    GBool bCoordBlockDataOnly /*=FALSE*/,
6852
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6853
0
{
6854
0
    GInt32 nX, nY;
6855
6856
    /*-----------------------------------------------------------------
6857
     * We assume that ValidateMapInfoType() was called already and that
6858
     * the type in poObjHdr->m_nType is valid.
6859
     *----------------------------------------------------------------*/
6860
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
6861
6862
0
    TABMAPObjMultiPoint *poMPointHdr =
6863
0
        cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6864
6865
    /*-----------------------------------------------------------------
6866
     * Fetch and validate geometry
6867
     *----------------------------------------------------------------*/
6868
0
    OGRGeometry *poGeom = GetGeometryRef();
6869
0
    OGRMultiPoint *poMPoint = nullptr;
6870
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6871
0
        poMPoint = poGeom->toMultiPoint();
6872
0
    else
6873
0
    {
6874
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6875
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
6876
0
        return -1;
6877
0
    }
6878
6879
0
    poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
6880
6881
    /*-----------------------------------------------------------------
6882
     * Write data to coordinate block
6883
     *----------------------------------------------------------------*/
6884
0
    const GBool bCompressed = poObjHdr->IsCompressedType();
6885
6886
0
    TABMAPCoordBlock *poCoordBlock = nullptr;
6887
0
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6888
0
        poCoordBlock = *ppoCoordBlock;
6889
0
    else
6890
0
        poCoordBlock = poMapFile->GetCurCoordBlock();
6891
0
    poCoordBlock->StartNewFeature();
6892
0
    poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
6893
0
    poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6894
6895
0
    for (int iPoint = 0, nStatus = 0;
6896
0
         nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6897
0
    {
6898
0
        poGeom = poMPoint->getGeometryRef(iPoint);
6899
6900
0
        if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6901
0
        {
6902
0
            OGRPoint *poPoint = poGeom->toPoint();
6903
6904
0
            poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
6905
0
            if (iPoint == 0)
6906
0
            {
6907
                // Default to the first point, we may use explicit value below
6908
0
                poMPointHdr->m_nLabelX = nX;
6909
0
                poMPointHdr->m_nLabelY = nY;
6910
0
            }
6911
6912
0
            if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
6913
0
                0)
6914
0
            {
6915
                // Failed ... error message has already been produced
6916
0
                return nStatus;
6917
0
            }
6918
0
        }
6919
0
        else
6920
0
        {
6921
0
            CPLError(CE_Failure, CPLE_AssertionFailed,
6922
0
                     "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
6923
0
            return -1;
6924
0
        }
6925
0
    }
6926
6927
    /*-----------------------------------------------------------------
6928
     * Copy object information
6929
     *----------------------------------------------------------------*/
6930
6931
    // Compressed coordinate origin (useful only in compressed case!)
6932
0
    poMPointHdr->m_nComprOrgX = m_nComprOrgX;
6933
0
    poMPointHdr->m_nComprOrgY = m_nComprOrgY;
6934
6935
0
    poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
6936
0
    poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
6937
6938
    // Center/label point (default value already set above)
6939
0
    double dX = 0.0;
6940
0
    double dY = 0.0;
6941
0
    if (GetCenter(dX, dY) != -1)
6942
0
    {
6943
0
        poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
6944
0
                                poMPointHdr->m_nLabelY);
6945
0
    }
6946
6947
0
    if (!bCoordBlockDataOnly)
6948
0
    {
6949
0
        m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
6950
0
        poMPointHdr->m_nSymbolId =
6951
0
            static_cast<GByte>(m_nSymbolDefIndex);  // Symbol index
6952
0
    }
6953
6954
0
    if (CPLGetLastErrorType() == CE_Failure)
6955
0
        return -1;
6956
6957
    /* Return a ref to coord block so that caller can continue writing
6958
     * after the end of this object (used by index splitting)
6959
     */
6960
0
    if (ppoCoordBlock)
6961
0
        *ppoCoordBlock = poCoordBlock;
6962
6963
0
    return 0;
6964
0
}
6965
6966
/**********************************************************************
6967
 *                   TABMultiPoint::GetXY()
6968
 *
6969
 * Return this point's X,Y coordinates.
6970
 **********************************************************************/
6971
int TABMultiPoint::GetXY(int i, double &dX, double &dY)
6972
0
{
6973
    /*-----------------------------------------------------------------
6974
     * Fetch and validate geometry
6975
     *----------------------------------------------------------------*/
6976
0
    OGRGeometry *poGeom = GetGeometryRef();
6977
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6978
0
    {
6979
0
        OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6980
6981
0
        if (i >= 0 && i < poMPoint->getNumGeometries() &&
6982
0
            (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
6983
0
            wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6984
0
        {
6985
0
            OGRPoint *poPoint = poGeom->toPoint();
6986
6987
0
            dX = poPoint->getX();
6988
0
            dY = poPoint->getY();
6989
0
        }
6990
0
    }
6991
0
    else
6992
0
    {
6993
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
6994
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
6995
0
        dX = 0.0;
6996
0
        dY = 0.0;
6997
0
        return -1;
6998
0
    }
6999
7000
0
    return 0;
7001
0
}
7002
7003
/**********************************************************************
7004
 *                   TABMultiPoint::GetNumPoints()
7005
 *
7006
 * Return the number of points in this multipoint object
7007
 **********************************************************************/
7008
int TABMultiPoint::GetNumPoints()
7009
0
{
7010
    /*-----------------------------------------------------------------
7011
     * Fetch and validate geometry
7012
     *----------------------------------------------------------------*/
7013
0
    OGRGeometry *poGeom = GetGeometryRef();
7014
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7015
0
    {
7016
0
        OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
7017
7018
0
        return poMPoint->getNumGeometries();
7019
0
    }
7020
0
    else
7021
0
    {
7022
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
7023
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
7024
0
        return 0;
7025
0
    }
7026
0
}
7027
7028
/**********************************************************************
7029
 *                   TABMultiPoint::GetStyleString() const
7030
 *
7031
 * Return style string for this feature.
7032
 *
7033
 * Style String is built only once during the first call to GetStyleString().
7034
 **********************************************************************/
7035
const char *TABMultiPoint::GetStyleString() const
7036
0
{
7037
0
    if (m_pszStyleString == nullptr)
7038
0
    {
7039
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
7040
0
    }
7041
7042
0
    return m_pszStyleString;
7043
0
}
7044
7045
/**********************************************************************
7046
 *                   TABMultiPoint::GetCenter()
7047
 *
7048
 * Returns the center point (or label point?) of the object.  Compute one
7049
 * if it was not explicitly set:
7050
 *
7051
 * The default seems to be to use the first point in the collection as
7052
 * the center.. so we'll use that.
7053
 *
7054
 * Returns 0 on success, -1 on error.
7055
 **********************************************************************/
7056
int TABMultiPoint::GetCenter(double &dX, double &dY)
7057
0
{
7058
0
    if (!m_bCenterIsSet && GetNumPoints() > 0)
7059
0
    {
7060
        // The default seems to be to use the first point in the collection
7061
        // as the center... so we'll use that.
7062
0
        if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
7063
0
            m_bCenterIsSet = TRUE;
7064
0
    }
7065
7066
0
    if (!m_bCenterIsSet)
7067
0
        return -1;
7068
7069
0
    dX = m_dCenterX;
7070
0
    dY = m_dCenterY;
7071
0
    return 0;
7072
0
}
7073
7074
/**********************************************************************
7075
 *                   TABMultiPoint::SetCenter()
7076
 *
7077
 * Set the X,Y coordinates to use as center point (or label point?)
7078
 **********************************************************************/
7079
void TABMultiPoint::SetCenter(double dX, double dY)
7080
11.9k
{
7081
11.9k
    m_dCenterX = dX;
7082
11.9k
    m_dCenterY = dY;
7083
11.9k
    m_bCenterIsSet = TRUE;
7084
11.9k
}
7085
7086
/**********************************************************************
7087
 *                   TABMultiPoint::DumpMIF()
7088
 *
7089
 * Dump feature geometry in a format similar to .MIF POINTs.
7090
 **********************************************************************/
7091
void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
7092
0
{
7093
0
    if (fpOut == nullptr)
7094
0
        fpOut = stdout;
7095
7096
    /*-----------------------------------------------------------------
7097
     * Fetch and validate geometry
7098
     *----------------------------------------------------------------*/
7099
0
    OGRGeometry *poGeom = GetGeometryRef();
7100
0
    OGRMultiPoint *poMPoint = nullptr;
7101
0
    if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7102
0
        poMPoint = poGeom->toMultiPoint();
7103
0
    else
7104
0
    {
7105
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
7106
0
                 "TABMultiPoint: Missing or Invalid Geometry!");
7107
0
        return;
7108
0
    }
7109
7110
    /*-----------------------------------------------------------------
7111
     * Generate output
7112
     *----------------------------------------------------------------*/
7113
0
    fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
7114
7115
0
    for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
7116
0
    {
7117
0
        poGeom = poMPoint->getGeometryRef(iPoint);
7118
7119
0
        if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
7120
0
        {
7121
0
            OGRPoint *poPoint = poGeom->toPoint();
7122
0
            fprintf(fpOut, "  %.15g %.15g\n", poPoint->getX(), poPoint->getY());
7123
0
        }
7124
0
        else
7125
0
        {
7126
0
            CPLError(CE_Failure, CPLE_AssertionFailed,
7127
0
                     "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
7128
0
            return;
7129
0
        }
7130
0
    }
7131
7132
0
    DumpSymbolDef(fpOut);
7133
7134
0
    if (m_bCenterIsSet)
7135
0
        fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
7136
7137
0
    fflush(fpOut);
7138
0
}
7139
7140
/*=====================================================================
7141
 *                      class TABCollection
7142
 *====================================================================*/
7143
7144
/**********************************************************************
7145
 *                   TABCollection::TABCollection()
7146
 *
7147
 * Constructor.
7148
 **********************************************************************/
7149
TABCollection::TABCollection(const OGRFeatureDefn *poDefnIn)
7150
22.2k
    : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
7151
22.2k
      m_poMpoint(nullptr)
7152
22.2k
{
7153
22.2k
}
7154
7155
/**********************************************************************
7156
 *                   TABCollection::~TABCollection()
7157
 *
7158
 * Destructor.
7159
 **********************************************************************/
7160
TABCollection::~TABCollection()
7161
22.2k
{
7162
22.2k
    EmptyCollection();
7163
22.2k
}
7164
7165
/**********************************************************************
7166
 *                   TABCollection::EmptyCollection()
7167
 *
7168
 * Delete/free all collection components.
7169
 **********************************************************************/
7170
void TABCollection::EmptyCollection()
7171
44.3k
{
7172
7173
44.3k
    if (m_poRegion)
7174
15.2k
    {
7175
15.2k
        delete m_poRegion;
7176
15.2k
        m_poRegion = nullptr;
7177
15.2k
    }
7178
7179
44.3k
    if (m_poPline)
7180
9.42k
    {
7181
9.42k
        delete m_poPline;
7182
9.42k
        m_poPline = nullptr;
7183
9.42k
    }
7184
7185
44.3k
    if (m_poMpoint)
7186
12.6k
    {
7187
12.6k
        delete m_poMpoint;
7188
12.6k
        m_poMpoint = nullptr;
7189
12.6k
    }
7190
7191
    // Empty OGR Geometry Collection as well
7192
44.3k
    SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
7193
44.3k
}
7194
7195
/**********************************************************************
7196
 *                     TABCollection::CloneTABFeature()
7197
 *
7198
 * Duplicate feature, including stuff specific to each TABFeature type.
7199
 *
7200
 * This method calls the generic TABFeature::CloneTABFeature() and
7201
 * then copies any members specific to its own type.
7202
 **********************************************************************/
7203
TABFeature *
7204
TABCollection::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
7205
0
{
7206
    /*-----------------------------------------------------------------
7207
     * Alloc new feature and copy the base stuff
7208
     *----------------------------------------------------------------*/
7209
0
    TABCollection *poNew =
7210
0
        new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
7211
7212
0
    CopyTABFeatureBase(poNew);
7213
7214
    /*-----------------------------------------------------------------
7215
     * And members specific to this class
7216
     *----------------------------------------------------------------*/
7217
7218
0
    if (m_poRegion)
7219
0
        poNew->SetRegionDirectly(
7220
0
            cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
7221
7222
0
    if (m_poPline)
7223
0
        poNew->SetPolylineDirectly(
7224
0
            cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
7225
7226
0
    if (m_poMpoint)
7227
0
        poNew->SetMultiPointDirectly(
7228
0
            cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
7229
7230
0
    return poNew;
7231
0
}
7232
7233
/**********************************************************************
7234
 *                   TABCollection::ValidateMapInfoType()
7235
 *
7236
 * Check the feature's geometry part and return the corresponding
7237
 * mapinfo object type code.  The m_nMapInfoType member will also
7238
 * be updated for further calls to GetMapInfoType();
7239
 *
7240
 * Returns TAB_GEOM_NONE if the geometry is not compatible with what
7241
 * is expected for this object class.
7242
 **********************************************************************/
7243
TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
7244
0
{
7245
0
    int nRegionType = TAB_GEOM_NONE;
7246
0
    int nPLineType = TAB_GEOM_NONE;
7247
0
    int nMPointType = TAB_GEOM_NONE;
7248
0
    int nVersion = 650;
7249
7250
    /*-----------------------------------------------------------------
7251
     * Fetch and validate geometry
7252
     *----------------------------------------------------------------*/
7253
0
    OGRGeometry *poGeom = GetGeometryRef();
7254
0
    if (poGeom &&
7255
0
        wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
7256
0
    {
7257
0
        m_nMapInfoType = TAB_GEOM_COLLECTION;
7258
0
    }
7259
0
    else
7260
0
    {
7261
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
7262
0
                 "TABCollection: Missing or Invalid Geometry!");
7263
0
        m_nMapInfoType = TAB_GEOM_NONE;
7264
0
    }
7265
7266
    /*-----------------------------------------------------------------
7267
     * Decide if coordinates should be compressed or not.
7268
     *----------------------------------------------------------------*/
7269
0
    GBool bComprCoord = ValidateCoordType(poMapFile);
7270
7271
    /*-----------------------------------------------------------------
7272
     * Since all members of the collection share the same compressed coord
7273
     * origin, we should force the compressed origin in all components
7274
     * to be the same.
7275
     * This also implies that ValidateMapInfoType() should *NOT* be called
7276
     * again until the collection components are written by WriteGeom...()
7277
     *----------------------------------------------------------------*/
7278
7279
    // First pass to figure collection type...
7280
0
    if (m_poRegion)
7281
0
    {
7282
0
        m_poRegion->ValidateCoordType(poMapFile);
7283
0
        nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
7284
0
        if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
7285
0
            nVersion = TAB_GEOM_GET_VERSION(nRegionType);
7286
0
    }
7287
7288
0
    if (m_poPline)
7289
0
    {
7290
0
        m_poPline->ValidateCoordType(poMapFile);
7291
0
        nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
7292
0
        if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
7293
0
            nVersion = TAB_GEOM_GET_VERSION(nPLineType);
7294
0
    }
7295
7296
0
    if (m_poMpoint)
7297
0
    {
7298
0
        m_poMpoint->ValidateCoordType(poMapFile);
7299
0
        nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
7300
0
        if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
7301
0
            nVersion = TAB_GEOM_GET_VERSION(nMPointType);
7302
0
    }
7303
7304
    // Need to upgrade native type of collection?
7305
0
    if (nVersion == 800)
7306
0
    {
7307
0
        m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
7308
0
    }
7309
7310
    // Make another pass updating native type and coordinates type and origin
7311
    // of each component
7312
0
    if (m_poRegion && nRegionType != TAB_GEOM_NONE)
7313
0
    {
7314
0
        GInt32 nXMin = 0;
7315
0
        GInt32 nYMin = 0;
7316
0
        GInt32 nXMax = 0;
7317
0
        GInt32 nYMax = 0;
7318
0
        m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7319
0
        m_poRegion->ForceCoordTypeAndOrigin(
7320
0
            (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
7321
0
            bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7322
0
            nYMax);
7323
0
    }
7324
7325
0
    if (m_poPline && nPLineType != TAB_GEOM_NONE)
7326
0
    {
7327
0
        GInt32 nXMin, nYMin, nXMax, nYMax;
7328
0
        m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7329
0
        m_poPline->ForceCoordTypeAndOrigin(
7330
0
            (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
7331
0
                             : TAB_GEOM_V450_MULTIPLINE),
7332
0
            bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7333
0
            nYMax);
7334
0
    }
7335
7336
0
    if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
7337
0
    {
7338
0
        GInt32 nXMin, nYMin, nXMax, nYMax;
7339
0
        m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7340
0
        m_poMpoint->ForceCoordTypeAndOrigin(
7341
0
            (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
7342
0
            bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7343
0
            nYMax);
7344
0
    }
7345
7346
0
    return m_nMapInfoType;
7347
0
}
7348
7349
/**********************************************************************
7350
 *                   TABCollection::ReadLabelAndMBR()
7351
 *
7352
 * Reads the label and MBR elements of the header of a collection component
7353
 *
7354
 * Returns 0 on success, -1 on failure.
7355
 **********************************************************************/
7356
int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7357
                                   GBool bComprCoord, GInt32 nComprOrgX,
7358
                                   GInt32 nComprOrgY, GInt32 &pnMinX,
7359
                                   GInt32 &pnMinY, GInt32 &pnMaxX,
7360
                                   GInt32 &pnMaxY, GInt32 &pnLabelX,
7361
                                   GInt32 &pnLabelY)
7362
24
{
7363
    //
7364
    // The sections in the collection's coord blocks start with center/label
7365
    // point + MBR that are normally found in the object data blocks
7366
    // of regular region/pline/mulitpoint objects.
7367
    //
7368
7369
24
    if (bComprCoord)
7370
18
    {
7371
        // Region center/label point, relative to compr. coord. origin
7372
        // No it is not relative to the Object block center
7373
18
        pnLabelX = poCoordBlock->ReadInt16();
7374
18
        pnLabelY = poCoordBlock->ReadInt16();
7375
7376
18
        TABSaturatedAdd(pnLabelX, nComprOrgX);
7377
18
        TABSaturatedAdd(pnLabelY, nComprOrgY);
7378
7379
18
        pnMinX = poCoordBlock->ReadInt16();  // Read MBR
7380
18
        pnMinY = poCoordBlock->ReadInt16();
7381
18
        pnMaxX = poCoordBlock->ReadInt16();
7382
18
        pnMaxY = poCoordBlock->ReadInt16();
7383
18
        TABSaturatedAdd(pnMinX, nComprOrgX);
7384
18
        TABSaturatedAdd(pnMinY, nComprOrgY);
7385
18
        TABSaturatedAdd(pnMaxX, nComprOrgX);
7386
18
        TABSaturatedAdd(pnMaxY, nComprOrgY);
7387
18
    }
7388
6
    else
7389
6
    {
7390
        // Region center/label point, relative to compr. coord. origin
7391
        // No it is not relative to the Object block center
7392
6
        pnLabelX = poCoordBlock->ReadInt32();
7393
6
        pnLabelY = poCoordBlock->ReadInt32();
7394
7395
6
        pnMinX = poCoordBlock->ReadInt32();  // Read MBR
7396
6
        pnMinY = poCoordBlock->ReadInt32();
7397
6
        pnMaxX = poCoordBlock->ReadInt32();
7398
6
        pnMaxY = poCoordBlock->ReadInt32();
7399
6
    }
7400
7401
24
    return 0;
7402
24
}
7403
7404
/**********************************************************************
7405
 *                   TABCollection::WriteLabelAndMBR()
7406
 *
7407
 * Writes the label and MBR elements of the header of a collection component
7408
 *
7409
 * Returns 0 on success, -1 on failure.
7410
 **********************************************************************/
7411
int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7412
                                    GBool bComprCoord, GInt32 nMinX,
7413
                                    GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
7414
                                    GInt32 nLabelX, GInt32 nLabelY)
7415
0
{
7416
    //
7417
    // The sections in the collection's coord blocks start with center/label
7418
    // point + MBR that are normally found in the object data blocks
7419
    // of regular region/pline/mulitpoint objects.
7420
    //
7421
7422
0
    int nStatus = 0;
7423
0
    if ((nStatus =
7424
0
             poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
7425
0
        (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
7426
0
            0 ||
7427
0
        (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
7428
0
    {
7429
        // Failed ... error message has already been produced
7430
0
        return nStatus;
7431
0
    }
7432
7433
0
    return 0;
7434
0
}
7435
7436
/**********************************************************************
7437
 *                   TABCollection::ReadGeometryFromMAPFile()
7438
 *
7439
 * Fill the geometry and representation (color, etc...) part of the
7440
 * feature from the contents of the .MAP object pointed to by poMAPFile.
7441
 *
7442
 * It is assumed that poMAPFile currently points to the beginning of
7443
 * a map object.
7444
 *
7445
 * Returns 0 on success, -1 on error, in which case CPLError() will have
7446
 * been called.
7447
 **********************************************************************/
7448
int TABCollection::ReadGeometryFromMAPFile(
7449
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7450
    GBool bCoordBlockDataOnly /*=FALSE*/,
7451
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7452
16
{
7453
16
    const GBool bComprCoord = poObjHdr->IsCompressedType();
7454
7455
    /*-----------------------------------------------------------------
7456
     * Fetch and validate geometry type
7457
     *----------------------------------------------------------------*/
7458
16
    m_nMapInfoType = poObjHdr->m_nType;
7459
7460
16
    if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
7461
9
        m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
7462
0
        m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
7463
0
        m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
7464
0
    {
7465
0
        CPLError(
7466
0
            CE_Failure, CPLE_AssertionFailed,
7467
0
            "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
7468
0
            m_nMapInfoType, m_nMapInfoType);
7469
0
        return -1;
7470
0
    }
7471
7472
16
    int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7473
7474
    // Make sure collection is empty
7475
16
    EmptyCollection();
7476
7477
    /*-------------------------------------------------------------
7478
     * Copy data from poObjHdr
7479
     *------------------------------------------------------------*/
7480
16
    TABMAPObjCollection *poCollHdr =
7481
16
        cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7482
7483
    // MBR
7484
16
    double dXMin = 0.0;
7485
16
    double dYMin = 0.0;
7486
16
    double dXMax = 0.0;
7487
16
    double dYMax = 0.0;
7488
16
    poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
7489
16
                            dYMin);
7490
16
    poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
7491
16
                            dYMax);
7492
7493
16
    SetMBR(dXMin, dYMin, dXMax, dYMax);
7494
7495
16
    SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
7496
16
              poObjHdr->m_nMaxY);
7497
7498
16
    int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
7499
16
    TABMAPCoordBlock *poCoordBlock = nullptr;
7500
16
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7501
0
        poCoordBlock = *ppoCoordBlock;
7502
16
    else
7503
16
        poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
7504
7505
    // Compressed coordinate origin (useful only in compressed case!)
7506
16
    m_nComprOrgX = poCollHdr->m_nComprOrgX;
7507
16
    m_nComprOrgY = poCollHdr->m_nComprOrgY;
7508
7509
    /*-----------------------------------------------------------------
7510
     * Region Component
7511
     *----------------------------------------------------------------*/
7512
16
    if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
7513
7
    {
7514
        //
7515
        // Build fake coord section header to pass to TABRegion::ReadGeom...()
7516
        //
7517
7
        TABMAPObjPLine oRegionHdr;
7518
7519
7
        oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7520
7
        oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7521
7522
        //
7523
        // The region section in the coord block starts with center/label
7524
        // point + MBR that are normally found in the object data blocks
7525
        // of regular region objects.
7526
        //
7527
7528
        // In V800 the mini-header starts with a copy of num_parts
7529
7
        if (nVersion >= 800)
7530
0
        {
7531
            // int numParts = poCoordBlock->ReadInt32();
7532
0
            CPLAssert(poCoordBlock->ReadInt32() ==
7533
0
                      poCollHdr->m_nNumRegSections);
7534
0
        }
7535
7536
7
        ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
7537
7
                        oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
7538
7
                        oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
7539
7
                        oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
7540
7
                        oRegionHdr.m_nLabelY);
7541
7542
        // Set CoordBlockPtr so that TABRegion continues reading here
7543
7
        oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7544
7545
7
        if (bComprCoord)
7546
7
            oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
7547
0
        else
7548
0
            oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
7549
7
        if (nVersion == 800)
7550
0
            oRegionHdr.m_nType = static_cast<TABGeomType>(
7551
0
                oRegionHdr.m_nType +
7552
0
                (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
7553
7554
7
        oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
7555
7
        oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
7556
7
        oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
7557
7
        oRegionHdr.m_bSmooth = 0;  // TODO
7558
7559
        //
7560
        // Use a TABRegion to read/store the Region coord data
7561
        //
7562
7
        m_poRegion = new TABRegion(GetDefnRef());
7563
7
        if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
7564
7
                                                bCoordBlockDataOnly,
7565
7
                                                &poCoordBlock) != 0)
7566
1
            return -1;
7567
7568
        // Set new coord block ptr for next object
7569
        /*if (poCoordBlock)
7570
            nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7571
7
    }
7572
7573
    /*-----------------------------------------------------------------
7574
     * PLine Component
7575
     *----------------------------------------------------------------*/
7576
15
    if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
7577
6
    {
7578
        //
7579
        // Build fake coord section header to pass to TABPolyline::ReadGeom..()
7580
        //
7581
6
        TABMAPObjPLine oPLineHdr;
7582
7583
6
        oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7584
6
        oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7585
7586
        //
7587
        // The pline section in the coord block starts with center/label
7588
        // point + MBR that are normally found in the object data blocks
7589
        // of regular pline objects.
7590
        //
7591
7592
        // In V800 the mini-header starts with a copy of num_parts
7593
6
        if (nVersion >= 800)
7594
0
        {
7595
            // int numParts = poCoordBlock->ReadInt32();
7596
0
            CPLAssert(poCoordBlock->ReadInt32() ==
7597
0
                      poCollHdr->m_nNumPLineSections);
7598
0
        }
7599
7600
6
        ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
7601
6
                        oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
7602
6
                        oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
7603
6
                        oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
7604
7605
        // Set CoordBlockPtr so that TABRegion continues reading here
7606
6
        oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7607
7608
6
        if (bComprCoord)
7609
6
            oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
7610
0
        else
7611
0
            oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
7612
6
        if (nVersion == 800)
7613
0
            oPLineHdr.m_nType = static_cast<TABGeomType>(
7614
0
                oPLineHdr.m_nType +
7615
0
                (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
7616
7617
6
        oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
7618
6
        oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
7619
6
        oPLineHdr.m_bSmooth = 0;  // TODO
7620
7621
        //
7622
        // Use a TABPolyline to read/store the Polyline coord data
7623
        //
7624
6
        m_poPline = new TABPolyline(GetDefnRef());
7625
6
        if (m_poPline->ReadGeometryFromMAPFile(
7626
6
                poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7627
1
            return -1;
7628
7629
        // Set new coord block ptr for next object
7630
        /*if (poCoordBlock)
7631
            nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7632
6
    }
7633
7634
    /*-----------------------------------------------------------------
7635
     * MultiPoint Component
7636
     *----------------------------------------------------------------*/
7637
14
    if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
7638
11
    {
7639
        //
7640
        // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
7641
        //
7642
11
        TABMAPObjMultiPoint oMPointHdr;
7643
7644
11
        oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7645
11
        oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7646
7647
        //
7648
        // The pline section in the coord block starts with center/label
7649
        // point + MBR that are normally found in the object data blocks
7650
        // of regular pline objects.
7651
        //
7652
11
        ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
7653
11
                        oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
7654
11
                        oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
7655
11
                        oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
7656
11
                        oMPointHdr.m_nLabelY);
7657
7658
        // Set CoordBlockPtr so that TABRegion continues reading here
7659
11
        oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7660
7661
11
        if (bComprCoord)
7662
5
            oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
7663
6
        else
7664
6
            oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
7665
11
        if (nVersion == 800)
7666
0
            oMPointHdr.m_nType = static_cast<TABGeomType>(
7667
0
                oMPointHdr.m_nType +
7668
0
                (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
7669
7670
11
        oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
7671
11
        oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
7672
7673
        //
7674
        // Use a TABMultiPoint to read/store the coord data
7675
        //
7676
11
        m_poMpoint = new TABMultiPoint(GetDefnRef());
7677
11
        if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
7678
11
                                                bCoordBlockDataOnly,
7679
11
                                                &poCoordBlock) != 0)
7680
0
            return -1;
7681
7682
        // Set new coord block ptr for next object (not really useful here)
7683
        /*if (poCoordBlock)
7684
            nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7685
11
    }
7686
7687
    /*-----------------------------------------------------------------
7688
     * Set the main OGRFeature Geometry
7689
     * (this is actually duplicating geometries from each member)
7690
     *----------------------------------------------------------------*/
7691
14
    if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
7692
0
        return -1;
7693
7694
    /* Return a ref to coord block so that caller can continue reading
7695
     * after the end of this object (used by index splitting)
7696
     */
7697
14
    if (ppoCoordBlock)
7698
0
        *ppoCoordBlock = poCoordBlock;
7699
7700
14
    return 0;
7701
14
}
7702
7703
/**********************************************************************
7704
 *                   TABCollection::WriteGeometryToMAPFile()
7705
 *
7706
 * Write the geometry and representation (color, etc...) part of the
7707
 * feature to the .MAP object pointed to by poMAPFile.
7708
 *
7709
 * It is assumed that poMAPFile currently points to a valid map object.
7710
 *
7711
 * Returns 0 on success, -1 on error, in which case CPLError() will have
7712
 * been called.
7713
 **********************************************************************/
7714
int TABCollection::WriteGeometryToMAPFile(
7715
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7716
    GBool bCoordBlockDataOnly /*=FALSE*/,
7717
    TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7718
0
{
7719
    /*-----------------------------------------------------------------
7720
     * Note that the current implementation does not allow setting the
7721
     * Geometry via OGRFeature::SetGeometry(). The geometries must be set
7722
     * via the SetRegion/Pline/MpointDirectly() methods which will take
7723
     * care of keeping the OGRFeature's geometry in sync.
7724
     *
7725
     * TODO: If we ever want to support sync'ing changes from the OGRFeature's
7726
     * geometry to the m_poRegion/Pline/Mpoint then a call should be added
7727
     * here, or perhaps in ValidateMapInfoType(), or even better in
7728
     * custom TABCollection::SetGeometry*()... but then this last option
7729
     * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
7730
     *----------------------------------------------------------------*/
7731
7732
    /*-----------------------------------------------------------------
7733
     * We assume that ValidateMapInfoType() was called already and that
7734
     * the type in poObjHdr->m_nType is valid.
7735
     *----------------------------------------------------------------*/
7736
0
    CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
7737
7738
0
    TABMAPObjCollection *poCollHdr =
7739
0
        cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7740
7741
    /*-----------------------------------------------------------------
7742
     * Write data to coordinate block for each component...
7743
     *
7744
     * Note that at this point, the caller (TABFile) has called
7745
     * TABCollection::ValidateMapInfoType() which in turn has called
7746
     * each component's respective ValidateMapInfoType() and
7747
     * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
7748
     * their respective WriteGeometryToMapFile() called.
7749
     *----------------------------------------------------------------*/
7750
0
    const GBool bCompressed = poObjHdr->IsCompressedType();
7751
    // TODO: ??? Do we need to track overall collection coord data size???
7752
0
    int nTotalFeatureDataSize = 0;
7753
7754
0
    const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7755
7756
0
    TABMAPCoordBlock *poCoordBlock = nullptr;
7757
0
    if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7758
0
        poCoordBlock = *ppoCoordBlock;
7759
0
    else
7760
0
        poCoordBlock = poMapFile->GetCurCoordBlock();
7761
0
    poCoordBlock->StartNewFeature();
7762
0
    poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7763
0
    poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
7764
7765
    /*-----------------------------------------------------------------
7766
     * Region component
7767
     *----------------------------------------------------------------*/
7768
0
    if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
7769
0
    {
7770
0
        CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
7771
0
                  m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
7772
0
                  m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
7773
0
                  m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
7774
7775
0
        TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
7776
0
            TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
7777
7778
        // Update count of objects by type in header
7779
0
        if (!bCoordBlockDataOnly)
7780
0
            poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
7781
7782
        // Write a placeholder for centroid/label point and MBR mini-header
7783
        // and we'll come back later to write the real values.
7784
        //
7785
        // Note that the call to WriteGeometryToMAPFile() below will call
7786
        // StartNewFeature() as well, so we need to track the current
7787
        // value before calling it
7788
7789
0
        poCoordBlock->StartNewFeature();
7790
0
        int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7791
7792
        // In V800 the mini-header starts with a copy of num_parts
7793
0
        if (nVersion >= 800)
7794
0
        {
7795
0
            poCoordBlock->WriteInt32(0);
7796
0
        }
7797
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7798
0
        nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7799
7800
0
        if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
7801
0
                                               bCoordBlockDataOnly,
7802
0
                                               &poCoordBlock) != 0)
7803
0
        {
7804
0
            CPLError(CE_Failure, CPLE_FileIO,
7805
0
                     "Failed writing Region part in collection.");
7806
0
            delete poRegionHdr;
7807
0
            return -1;
7808
0
        }
7809
7810
0
        nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
7811
7812
        // Come back to write the real values in the mini-header
7813
0
        int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7814
0
        poCoordBlock->StartNewFeature();
7815
7816
0
        if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7817
0
        {
7818
0
            delete poRegionHdr;
7819
0
            return -1;
7820
0
        }
7821
7822
        // In V800 the mini-header starts with a copy of num_parts
7823
0
        if (nVersion >= 800)
7824
0
        {
7825
0
            poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
7826
0
        }
7827
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
7828
0
                         poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
7829
0
                         poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
7830
0
                         poRegionHdr->m_nLabelY);
7831
7832
        // And finally move the pointer back to the end of this component
7833
0
        if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7834
0
        {
7835
0
            delete poRegionHdr;
7836
0
            return -1;
7837
0
        }
7838
7839
        // Copy other header members to the main collection header
7840
        // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7841
        //       mini-header???
7842
0
        poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
7843
0
        poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
7844
7845
0
        if (!bCoordBlockDataOnly)
7846
0
        {
7847
0
            poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
7848
0
            poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
7849
            // TODO: Smooth flag         = poRegionHdr->m_bSmooth;
7850
0
        }
7851
7852
0
        delete poRegionHdr;
7853
0
    }
7854
0
    else
7855
0
    {
7856
        // No Region component. Set corresponding header fields to 0
7857
7858
0
        poCollHdr->m_nRegionDataSize = 0;
7859
0
        poCollHdr->m_nNumRegSections = 0;
7860
0
        poCollHdr->m_nRegionPenId = 0;
7861
0
        poCollHdr->m_nRegionBrushId = 0;
7862
0
    }
7863
7864
    /*-----------------------------------------------------------------
7865
     * PLine component
7866
     *----------------------------------------------------------------*/
7867
0
    if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
7868
0
    {
7869
0
        CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
7870
0
                  m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
7871
0
                  m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
7872
0
                  m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
7873
7874
0
        TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
7875
0
            TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
7876
7877
        // Update count of objects by type in header
7878
0
        if (!bCoordBlockDataOnly)
7879
0
            poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
7880
7881
        // Write a placeholder for centroid/label point and MBR mini-header
7882
        // and we'll come back later to write the real values.
7883
        //
7884
        // Note that the call to WriteGeometryToMAPFile() below will call
7885
        // StartNewFeature() as well, so we need to track the current
7886
        // value before calling it
7887
7888
0
        poCoordBlock->StartNewFeature();
7889
0
        int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7890
7891
        // In V800 the mini-header starts with a copy of num_parts
7892
0
        if (nVersion >= 800)
7893
0
        {
7894
0
            poCoordBlock->WriteInt32(0);
7895
0
        }
7896
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7897
0
        nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7898
7899
0
        if (m_poPline->WriteGeometryToMAPFile(
7900
0
                poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7901
0
        {
7902
0
            CPLError(CE_Failure, CPLE_FileIO,
7903
0
                     "Failed writing Region part in collection.");
7904
0
            delete poPlineHdr;
7905
0
            return -1;
7906
0
        }
7907
7908
0
        nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
7909
7910
        // Come back to write the real values in the mini-header
7911
0
        int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7912
0
        poCoordBlock->StartNewFeature();
7913
7914
0
        if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7915
0
        {
7916
0
            delete poPlineHdr;
7917
0
            return -1;
7918
0
        }
7919
7920
        // In V800 the mini-header starts with a copy of num_parts
7921
0
        if (nVersion >= 800)
7922
0
        {
7923
0
            poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
7924
0
        }
7925
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
7926
0
                         poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
7927
0
                         poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
7928
0
                         poPlineHdr->m_nLabelY);
7929
7930
        // And finally move the pointer back to the end of this component
7931
0
        if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7932
0
        {
7933
0
            delete poPlineHdr;
7934
0
            return -1;
7935
0
        }
7936
7937
        // Copy other header members to the main collection header
7938
        // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7939
        //       mini-header???
7940
0
        poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
7941
0
        poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
7942
0
        if (!bCoordBlockDataOnly)
7943
0
        {
7944
0
            poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
7945
            // TODO: Smooth flag           = poPlineHdr->m_bSmooth;
7946
0
        }
7947
7948
0
        delete poPlineHdr;
7949
0
    }
7950
0
    else
7951
0
    {
7952
        // No Polyline component. Set corresponding header fields to 0
7953
7954
0
        poCollHdr->m_nPolylineDataSize = 0;
7955
0
        poCollHdr->m_nNumPLineSections = 0;
7956
0
        poCollHdr->m_nPolylinePenId = 0;
7957
0
    }
7958
7959
    /*-----------------------------------------------------------------
7960
     * MultiPoint component
7961
     *----------------------------------------------------------------*/
7962
0
    if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
7963
0
    {
7964
0
        CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
7965
0
                  m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
7966
0
                  m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
7967
0
                  m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
7968
7969
0
        TABMAPObjMultiPoint *poMpointHdr =
7970
0
            cpl::down_cast<TABMAPObjMultiPoint *>(
7971
0
                TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
7972
7973
        // Update count of objects by type in header
7974
0
        if (!bCoordBlockDataOnly)
7975
0
            poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
7976
7977
        // Write a placeholder for centroid/label point and MBR mini-header
7978
        // and we'll come back later to write the real values.
7979
        //
7980
        // Note that the call to WriteGeometryToMAPFile() below will call
7981
        // StartNewFeature() as well, so we need to track the current
7982
        // value before calling it
7983
7984
0
        poCoordBlock->StartNewFeature();
7985
0
        int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7986
7987
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7988
0
        nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7989
7990
0
        if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
7991
0
                                               bCoordBlockDataOnly,
7992
0
                                               &poCoordBlock) != 0)
7993
0
        {
7994
0
            CPLError(CE_Failure, CPLE_FileIO,
7995
0
                     "Failed writing Region part in collection.");
7996
0
            delete poMpointHdr;
7997
0
            return -1;
7998
0
        }
7999
8000
0
        nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
8001
8002
        // Come back to write the real values in the mini-header
8003
0
        int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
8004
0
        poCoordBlock->StartNewFeature();
8005
8006
0
        if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
8007
0
        {
8008
0
            delete poMpointHdr;
8009
0
            return -1;
8010
0
        }
8011
8012
0
        WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
8013
0
                         poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
8014
0
                         poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
8015
0
                         poMpointHdr->m_nLabelY);
8016
8017
        // And finally move the pointer back to the end of this component
8018
0
        if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
8019
0
        {
8020
0
            delete poMpointHdr;
8021
0
            return -1;
8022
0
        }
8023
8024
        // Copy other header members to the main collection header
8025
        // TODO: Does m_nRegionDataSize need to include the centroid+mbr
8026
        //       mini-header???
8027
0
        poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
8028
0
        poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
8029
0
        if (!bCoordBlockDataOnly)
8030
0
        {
8031
0
            poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
8032
0
        }
8033
8034
0
        delete poMpointHdr;
8035
0
    }
8036
0
    else
8037
0
    {
8038
        // No Multipoint component. Set corresponding header fields to 0
8039
8040
0
        poCollHdr->m_nMPointDataSize = 0;
8041
0
        poCollHdr->m_nNumMultiPoints = 0;
8042
0
        poCollHdr->m_nMultiPointSymbolId = 0;
8043
0
    }
8044
8045
    /*-----------------------------------------------------------------
8046
     * Copy object information
8047
     *----------------------------------------------------------------*/
8048
8049
    // Compressed coordinate origin (useful only in compressed case!)
8050
0
    poCollHdr->m_nComprOrgX = m_nComprOrgX;
8051
0
    poCollHdr->m_nComprOrgY = m_nComprOrgY;
8052
8053
0
    poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
8054
8055
0
    poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
8056
8057
0
    if (CPLGetLastErrorType() == CE_Failure)
8058
0
        return -1;
8059
8060
    /* Return a ref to coord block so that caller can continue writing
8061
     * after the end of this object (used by index splitting)
8062
     */
8063
0
    if (ppoCoordBlock)
8064
0
        *ppoCoordBlock = poCoordBlock;
8065
8066
0
    return 0;
8067
0
}
8068
8069
/**********************************************************************
8070
 *                   TABCollection::SyncOGRGeometryCollection()
8071
 *
8072
 * Copy the region/pline/multipoint's geometries to the OGRFeature's
8073
 * geometry.
8074
 **********************************************************************/
8075
int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
8076
                                             GBool bSyncPline,
8077
                                             GBool bSyncMpoint)
8078
44.4k
{
8079
44.4k
    OGRGeometry *poThisGeom = GetGeometryRef();
8080
44.4k
    OGRGeometryCollection *poGeomColl = nullptr;
8081
8082
    // poGeometry is defined in the OGRFeature class
8083
44.4k
    if (poThisGeom == nullptr)
8084
22.2k
    {
8085
22.2k
        poGeomColl = new OGRGeometryCollection();
8086
22.2k
    }
8087
22.1k
    else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
8088
22.1k
    {
8089
22.1k
        poGeomColl = poThisGeom->toGeometryCollection();
8090
22.1k
    }
8091
0
    else
8092
0
    {
8093
0
        CPLError(
8094
0
            CE_Failure, CPLE_AssertionFailed,
8095
0
            "TABCollection: Invalid Geometry. Type must be OGRCollection.");
8096
0
        return -1;
8097
0
    }
8098
8099
    /*-----------------------------------------------------------------
8100
     * Start by removing geometries that need to be replaced
8101
     * In theory there should be a single geometry of each type, but
8102
     * just in case, we'll loop over the whole collection and delete all
8103
     * instances of each type if there are some.
8104
     *----------------------------------------------------------------*/
8105
44.4k
    int numGeometries = poGeomColl->getNumGeometries();
8106
63.2k
    for (int i = 0; i < numGeometries; i++)
8107
18.7k
    {
8108
18.7k
        OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
8109
18.7k
        if (!poGeom)
8110
0
            continue;
8111
8112
18.7k
        if ((bSyncRegion &&
8113
18.7k
             (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
8114
15.1k
              wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
8115
12.7k
            (bSyncPline &&
8116
12.7k
             (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
8117
9.14k
              wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
8118
8.28k
            (bSyncMpoint &&
8119
8.28k
             (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
8120
18.7k
        {
8121
            // Remove this geometry
8122
18.7k
            poGeomColl->removeGeometry(i);
8123
8124
            // Unless this was the last geometry, we need to restart
8125
            // scanning the collection since we modified it
8126
18.7k
            if (i != numGeometries - 1)
8127
8.42k
            {
8128
8.42k
                i = 0;
8129
8.42k
                numGeometries = poGeomColl->getNumGeometries();
8130
8.42k
            }
8131
18.7k
        }
8132
18.7k
    }
8133
8134
    /*-----------------------------------------------------------------
8135
     * Copy TAB Feature geometries to OGRGeometryCollection
8136
     *----------------------------------------------------------------*/
8137
44.4k
    if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
8138
5
        poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
8139
8140
44.4k
    if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
8141
5
        poGeomColl->addGeometry(m_poPline->GetGeometryRef());
8142
8143
44.4k
    if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
8144
10
        poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
8145
8146
44.4k
    if (poThisGeom == nullptr)
8147
22.2k
        SetGeometryDirectly(poGeomColl);
8148
8149
44.4k
    return 0;
8150
44.4k
}
8151
8152
/**********************************************************************
8153
 *                   TABCollection::SetRegionDirectly()
8154
 *
8155
 * Set the region component of the collection, deleting the current
8156
 * region component if there is one. The object is then owned by the
8157
 * TABCollection object. Passing NULL just deletes it.
8158
 *
8159
 * Note that an intentional side-effect is that calling this method
8160
 * with the same poRegion pointer that is already owned by this object
8161
 * will force resync'ing the OGR Geometry member.
8162
 **********************************************************************/
8163
int TABCollection::SetRegionDirectly(TABRegion *poRegion)
8164
0
{
8165
0
    if (m_poRegion && m_poRegion != poRegion)
8166
0
        delete m_poRegion;
8167
0
    m_poRegion = poRegion;
8168
8169
    // Update OGRGeometryCollection component as well
8170
0
    return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
8171
0
}
8172
8173
/**********************************************************************
8174
 *                   TABCollection::SetPolylineDirectly()
8175
 *
8176
 * Set the polyline component of the collection, deleting the current
8177
 * polyline component if there is one. The object is then owned by the
8178
 * TABCollection object. Passing NULL just deletes it.
8179
 *
8180
 * Note that an intentional side-effect is that calling this method
8181
 * with the same poPline pointer that is already owned by this object
8182
 * will force resync'ing the OGR Geometry member.
8183
 **********************************************************************/
8184
int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
8185
0
{
8186
0
    if (m_poPline && m_poPline != poPline)
8187
0
        delete m_poPline;
8188
0
    m_poPline = poPline;
8189
8190
    // Update OGRGeometryCollection component as well
8191
0
    return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
8192
0
}
8193
8194
/**********************************************************************
8195
 *                   TABCollection::SetMultiPointDirectly()
8196
 *
8197
 * Set the multipoint component of the collection, deleting the current
8198
 * multipoint component if there is one. The object is then owned by the
8199
 * TABCollection object. Passing NULL just deletes it.
8200
 *
8201
 * Note that an intentional side-effect is that calling this method
8202
 * with the same poMpoint pointer that is already owned by this object
8203
 * will force resync'ing the OGR Geometry member.
8204
 **********************************************************************/
8205
int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
8206
0
{
8207
0
    if (m_poMpoint && m_poMpoint != poMpoint)
8208
0
        delete m_poMpoint;
8209
0
    m_poMpoint = poMpoint;
8210
8211
    // Update OGRGeometryCollection component as well
8212
0
    return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
8213
0
}
8214
8215
/**********************************************************************
8216
 *                   TABCollection::GetStyleString() const
8217
 *
8218
 * Return style string for this feature.
8219
 *
8220
 * Style String is built only once during the first call to GetStyleString().
8221
 **********************************************************************/
8222
const char *TABCollection::GetStyleString() const
8223
0
{
8224
0
    if (m_pszStyleString == nullptr)
8225
0
    {
8226
0
        m_pszStyleString = CPLStrdup(GetSymbolStyleString());
8227
0
    }
8228
8229
0
    return m_pszStyleString;
8230
0
}
8231
8232
/**********************************************************************
8233
 *                   TABCollection::DumpMIF()
8234
 *
8235
 * Dump feature geometry
8236
 **********************************************************************/
8237
void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
8238
0
{
8239
0
    if (fpOut == nullptr)
8240
0
        fpOut = stdout;
8241
8242
    /*-----------------------------------------------------------------
8243
     * Generate output
8244
     *----------------------------------------------------------------*/
8245
0
    int numParts = 0;
8246
0
    if (m_poRegion)
8247
0
        numParts++;
8248
0
    if (m_poPline)
8249
0
        numParts++;
8250
0
    if (m_poMpoint)
8251
0
        numParts++;
8252
8253
0
    fprintf(fpOut, "COLLECTION %d\n", numParts);
8254
8255
0
    if (m_poRegion)
8256
0
        m_poRegion->DumpMIF(fpOut);
8257
8258
0
    if (m_poPline)
8259
0
        m_poPline->DumpMIF(fpOut);
8260
8261
0
    if (m_poMpoint)
8262
0
        m_poMpoint->DumpMIF(fpOut);
8263
8264
0
    DumpSymbolDef(fpOut);
8265
8266
0
    fflush(fpOut);
8267
0
}
8268
8269
/*=====================================================================
8270
 *                      class TABDebugFeature
8271
 *====================================================================*/
8272
8273
/**********************************************************************
8274
 *                   TABDebugFeature::TABDebugFeature()
8275
 *
8276
 * Constructor.
8277
 **********************************************************************/
8278
TABDebugFeature::TABDebugFeature(const OGRFeatureDefn *poDefnIn)
8279
0
    : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
8280
0
{
8281
0
    memset(m_abyBuf, 0, sizeof(m_abyBuf));
8282
0
}
8283
8284
/**********************************************************************
8285
 *                   TABDebugFeature::~TABDebugFeature()
8286
 *
8287
 * Destructor.
8288
 **********************************************************************/
8289
TABDebugFeature::~TABDebugFeature()
8290
{
8291
}
8292
8293
/**********************************************************************
8294
 *                   TABDebugFeature::ReadGeometryFromMAPFile()
8295
 *
8296
 * Fill the geometry and representation (color, etc...) part of the
8297
 * feature from the contents of the .MAP object pointed to by poMAPFile.
8298
 *
8299
 * It is assumed that poMAPFile currently points to the beginning of
8300
 * a map object.
8301
 *
8302
 * Returns 0 on success, -1 on error, in which case CPLError() will have
8303
 * been called.
8304
 **********************************************************************/
8305
int TABDebugFeature::ReadGeometryFromMAPFile(
8306
    TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
8307
    GBool /*bCoordBlockDataOnly=FALSE*/,
8308
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8309
0
{
8310
    /*-----------------------------------------------------------------
8311
     * Fetch geometry type
8312
     *----------------------------------------------------------------*/
8313
0
    m_nMapInfoType = poObjHdr->m_nType;
8314
8315
0
    TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
8316
0
    TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
8317
8318
    /*-----------------------------------------------------------------
8319
     * If object type has coords in a type 3 block, then its position
8320
     * follows
8321
     *----------------------------------------------------------------*/
8322
0
    if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
8323
0
    {
8324
0
        m_nCoordDataPtr = poObjBlock->ReadInt32();
8325
0
        m_nCoordDataSize = poObjBlock->ReadInt32();
8326
0
    }
8327
0
    else
8328
0
    {
8329
0
        m_nCoordDataPtr = -1;
8330
0
        m_nCoordDataSize = 0;
8331
0
    }
8332
8333
0
    m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
8334
0
    if (m_nSize > 0)
8335
0
    {
8336
0
        poObjBlock->GotoByteRel(-5);  // Go back to beginning of header
8337
0
        poObjBlock->ReadBytes(
8338
0
            std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
8339
0
    }
8340
8341
0
    return 0;
8342
0
}
8343
8344
/**********************************************************************
8345
 *                   TABDebugFeature::WriteGeometryToMAPFile()
8346
 *
8347
 * Write the geometry and representation (color, etc...) part of the
8348
 * feature to the .MAP object pointed to by poMAPFile.
8349
 *
8350
 * It is assumed that poMAPFile currently points to a valid map object.
8351
 *
8352
 * Returns 0 on success, -1 on error, in which case CPLError() will have
8353
 * been called.
8354
 **********************************************************************/
8355
int TABDebugFeature::WriteGeometryToMAPFile(
8356
    TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
8357
    GBool /*bCoordBlockDataOnly=FALSE*/,
8358
    TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8359
0
{
8360
    // Nothing to do here!
8361
8362
0
    CPLError(CE_Failure, CPLE_NotSupported,
8363
0
             "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
8364
8365
0
    return -1;
8366
0
}
8367
8368
/**********************************************************************
8369
 *                   TABDebugFeature::DumpMIF()
8370
 *
8371
 * Dump feature contents... available only in DEBUG mode.
8372
 **********************************************************************/
8373
void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
8374
0
{
8375
0
    if (fpOut == nullptr)
8376
0
        fpOut = stdout;
8377
8378
0
    fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
8379
0
            GetMapInfoType());
8380
0
    fprintf(fpOut, "  Object size: %d bytes\n", m_nSize);
8381
0
    fprintf(fpOut, "  m_nCoordDataPtr  = %d\n", m_nCoordDataPtr);
8382
0
    fprintf(fpOut, "  m_nCoordDataSize = %d\n", m_nCoordDataSize);
8383
0
    fprintf(fpOut, "  ");
8384
8385
0
    for (int i = 0; i < m_nSize; i++)
8386
0
        fprintf(fpOut, " %2.2x", m_abyBuf[i]);
8387
8388
0
    fprintf(fpOut, "  \n");
8389
8390
0
    fflush(fpOut);
8391
0
}
8392
8393
/*=====================================================================
8394
 *                      class ITABFeaturePen
8395
 *====================================================================*/
8396
8397
/**********************************************************************
8398
 *                   ITABFeaturePen::ITABFeaturePen()
8399
 **********************************************************************/
8400
8401
// MI default is PEN(1, 2, 0)
8402
static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
8403
8404
ITABFeaturePen::ITABFeaturePen()
8405
395k
    : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
8406
395k
{
8407
395k
}
8408
8409
/**********************************************************************
8410
 *                   ITABFeaturePen::~ITABFeaturePen()
8411
 **********************************************************************/
8412
8413
395k
ITABFeaturePen::~ITABFeaturePen() = default;
8414
8415
/**********************************************************************
8416
 *                   ITABFeaturePen::GetPenWidthPixel()
8417
 *                   ITABFeaturePen::SetPenWidthPixel()
8418
 *                   ITABFeaturePen::GetPenWidthPoint()
8419
 *                   ITABFeaturePen::SetPenWidthPoint()
8420
 *
8421
 * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
8422
 * in points (value from 0.1 to 203.7 points). The default pen width
8423
 * in MapInfo is 1 pixel.  Pen width in points exist only in file version 450.
8424
 *
8425
 * The following methods hide the way the pen width is stored in the files.
8426
 *
8427
 * In order to establish if a given pen def had its width specified in
8428
 * pixels or in points, one should first call GetPenWidthPoint(), and if
8429
 * it returns 0 then the Pixel width should be used instead:
8430
 *    if (GetPenWidthPoint() == 0)
8431
 *       ... use pen width in points ...
8432
 *    else
8433
 *       ... use Pixel width from GetPenWidthPixel()
8434
 *
8435
 * Note that the reverse is not true: the default pixel width is always 1,
8436
 * even when the pen width was actually set in points.
8437
 **********************************************************************/
8438
8439
GByte ITABFeaturePen::GetPenWidthPixel() const
8440
0
{
8441
0
    return m_sPenDef.nPixelWidth;
8442
0
}
8443
8444
void ITABFeaturePen::SetPenWidthPixel(GByte val)
8445
0
{
8446
0
    const GByte nPixelWidthMin = 1;
8447
0
    const GByte nPixelWidthMax = 7;
8448
0
    m_sPenDef.nPixelWidth =
8449
0
        std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
8450
0
    m_sPenDef.nPointWidth = 0;
8451
0
}
8452
8453
double ITABFeaturePen::GetPenWidthPoint() const
8454
0
{
8455
    // We store point width internally as tenths of points
8456
0
    return m_sPenDef.nPointWidth / 10.0;
8457
0
}
8458
8459
void ITABFeaturePen::SetPenWidthPoint(double val)
8460
0
{
8461
0
    m_sPenDef.nPointWidth =
8462
0
        std::min(std::max(static_cast<int>(val * 10), 1), 2037);
8463
0
    m_sPenDef.nPixelWidth = 1;
8464
0
}
8465
8466
/**********************************************************************
8467
 *                   ITABFeaturePen::GetPenWidthMIF()
8468
 *                   ITABFeaturePen::SetPenWidthMIF()
8469
 *
8470
 * The MIF representation for pen width is either a value from 1 to 7
8471
 * for a pen width in pixels, or a value from 11 to 2047 for a pen
8472
 * width in points = 10 + (point_width*10)
8473
 **********************************************************************/
8474
int ITABFeaturePen::GetPenWidthMIF() const
8475
204
{
8476
204
    return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
8477
204
                                      : m_sPenDef.nPixelWidth);
8478
204
}
8479
8480
void ITABFeaturePen::SetPenWidthMIF(int val)
8481
28.9k
{
8482
28.9k
    if (val > 10)
8483
7.30k
    {
8484
7.30k
        m_sPenDef.nPointWidth = std::min((val - 10), 2037);
8485
7.30k
        m_sPenDef.nPixelWidth = 0;
8486
7.30k
    }
8487
21.6k
    else
8488
21.6k
    {
8489
21.6k
        m_sPenDef.nPixelWidth =
8490
21.6k
            static_cast<GByte>(std::min(std::max(val, 1), 7));
8491
21.6k
        m_sPenDef.nPointWidth = 0;
8492
21.6k
    }
8493
28.9k
}
8494
8495
/**********************************************************************
8496
 *                   ITABFeaturePen::GetPenStyleString()
8497
 *
8498
 *  Return a PEN() string. All representations info for the pen are here.
8499
 **********************************************************************/
8500
const char *ITABFeaturePen::GetPenStyleString() const
8501
0
{
8502
0
    const char *pszStyle = nullptr;
8503
0
    int nOGRStyle = 0;
8504
0
    char szPattern[20];
8505
8506
0
    szPattern[0] = '\0';
8507
8508
    // For now, I only add the 25 first styles
8509
0
    switch (GetPenPattern())
8510
0
    {
8511
0
        case 1:
8512
0
            nOGRStyle = 1;
8513
0
            break;
8514
0
        case 2:
8515
0
            nOGRStyle = 0;
8516
0
            break;
8517
0
        case 3:
8518
0
            nOGRStyle = 3;
8519
0
            strcpy(szPattern, "1 1");
8520
0
            break;
8521
0
        case 4:
8522
0
            nOGRStyle = 3;
8523
0
            strcpy(szPattern, "2 1");
8524
0
            break;
8525
0
        case 5:
8526
0
            nOGRStyle = 3;
8527
0
            strcpy(szPattern, "3 1");
8528
0
            break;
8529
0
        case 6:
8530
0
            nOGRStyle = 3;
8531
0
            strcpy(szPattern, "6 1");
8532
0
            break;
8533
0
        case 7:
8534
0
            nOGRStyle = 4;
8535
0
            strcpy(szPattern, "12 2");
8536
0
            break;
8537
0
        case 8:
8538
0
            nOGRStyle = 4;
8539
0
            strcpy(szPattern, "24 4");
8540
0
            break;
8541
0
        case 9:
8542
0
            nOGRStyle = 3;
8543
0
            strcpy(szPattern, "4 3");
8544
0
            break;
8545
0
        case 10:
8546
0
            nOGRStyle = 5;
8547
0
            strcpy(szPattern, "1 4");
8548
0
            break;
8549
0
        case 11:
8550
0
            nOGRStyle = 3;
8551
0
            strcpy(szPattern, "4 6");
8552
0
            break;
8553
0
        case 12:
8554
0
            nOGRStyle = 3;
8555
0
            strcpy(szPattern, "6 4");
8556
0
            break;
8557
0
        case 13:
8558
0
            nOGRStyle = 4;
8559
0
            strcpy(szPattern, "12 12");
8560
0
            break;
8561
0
        case 14:
8562
0
            nOGRStyle = 6;
8563
0
            strcpy(szPattern, "8 2 1 2");
8564
0
            break;
8565
0
        case 15:
8566
0
            nOGRStyle = 6;
8567
0
            strcpy(szPattern, "12 1 1 1");
8568
0
            break;
8569
0
        case 16:
8570
0
            nOGRStyle = 6;
8571
0
            strcpy(szPattern, "12 1 3 1");
8572
0
            break;
8573
0
        case 17:
8574
0
            nOGRStyle = 6;
8575
0
            strcpy(szPattern, "24 6 4 6");
8576
0
            break;
8577
0
        case 18:
8578
0
            nOGRStyle = 7;
8579
0
            strcpy(szPattern, "24 3 3 3 3 3");
8580
0
            break;
8581
0
        case 19:
8582
0
            nOGRStyle = 7;
8583
0
            strcpy(szPattern, "24 3 3 3 3 3 3 3");
8584
0
            break;
8585
0
        case 20:
8586
0
            nOGRStyle = 7;
8587
0
            strcpy(szPattern, "6 3 1 3 1 3");
8588
0
            break;
8589
0
        case 21:
8590
0
            nOGRStyle = 7;
8591
0
            strcpy(szPattern, "12 2 1 2 1 2");
8592
0
            break;
8593
0
        case 22:
8594
0
            nOGRStyle = 7;
8595
0
            strcpy(szPattern, "12 2 1 2 1 2 1 2");
8596
0
            break;
8597
0
        case 23:
8598
0
            nOGRStyle = 6;
8599
0
            strcpy(szPattern, "4 1 1 1");
8600
0
            break;
8601
0
        case 24:
8602
0
            nOGRStyle = 7;
8603
0
            strcpy(szPattern, "4 1 1 1 1");
8604
0
            break;
8605
0
        case 25:
8606
0
            nOGRStyle = 6;
8607
0
            strcpy(szPattern, "4 1 1 1 2 1 1 1");
8608
0
            break;
8609
8610
0
        default:
8611
0
            nOGRStyle = 0;
8612
0
            break;
8613
0
    }
8614
8615
    // note - MapInfo renders all lines using a round pen cap and round pen join
8616
    // which are not the default values for OGR pen cap/join styles. So we need
8617
    // to explicitly include the cap/j parameters in these strings
8618
0
    if (strlen(szPattern) != 0)
8619
0
    {
8620
0
        if (m_sPenDef.nPointWidth > 0)
8621
0
            pszStyle = CPLSPrintf("PEN(w:%.1fpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
8622
0
                                  "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8623
0
                                  GetPenWidthPoint(), m_sPenDef.rgbColor,
8624
0
                                  GetPenPattern(), nOGRStyle, szPattern);
8625
0
        else
8626
0
            pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
8627
0
                                  "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8628
0
                                  GetPenWidthPixel(), m_sPenDef.rgbColor,
8629
0
                                  GetPenPattern(), nOGRStyle, szPattern);
8630
0
    }
8631
0
    else
8632
0
    {
8633
0
        if (m_sPenDef.nPointWidth > 0)
8634
0
            pszStyle = CPLSPrintf("PEN(w:%.1fpt,c:#%6.6x,id:\""
8635
0
                                  "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8636
0
                                  GetPenWidthPoint(), m_sPenDef.rgbColor,
8637
0
                                  GetPenPattern(), nOGRStyle);
8638
0
        else
8639
0
            pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
8640
0
                                  "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8641
0
                                  GetPenWidthPixel(), m_sPenDef.rgbColor,
8642
0
                                  GetPenPattern(), nOGRStyle);
8643
0
    }
8644
8645
0
    return pszStyle;
8646
0
}
8647
8648
/**********************************************************************
8649
 *                   ITABFeaturePen::SetPenFromStyleString()
8650
 *
8651
 *  Init the Pen properties from a style string.
8652
 **********************************************************************/
8653
void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
8654
1
{
8655
1
    GBool bIsNull = 0;
8656
8657
    // Use the Style Manager to retrieve all the information we need.
8658
1
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8659
1
    OGRStyleTool *poStylePart = nullptr;
8660
8661
    // Init the StyleMgr with the StyleString.
8662
1
    poStyleMgr->InitStyleString(pszStyleString);
8663
8664
    // Retrieve the Pen info.
8665
1
    const int numParts = poStyleMgr->GetPartCount();
8666
2
    for (int i = 0; i < numParts; i++)
8667
1
    {
8668
1
        poStylePart = poStyleMgr->GetPart(i);
8669
1
        if (poStylePart == nullptr)
8670
1
            continue;
8671
8672
0
        if (poStylePart->GetType() == OGRSTCPen)
8673
0
        {
8674
0
            break;
8675
0
        }
8676
0
        else
8677
0
        {
8678
0
            delete poStylePart;
8679
0
            poStylePart = nullptr;
8680
0
        }
8681
0
    }
8682
8683
    // If the no Pen found, do nothing.
8684
1
    if (poStylePart == nullptr)
8685
1
    {
8686
1
        delete poStyleMgr;
8687
1
        return;
8688
1
    }
8689
8690
0
    OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
8691
8692
    // With Pen, we always want to output points or pixels
8693
8694
    // Get the Pen Id or pattern
8695
0
    const char *pszPenName = poPenStyle->Id(bIsNull);
8696
0
    if (bIsNull)
8697
0
        pszPenName = nullptr;
8698
8699
    // Set the width
8700
0
    OGRSTUnitId ePenWidthUnit = OGRSTUGround;
8701
    // Respect the original unit if it is points vs pixel. Otherwise convert
8702
    // to points.
8703
0
    const double dfPenWidth = poPenStyle->RawWidth(ePenWidthUnit, bIsNull);
8704
0
    if (dfPenWidth != 0.0)
8705
0
    {
8706
0
        if (ePenWidthUnit == OGRSTUPoints)
8707
0
        {
8708
0
            SetPenWidthPoint(dfPenWidth);
8709
0
        }
8710
0
        else if (ePenWidthUnit == OGRSTUPixel)
8711
0
        {
8712
0
            SetPenWidthPixel(
8713
0
                static_cast<GByte>(std::clamp(dfPenWidth + 0.5, 0.0, 255.0)));
8714
0
        }
8715
0
        else
8716
0
        {
8717
0
            poPenStyle->SetUnit(OGRSTUPoints, 1);
8718
0
            SetPenWidthPoint(poPenStyle->Width(bIsNull));
8719
0
        }
8720
0
    }
8721
8722
    // Set the color
8723
0
    const char *pszPenColor = poPenStyle->Color(bIsNull);
8724
0
    if (pszPenColor != nullptr)
8725
0
    {
8726
0
        if (pszPenColor[0] == '#')
8727
0
            pszPenColor++;
8728
        // The Pen color is an Hexa string that need to be convert in a int
8729
0
        const GInt32 nPenColor =
8730
0
            static_cast<int>(strtol(pszPenColor, nullptr, 16));
8731
0
        SetPenColor(nPenColor);
8732
0
    }
8733
8734
0
    const char *pszPenPattern = nullptr;
8735
8736
    // Set the Id of the Pen, use Pattern if necessary.
8737
0
    if (pszPenName &&
8738
0
        (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
8739
0
    {
8740
0
        const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
8741
0
        if (pszPenId != nullptr)
8742
0
        {
8743
0
            const int nPenId = atoi(pszPenId + 12);
8744
0
            SetPenPattern(static_cast<GByte>(nPenId));
8745
0
        }
8746
0
        else
8747
0
        {
8748
0
            pszPenId = strstr(pszPenName, "ogr-pen-");
8749
0
            if (pszPenId != nullptr)
8750
0
            {
8751
0
                int nPenId = atoi(pszPenId + 8);
8752
0
                if (nPenId == 0)
8753
0
                    nPenId = 2;
8754
0
                SetPenPattern(static_cast<GByte>(nPenId));
8755
0
            }
8756
0
        }
8757
0
    }
8758
0
    else
8759
0
    {
8760
        // If no Pen Id, use the Pen Pattern to retrieve the Id.
8761
0
        pszPenPattern = poPenStyle->Pattern(bIsNull);
8762
0
        if (bIsNull)
8763
0
            pszPenPattern = nullptr;
8764
0
        else
8765
0
        {
8766
0
            if (strcmp(pszPenPattern, "1 1") == 0)
8767
0
                SetPenPattern(3);
8768
0
            else if (strcmp(pszPenPattern, "2 1") == 0)
8769
0
                SetPenPattern(4);
8770
0
            else if (strcmp(pszPenPattern, "3 1") == 0)
8771
0
                SetPenPattern(5);
8772
0
            else if (strcmp(pszPenPattern, "6 1") == 0)
8773
0
                SetPenPattern(6);
8774
0
            else if (strcmp(pszPenPattern, "12 2") == 0)
8775
0
                SetPenPattern(7);
8776
0
            else if (strcmp(pszPenPattern, "24 4") == 0)
8777
0
                SetPenPattern(8);
8778
0
            else if (strcmp(pszPenPattern, "4 3") == 0)
8779
0
                SetPenPattern(9);
8780
0
            else if (strcmp(pszPenPattern, "1 4") == 0)
8781
0
                SetPenPattern(10);
8782
0
            else if (strcmp(pszPenPattern, "4 6") == 0)
8783
0
                SetPenPattern(11);
8784
0
            else if (strcmp(pszPenPattern, "6 4") == 0)
8785
0
                SetPenPattern(12);
8786
0
            else if (strcmp(pszPenPattern, "12 12") == 0)
8787
0
                SetPenPattern(13);
8788
0
            else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
8789
0
                SetPenPattern(14);
8790
0
            else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
8791
0
                SetPenPattern(15);
8792
0
            else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
8793
0
                SetPenPattern(16);
8794
0
            else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
8795
0
                SetPenPattern(17);
8796
0
            else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
8797
0
                SetPenPattern(18);
8798
0
            else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
8799
0
                SetPenPattern(19);
8800
0
            else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
8801
0
                SetPenPattern(20);
8802
0
            else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
8803
0
                SetPenPattern(21);
8804
0
            else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
8805
0
                SetPenPattern(22);
8806
0
            else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
8807
0
                SetPenPattern(23);
8808
0
            else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
8809
0
                SetPenPattern(24);
8810
0
            else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
8811
0
                SetPenPattern(25);
8812
0
        }
8813
0
    }
8814
8815
0
    delete poStyleMgr;
8816
0
    delete poStylePart;
8817
8818
0
    return;
8819
1
}
8820
8821
/**********************************************************************
8822
 *                   ITABFeaturePen::DumpPenDef()
8823
 *
8824
 * Dump pen definition information.
8825
 **********************************************************************/
8826
void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
8827
0
{
8828
0
    if (fpOut == nullptr)
8829
0
        fpOut = stdout;
8830
8831
0
    fprintf(fpOut, "  m_nPenDefIndex         = %d\n", m_nPenDefIndex);
8832
0
    fprintf(fpOut, "  m_sPenDef.nRefCount    = %d\n", m_sPenDef.nRefCount);
8833
0
    fprintf(fpOut, "  m_sPenDef.nPixelWidth  = %u\n", m_sPenDef.nPixelWidth);
8834
0
    fprintf(fpOut, "  m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
8835
0
    fprintf(fpOut, "  m_sPenDef.nPointWidth  = %d\n", m_sPenDef.nPointWidth);
8836
0
    fprintf(fpOut, "  m_sPenDef.rgbColor     = 0x%6.6x (%d)\n",
8837
0
            m_sPenDef.rgbColor, m_sPenDef.rgbColor);
8838
8839
0
    fflush(fpOut);
8840
0
}
8841
8842
/*=====================================================================
8843
 *                      class ITABFeatureBrush
8844
 *====================================================================*/
8845
8846
/**********************************************************************
8847
 *                   ITABFeatureBrush::ITABFeatureBrush()
8848
 **********************************************************************/
8849
8850
// MI default is BRUSH(2, 16777215, 16777215)
8851
static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
8852
8853
ITABFeatureBrush::ITABFeatureBrush()
8854
256k
    : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
8855
256k
{
8856
256k
}
8857
8858
/**********************************************************************
8859
 *                   ITABFeatureBrush::~ITABFeatureBrush()
8860
 **********************************************************************/
8861
8862
256k
ITABFeatureBrush::~ITABFeatureBrush() = default;
8863
8864
/**********************************************************************
8865
 *                   ITABFeatureBrush::GetBrushStyleString()
8866
 *
8867
 *  Return a Brush() string. All representations info for the Brush are here.
8868
 **********************************************************************/
8869
const char *ITABFeatureBrush::GetBrushStyleString() const
8870
0
{
8871
0
    const char *pszStyle = nullptr;
8872
0
    int nOGRStyle = 0;
8873
    /* char szPattern[20]; */
8874
    //* szPattern[0] = '\0'; */
8875
8876
0
    if (m_sBrushDef.nFillPattern == 1)
8877
0
        nOGRStyle = 1;
8878
0
    else if (m_sBrushDef.nFillPattern == 3)
8879
0
        nOGRStyle = 2;
8880
0
    else if (m_sBrushDef.nFillPattern == 4)
8881
0
        nOGRStyle = 3;
8882
0
    else if (m_sBrushDef.nFillPattern == 5)
8883
0
        nOGRStyle = 5;
8884
0
    else if (m_sBrushDef.nFillPattern == 6)
8885
0
        nOGRStyle = 4;
8886
0
    else if (m_sBrushDef.nFillPattern == 7)
8887
0
        nOGRStyle = 6;
8888
0
    else if (m_sBrushDef.nFillPattern == 8)
8889
0
        nOGRStyle = 7;
8890
8891
0
    if (GetBrushTransparent())
8892
0
    {
8893
        /* Omit BG Color for transparent brushes */
8894
0
        pszStyle = CPLSPrintf(
8895
0
            "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8896
0
            m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
8897
0
    }
8898
0
    else
8899
0
    {
8900
0
        pszStyle = CPLSPrintf(
8901
0
            "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8902
0
            m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
8903
0
            m_sBrushDef.nFillPattern, nOGRStyle);
8904
0
    }
8905
8906
0
    return pszStyle;
8907
0
}
8908
8909
/**********************************************************************
8910
 *                   ITABFeatureBrush::SetBrushFromStyleString()
8911
 *
8912
 *  Set all Brush elements from a StyleString.
8913
 *  Use StyleMgr to do so.
8914
 **********************************************************************/
8915
void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
8916
0
{
8917
0
    GBool bIsNull = 0;
8918
8919
    // Use the Style Manager to retrieve all the information we need.
8920
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8921
0
    OGRStyleTool *poStylePart = nullptr;
8922
8923
    // Init the StyleMgr with the StyleString.
8924
0
    poStyleMgr->InitStyleString(pszStyleString);
8925
8926
    // Retrieve the Brush info.
8927
0
    const int numParts = poStyleMgr->GetPartCount();
8928
0
    for (int i = 0; i < numParts; i++)
8929
0
    {
8930
0
        poStylePart = poStyleMgr->GetPart(i);
8931
0
        if (poStylePart == nullptr)
8932
0
            continue;
8933
8934
0
        if (poStylePart->GetType() == OGRSTCBrush)
8935
0
        {
8936
0
            break;
8937
0
        }
8938
0
        else
8939
0
        {
8940
0
            delete poStylePart;
8941
0
            poStylePart = nullptr;
8942
0
        }
8943
0
    }
8944
8945
    // If the no Brush found, do nothing.
8946
0
    if (poStylePart == nullptr)
8947
0
    {
8948
0
        delete poStyleMgr;
8949
0
        return;
8950
0
    }
8951
8952
0
    OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
8953
8954
    // Set the Brush Id (FillPattern)
8955
0
    const char *pszBrushId = poBrushStyle->Id(bIsNull);
8956
0
    if (bIsNull)
8957
0
        pszBrushId = nullptr;
8958
0
    bool bHasBrushId = false;
8959
8960
0
    if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
8961
0
                       strstr(pszBrushId, "ogr-brush-")))
8962
0
    {
8963
0
        if (strstr(pszBrushId, "mapinfo-brush-"))
8964
0
        {
8965
0
            const int nBrushId = atoi(pszBrushId + 14);
8966
0
            SetBrushPattern(static_cast<GByte>(nBrushId));
8967
0
            bHasBrushId = true;
8968
0
        }
8969
0
        else if (strstr(pszBrushId, "ogr-brush-"))
8970
0
        {
8971
0
            int nBrushId = atoi(pszBrushId + 10);
8972
0
            if (nBrushId > 1)
8973
0
                nBrushId++;
8974
0
            SetBrushPattern(static_cast<GByte>(nBrushId));
8975
0
            bHasBrushId = true;
8976
0
        }
8977
0
    }
8978
8979
    // Set the BackColor, if not set, then it is transparent
8980
0
    const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
8981
0
    if (bIsNull)
8982
0
        pszBrushColor = nullptr;
8983
8984
0
    if (pszBrushColor)
8985
0
    {
8986
0
        if (pszBrushColor[0] == '#')
8987
0
            pszBrushColor++;
8988
0
        if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
8989
0
            pszBrushColor[7] == '0')
8990
0
        {
8991
0
            SetBrushTransparent(1);
8992
0
        }
8993
0
        else
8994
0
        {
8995
0
            CPLString osBrushColor(pszBrushColor);
8996
0
            if (strlen(pszBrushColor) > 6)
8997
0
                osBrushColor.resize(6);
8998
0
            const int nBrushColor =
8999
0
                static_cast<int>(strtol(osBrushColor, nullptr, 16));
9000
0
            SetBrushBGColor(static_cast<GInt32>(nBrushColor));
9001
0
        }
9002
0
    }
9003
0
    else
9004
0
    {
9005
0
        SetBrushTransparent(1);
9006
0
    }
9007
9008
    // Set the ForeColor
9009
0
    pszBrushColor = poBrushStyle->ForeColor(bIsNull);
9010
0
    if (bIsNull)
9011
0
        pszBrushColor = nullptr;
9012
9013
0
    if (pszBrushColor)
9014
0
    {
9015
0
        if (pszBrushColor[0] == '#')
9016
0
            pszBrushColor++;
9017
0
        if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
9018
0
            pszBrushColor[7] == '0')
9019
0
        {
9020
0
            if (!bHasBrushId)
9021
0
                SetBrushPattern(static_cast<GByte>(1));  // No-fill
9022
0
        }
9023
0
        else
9024
0
        {
9025
0
            if (!bHasBrushId)
9026
0
                SetBrushPattern(static_cast<GByte>(2));  // Solid-fill
9027
0
        }
9028
9029
0
        CPLString osBrushColor(pszBrushColor);
9030
0
        if (strlen(pszBrushColor) > 6)
9031
0
            osBrushColor.resize(6);
9032
0
        const int nBrushColor =
9033
0
            static_cast<int>(strtol(osBrushColor, nullptr, 16));
9034
0
        SetBrushFGColor(static_cast<GInt32>(nBrushColor));
9035
0
    }
9036
9037
0
    delete poStyleMgr;
9038
0
    delete poStylePart;
9039
9040
0
    return;
9041
0
}
9042
9043
/**********************************************************************
9044
 *                   ITABFeatureBrush::DumpBrushDef()
9045
 *
9046
 * Dump Brush definition information.
9047
 **********************************************************************/
9048
void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
9049
0
{
9050
0
    if (fpOut == nullptr)
9051
0
        fpOut = stdout;
9052
9053
0
    fprintf(fpOut, "  m_nBrushDefIndex         = %d\n", m_nBrushDefIndex);
9054
0
    fprintf(fpOut, "  m_sBrushDef.nRefCount    = %d\n", m_sBrushDef.nRefCount);
9055
0
    fprintf(fpOut, "  m_sBrushDef.nFillPattern = %d\n",
9056
0
            static_cast<int>(m_sBrushDef.nFillPattern));
9057
0
    fprintf(fpOut, "  m_sBrushDef.bTransparentFill = %d\n",
9058
0
            static_cast<int>(m_sBrushDef.bTransparentFill));
9059
0
    fprintf(fpOut, "  m_sBrushDef.rgbFGColor   = 0x%6.6x (%d)\n",
9060
0
            m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
9061
0
    fprintf(fpOut, "  m_sBrushDef.rgbBGColor   = 0x%6.6x (%d)\n",
9062
0
            m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
9063
9064
0
    fflush(fpOut);
9065
0
}
9066
9067
/*=====================================================================
9068
 *                      class ITABFeatureFont
9069
 *====================================================================*/
9070
9071
/**********************************************************************
9072
 *                   ITABFeatureFont::ITABFeatureFont()
9073
 **********************************************************************/
9074
9075
// MI default is Font("Arial", 0, 0, 0)
9076
static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
9077
9078
ITABFeatureFont::ITABFeatureFont()
9079
84.3k
    : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
9080
84.3k
{
9081
84.3k
}
9082
9083
/**********************************************************************
9084
 *                   ITABFeatureFont::~ITABFeatureFont()
9085
 **********************************************************************/
9086
9087
84.3k
ITABFeatureFont::~ITABFeatureFont() = default;
9088
9089
/**********************************************************************
9090
 *                   ITABFeatureFont::SetFontName()
9091
 **********************************************************************/
9092
void ITABFeatureFont::SetFontName(const char *pszName)
9093
21.2k
{
9094
21.2k
    strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
9095
21.2k
    m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
9096
21.2k
}
9097
9098
/**********************************************************************
9099
 *                   ITABFeatureFont::DumpFontDef()
9100
 *
9101
 * Dump Font definition information.
9102
 **********************************************************************/
9103
void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
9104
0
{
9105
0
    if (fpOut == nullptr)
9106
0
        fpOut = stdout;
9107
9108
0
    fprintf(fpOut, "  m_nFontDefIndex       = %d\n", m_nFontDefIndex);
9109
0
    fprintf(fpOut, "  m_sFontDef.nRefCount  = %d\n", m_sFontDef.nRefCount);
9110
0
    fprintf(fpOut, "  m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
9111
9112
0
    fflush(fpOut);
9113
0
}
9114
9115
/*=====================================================================
9116
 *                      class ITABFeatureSymbol
9117
 *====================================================================*/
9118
9119
/**********************************************************************
9120
 *                   ITABFeatureSymbol::ITABFeatureSymbol()
9121
 **********************************************************************/
9122
9123
// MI default is Symbol(35, 0, 12)
9124
static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
9125
9126
ITABFeatureSymbol::ITABFeatureSymbol()
9127
87.8k
    : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
9128
87.8k
{
9129
87.8k
}
9130
9131
/**********************************************************************
9132
 *                   ITABFeatureSymbol::GetSymbolStyleString()
9133
 *
9134
 *  Return a Symbol() string. All representations info for the Symbol are here.
9135
 **********************************************************************/
9136
const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
9137
0
{
9138
0
    const char *pszStyle = nullptr;
9139
0
    int nOGRStyle = 0;
9140
    /* char szPattern[20]; */
9141
0
    int nAngle = 0;
9142
    /* szPattern[0] = '\0'; */
9143
9144
0
    switch (m_sSymbolDef.nSymbolNo)
9145
0
    {
9146
0
        case 31:
9147
            // this is actually a "null" symbol in MapInfo!
9148
0
            nOGRStyle = 0;
9149
0
            break;
9150
0
        case 32:  // filled square
9151
0
            nOGRStyle = 5;
9152
0
            break;
9153
0
        case 33:  // filled diamond
9154
0
            nAngle = 45;
9155
0
            nOGRStyle = 5;
9156
0
            break;
9157
0
        case 34:  // filled circle
9158
0
            nOGRStyle = 3;
9159
0
            break;
9160
0
        case 35:  // filled star
9161
0
            nOGRStyle = 9;
9162
0
            break;
9163
0
        case 36:  // filled upward pointing triangle
9164
0
            nOGRStyle = 7;
9165
0
            break;
9166
0
        case 37:  // filled downward pointing triangle
9167
0
            nAngle = 180;
9168
0
            nOGRStyle = 7;
9169
0
            break;
9170
0
        case 38:  // hollow square
9171
0
            nOGRStyle = 4;
9172
0
            break;
9173
0
        case 39:  // hollow diamond
9174
0
            nAngle = 45;
9175
0
            nOGRStyle = 4;
9176
0
            break;
9177
0
        case 40:  // hollow circle
9178
0
            nOGRStyle = 2;
9179
0
            break;
9180
0
        case 41:  // hollow star
9181
0
            nOGRStyle = 8;
9182
0
            break;
9183
0
        case 42:  // hollow upward pointing triangle
9184
0
            nOGRStyle = 6;
9185
0
            break;
9186
0
        case 43:  // hollow downward pointing triangle
9187
0
            nAngle = 180;
9188
0
            nOGRStyle = 6;
9189
0
            break;
9190
0
        case 44:  // filled square (with shadow)
9191
0
            nOGRStyle = 5;
9192
0
            break;
9193
0
        case 45:  // filled upward triangle (with shadow)
9194
0
            nOGRStyle = 7;
9195
0
            break;
9196
0
        case 46:  // filled circle (with shadow)
9197
0
            nOGRStyle = 3;
9198
0
            break;
9199
0
        case 49:  // crossed lines
9200
0
            nOGRStyle = 0;
9201
0
            break;
9202
0
        case 50:  // X crossed lines
9203
0
            nOGRStyle = 1;
9204
0
            break;
9205
0
    }
9206
9207
0
    nAngle += static_cast<int>(dfAngle);
9208
9209
0
    pszStyle = CPLSPrintf(
9210
0
        "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
9211
0
        m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
9212
0
        nOGRStyle);
9213
9214
0
    return pszStyle;
9215
0
}
9216
9217
/**********************************************************************
9218
 *                   ITABFeatureSymbol::SetSymbolFromStyleString()
9219
 *
9220
 *  Set all Symbol var from a OGRStyleSymbol.
9221
 **********************************************************************/
9222
void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
9223
0
{
9224
0
    GBool bIsNull = 0;
9225
9226
    // Set the Symbol Id (SymbolNo)
9227
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9228
0
    if (bIsNull)
9229
0
        pszSymbolId = nullptr;
9230
9231
0
    if (pszSymbolId)
9232
0
    {
9233
0
        if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
9234
0
        {
9235
0
            const int nSymbolId = atoi(pszSymbolId + 12);
9236
0
            SetSymbolNo(static_cast<GByte>(nSymbolId));
9237
0
        }
9238
0
        else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
9239
0
        {
9240
0
            const int nSymbolId = atoi(pszSymbolId + 8);
9241
9242
            // The OGR symbol is not the MapInfo one
9243
            // Here's some mapping
9244
0
            switch (nSymbolId)
9245
0
            {
9246
0
                case 0:
9247
0
                    SetSymbolNo(49);
9248
0
                    break;
9249
0
                case 1:
9250
0
                    SetSymbolNo(50);
9251
0
                    break;
9252
0
                case 2:
9253
0
                    SetSymbolNo(40);
9254
0
                    break;
9255
0
                case 3:
9256
0
                    SetSymbolNo(34);
9257
0
                    break;
9258
0
                case 4:
9259
0
                    SetSymbolNo(38);
9260
0
                    break;
9261
0
                case 5:
9262
0
                    SetSymbolNo(32);
9263
0
                    break;
9264
0
                case 6:
9265
0
                    SetSymbolNo(42);
9266
0
                    break;
9267
0
                case 7:
9268
0
                    SetSymbolNo(36);
9269
0
                    break;
9270
0
                case 8:
9271
0
                    SetSymbolNo(41);
9272
0
                    break;
9273
0
                case 9:
9274
0
                    SetSymbolNo(35);
9275
0
                    break;
9276
0
                case 10:  // vertical bar -- no mapinfo equivalent, so use
9277
                          // crosshairs as closest match
9278
0
                    SetSymbolNo(49);
9279
0
                    break;
9280
0
            }
9281
0
        }
9282
0
    }
9283
9284
    // Set SymbolSize
9285
0
    const double dSymbolSize = poSymbolStyle->Size(bIsNull);
9286
0
    if (dSymbolSize != 0.0)
9287
0
    {
9288
0
        SetSymbolSize(static_cast<GInt16>(dSymbolSize));
9289
0
    }
9290
9291
    // Set Symbol Color
9292
0
    const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
9293
0
    if (pszSymbolColor)
9294
0
    {
9295
0
        if (pszSymbolColor[0] == '#')
9296
0
            pszSymbolColor++;
9297
0
        int nSymbolColor =
9298
0
            static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
9299
0
        SetSymbolColor(static_cast<GInt32>(nSymbolColor));
9300
0
    }
9301
0
}
9302
9303
/**********************************************************************
9304
 *                   ITABFeatureSymbol::SetSymbolFromStyleString()
9305
 *
9306
 *  Set all Symbol var from a StyleString. Use StyleMgr to do so.
9307
 **********************************************************************/
9308
void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
9309
0
{
9310
    // Use the Style Manager to retrieve all the information we need.
9311
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9312
0
    OGRStyleTool *poStylePart = nullptr;
9313
9314
    // Init the StyleMgr with the StyleString.
9315
0
    poStyleMgr->InitStyleString(pszStyleString);
9316
9317
    // Retrieve the Symbol info.
9318
0
    const int numParts = poStyleMgr->GetPartCount();
9319
0
    for (int i = 0; i < numParts; i++)
9320
0
    {
9321
0
        poStylePart = poStyleMgr->GetPart(i);
9322
0
        if (poStylePart == nullptr)
9323
0
            continue;
9324
9325
0
        if (poStylePart->GetType() == OGRSTCSymbol)
9326
0
        {
9327
0
            break;
9328
0
        }
9329
0
        else
9330
0
        {
9331
0
            delete poStylePart;
9332
0
            poStylePart = nullptr;
9333
0
        }
9334
0
    }
9335
9336
    // If the no Symbol found, do nothing.
9337
0
    if (poStylePart == nullptr)
9338
0
    {
9339
0
        delete poStyleMgr;
9340
0
        return;
9341
0
    }
9342
9343
0
    OGRStyleSymbol *poSymbolStyle =
9344
0
        cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9345
9346
    // With Symbol, we always want to output points
9347
    //
9348
    // It's very important to set the output unit of the feature.
9349
    // The default value is meter. If we don't do it all numerical values
9350
    // will be assumed to be converted from the input unit to meter when we
9351
    // will get them via GetParam...() functions.
9352
    // See OGRStyleTool::Parse() for more details.
9353
0
    poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
9354
9355
0
    SetSymbolFromStyle(poSymbolStyle);
9356
9357
0
    delete poStyleMgr;
9358
0
    delete poStylePart;
9359
9360
0
    return;
9361
0
}
9362
9363
/**********************************************************************
9364
 *                   ITABFeatureSymbol::GetSymbolFeatureClass()
9365
 *
9366
 *  Return the feature class needed to represent the style string.
9367
 **********************************************************************/
9368
TABFeatureClass
9369
ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
9370
0
{
9371
    // Use the Style Manager to retrieve all the information we need.
9372
0
    OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9373
0
    OGRStyleTool *poStylePart = nullptr;
9374
9375
    // Init the StyleMgr with the StyleString.
9376
0
    poStyleMgr->InitStyleString(pszStyleString);
9377
9378
    // Retrieve the Symbol info.
9379
0
    const int numParts = poStyleMgr->GetPartCount();
9380
0
    for (int i = 0; i < numParts; i++)
9381
0
    {
9382
0
        poStylePart = poStyleMgr->GetPart(i);
9383
0
        if (poStylePart == nullptr)
9384
0
        {
9385
0
            continue;
9386
0
        }
9387
9388
0
        if (poStylePart->GetType() == OGRSTCSymbol)
9389
0
        {
9390
0
            break;
9391
0
        }
9392
0
        else
9393
0
        {
9394
0
            delete poStylePart;
9395
0
            poStylePart = nullptr;
9396
0
        }
9397
0
    }
9398
9399
0
    TABFeatureClass result = TABFCPoint;
9400
9401
    // If the no Symbol found, do nothing.
9402
0
    if (poStylePart == nullptr)
9403
0
    {
9404
0
        delete poStyleMgr;
9405
0
        return result;
9406
0
    }
9407
9408
0
    OGRStyleSymbol *poSymbolStyle =
9409
0
        cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9410
9411
0
    GBool bIsNull = 0;
9412
9413
    // Set the Symbol Id (SymbolNo)
9414
0
    const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9415
0
    if (bIsNull)
9416
0
        pszSymbolId = nullptr;
9417
9418
0
    if (pszSymbolId)
9419
0
    {
9420
0
        if (STARTS_WITH(pszSymbolId, "font-sym-"))
9421
0
        {
9422
0
            result = TABFCFontPoint;
9423
0
        }
9424
0
        else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
9425
0
        {
9426
0
            result = TABFCCustomPoint;
9427
0
        }
9428
0
    }
9429
9430
0
    delete poStyleMgr;
9431
0
    delete poStylePart;
9432
9433
0
    return result;
9434
0
}
9435
9436
/**********************************************************************
9437
 *                   ITABFeatureSymbol::DumpSymbolDef()
9438
 *
9439
 * Dump Symbol definition information.
9440
 **********************************************************************/
9441
void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
9442
0
{
9443
0
    if (fpOut == nullptr)
9444
0
        fpOut = stdout;
9445
9446
0
    fprintf(fpOut, "  m_nSymbolDefIndex       = %d\n", m_nSymbolDefIndex);
9447
0
    fprintf(fpOut, "  m_sSymbolDef.nRefCount  = %d\n", m_sSymbolDef.nRefCount);
9448
0
    fprintf(fpOut, "  m_sSymbolDef.nSymbolNo  = %d\n", m_sSymbolDef.nSymbolNo);
9449
0
    fprintf(fpOut, "  m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
9450
0
    fprintf(fpOut, "  m_sSymbolDef._unknown_  = %d\n",
9451
0
            static_cast<int>(m_sSymbolDef._nUnknownValue_));
9452
0
    fprintf(fpOut, "  m_sSymbolDef.rgbColor   = 0x%6.6x (%d)\n",
9453
0
            m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
9454
9455
0
    fflush(fpOut);
9456
0
}