Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_feature.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the feature classes specific to MapInfo files.
7 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
8 : *
9 : **********************************************************************
10 : * Copyright (c) 1999-2002, Daniel Morissette
11 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : **********************************************************************/
15 :
16 : #include "cpl_port.h"
17 : #include "mitab.h"
18 : #include "mitab_geometry.h"
19 : #include "mitab_utils.h"
20 :
21 : #include <cctype>
22 : #include <cmath>
23 : #include <cstdio>
24 : #include <cstdlib>
25 : #include <cstring>
26 : #include <algorithm>
27 : #include <utility>
28 :
29 : #include "cpl_conv.h"
30 : #include "cpl_error.h"
31 : #include "cpl_string.h"
32 : #include "cpl_vsi.h"
33 : #include "mitab.h"
34 : #include "mitab_geometry.h"
35 : #include "mitab_priv.h"
36 : #include "mitab_utils.h"
37 : #include "ogr_core.h"
38 : #include "ogr_feature.h"
39 : #include "ogr_featurestyle.h"
40 : #include "ogr_geometry.h"
41 :
42 : /*=====================================================================
43 : * class TABFeature
44 : *====================================================================*/
45 :
46 : /**********************************************************************
47 : * TABFeature::TABFeature()
48 : *
49 : * Constructor.
50 : **********************************************************************/
51 552482 : TABFeature::TABFeature(OGRFeatureDefn *poDefnIn)
52 : : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
53 : m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
54 552482 : m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
55 : {
56 552482 : }
57 :
58 : /**********************************************************************
59 : * TABFeature::~TABFeature()
60 : *
61 : * Destructor.
62 : **********************************************************************/
63 552746 : TABFeature::~TABFeature()
64 : {
65 552746 : }
66 :
67 : /**********************************************************************
68 : * TABFeature::CreateFromMapInfoType()
69 : *
70 : * Factory that creates a TABFeature of the right class for the specified
71 : * MapInfo Type
72 : *
73 : **********************************************************************/
74 529600 : TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
75 : OGRFeatureDefn *poDefn)
76 : {
77 529600 : TABFeature *poFeature = nullptr;
78 :
79 : /*-----------------------------------------------------------------
80 : * Create new feature object of the right type
81 : *----------------------------------------------------------------*/
82 529600 : switch (nMapInfoType)
83 : {
84 154 : case TAB_GEOM_NONE:
85 154 : poFeature = new TABFeature(poDefn);
86 154 : break;
87 527310 : case TAB_GEOM_SYMBOL_C:
88 : case TAB_GEOM_SYMBOL:
89 527310 : poFeature = new TABPoint(poDefn);
90 527310 : break;
91 6 : case TAB_GEOM_FONTSYMBOL_C:
92 : case TAB_GEOM_FONTSYMBOL:
93 6 : poFeature = new TABFontPoint(poDefn);
94 6 : break;
95 6 : case TAB_GEOM_CUSTOMSYMBOL_C:
96 : case TAB_GEOM_CUSTOMSYMBOL:
97 6 : poFeature = new TABCustomPoint(poDefn);
98 6 : break;
99 1578 : case TAB_GEOM_LINE_C:
100 : case TAB_GEOM_LINE:
101 : case TAB_GEOM_PLINE_C:
102 : case TAB_GEOM_PLINE:
103 : case TAB_GEOM_MULTIPLINE_C:
104 : case TAB_GEOM_MULTIPLINE:
105 : case TAB_GEOM_V450_MULTIPLINE_C:
106 : case TAB_GEOM_V450_MULTIPLINE:
107 : case TAB_GEOM_V800_MULTIPLINE_C:
108 : case TAB_GEOM_V800_MULTIPLINE:
109 1578 : poFeature = new TABPolyline(poDefn);
110 1578 : break;
111 8 : case TAB_GEOM_ARC_C:
112 : case TAB_GEOM_ARC:
113 8 : poFeature = new TABArc(poDefn);
114 8 : break;
115 :
116 510 : case TAB_GEOM_REGION_C:
117 : case TAB_GEOM_REGION:
118 : case TAB_GEOM_V450_REGION_C:
119 : case TAB_GEOM_V450_REGION:
120 : case TAB_GEOM_V800_REGION_C:
121 : case TAB_GEOM_V800_REGION:
122 510 : poFeature = new TABRegion(poDefn);
123 510 : break;
124 8 : case TAB_GEOM_RECT_C:
125 : case TAB_GEOM_RECT:
126 : case TAB_GEOM_ROUNDRECT_C:
127 : case TAB_GEOM_ROUNDRECT:
128 8 : poFeature = new TABRectangle(poDefn);
129 8 : break;
130 4 : case TAB_GEOM_ELLIPSE_C:
131 : case TAB_GEOM_ELLIPSE:
132 4 : poFeature = new TABEllipse(poDefn);
133 4 : break;
134 8 : case TAB_GEOM_TEXT_C:
135 : case TAB_GEOM_TEXT:
136 8 : poFeature = new TABText(poDefn);
137 8 : break;
138 4 : case TAB_GEOM_MULTIPOINT_C:
139 : case TAB_GEOM_MULTIPOINT:
140 : case TAB_GEOM_V800_MULTIPOINT_C:
141 : case TAB_GEOM_V800_MULTIPOINT:
142 4 : poFeature = new TABMultiPoint(poDefn);
143 4 : break;
144 4 : case TAB_GEOM_COLLECTION_C:
145 : case TAB_GEOM_COLLECTION:
146 : case TAB_GEOM_V800_COLLECTION_C:
147 : case TAB_GEOM_V800_COLLECTION:
148 4 : poFeature = new TABCollection(poDefn);
149 4 : break;
150 0 : default:
151 : /*-------------------------------------------------------------
152 : * Unsupported feature type... we still return a valid feature
153 : * with NONE geometry after producing a Warning.
154 : * Callers can trap that case by checking CPLGetLastErrorNo()
155 : * against TAB_WarningFeatureTypeNotSupported
156 : *------------------------------------------------------------*/
157 : // poFeature = new TABDebugFeature(poDefn);
158 0 : poFeature = new TABFeature(poDefn);
159 :
160 0 : CPLError(
161 : CE_Warning,
162 : static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
163 : "Unsupported object type %d (0x%2.2x). Feature will be "
164 : "returned with NONE geometry.",
165 : nMapInfoType, nMapInfoType);
166 : }
167 :
168 529600 : return poFeature;
169 : }
170 :
171 : /**********************************************************************
172 : * TABFeature::CopyTABFeatureBase()
173 : *
174 : * Used by CloneTABFeature() to copy the basic (fields, geometry, etc.)
175 : * TABFeature members.
176 : *
177 : * The newly created feature is owned by the caller, and will have its own
178 : * reference to the OGRFeatureDefn.
179 : *
180 : * It is possible to create the clone with a different OGRFeatureDefn,
181 : * in this case, the fields won't be copied of course.
182 : *
183 : **********************************************************************/
184 2 : void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature)
185 : {
186 : /*-----------------------------------------------------------------
187 : * Copy fields only if OGRFeatureDefn is the same
188 : *----------------------------------------------------------------*/
189 2 : OGRFeatureDefn *poThisDefnRef = GetDefnRef();
190 :
191 2 : if (poThisDefnRef == poDestFeature->GetDefnRef())
192 : {
193 0 : for (int i = 0; i < poThisDefnRef->GetFieldCount(); i++)
194 : {
195 0 : poDestFeature->SetField(i, GetRawFieldRef(i));
196 : }
197 : }
198 :
199 : /*-----------------------------------------------------------------
200 : * Copy the geometry
201 : *----------------------------------------------------------------*/
202 2 : poDestFeature->SetGeometry(GetGeometryRef());
203 :
204 2 : double dXMin = 0.0;
205 2 : double dYMin = 0.0;
206 2 : double dXMax = 0.0;
207 2 : double dYMax = 0.0;
208 2 : GetMBR(dXMin, dYMin, dXMax, dYMax);
209 2 : poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
210 :
211 2 : GInt32 nXMin = 0;
212 2 : GInt32 nYMin = 0;
213 2 : GInt32 nXMax = 0;
214 2 : GInt32 nYMax = 0;
215 2 : GetIntMBR(nXMin, nYMin, nXMax, nYMax);
216 2 : poDestFeature->SetIntMBR(nXMin, nYMin, nXMax, nYMax);
217 :
218 : // m_nMapInfoType is not carried but it is not required anyways.
219 : // it will default to TAB_GEOM_NONE
220 2 : }
221 :
222 : /**********************************************************************
223 : * TABFeature::CloneTABFeature()
224 : *
225 : * Duplicate feature, including stuff specific to each TABFeature type.
226 : *
227 : * The newly created feature is owned by the caller, and will have its own
228 : * reference to the OGRFeatureDefn.
229 : *
230 : * It is possible to create the clone with a different OGRFeatureDefn,
231 : * in this case, the fields won't be copied of course.
232 : *
233 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
234 : * then copies any members specific to its own type.
235 : **********************************************************************/
236 0 : TABFeature *TABFeature::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
237 : {
238 : /*-----------------------------------------------------------------
239 : * Alloc new feature and copy the base stuff
240 : *----------------------------------------------------------------*/
241 0 : TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
242 :
243 0 : CopyTABFeatureBase(poNew);
244 :
245 : /*-----------------------------------------------------------------
246 : * And members specific to this class
247 : *----------------------------------------------------------------*/
248 : // Nothing to do for this class
249 :
250 0 : return poNew;
251 : }
252 :
253 : /**********************************************************************
254 : * TABFeature::SetMBR()
255 : *
256 : * Set the values for the MBR corners for this feature.
257 : **********************************************************************/
258 536737 : void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
259 : {
260 536737 : m_dXMin = std::min(dXMin, dXMax);
261 536737 : m_dYMin = std::min(dYMin, dYMax);
262 536737 : m_dXMax = std::max(dXMin, dXMax);
263 536737 : m_dYMax = std::max(dYMin, dYMax);
264 536737 : }
265 :
266 : /**********************************************************************
267 : * TABFeature::GetMBR()
268 : *
269 : * Return the values for the MBR corners for this feature.
270 : **********************************************************************/
271 1227 : void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
272 : double &dYMax)
273 : {
274 1227 : dXMin = m_dXMin;
275 1227 : dYMin = m_dYMin;
276 1227 : dXMax = m_dXMax;
277 1227 : dYMax = m_dYMax;
278 1227 : }
279 :
280 : /**********************************************************************
281 : * TABFeature::SetIntMBR()
282 : *
283 : * Set the integer coordinates values of the MBR of this feature.
284 : **********************************************************************/
285 529452 : void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
286 : GInt32 nYMax)
287 : {
288 529452 : m_nXMin = nXMin;
289 529452 : m_nYMin = nYMin;
290 529452 : m_nXMax = nXMax;
291 529452 : m_nYMax = nYMax;
292 529452 : }
293 :
294 : /**********************************************************************
295 : * TABFeature::GetIntMBR()
296 : *
297 : * Return the integer coordinates values of the MBR of this feature.
298 : **********************************************************************/
299 15023 : void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
300 : GInt32 &nYMax)
301 : {
302 15023 : nXMin = m_nXMin;
303 15023 : nYMin = m_nYMin;
304 15023 : nXMax = m_nXMax;
305 15023 : nYMax = m_nYMax;
306 15023 : }
307 :
308 : /**********************************************************************
309 : * TABFeature::ReadRecordFromDATFile()
310 : *
311 : * Fill the fields part of the feature from the contents of the
312 : * table record pointed to by poDATFile.
313 : *
314 : * It is assumed that poDATFile currently points to the beginning of
315 : * the table record and that this feature's OGRFeatureDefn has been
316 : * properly initialized for this table.
317 : **********************************************************************/
318 529432 : int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
319 : {
320 529432 : CPLAssert(poDATFile);
321 :
322 529432 : const int numFields = poDATFile->GetNumFields();
323 :
324 1060650 : for (int iField = 0; iField < numFields; iField++)
325 : {
326 531221 : switch (poDATFile->GetFieldType(iField))
327 : {
328 1308 : case TABFChar:
329 : {
330 1308 : int iWidth(poDATFile->GetFieldWidth(iField));
331 2616 : CPLString osValue(poDATFile->ReadCharField(iWidth));
332 :
333 1308 : if (!poDATFile->GetEncoding().empty())
334 : {
335 110 : osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
336 : }
337 1308 : SetField(iField, osValue);
338 1308 : break;
339 : }
340 28 : case TABFDecimal:
341 : {
342 28 : const double dValue = poDATFile->ReadDecimalField(
343 : poDATFile->GetFieldWidth(iField));
344 28 : SetField(iField, dValue);
345 28 : break;
346 : }
347 529212 : case TABFInteger:
348 : {
349 529212 : const int nValue = poDATFile->ReadIntegerField(
350 : poDATFile->GetFieldWidth(iField));
351 529212 : SetField(iField, nValue);
352 529212 : break;
353 : }
354 4 : case TABFSmallInt:
355 : {
356 4 : const int nValue = poDATFile->ReadSmallIntField(
357 4 : poDATFile->GetFieldWidth(iField));
358 4 : SetField(iField, nValue);
359 4 : break;
360 : }
361 6 : case TABFLargeInt:
362 : {
363 6 : const GInt64 nValue = poDATFile->ReadLargeIntField(
364 : poDATFile->GetFieldWidth(iField));
365 6 : SetField(iField, nValue);
366 6 : break;
367 : }
368 598 : case TABFFloat:
369 : {
370 : const double dValue =
371 598 : poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
372 598 : SetField(iField, dValue);
373 598 : break;
374 : }
375 6 : case TABFLogical:
376 : {
377 6 : const bool bValue = poDATFile->ReadLogicalField(
378 : poDATFile->GetFieldWidth(iField));
379 6 : SetField(iField, bValue ? 1 : 0);
380 6 : break;
381 : }
382 29 : case TABFDate:
383 : {
384 : #ifdef MITAB_USE_OFTDATETIME
385 29 : int nYear = 0;
386 29 : int nMonth = 0;
387 29 : int nDay = 0;
388 29 : const int status = poDATFile->ReadDateField(
389 : poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
390 29 : if (status == 0)
391 : {
392 12 : SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
393 : }
394 : #else
395 : const char *pszValue =
396 : poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
397 : SetField(iField, pszValue);
398 : #endif
399 29 : break;
400 : }
401 7 : case TABFTime:
402 : {
403 : #ifdef MITAB_USE_OFTDATETIME
404 7 : int nHour = 0;
405 7 : int nMin = 0;
406 7 : int nMS = 0;
407 7 : int nSec = 0;
408 : const int status =
409 7 : poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
410 : &nHour, &nMin, &nSec, &nMS);
411 7 : if (status == 0)
412 : {
413 6 : int nYear = 0;
414 6 : int nMonth = 0;
415 6 : int nDay = 0;
416 6 : SetField(iField, nYear, nMonth, nDay, nHour, nMin,
417 6 : nSec + nMS / 1000.0f, 0);
418 : }
419 : #else
420 : const char *pszValue =
421 : poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
422 : SetField(iField, pszValue);
423 : #endif
424 7 : break;
425 : }
426 23 : case TABFDateTime:
427 : {
428 : #ifdef MITAB_USE_OFTDATETIME
429 23 : int nYear = 0;
430 23 : int nMonth = 0;
431 23 : int nDay = 0;
432 23 : int nHour = 0;
433 23 : int nMin = 0;
434 23 : int nMS = 0;
435 23 : int nSec = 0;
436 23 : const int status = poDATFile->ReadDateTimeField(
437 : poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
438 : &nHour, &nMin, &nSec, &nMS);
439 23 : if (status == 0)
440 : {
441 6 : SetField(iField, nYear, nMonth, nDay, nHour, nMin,
442 6 : nSec + nMS / 1000.0f, 0);
443 : }
444 : #else
445 : const char *pszValue = poDATFile->ReadDateTimeField(
446 : poDATFile->GetFieldWidth(iField));
447 : SetField(iField, pszValue);
448 : #endif
449 23 : break;
450 : }
451 0 : default:
452 : // Other type??? Impossible!
453 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
454 : "Unsupported field type!");
455 : }
456 : }
457 :
458 529432 : return 0;
459 : }
460 :
461 : /**********************************************************************
462 : * TABFeature::WriteRecordToDATFile()
463 : *
464 : * Write the attribute part of the feature to the .DAT file.
465 : *
466 : * It is assumed that poDATFile currently points to the beginning of
467 : * the table record and that this feature's OGRFeatureDefn has been
468 : * properly initialized for this table.
469 : *
470 : * Returns 0 on success, -1 on error.
471 : **********************************************************************/
472 15087 : int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
473 : TABINDFile *poINDFile, int *panIndexNo)
474 : {
475 : #ifdef MITAB_USE_OFTDATETIME
476 15087 : int nYear = 0;
477 15087 : int nMon = 0;
478 15087 : int nDay = 0;
479 15087 : int nHour = 0;
480 15087 : int nMin = 0;
481 15087 : int nTZFlag = 0;
482 15087 : float fSec = 0.0f;
483 : #endif
484 :
485 15087 : CPLAssert(poDATFile);
486 :
487 15087 : const int numFields = poDATFile->GetNumFields();
488 :
489 15087 : poDATFile->MarkRecordAsExisting();
490 :
491 15087 : int nStatus = 0;
492 30858 : for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
493 : {
494 : // Hack for "extra" introduced field.
495 15771 : if (iField >= GetDefnRef()->GetFieldCount())
496 : {
497 1 : CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
498 : iField == 0);
499 1 : nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
500 : poINDFile, 0);
501 1 : continue;
502 : }
503 15770 : CPLAssert(panIndexNo != nullptr);
504 :
505 15770 : switch (poDATFile->GetFieldType(iField))
506 : {
507 377 : case TABFChar:
508 : {
509 377 : CPLString osValue(GetFieldAsString(iField));
510 377 : if (!poDATFile->GetEncoding().empty())
511 : {
512 30 : osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
513 : }
514 377 : nStatus = poDATFile->WriteCharField(
515 : osValue, poDATFile->GetFieldWidth(iField), poINDFile,
516 377 : panIndexNo[iField]);
517 : }
518 377 : break;
519 5 : case TABFDecimal:
520 5 : nStatus = poDATFile->WriteDecimalField(
521 : GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
522 : poDATFile->GetFieldPrecision(iField), poINDFile,
523 5 : panIndexNo[iField]);
524 5 : break;
525 14993 : case TABFInteger:
526 14993 : nStatus = poDATFile->WriteIntegerField(
527 14993 : GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
528 14993 : break;
529 0 : case TABFSmallInt:
530 0 : nStatus = poDATFile->WriteSmallIntField(
531 0 : static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
532 0 : panIndexNo[iField]);
533 0 : break;
534 2 : case TABFLargeInt:
535 6 : nStatus = poDATFile->WriteLargeIntField(
536 2 : static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
537 2 : panIndexNo[iField]);
538 2 : break;
539 237 : case TABFFloat:
540 237 : nStatus = poDATFile->WriteFloatField(
541 237 : GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
542 237 : break;
543 2 : case TABFLogical:
544 : nStatus =
545 2 : poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
546 2 : poINDFile, panIndexNo[iField]);
547 2 : break;
548 76 : case TABFDate:
549 : #ifdef MITAB_USE_OFTDATETIME
550 76 : if (IsFieldSetAndNotNull(iField))
551 : {
552 59 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
553 : &nMin, &fSec, &nTZFlag);
554 : }
555 : else
556 : {
557 17 : nYear = 0;
558 17 : nMon = 0;
559 17 : nDay = 0;
560 : }
561 :
562 152 : nStatus = poDATFile->WriteDateField(
563 76 : nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
564 : #else
565 : nStatus = poDATFile->WriteDateField(
566 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
567 : #endif
568 76 : break;
569 3 : case TABFTime:
570 : #ifdef MITAB_USE_OFTDATETIME
571 3 : if (IsFieldSetAndNotNull(iField))
572 : {
573 2 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
574 : &nMin, &fSec, &nTZFlag);
575 : }
576 : else
577 : {
578 : // Put negative values, so that WriteTimeField() forges
579 : // a negative value, and ultimately write -1 in the binary
580 : // field
581 1 : nHour = -1;
582 1 : nMin = -1;
583 1 : fSec = -1;
584 : }
585 3 : nStatus = poDATFile->WriteTimeField(
586 : nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
587 3 : poINDFile, panIndexNo[iField]);
588 :
589 : #else
590 : nStatus = poDATFile->WriteTimeField(
591 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
592 : #endif
593 3 : break;
594 75 : case TABFDateTime:
595 : #ifdef MITAB_USE_OFTDATETIME
596 75 : if (IsFieldSetAndNotNull(iField))
597 : {
598 58 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
599 : &nMin, &fSec, &nTZFlag);
600 : }
601 : else
602 : {
603 17 : nYear = 0;
604 17 : nMon = 0;
605 17 : nDay = 0;
606 17 : nHour = 0;
607 17 : nMin = 0;
608 17 : fSec = 0;
609 : }
610 :
611 75 : nStatus = poDATFile->WriteDateTimeField(
612 : nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
613 75 : OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
614 : #else
615 : nStatus = poDATFile->WriteDateTimeField(
616 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
617 : #endif
618 75 : break;
619 0 : default:
620 : // Other type??? Impossible!
621 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
622 : "Unsupported field type!");
623 : }
624 : }
625 :
626 15087 : if (nStatus != 0)
627 1 : return nStatus;
628 :
629 15086 : if (poDATFile->CommitRecordToFile() != 0)
630 0 : return -1;
631 :
632 15086 : return 0;
633 : }
634 :
635 : /**********************************************************************
636 : * TABFeature::ReadGeometryFromMAPFile()
637 : *
638 : * In derived classes, this method should be reimplemented to
639 : * fill the geometry and representation (color, etc...) part of the
640 : * feature from the contents of the .MAP object pointed to by poMAPFile.
641 : *
642 : * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
643 : * currently points to the beginning of a map object.
644 : *
645 : * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
646 : * the CoordBlock data during splitting of object blocks. In this case we
647 : * need to process only the information related to the CoordBlock. One
648 : * important thing to avoid is reading/writing pen/brush/symbol definitions
649 : * as that would screw up their ref counters.
650 : *
651 : * ppoCoordBlock is used by TABCollection and by index splitting code
652 : * to provide a CoordBlock to use instead of the one from the poMAPFile and
653 : * return the current pointer at the end of the call.
654 : *
655 : * The current implementation does nothing since instances of TABFeature
656 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
657 : *
658 : * Returns 0 on success, -1 on error, in which case CPLError() will have
659 : * been called.
660 : **********************************************************************/
661 154 : int TABFeature::ReadGeometryFromMAPFile(
662 : TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
663 : GBool /*bCoordBlockDataOnly=FALSE*/,
664 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
665 : {
666 : // Nothing to do. Instances of TABFeature objects contain no geometry.
667 154 : return 0;
668 : }
669 :
670 : /**********************************************************************
671 : * TABFeature::UpdateMBR()
672 : *
673 : * Fetch envelope of poGeom and update MBR.
674 : * Integer coord MBR is updated only if poMapFile is not NULL.
675 : *
676 : * Returns 0 on success, or -1 if there is no geometry in object
677 : **********************************************************************/
678 15017 : int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
679 : {
680 15017 : OGRGeometry *poGeom = GetGeometryRef();
681 :
682 15017 : if (poGeom)
683 : {
684 15017 : OGREnvelope oEnv;
685 15017 : poGeom->getEnvelope(&oEnv);
686 :
687 15017 : m_dXMin = oEnv.MinX;
688 15017 : m_dYMin = oEnv.MinY;
689 15017 : m_dXMax = oEnv.MaxX;
690 15017 : m_dYMax = oEnv.MaxY;
691 :
692 15017 : if (poMapFile)
693 : {
694 15017 : poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
695 15017 : poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
696 : // Coordsy2Int can transform a min value to a max one and vice
697 : // versa.
698 15017 : if (m_nXMin > m_nXMax)
699 : {
700 0 : std::swap(m_nXMin, m_nXMax);
701 : }
702 15017 : if (m_nYMin > m_nYMax)
703 : {
704 0 : std::swap(m_nYMin, m_nYMax);
705 : }
706 : }
707 :
708 15017 : return 0;
709 : }
710 :
711 0 : return -1;
712 : }
713 :
714 : /**********************************************************************
715 : * TABFeature::ValidateCoordType()
716 : *
717 : * Checks the feature envelope to establish if the feature should be
718 : * written using Compressed coordinates or not and adjust m_nMapInfoType
719 : * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
720 : * m_nXMax, m_nYMax
721 : *
722 : * This function should be used only by the ValidateMapInfoType()
723 : * implementations.
724 : *
725 : * Returns TRUE if coord. should be compressed, FALSE otherwise
726 : **********************************************************************/
727 303 : GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
728 : {
729 303 : GBool bCompr = FALSE;
730 :
731 : /*-------------------------------------------------------------
732 : * Decide if coordinates should be compressed or not.
733 : *------------------------------------------------------------*/
734 303 : if (UpdateMBR(poMapFile) == 0)
735 : {
736 : /* Test for max range < 65535 here instead of < 65536 to avoid
737 : * compressed coordinate overflows in some boundary situations
738 : */
739 303 : if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
740 294 : (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
741 : {
742 294 : bCompr = TRUE;
743 : }
744 303 : m_nComprOrgX =
745 303 : static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
746 303 : m_nComprOrgY =
747 303 : static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
748 : }
749 :
750 : /*-------------------------------------------------------------
751 : * Adjust native type
752 : *------------------------------------------------------------*/
753 303 : if (bCompr && ((m_nMapInfoType % 3) == 2))
754 294 : m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
755 : 1); // compr = 1, 4, 7, ...
756 9 : else if (!bCompr && ((m_nMapInfoType % 3) == 1))
757 0 : m_nMapInfoType = static_cast<TABGeomType>(
758 0 : m_nMapInfoType + 1); // non-compr = 2, 5, 8, ...
759 :
760 303 : return bCompr;
761 : }
762 :
763 : /**********************************************************************
764 : * TABFeature::ForceCoordTypeAndOrigin()
765 : *
766 : * This function is used by TABCollection::ValidateMapInfoType() to force
767 : * the coord type and compressed origin of all members of a collection
768 : * to be the same. (A replacement for ValidateCoordType() for this
769 : * specific case)
770 : **********************************************************************/
771 0 : void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
772 : GInt32 nComprOrgX, GInt32 nComprOrgY,
773 : GInt32 nXMin, GInt32 nYMin,
774 : GInt32 nXMax, GInt32 nYMax)
775 : {
776 : /*-------------------------------------------------------------
777 : * Set Compressed Origin and adjust native type
778 : *------------------------------------------------------------*/
779 0 : m_nComprOrgX = nComprOrgX;
780 0 : m_nComprOrgY = nComprOrgY;
781 :
782 0 : m_nMapInfoType = nMapInfoType;
783 :
784 0 : if (bCompr && ((m_nMapInfoType % 3) == 2))
785 0 : m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
786 : 1); // compr = 1, 4, 7, ...
787 0 : else if (!bCompr && ((m_nMapInfoType % 3) == 1))
788 0 : m_nMapInfoType = static_cast<TABGeomType>(
789 0 : m_nMapInfoType + 1); // non-compr = 2, 5, 8, ...
790 :
791 0 : m_nXMin = nXMin;
792 0 : m_nYMin = nYMin;
793 0 : m_nXMax = nXMax;
794 0 : m_nYMax = nYMax;
795 0 : }
796 :
797 : /**********************************************************************
798 : * TABFeature::WriteGeometryToMAPFile()
799 : *
800 : *
801 : * In derived classes, this method should be reimplemented to
802 : * write the geometry and representation (color, etc...) part of the
803 : * feature to the .MAP object pointed to by poMAPFile.
804 : *
805 : * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
806 : * currently points to a valid map object.
807 : *
808 : * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
809 : * the CoordBlock data during splitting of object blocks. In this case we
810 : * need to process only the information related to the CoordBlock. One
811 : * important thing to avoid is reading/writing pen/brush/symbol definitions
812 : * as that would screw up their ref counters.
813 : *
814 : * ppoCoordBlock is used by TABCollection and by index splitting code
815 : * to provide a CoordBlock to use instead of the one from the poMAPFile and
816 : * return the current pointer at the end of the call.
817 : *
818 : * The current implementation does nothing since instances of TABFeature
819 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
820 : *
821 : * Returns 0 on success, -1 on error, in which case CPLError() will have
822 : * been called.
823 : **********************************************************************/
824 60 : int TABFeature::WriteGeometryToMAPFile(
825 : TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
826 : GBool /*bCoordBlockDataOnly=FALSE*/,
827 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
828 : {
829 : /*-----------------------------------------------------------------
830 : * Nothing to do... instances of TABFeature objects contain no geometry.
831 : *----------------------------------------------------------------*/
832 :
833 60 : return 0;
834 : }
835 :
836 : /**********************************************************************
837 : * TABFeature::DumpMID()
838 : *
839 : * Dump feature attributes in a format similar to .MID data records.
840 : **********************************************************************/
841 0 : void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
842 : {
843 0 : OGRFeatureDefn *l_poDefn = GetDefnRef();
844 :
845 0 : if (fpOut == nullptr)
846 0 : fpOut = stdout;
847 :
848 0 : for (int iField = 0; iField < GetFieldCount(); iField++)
849 : {
850 0 : OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
851 :
852 0 : fprintf(fpOut, " %s (%s) = %s\n", poFDefn->GetNameRef(),
853 : OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
854 : GetFieldAsString(iField));
855 : }
856 :
857 0 : fflush(fpOut);
858 0 : }
859 :
860 : /**********************************************************************
861 : * TABFeature::DumpMIF()
862 : *
863 : * Dump feature geometry in a format similar to .MIF files.
864 : **********************************************************************/
865 0 : void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
866 : {
867 0 : if (fpOut == nullptr)
868 0 : fpOut = stdout;
869 :
870 : /*-----------------------------------------------------------------
871 : * Generate output... not much to do, feature contains no geometry.
872 : *----------------------------------------------------------------*/
873 0 : fprintf(fpOut, "NONE\n");
874 :
875 0 : fflush(fpOut);
876 0 : }
877 :
878 : /*=====================================================================
879 : * class TABPoint
880 : *====================================================================*/
881 :
882 : /**********************************************************************
883 : * TABPoint::TABPoint()
884 : *
885 : * Constructor.
886 : **********************************************************************/
887 544164 : TABPoint::TABPoint(OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
888 : {
889 544164 : }
890 :
891 : /**********************************************************************
892 : * TABPoint::~TABPoint()
893 : *
894 : * Destructor.
895 : **********************************************************************/
896 1086999 : TABPoint::~TABPoint()
897 : {
898 1086999 : }
899 :
900 : /**********************************************************************
901 : * TABPoint::CloneTABFeature()
902 : *
903 : * Duplicate feature, including stuff specific to each TABFeature type.
904 : *
905 : * This method calls the generic TABFeature::CloneTABFeature() and
906 : * then copies any members specific to its own type.
907 : **********************************************************************/
908 2 : TABFeature *TABPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
909 : {
910 : /*-----------------------------------------------------------------
911 : * Alloc new feature and copy the base stuff
912 : *----------------------------------------------------------------*/
913 2 : TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
914 :
915 2 : CopyTABFeatureBase(poNew);
916 :
917 : /*-----------------------------------------------------------------
918 : * And members specific to this class
919 : *----------------------------------------------------------------*/
920 : // ITABFeatureSymbol
921 2 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
922 :
923 2 : return poNew;
924 : }
925 :
926 : /**********************************************************************
927 : * TABPoint::ValidateMapInfoType()
928 : *
929 : * Check the feature's geometry part and return the corresponding
930 : * mapinfo object type code. The m_nMapInfoType member will also
931 : * be updated for further calls to GetMapInfoType();
932 : *
933 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
934 : * is expected for this object class.
935 : **********************************************************************/
936 14691 : TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
937 : {
938 : /*-----------------------------------------------------------------
939 : * Fetch and validate geometry
940 : * __TODO__ For now we always write in uncompressed format (until we
941 : * find that this is not correct... note that at this point the
942 : * decision to use compressed/uncompressed will likely be based on
943 : * the distance between the point and the object block center in
944 : * integer coordinates being > 32767 or not... remains to be verified)
945 : *----------------------------------------------------------------*/
946 14691 : OGRGeometry *poGeom = GetGeometryRef();
947 14691 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
948 : {
949 14691 : switch (GetFeatureClass())
950 : {
951 2 : case TABFCFontPoint:
952 2 : m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
953 2 : break;
954 2 : case TABFCCustomPoint:
955 2 : m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
956 2 : break;
957 14687 : case TABFCPoint:
958 : default:
959 14687 : m_nMapInfoType = TAB_GEOM_SYMBOL;
960 14687 : break;
961 : }
962 : }
963 : else
964 : {
965 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
966 : "TABPoint: Missing or Invalid Geometry!");
967 0 : m_nMapInfoType = TAB_GEOM_NONE;
968 : }
969 :
970 14691 : UpdateMBR(poMapFile);
971 :
972 14691 : return m_nMapInfoType;
973 : }
974 :
975 : /**********************************************************************
976 : * TABPoint::ReadGeometryFromMAPFile()
977 : *
978 : * Fill the geometry and representation (color, etc...) part of the
979 : * feature from the contents of the .MAP object pointed to by poMAPFile.
980 : *
981 : * It is assumed that poMAPFile currently points to the beginning of
982 : * a map object.
983 : *
984 : * Returns 0 on success, -1 on error, in which case CPLError() will have
985 : * been called.
986 : **********************************************************************/
987 527310 : int TABPoint::ReadGeometryFromMAPFile(
988 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
989 : GBool bCoordBlockDataOnly /*=FALSE*/,
990 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
991 : {
992 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
993 527310 : if (bCoordBlockDataOnly)
994 0 : return 0;
995 :
996 : /*-----------------------------------------------------------------
997 : * Fetch and validate geometry type
998 : *----------------------------------------------------------------*/
999 527310 : m_nMapInfoType = poObjHdr->m_nType;
1000 :
1001 527310 : if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
1002 12888 : m_nMapInfoType != TAB_GEOM_SYMBOL_C)
1003 : {
1004 0 : CPLError(
1005 : CE_Failure, CPLE_AssertionFailed,
1006 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1007 0 : m_nMapInfoType, m_nMapInfoType);
1008 0 : return -1;
1009 : }
1010 :
1011 : /*-----------------------------------------------------------------
1012 : * Read object information
1013 : *----------------------------------------------------------------*/
1014 527310 : TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1015 :
1016 527310 : m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index
1017 :
1018 527310 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1019 :
1020 : /*-----------------------------------------------------------------
1021 : * Create and fill geometry object
1022 : *----------------------------------------------------------------*/
1023 527310 : double dX = 0.0;
1024 527310 : double dY = 0.0;
1025 :
1026 527310 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1027 527310 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1028 :
1029 527310 : SetGeometryDirectly(poGeometry);
1030 :
1031 527310 : SetMBR(dX, dY, dX, dY);
1032 527310 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1033 : poObjHdr->m_nMaxY);
1034 :
1035 527310 : return 0;
1036 : }
1037 :
1038 : /**********************************************************************
1039 : * TABPoint::WriteGeometryToMAPFile()
1040 : *
1041 : * Write the geometry and representation (color, etc...) part of the
1042 : * feature to the .MAP object pointed to by poMAPFile.
1043 : *
1044 : * It is assumed that poMAPFile currently points to a valid map object.
1045 : *
1046 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1047 : * been called.
1048 : **********************************************************************/
1049 14687 : int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
1050 : TABMAPObjHdr *poObjHdr,
1051 : GBool bCoordBlockDataOnly /*=FALSE*/,
1052 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1053 : {
1054 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1055 14687 : if (bCoordBlockDataOnly)
1056 0 : return 0;
1057 :
1058 : /*-----------------------------------------------------------------
1059 : * We assume that ValidateMapInfoType() was called already and that
1060 : * the type in poObjHdr->m_nType is valid.
1061 : *----------------------------------------------------------------*/
1062 14687 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1063 :
1064 : /*-----------------------------------------------------------------
1065 : * Fetch and validate geometry
1066 : *----------------------------------------------------------------*/
1067 14687 : OGRGeometry *poGeom = GetGeometryRef();
1068 14687 : OGRPoint *poPoint = nullptr;
1069 14687 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1070 14687 : poPoint = poGeom->toPoint();
1071 : else
1072 : {
1073 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1074 : "TABPoint: Missing or Invalid Geometry!");
1075 0 : return -1;
1076 : }
1077 :
1078 14687 : GInt32 nX = 0;
1079 14687 : GInt32 nY = 0;
1080 14687 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1081 :
1082 : /*-----------------------------------------------------------------
1083 : * Copy object information
1084 : *----------------------------------------------------------------*/
1085 14687 : TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1086 :
1087 14687 : poPointHdr->m_nX = nX;
1088 14687 : poPointHdr->m_nY = nY;
1089 14687 : poPointHdr->SetMBR(nX, nY, nX, nY);
1090 :
1091 14687 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1092 14687 : poPointHdr->m_nSymbolId =
1093 14687 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
1094 :
1095 14687 : if (CPLGetLastErrorType() == CE_Failure)
1096 0 : return -1;
1097 :
1098 14687 : return 0;
1099 : }
1100 :
1101 : /**********************************************************************
1102 : * TABPoint::GetX()
1103 : *
1104 : * Return this point's X coordinate.
1105 : **********************************************************************/
1106 0 : double TABPoint::GetX()
1107 : {
1108 :
1109 : /*-----------------------------------------------------------------
1110 : * Fetch and validate geometry
1111 : *----------------------------------------------------------------*/
1112 0 : OGRGeometry *poGeom = GetGeometryRef();
1113 0 : OGRPoint *poPoint = nullptr;
1114 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1115 0 : poPoint = poGeom->toPoint();
1116 : else
1117 : {
1118 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1119 : "TABPoint: Missing or Invalid Geometry!");
1120 0 : return 0.0;
1121 : }
1122 :
1123 0 : return poPoint->getX();
1124 : }
1125 :
1126 : /**********************************************************************
1127 : * TABPoint::GetY()
1128 : *
1129 : * Return this point's Y coordinate.
1130 : **********************************************************************/
1131 0 : double TABPoint::GetY()
1132 : {
1133 : /*-----------------------------------------------------------------
1134 : * Fetch and validate geometry
1135 : *----------------------------------------------------------------*/
1136 0 : OGRGeometry *poGeom = GetGeometryRef();
1137 0 : OGRPoint *poPoint = nullptr;
1138 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1139 0 : poPoint = poGeom->toPoint();
1140 : else
1141 : {
1142 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1143 : "TABPoint: Missing or Invalid Geometry!");
1144 0 : return 0.0;
1145 : }
1146 :
1147 0 : return poPoint->getY();
1148 : }
1149 :
1150 : /**********************************************************************
1151 : * TABPoint::GetStyleString() const
1152 : *
1153 : * Return style string for this feature.
1154 : *
1155 : * Style String is built only once during the first call to GetStyleString().
1156 : **********************************************************************/
1157 239 : const char *TABPoint::GetStyleString() const
1158 : {
1159 239 : if (m_pszStyleString == nullptr)
1160 : {
1161 27 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1162 : }
1163 :
1164 239 : return m_pszStyleString;
1165 : }
1166 :
1167 : /**********************************************************************
1168 : * TABPoint::DumpMIF()
1169 : *
1170 : * Dump feature geometry in a format similar to .MIF POINTs.
1171 : **********************************************************************/
1172 0 : void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
1173 : {
1174 0 : if (fpOut == nullptr)
1175 0 : fpOut = stdout;
1176 :
1177 : /*-----------------------------------------------------------------
1178 : * Fetch and validate geometry
1179 : *----------------------------------------------------------------*/
1180 0 : OGRGeometry *poGeom = GetGeometryRef();
1181 0 : OGRPoint *poPoint = nullptr;
1182 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1183 0 : poPoint = poGeom->toPoint();
1184 : else
1185 : {
1186 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1187 : "TABPoint: Missing or Invalid Geometry!");
1188 0 : return;
1189 : }
1190 :
1191 : /*-----------------------------------------------------------------
1192 : * Generate output
1193 : *----------------------------------------------------------------*/
1194 0 : fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
1195 :
1196 0 : DumpSymbolDef(fpOut);
1197 :
1198 : /*-----------------------------------------------------------------
1199 : * Handle stuff specific to derived classes
1200 : *----------------------------------------------------------------*/
1201 : // cppcheck-suppress knownConditionTrueFalse
1202 0 : if (GetFeatureClass() == TABFCFontPoint)
1203 : {
1204 0 : TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
1205 0 : fprintf(fpOut, " m_nFontStyle = 0x%2.2x (%d)\n",
1206 : poFeature->GetFontStyleTABValue(),
1207 : poFeature->GetFontStyleTABValue());
1208 :
1209 0 : poFeature->DumpFontDef(fpOut);
1210 : }
1211 : // cppcheck-suppress knownConditionTrueFalse
1212 0 : if (GetFeatureClass() == TABFCCustomPoint)
1213 : {
1214 0 : TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
1215 :
1216 0 : fprintf(fpOut, " m_nUnknown_ = 0x%2.2x (%d)\n",
1217 0 : poFeature->m_nUnknown_, poFeature->m_nUnknown_);
1218 0 : fprintf(fpOut, " m_nCustomStyle = 0x%2.2x (%d)\n",
1219 0 : poFeature->GetCustomSymbolStyle(),
1220 0 : poFeature->GetCustomSymbolStyle());
1221 :
1222 0 : poFeature->DumpFontDef(fpOut);
1223 : }
1224 :
1225 0 : fflush(fpOut);
1226 : }
1227 :
1228 : /*=====================================================================
1229 : * class TABFontPoint
1230 : *====================================================================*/
1231 :
1232 : /**********************************************************************
1233 : * TABFontPoint::TABFontPoint()
1234 : *
1235 : * Constructor.
1236 : **********************************************************************/
1237 643 : TABFontPoint::TABFontPoint(OGRFeatureDefn *poDefnIn)
1238 643 : : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
1239 : {
1240 643 : }
1241 :
1242 : /**********************************************************************
1243 : * TABFontPoint::~TABFontPoint()
1244 : *
1245 : * Destructor.
1246 : **********************************************************************/
1247 1286 : TABFontPoint::~TABFontPoint()
1248 : {
1249 1286 : }
1250 :
1251 : /**********************************************************************
1252 : * TABFontPoint::CloneTABFeature()
1253 : *
1254 : * Duplicate feature, including stuff specific to each TABFeature type.
1255 : *
1256 : * This method calls the generic TABFeature::CloneTABFeature() and
1257 : * then copies any members specific to its own type.
1258 : **********************************************************************/
1259 0 : TABFeature *TABFontPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1260 : {
1261 : /*-----------------------------------------------------------------
1262 : * Alloc new feature and copy the base stuff
1263 : *----------------------------------------------------------------*/
1264 : TABFontPoint *poNew =
1265 0 : new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
1266 :
1267 0 : CopyTABFeatureBase(poNew);
1268 :
1269 : /*-----------------------------------------------------------------
1270 : * And members specific to this class
1271 : *----------------------------------------------------------------*/
1272 : // ITABFeatureSymbol
1273 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1274 :
1275 : // ITABFeatureFont
1276 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
1277 :
1278 0 : poNew->SetSymbolAngle(GetSymbolAngle());
1279 0 : poNew->SetFontStyleTABValue(GetFontStyleTABValue());
1280 :
1281 0 : return poNew;
1282 : }
1283 :
1284 : /**********************************************************************
1285 : * TABFontPoint::ReadGeometryFromMAPFile()
1286 : *
1287 : * Fill the geometry and representation (color, etc...) part of the
1288 : * feature from the contents of the .MAP object pointed to by poMAPFile.
1289 : *
1290 : * It is assumed that poMAPFile currently points to the beginning of
1291 : * a map object.
1292 : *
1293 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1294 : * been called.
1295 : **********************************************************************/
1296 6 : int TABFontPoint::ReadGeometryFromMAPFile(
1297 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1298 : GBool bCoordBlockDataOnly /*=FALSE*/,
1299 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1300 : {
1301 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1302 6 : if (bCoordBlockDataOnly)
1303 0 : return 0;
1304 :
1305 : /*-----------------------------------------------------------------
1306 : * Fetch and validate geometry type
1307 : *----------------------------------------------------------------*/
1308 6 : m_nMapInfoType = poObjHdr->m_nType;
1309 :
1310 6 : if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
1311 0 : m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
1312 : {
1313 0 : CPLError(
1314 : CE_Failure, CPLE_AssertionFailed,
1315 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1316 0 : m_nMapInfoType, m_nMapInfoType);
1317 0 : return -1;
1318 : }
1319 :
1320 : /*-----------------------------------------------------------------
1321 : * Read object information
1322 : * NOTE: This symbol type does not contain a reference to a
1323 : * SymbolDef block in the file, but we still use the m_sSymbolDef
1324 : * structure to store the information inside the class so that the
1325 : * ITABFeatureSymbol methods work properly for the class user.
1326 : *----------------------------------------------------------------*/
1327 : TABMAPObjFontPoint *poPointHdr =
1328 6 : cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1329 :
1330 6 : m_nSymbolDefIndex = -1;
1331 6 : m_sSymbolDef.nRefCount = 0;
1332 :
1333 6 : m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId; // shape
1334 6 : m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize; // point size
1335 :
1336 6 : m_nFontStyle = poPointHdr->m_nFontStyle; // font style
1337 :
1338 6 : m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
1339 6 : poPointHdr->m_nG * 256 + poPointHdr->m_nB;
1340 :
1341 : /*-------------------------------------------------------------
1342 : * Symbol Angle, in tenths of degree.
1343 : * Contrary to arc start/end angles, no conversion based on
1344 : * origin quadrant is required here.
1345 : *------------------------------------------------------------*/
1346 6 : m_dAngle = poPointHdr->m_nAngle / 10.0;
1347 :
1348 6 : m_nFontDefIndex = poPointHdr->m_nFontId; // Font name index
1349 :
1350 6 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1351 :
1352 : /*-----------------------------------------------------------------
1353 : * Create and fill geometry object
1354 : *----------------------------------------------------------------*/
1355 6 : double dX = 0.0;
1356 6 : double dY = 0.0;
1357 6 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1358 6 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1359 :
1360 6 : SetGeometryDirectly(poGeometry);
1361 :
1362 6 : SetMBR(dX, dY, dX, dY);
1363 6 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1364 : poObjHdr->m_nMaxY);
1365 :
1366 6 : return 0;
1367 : }
1368 :
1369 : /**********************************************************************
1370 : * TABFontPoint::WriteGeometryToMAPFile()
1371 : *
1372 : * Write the geometry and representation (color, etc...) part of the
1373 : * feature to the .MAP object pointed to by poMAPFile.
1374 : *
1375 : * It is assumed that poMAPFile currently points to a valid map object.
1376 : *
1377 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1378 : * been called.
1379 : **********************************************************************/
1380 2 : int TABFontPoint::WriteGeometryToMAPFile(
1381 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1382 : GBool bCoordBlockDataOnly /*=FALSE*/,
1383 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1384 : {
1385 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1386 2 : if (bCoordBlockDataOnly)
1387 0 : return 0;
1388 :
1389 : /*-----------------------------------------------------------------
1390 : * We assume that ValidateMapInfoType() was called already and that
1391 : * the type in poObjHdr->m_nType is valid.
1392 : *----------------------------------------------------------------*/
1393 2 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1394 :
1395 : /*-----------------------------------------------------------------
1396 : * Fetch and validate geometry
1397 : *----------------------------------------------------------------*/
1398 2 : OGRGeometry *poGeom = GetGeometryRef();
1399 2 : OGRPoint *poPoint = nullptr;
1400 2 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1401 2 : poPoint = poGeom->toPoint();
1402 : else
1403 : {
1404 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1405 : "TABFontPoint: Missing or Invalid Geometry!");
1406 0 : return -1;
1407 : }
1408 :
1409 2 : GInt32 nX = 0;
1410 2 : GInt32 nY = 0;
1411 2 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1412 :
1413 : /*-----------------------------------------------------------------
1414 : * Copy object information
1415 : * NOTE: This symbol type does not contain a reference to a
1416 : * SymbolDef block in the file, but we still use the m_sSymbolDef
1417 : * structure to store the information inside the class so that the
1418 : * ITABFeatureSymbol methods work properly for the class user.
1419 : *----------------------------------------------------------------*/
1420 : TABMAPObjFontPoint *poPointHdr =
1421 2 : cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1422 :
1423 2 : poPointHdr->m_nX = nX;
1424 2 : poPointHdr->m_nY = nY;
1425 2 : poPointHdr->SetMBR(nX, nY, nX, nY);
1426 :
1427 2 : poPointHdr->m_nSymbolId =
1428 2 : static_cast<GByte>(m_sSymbolDef.nSymbolNo); // shape
1429 2 : poPointHdr->m_nPointSize =
1430 2 : static_cast<GByte>(m_sSymbolDef.nPointSize); // point size
1431 2 : poPointHdr->m_nFontStyle = m_nFontStyle; // font style
1432 :
1433 2 : poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
1434 2 : poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
1435 2 : poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
1436 :
1437 : /*-------------------------------------------------------------
1438 : * Symbol Angle, in tenths of degree.
1439 : * Contrary to arc start/end angles, no conversion based on
1440 : * origin quadrant is required here.
1441 : *------------------------------------------------------------*/
1442 2 : poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
1443 :
1444 : // Write Font Def
1445 2 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1446 2 : poPointHdr->m_nFontId =
1447 2 : static_cast<GByte>(m_nFontDefIndex); // Font name index
1448 :
1449 2 : if (CPLGetLastErrorType() == CE_Failure)
1450 0 : return -1;
1451 :
1452 2 : return 0;
1453 : }
1454 :
1455 : /**********************************************************************
1456 : * TABFontPoint::QueryFontStyle()
1457 : *
1458 : * Return TRUE if the specified font style attribute is turned ON,
1459 : * or FALSE otherwise. See enum TABFontStyle for the list of styles
1460 : * that can be queried on.
1461 : **********************************************************************/
1462 0 : GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
1463 : {
1464 0 : return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
1465 : }
1466 :
1467 0 : void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
1468 : {
1469 0 : if (bStyleOn)
1470 0 : m_nFontStyle |= static_cast<int>(eStyleToToggle);
1471 : else
1472 0 : m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
1473 0 : }
1474 :
1475 : /**********************************************************************
1476 : * TABFontPoint::GetFontStyleMIFValue()
1477 : *
1478 : * Return the Font Style value for this object using the style values
1479 : * that are used in a MIF FONT() clause. See MIF specs (appendix A).
1480 : *
1481 : * The reason why we have to differentiate between the TAB and the MIF font
1482 : * style values is that in TAB, TABFSBox is included in the style value
1483 : * as code 0x100, but in MIF it is not included, instead it is implied by
1484 : * the presence of the BG color in the FONT() clause (the BG color is
1485 : * present only when TABFSBox or TABFSHalo is set).
1486 : * This also has the effect of shifting all the other style values > 0x100
1487 : * by 1 byte.
1488 : *
1489 : * NOTE: Even if there is no BG color for font symbols, we inherit this
1490 : * problem because Font Point styles use the same codes as Text Font styles.
1491 : **********************************************************************/
1492 0 : int TABFontPoint::GetFontStyleMIFValue()
1493 : {
1494 : // The conversion is simply to remove bit 0x100 from the value and shift
1495 : // down all values past this bit.
1496 0 : return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
1497 : }
1498 :
1499 635 : void TABFontPoint::SetFontStyleMIFValue(int nStyle)
1500 : {
1501 635 : m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
1502 635 : }
1503 :
1504 : /**********************************************************************
1505 : * TABFontPoint::SetSymbolAngle()
1506 : *
1507 : * Set the symbol angle value in degrees, making sure the value is
1508 : * always in the range [0..360]
1509 : **********************************************************************/
1510 635 : void TABFontPoint::SetSymbolAngle(double dAngle)
1511 : {
1512 635 : dAngle = fmod(dAngle, 360.0);
1513 635 : if (dAngle < 0.0)
1514 0 : dAngle += 360.0;
1515 :
1516 635 : m_dAngle = dAngle;
1517 635 : }
1518 :
1519 : /**********************************************************************
1520 : * TABFontPoint::GetSymbolStyleString()
1521 : *
1522 : * Return a Symbol() string. All representations info for the Symbol are here.
1523 : **********************************************************************/
1524 7 : const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
1525 : {
1526 : /* Get the SymbolStyleString, and add the outline Color
1527 : (halo/border in MapInfo Symbol terminology) */
1528 7 : const char *outlineColor = nullptr;
1529 7 : if (m_nFontStyle & 16)
1530 0 : outlineColor = ",o:#000000";
1531 7 : else if (m_nFontStyle & 512)
1532 0 : outlineColor = ",o:#ffffff";
1533 : else
1534 7 : outlineColor = "";
1535 :
1536 7 : int nAngle = static_cast<int>(dfAngle);
1537 : const char *pszStyle;
1538 :
1539 7 : pszStyle = CPLSPrintf(
1540 : "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
1541 7 : nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
1542 7 : m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
1543 7 : return pszStyle;
1544 : }
1545 :
1546 : /**********************************************************************
1547 : * TABFontPoint::GetStyleString() const
1548 : *
1549 : * Return style string for this feature.
1550 : *
1551 : * Style String is built only once during the first call to GetStyleString().
1552 : **********************************************************************/
1553 9 : const char *TABFontPoint::GetStyleString() const
1554 : {
1555 9 : if (m_pszStyleString == nullptr)
1556 : {
1557 7 : m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
1558 : }
1559 :
1560 9 : return m_pszStyleString;
1561 : }
1562 :
1563 : /**********************************************************************
1564 : * TABFontPoint::SetSymbolFromStyle()
1565 : *
1566 : * Set all Symbol var from a OGRStyleSymbol.
1567 : **********************************************************************/
1568 2 : void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1569 : {
1570 2 : ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1571 :
1572 2 : GBool bIsNull = 0;
1573 :
1574 : // Try to set font glyph number
1575 2 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1576 2 : if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
1577 : {
1578 2 : const int nSymbolId = atoi(pszSymbolId + 9);
1579 2 : SetSymbolNo(static_cast<GInt16>(nSymbolId));
1580 : }
1581 :
1582 2 : const char *pszFontName = poSymbolStyle->FontName(bIsNull);
1583 2 : if ((!bIsNull) && pszFontName)
1584 : {
1585 2 : SetFontName(pszFontName);
1586 : }
1587 2 : }
1588 :
1589 : /*=====================================================================
1590 : * class TABCustomPoint
1591 : *====================================================================*/
1592 :
1593 : /**********************************************************************
1594 : * TABCustomPoint::TABCustomPoint()
1595 : *
1596 : * Constructor.
1597 : **********************************************************************/
1598 686 : TABCustomPoint::TABCustomPoint(OGRFeatureDefn *poDefnIn)
1599 686 : : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
1600 : {
1601 686 : }
1602 :
1603 : /**********************************************************************
1604 : * TABCustomPoint::~TABCustomPoint()
1605 : *
1606 : * Destructor.
1607 : **********************************************************************/
1608 1372 : TABCustomPoint::~TABCustomPoint()
1609 : {
1610 1372 : }
1611 :
1612 : /**********************************************************************
1613 : * TABCustomPoint::CloneTABFeature()
1614 : *
1615 : * Duplicate feature, including stuff specific to each TABFeature type.
1616 : *
1617 : * This method calls the generic TABFeature::CloneTABFeature() and
1618 : * then copies any members specific to its own type.
1619 : **********************************************************************/
1620 0 : TABFeature *TABCustomPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1621 : {
1622 : /*-----------------------------------------------------------------
1623 : * Alloc new feature and copy the base stuff
1624 : *----------------------------------------------------------------*/
1625 : TABCustomPoint *poNew =
1626 0 : new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
1627 :
1628 0 : CopyTABFeatureBase(poNew);
1629 :
1630 : /*-----------------------------------------------------------------
1631 : * And members specific to this class
1632 : *----------------------------------------------------------------*/
1633 : // ITABFeatureSymbol
1634 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1635 :
1636 : // ITABFeatureFont
1637 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
1638 :
1639 0 : poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
1640 :
1641 0 : return poNew;
1642 : }
1643 :
1644 : /**********************************************************************
1645 : * TABCustomPoint::ReadGeometryFromMAPFile()
1646 : *
1647 : * Fill the geometry and representation (color, etc...) part of the
1648 : * feature from the contents of the .MAP object pointed to by poMAPFile.
1649 : *
1650 : * It is assumed that poMAPFile currently points to the beginning of
1651 : * a map object.
1652 : *
1653 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1654 : * been called.
1655 : **********************************************************************/
1656 6 : int TABCustomPoint::ReadGeometryFromMAPFile(
1657 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1658 : GBool bCoordBlockDataOnly /*=FALSE*/,
1659 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1660 : {
1661 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1662 6 : if (bCoordBlockDataOnly)
1663 0 : return 0;
1664 :
1665 : /*-----------------------------------------------------------------
1666 : * Fetch and validate geometry type
1667 : *----------------------------------------------------------------*/
1668 6 : m_nMapInfoType = poObjHdr->m_nType;
1669 :
1670 6 : if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
1671 0 : m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
1672 : {
1673 0 : CPLError(
1674 : CE_Failure, CPLE_AssertionFailed,
1675 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1676 0 : m_nMapInfoType, m_nMapInfoType);
1677 0 : return -1;
1678 : }
1679 :
1680 : /*-----------------------------------------------------------------
1681 : * Read object information
1682 : *----------------------------------------------------------------*/
1683 : TABMAPObjCustomPoint *poPointHdr =
1684 6 : cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1685 :
1686 6 : m_nUnknown_ = poPointHdr->m_nUnknown_; // ???
1687 6 : m_nCustomStyle = poPointHdr->m_nCustomStyle; // 0x01=Show BG,
1688 : // 0x02=Apply Color
1689 :
1690 6 : m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index
1691 6 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1692 :
1693 6 : m_nFontDefIndex = poPointHdr->m_nFontId; // Font index
1694 6 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1695 :
1696 : /*-----------------------------------------------------------------
1697 : * Create and fill geometry object
1698 : *----------------------------------------------------------------*/
1699 6 : double dX = 0.0;
1700 6 : double dY = 0.0;
1701 6 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1702 6 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1703 :
1704 6 : SetGeometryDirectly(poGeometry);
1705 :
1706 6 : SetMBR(dX, dY, dX, dY);
1707 6 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1708 : poObjHdr->m_nMaxY);
1709 :
1710 6 : return 0;
1711 : }
1712 :
1713 : /**********************************************************************
1714 : * TABCustomPoint::WriteGeometryToMAPFile()
1715 : *
1716 : * Write the geometry and representation (color, etc...) part of the
1717 : * feature to the .MAP object pointed to by poMAPFile.
1718 : *
1719 : * It is assumed that poMAPFile currently points to a valid map object.
1720 : *
1721 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1722 : * been called.
1723 : **********************************************************************/
1724 2 : int TABCustomPoint::WriteGeometryToMAPFile(
1725 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1726 : GBool bCoordBlockDataOnly /*=FALSE*/,
1727 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1728 : {
1729 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1730 2 : if (bCoordBlockDataOnly)
1731 0 : return 0;
1732 :
1733 : /*-----------------------------------------------------------------
1734 : * We assume that ValidateMapInfoType() was called already and that
1735 : * the type in poObjHdr->m_nType is valid.
1736 : *----------------------------------------------------------------*/
1737 2 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1738 :
1739 : /*-----------------------------------------------------------------
1740 : * Fetch and validate geometry
1741 : *----------------------------------------------------------------*/
1742 2 : OGRGeometry *poGeom = GetGeometryRef();
1743 2 : OGRPoint *poPoint = nullptr;
1744 2 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1745 2 : poPoint = poGeom->toPoint();
1746 : else
1747 : {
1748 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1749 : "TABCustomPoint: Missing or Invalid Geometry!");
1750 0 : return -1;
1751 : }
1752 :
1753 2 : GInt32 nX = 0;
1754 2 : GInt32 nY = 0;
1755 2 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1756 :
1757 : /*-----------------------------------------------------------------
1758 : * Copy object information
1759 : *----------------------------------------------------------------*/
1760 : TABMAPObjCustomPoint *poPointHdr =
1761 2 : cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1762 :
1763 2 : poPointHdr->m_nX = nX;
1764 2 : poPointHdr->m_nY = nY;
1765 2 : poPointHdr->SetMBR(nX, nY, nX, nY);
1766 2 : poPointHdr->m_nUnknown_ = m_nUnknown_;
1767 2 : poPointHdr->m_nCustomStyle = m_nCustomStyle; // 0x01=Show BG,
1768 : // 0x02=Apply Color
1769 :
1770 2 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1771 2 : poPointHdr->m_nSymbolId =
1772 2 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
1773 :
1774 2 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1775 2 : poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex); // Font index
1776 :
1777 2 : if (CPLGetLastErrorType() == CE_Failure)
1778 0 : return -1;
1779 :
1780 2 : return 0;
1781 : }
1782 :
1783 : /**********************************************************************
1784 : * TABCustomPoint::GetSymbolStyleString()
1785 : *
1786 : * Return a Symbol() string. All representations info for the Symbol are here.
1787 : **********************************************************************/
1788 7 : const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
1789 : {
1790 : /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
1791 : * "apply color". */
1792 7 : const char *color = nullptr;
1793 7 : if (m_nCustomStyle & 0x02)
1794 7 : color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
1795 : else
1796 0 : color = "";
1797 :
1798 7 : int nAngle = static_cast<int>(dfAngle);
1799 : const char *pszStyle;
1800 7 : const char *pszExt = CPLGetExtension(GetSymbolNameRef());
1801 7 : char szLowerExt[8] = "";
1802 7 : const char *pszPtr = pszExt;
1803 : int i;
1804 :
1805 7 : for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
1806 : {
1807 0 : szLowerExt[i] =
1808 0 : static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
1809 : }
1810 7 : szLowerExt[i] = '\0';
1811 :
1812 7 : pszStyle = CPLSPrintf(
1813 : "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
1814 7 : nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
1815 : GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
1816 7 : return pszStyle;
1817 : }
1818 :
1819 : /**********************************************************************
1820 : * TABCustomPoint::SetSymbolFromStyle()
1821 : *
1822 : * Set all Symbol var from a OGRStyleSymbol.
1823 : **********************************************************************/
1824 2 : void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1825 : {
1826 2 : ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1827 :
1828 2 : GBool bIsNull = 0;
1829 :
1830 : // Try to set font glyph number
1831 2 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1832 2 : if ((!bIsNull) && pszSymbolId &&
1833 2 : STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
1834 : {
1835 2 : const int nSymbolStyle = atoi(pszSymbolId + 19);
1836 2 : SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
1837 :
1838 2 : const char *pszPtr = pszSymbolId + 19;
1839 4 : while (*pszPtr != '-')
1840 : {
1841 2 : pszPtr++;
1842 : }
1843 2 : pszPtr++;
1844 :
1845 2 : char szSymbolName[256] = "";
1846 : int i;
1847 2 : for (i = 0;
1848 8 : i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
1849 : i++, pszPtr++)
1850 : {
1851 6 : szSymbolName[i] = *pszPtr;
1852 : }
1853 2 : szSymbolName[i] = '\0';
1854 2 : SetSymbolName(szSymbolName);
1855 : }
1856 2 : }
1857 :
1858 : /**********************************************************************
1859 : * TABCustomPoint::GetStyleString() const
1860 : *
1861 : * Return style string for this feature.
1862 : *
1863 : * Style String is built only once during the first call to GetStyleString().
1864 : **********************************************************************/
1865 9 : const char *TABCustomPoint::GetStyleString() const
1866 : {
1867 9 : if (m_pszStyleString == nullptr)
1868 : {
1869 7 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1870 : }
1871 :
1872 9 : return m_pszStyleString;
1873 : }
1874 :
1875 : /*=====================================================================
1876 : * class TABPolyline
1877 : *====================================================================*/
1878 :
1879 : /**********************************************************************
1880 : * TABPolyline::TABPolyline()
1881 : *
1882 : * Constructor.
1883 : **********************************************************************/
1884 4253 : TABPolyline::TABPolyline(OGRFeatureDefn *poDefnIn)
1885 : : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
1886 4253 : m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
1887 : {
1888 4253 : }
1889 :
1890 : /**********************************************************************
1891 : * TABPolyline::~TABPolyline()
1892 : *
1893 : * Destructor.
1894 : **********************************************************************/
1895 8506 : TABPolyline::~TABPolyline()
1896 : {
1897 8506 : }
1898 :
1899 : /**********************************************************************
1900 : * TABPolyline::CloneTABFeature()
1901 : *
1902 : * Duplicate feature, including stuff specific to each TABFeature type.
1903 : *
1904 : * This method calls the generic TABFeature::CloneTABFeature() and
1905 : * then copies any members specific to its own type.
1906 : **********************************************************************/
1907 0 : TABFeature *TABPolyline::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1908 : {
1909 : /*-----------------------------------------------------------------
1910 : * Alloc new feature and copy the base stuff
1911 : *----------------------------------------------------------------*/
1912 0 : TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
1913 :
1914 0 : CopyTABFeatureBase(poNew);
1915 :
1916 : /*-----------------------------------------------------------------
1917 : * And members specific to this class
1918 : *----------------------------------------------------------------*/
1919 : // ITABFeaturePen
1920 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
1921 :
1922 0 : poNew->m_bSmooth = m_bSmooth;
1923 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
1924 0 : poNew->m_dCenterX = m_dCenterX;
1925 0 : poNew->m_dCenterY = m_dCenterY;
1926 :
1927 0 : return poNew;
1928 : }
1929 :
1930 : /**********************************************************************
1931 : * TABPolyline::GetNumParts()
1932 : *
1933 : * Return the total number of parts in this object.
1934 : *
1935 : * Returns 0 if the geometry contained in the object is invalid or missing.
1936 : **********************************************************************/
1937 0 : int TABPolyline::GetNumParts()
1938 : {
1939 0 : int numParts = 0;
1940 :
1941 0 : OGRGeometry *poGeom = GetGeometryRef();
1942 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1943 : {
1944 : /*-------------------------------------------------------------
1945 : * Simple polyline
1946 : *------------------------------------------------------------*/
1947 0 : numParts = 1;
1948 : }
1949 0 : else if (poGeom &&
1950 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1951 : {
1952 : /*-------------------------------------------------------------
1953 : * Multiple polyline
1954 : *------------------------------------------------------------*/
1955 0 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1956 0 : numParts = poMultiLine->getNumGeometries();
1957 : }
1958 :
1959 0 : return numParts;
1960 : }
1961 :
1962 : /**********************************************************************
1963 : * TABPolyline::GetPartRef()
1964 : *
1965 : * Returns a reference to the specified OGRLineString number, hiding the
1966 : * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
1967 : *
1968 : * Returns NULL if the geometry contained in the object is invalid or
1969 : * missing or if the specified part index is invalid.
1970 : **********************************************************************/
1971 0 : OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
1972 : {
1973 0 : OGRGeometry *poGeom = GetGeometryRef();
1974 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1975 : nPartIndex == 0)
1976 : {
1977 : /*-------------------------------------------------------------
1978 : * Simple polyline
1979 : *------------------------------------------------------------*/
1980 0 : return poGeom->toLineString();
1981 : }
1982 0 : else if (poGeom &&
1983 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1984 : {
1985 : /*-------------------------------------------------------------
1986 : * Multiple polyline
1987 : *------------------------------------------------------------*/
1988 0 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1989 0 : if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
1990 : {
1991 0 : return poMultiLine->getGeometryRef(nPartIndex);
1992 : }
1993 : else
1994 0 : return nullptr;
1995 : }
1996 :
1997 0 : return nullptr;
1998 : }
1999 :
2000 : /**********************************************************************
2001 : * TABPolyline::ValidateMapInfoType()
2002 : *
2003 : * Check the feature's geometry part and return the corresponding
2004 : * mapinfo object type code. The m_nMapInfoType member will also
2005 : * be updated for further calls to GetMapInfoType();
2006 : *
2007 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
2008 : * is expected for this object class.
2009 : **********************************************************************/
2010 238 : TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
2011 : {
2012 : /*-----------------------------------------------------------------
2013 : * Fetch and validate geometry
2014 : *----------------------------------------------------------------*/
2015 238 : OGRGeometry *poGeom = GetGeometryRef();
2016 238 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2017 : {
2018 : /*-------------------------------------------------------------
2019 : * Simple polyline
2020 : *------------------------------------------------------------*/
2021 230 : OGRLineString *poLine = poGeom->toLineString();
2022 230 : if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
2023 : {
2024 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2025 : }
2026 230 : else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
2027 : {
2028 0 : m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2029 : }
2030 230 : else if (poLine->getNumPoints() > 2)
2031 : {
2032 207 : m_nMapInfoType = TAB_GEOM_PLINE;
2033 : }
2034 46 : else if ((poLine->getNumPoints() == 2) &&
2035 23 : (m_bWriteTwoPointLineAsPolyline == TRUE))
2036 : {
2037 0 : m_nMapInfoType = TAB_GEOM_PLINE;
2038 : }
2039 46 : else if ((poLine->getNumPoints() == 2) &&
2040 23 : (m_bWriteTwoPointLineAsPolyline == FALSE))
2041 : {
2042 23 : m_nMapInfoType = TAB_GEOM_LINE;
2043 : }
2044 : else
2045 : {
2046 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2047 : "TABPolyline: Geometry must contain at least 2 points.");
2048 0 : m_nMapInfoType = TAB_GEOM_NONE;
2049 : }
2050 : }
2051 16 : else if (poGeom &&
2052 8 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2053 : {
2054 : /*-------------------------------------------------------------
2055 : * Multiple polyline... validate all components
2056 : *------------------------------------------------------------*/
2057 8 : GInt32 numPointsTotal = 0;
2058 8 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2059 8 : int numLines = poMultiLine->getNumGeometries();
2060 :
2061 8 : m_nMapInfoType = TAB_GEOM_MULTIPLINE;
2062 :
2063 18 : for (int iLine = 0; iLine < numLines; iLine++)
2064 : {
2065 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2066 20 : if (poGeom == nullptr ||
2067 10 : wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2068 : {
2069 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2070 : "TABPolyline: Object contains an invalid Geometry!");
2071 0 : m_nMapInfoType = TAB_GEOM_NONE;
2072 0 : numPointsTotal = 0;
2073 0 : break;
2074 : }
2075 10 : OGRLineString *poLine = poGeom->toLineString();
2076 10 : numPointsTotal += poLine->getNumPoints();
2077 : }
2078 :
2079 8 : if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
2080 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2081 8 : else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
2082 0 : m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2083 : }
2084 : else
2085 : {
2086 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2087 : "TABPolyline: Missing or Invalid Geometry!");
2088 0 : m_nMapInfoType = TAB_GEOM_NONE;
2089 : }
2090 :
2091 : /*-----------------------------------------------------------------
2092 : * Decide if coordinates should be compressed or not.
2093 : *
2094 : * __TODO__ We never write type LINE (2 points line) as compressed
2095 : * for the moment. If we ever do it, then the decision to write
2096 : * a 2 point line in compressed coordinates or not should take into
2097 : * account the location of the object block MBR, so this would be
2098 : * better handled directly by TABMAPObjLine::WriteObject() since the
2099 : * object block center is not known until it is written to disk.
2100 : *----------------------------------------------------------------*/
2101 238 : if (m_nMapInfoType != TAB_GEOM_LINE)
2102 : {
2103 215 : ValidateCoordType(poMapFile);
2104 : }
2105 : else
2106 : {
2107 23 : UpdateMBR(poMapFile);
2108 : }
2109 :
2110 238 : return m_nMapInfoType;
2111 : }
2112 :
2113 : /**********************************************************************
2114 : * TABPolyline::ReadGeometryFromMAPFile()
2115 : *
2116 : * Fill the geometry and representation (color, etc...) part of the
2117 : * feature from the contents of the .MAP object pointed to by poMAPFile.
2118 : *
2119 : * It is assumed that poMAPFile currently points to the beginning of
2120 : * a map object.
2121 : *
2122 : * Returns 0 on success, -1 on error, in which case CPLError() will have
2123 : * been called.
2124 : **********************************************************************/
2125 1582 : int TABPolyline::ReadGeometryFromMAPFile(
2126 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2127 : GBool bCoordBlockDataOnly /*=FALSE*/,
2128 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2129 : {
2130 1582 : GInt32 nX = 0;
2131 1582 : GInt32 nY = 0;
2132 1582 : double dX = 0.0;
2133 1582 : double dY = 0.0;
2134 1582 : double dXMin = 0.0;
2135 1582 : double dYMin = 0.0;
2136 1582 : double dXMax = 0.0;
2137 1582 : double dYMax = 0.0;
2138 1582 : OGRGeometry *poGeometry = nullptr;
2139 1582 : GBool bComprCoord = poObjHdr->IsCompressedType();
2140 1582 : TABMAPCoordBlock *poCoordBlock = nullptr;
2141 :
2142 : /*-----------------------------------------------------------------
2143 : * Fetch and validate geometry type
2144 : *----------------------------------------------------------------*/
2145 1582 : m_nMapInfoType = poObjHdr->m_nType;
2146 :
2147 1582 : if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
2148 : {
2149 : /*=============================================================
2150 : * LINE (2 vertices)
2151 : *============================================================*/
2152 21 : TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2153 :
2154 21 : m_bSmooth = FALSE;
2155 :
2156 21 : auto poLine = new OGRLineString();
2157 21 : poGeometry = poLine;
2158 21 : poLine->setNumPoints(2);
2159 :
2160 21 : poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
2161 : dYMin);
2162 21 : poLine->setPoint(0, dXMin, dYMin);
2163 :
2164 21 : poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
2165 : dYMax);
2166 21 : poLine->setPoint(1, dXMax, dYMax);
2167 :
2168 21 : if (!bCoordBlockDataOnly)
2169 : {
2170 21 : m_nPenDefIndex = poLineHdr->m_nPenId; // Pen index
2171 21 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2172 21 : }
2173 : }
2174 1561 : else if (m_nMapInfoType == TAB_GEOM_PLINE ||
2175 1556 : m_nMapInfoType == TAB_GEOM_PLINE_C)
2176 : {
2177 : /*=============================================================
2178 : * PLINE ( > 2 vertices)
2179 : *============================================================*/
2180 :
2181 : /*-------------------------------------------------------------
2182 : * Copy data from poObjHdr
2183 : *------------------------------------------------------------*/
2184 1551 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2185 :
2186 1551 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2187 1551 : const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
2188 1551 : if (nCoordDataSize > 1024 * 1024 &&
2189 0 : nCoordDataSize > poMapFile->GetFileSize())
2190 : {
2191 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
2192 : nCoordDataSize);
2193 0 : return -1;
2194 : }
2195 : // numLineSections = poPLineHdr->m_numLineSections; // Always 1
2196 1551 : m_bSmooth = poPLineHdr->m_bSmooth;
2197 :
2198 : // Centroid/label point
2199 1551 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2200 : dX, dY);
2201 1551 : SetCenter(dX, dY);
2202 :
2203 : // Compressed coordinate origin (useful only in compressed case!)
2204 1551 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2205 1551 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2206 :
2207 : // MBR
2208 1551 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2209 : dYMin);
2210 1551 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2211 : dYMax);
2212 :
2213 1551 : if (!bCoordBlockDataOnly)
2214 : {
2215 1383 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
2216 1383 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2217 : }
2218 :
2219 : /*-------------------------------------------------------------
2220 : * Create Geometry and read coordinates
2221 : *------------------------------------------------------------*/
2222 1551 : const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
2223 :
2224 1551 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2225 168 : poCoordBlock = *ppoCoordBlock;
2226 : else
2227 1383 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2228 1551 : if (poCoordBlock == nullptr)
2229 : {
2230 0 : CPLError(CE_Failure, CPLE_FileIO,
2231 : "Can't access coordinate block at offset %d",
2232 : nCoordBlockPtr);
2233 0 : return -1;
2234 : }
2235 :
2236 1551 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2237 :
2238 1551 : auto poLine = new OGRLineString();
2239 1551 : poGeometry = poLine;
2240 1551 : poLine->setNumPoints(numPoints);
2241 :
2242 1551 : int nStatus = 0;
2243 11906 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2244 : {
2245 10355 : nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
2246 10355 : if (nStatus != 0)
2247 0 : break;
2248 10355 : poMapFile->Int2Coordsys(nX, nY, dX, dY);
2249 10355 : poLine->setPoint(i, dX, dY);
2250 : }
2251 :
2252 1551 : if (nStatus != 0)
2253 : {
2254 : // Failed ... error message has already been produced
2255 0 : delete poGeometry;
2256 0 : return nStatus;
2257 1551 : }
2258 : }
2259 10 : else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2260 10 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2261 4 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2262 4 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2263 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2264 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
2265 : {
2266 : /*=============================================================
2267 : * PLINE MULTIPLE
2268 : *============================================================*/
2269 10 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2270 :
2271 : /*-------------------------------------------------------------
2272 : * Copy data from poObjHdr
2273 : *------------------------------------------------------------*/
2274 10 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2275 :
2276 10 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2277 : /* GInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize; */
2278 10 : GInt32 numLineSections = poPLineHdr->m_numLineSections;
2279 10 : m_bSmooth = poPLineHdr->m_bSmooth;
2280 :
2281 : // Centroid/label point
2282 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2283 : dX, dY);
2284 10 : SetCenter(dX, dY);
2285 :
2286 : // Compressed coordinate origin (useful only in compressed case!)
2287 10 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2288 10 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2289 :
2290 : // MBR
2291 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2292 : dYMin);
2293 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2294 : dYMax);
2295 :
2296 10 : if (!bCoordBlockDataOnly)
2297 : {
2298 10 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
2299 10 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2300 : }
2301 :
2302 10 : const int nMinSizeOfSection = 24;
2303 10 : if (numLineSections > INT_MAX / nMinSizeOfSection)
2304 : {
2305 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2306 0 : return -1;
2307 : }
2308 10 : const GUInt32 nMinimumBytesForSections =
2309 10 : nMinSizeOfSection * numLineSections;
2310 10 : if (nMinimumBytesForSections > 1024 * 1024 &&
2311 0 : nMinimumBytesForSections > poMapFile->GetFileSize())
2312 : {
2313 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2314 0 : return -1;
2315 : }
2316 :
2317 : /*-------------------------------------------------------------
2318 : * Read data from the coord. block
2319 : *------------------------------------------------------------*/
2320 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2321 10 : VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
2322 10 : if (pasSecHdrs == nullptr)
2323 0 : return -1;
2324 :
2325 10 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2326 4 : poCoordBlock = *ppoCoordBlock;
2327 : else
2328 6 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2329 :
2330 10 : GInt32 numPointsTotal = 0;
2331 20 : if (poCoordBlock == nullptr ||
2332 10 : poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
2333 : numLineSections, pasSecHdrs,
2334 : numPointsTotal) != 0)
2335 : {
2336 0 : CPLError(CE_Failure, CPLE_FileIO,
2337 : "Failed reading coordinate data at offset %d",
2338 : nCoordBlockPtr);
2339 0 : CPLFree(pasSecHdrs);
2340 0 : return -1;
2341 : }
2342 :
2343 10 : const GUInt32 nMinimumBytesForPoints =
2344 10 : (bComprCoord ? 4 : 8) * numPointsTotal;
2345 10 : if (nMinimumBytesForPoints > 1024 * 1024 &&
2346 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
2347 : {
2348 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
2349 0 : CPLFree(pasSecHdrs);
2350 0 : return -1;
2351 : }
2352 :
2353 10 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2354 :
2355 : GInt32 *panXY = static_cast<GInt32 *>(
2356 10 : VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
2357 10 : if (panXY == nullptr)
2358 : {
2359 0 : CPLFree(pasSecHdrs);
2360 0 : return -1;
2361 : }
2362 :
2363 10 : if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
2364 : 0)
2365 : {
2366 0 : CPLError(CE_Failure, CPLE_FileIO,
2367 : "Failed reading coordinate data at offset %d",
2368 : nCoordBlockPtr);
2369 0 : CPLFree(pasSecHdrs);
2370 0 : CPLFree(panXY);
2371 0 : return -1;
2372 : }
2373 :
2374 : /*-------------------------------------------------------------
2375 : * Create a Geometry collection with one line geometry for
2376 : * each coordinates section
2377 : * If object contains only one section, then return a simple LineString
2378 : *------------------------------------------------------------*/
2379 10 : OGRMultiLineString *poMultiLine = nullptr;
2380 10 : if (numLineSections > 1)
2381 : {
2382 6 : poMultiLine = new OGRMultiLineString();
2383 6 : poGeometry = poMultiLine;
2384 : }
2385 :
2386 26 : for (int iSection = 0; iSection < numLineSections; iSection++)
2387 : {
2388 16 : const int numSectionVertices = pasSecHdrs[iSection].numVertices;
2389 16 : GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
2390 :
2391 16 : auto poLine = new OGRLineString();
2392 16 : poLine->setNumPoints(numSectionVertices);
2393 :
2394 48 : for (int i = 0; i < numSectionVertices; i++)
2395 : {
2396 32 : poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
2397 32 : poLine->setPoint(i, dX, dY);
2398 32 : pnXYPtr += 2;
2399 : }
2400 :
2401 16 : if (poGeometry == nullptr)
2402 4 : poGeometry = poLine;
2403 12 : else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
2404 : {
2405 0 : CPLAssert(false); // Just in case lower-level lib is modified
2406 : }
2407 : }
2408 :
2409 10 : CPLFree(pasSecHdrs);
2410 10 : CPLFree(panXY);
2411 : }
2412 : else
2413 : {
2414 0 : CPLError(
2415 : CE_Failure, CPLE_AssertionFailed,
2416 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
2417 0 : m_nMapInfoType, m_nMapInfoType);
2418 0 : return -1;
2419 : }
2420 :
2421 1582 : SetGeometryDirectly(poGeometry);
2422 :
2423 1582 : SetMBR(dXMin, dYMin, dXMax, dYMax);
2424 1582 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
2425 : poObjHdr->m_nMaxY);
2426 :
2427 : /* Return a ref to coord block so that caller can continue reading
2428 : * after the end of this object (used by TABCollection and index splitting)
2429 : */
2430 1582 : if (ppoCoordBlock)
2431 172 : *ppoCoordBlock = poCoordBlock;
2432 :
2433 1582 : return 0;
2434 : }
2435 :
2436 : /**********************************************************************
2437 : * TABPolyline::WriteGeometryToMAPFile()
2438 : *
2439 : * Write the geometry and representation (color, etc...) part of the
2440 : * feature to the .MAP object pointed to by poMAPFile.
2441 : *
2442 : * It is assumed that poMAPFile currently points to a valid map object.
2443 : *
2444 : * Returns 0 on success, -1 on error, in which case CPLError() will have
2445 : * been called.
2446 : **********************************************************************/
2447 406 : int TABPolyline::WriteGeometryToMAPFile(
2448 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2449 : GBool bCoordBlockDataOnly /*=FALSE*/,
2450 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2451 : {
2452 406 : GInt32 nX = 0;
2453 406 : GInt32 nY = 0;
2454 406 : OGRLineString *poLine = nullptr;
2455 406 : TABMAPCoordBlock *poCoordBlock = nullptr;
2456 :
2457 : /*-----------------------------------------------------------------
2458 : * We assume that ValidateMapInfoType() was called already and that
2459 : * the type in poObjHdr->m_nType is valid.
2460 : *----------------------------------------------------------------*/
2461 406 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
2462 406 : CPLErrorReset();
2463 :
2464 : /*-----------------------------------------------------------------
2465 : * Fetch and validate geometry
2466 : *----------------------------------------------------------------*/
2467 406 : OGRGeometry *poGeom = GetGeometryRef();
2468 :
2469 1195 : if ((m_nMapInfoType == TAB_GEOM_LINE ||
2470 406 : m_nMapInfoType == TAB_GEOM_LINE_C) &&
2471 835 : poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
2472 23 : (poLine = poGeom->toLineString())->getNumPoints() == 2)
2473 : {
2474 : /*=============================================================
2475 : * LINE (2 vertices)
2476 : *============================================================*/
2477 23 : TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2478 :
2479 23 : poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
2480 23 : poLineHdr->m_nX1, poLineHdr->m_nY1);
2481 23 : poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
2482 23 : poLineHdr->m_nX2, poLineHdr->m_nY2);
2483 23 : poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
2484 : poLineHdr->m_nY2);
2485 :
2486 23 : if (!bCoordBlockDataOnly)
2487 : {
2488 23 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2489 23 : poLineHdr->m_nPenId =
2490 23 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2491 : }
2492 : }
2493 1146 : else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
2494 383 : m_nMapInfoType == TAB_GEOM_PLINE_C) &&
2495 766 : poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2496 : {
2497 : /*=============================================================
2498 : * PLINE ( > 2 vertices and less than 32767 vertices)
2499 : *============================================================*/
2500 375 : GBool bCompressed = poObjHdr->IsCompressedType();
2501 :
2502 : /*-------------------------------------------------------------
2503 : * Process geometry first...
2504 : *------------------------------------------------------------*/
2505 375 : poLine = poGeom->toLineString();
2506 375 : const int numPoints = poLine->getNumPoints();
2507 375 : CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
2508 :
2509 375 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2510 168 : poCoordBlock = *ppoCoordBlock;
2511 : else
2512 207 : poCoordBlock = poMapFile->GetCurCoordBlock();
2513 375 : poCoordBlock->StartNewFeature();
2514 375 : const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2515 375 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2516 :
2517 375 : int nStatus = 0;
2518 5207 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2519 : {
2520 4832 : poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
2521 4832 : if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
2522 : 0)
2523 : {
2524 : // Failed ... error message has already been produced
2525 0 : return nStatus;
2526 : }
2527 : }
2528 :
2529 375 : const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2530 :
2531 : /*-------------------------------------------------------------
2532 : * Copy info to poObjHdr
2533 : *------------------------------------------------------------*/
2534 375 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2535 :
2536 375 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2537 375 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2538 375 : poPLineHdr->m_numLineSections = 1;
2539 :
2540 375 : poPLineHdr->m_bSmooth = m_bSmooth;
2541 :
2542 : // MBR
2543 375 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2544 :
2545 : // Polyline center/label point
2546 375 : double dX = 0.0;
2547 375 : double dY = 0.0;
2548 375 : if (GetCenter(dX, dY) != -1)
2549 : {
2550 375 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2551 375 : poPLineHdr->m_nLabelY);
2552 : }
2553 : else
2554 : {
2555 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
2556 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
2557 : }
2558 :
2559 : // Compressed coordinate origin (useful only in compressed case!)
2560 375 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2561 375 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2562 :
2563 375 : if (!bCoordBlockDataOnly)
2564 : {
2565 207 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2566 207 : poPLineHdr->m_nPenId =
2567 207 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2568 : }
2569 : }
2570 24 : else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2571 8 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2572 0 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2573 0 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2574 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2575 8 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
2576 16 : poGeom &&
2577 8 : (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
2578 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
2579 : {
2580 : /*=============================================================
2581 : * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
2582 : *============================================================*/
2583 :
2584 8 : CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2585 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2586 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2587 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2588 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2589 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
2590 :
2591 8 : int nStatus = 0;
2592 8 : OGREnvelope sEnvelope;
2593 8 : GBool bCompressed = poObjHdr->IsCompressedType();
2594 :
2595 : /*-------------------------------------------------------------
2596 : * Process geometry first...
2597 : *------------------------------------------------------------*/
2598 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2599 0 : poCoordBlock = *ppoCoordBlock;
2600 : else
2601 8 : poCoordBlock = poMapFile->GetCurCoordBlock();
2602 8 : poCoordBlock->StartNewFeature();
2603 8 : const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2604 8 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2605 :
2606 8 : OGRMultiLineString *poMultiLine = nullptr;
2607 8 : GInt32 numLines = 1;
2608 8 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2609 : {
2610 8 : poMultiLine = poGeom->toMultiLineString();
2611 8 : numLines = poMultiLine->getNumGeometries();
2612 : }
2613 : // else
2614 : // {
2615 : // poMultiLine = NULL;
2616 : // numLines = 1;
2617 : // }
2618 :
2619 : /*-------------------------------------------------------------
2620 : * Build and write array of coord sections headers
2621 : *------------------------------------------------------------*/
2622 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2623 8 : VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
2624 8 : if (pasSecHdrs == nullptr)
2625 : {
2626 0 : return -1;
2627 : }
2628 :
2629 : /*-------------------------------------------------------------
2630 : * In calculation of nDataOffset, we have to take into account that
2631 : * V450 header section uses int32 instead of int16 for numVertices
2632 : * and we add another 2 bytes to align with a 4 bytes boundary.
2633 : *------------------------------------------------------------*/
2634 8 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2635 :
2636 8 : const int nTotalHdrSizeUncompressed =
2637 8 : (nVersion >= 450 ? 28 : 24) * numLines;
2638 :
2639 8 : GInt32 numPointsTotal = 0;
2640 18 : for (int iLine = 0; iLine < numLines; iLine++)
2641 : {
2642 10 : if (poMultiLine)
2643 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2644 :
2645 20 : if (poGeom &&
2646 10 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2647 : {
2648 10 : poLine = poGeom->toLineString();
2649 10 : const GInt32 numPoints = poLine->getNumPoints();
2650 10 : poLine->getEnvelope(&sEnvelope);
2651 :
2652 10 : pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
2653 10 : pasSecHdrs[iLine].numHoles = 0; // It is a line!
2654 :
2655 10 : poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
2656 10 : pasSecHdrs[iLine].nXMin,
2657 10 : pasSecHdrs[iLine].nYMin);
2658 10 : poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
2659 10 : pasSecHdrs[iLine].nXMax,
2660 10 : pasSecHdrs[iLine].nYMax);
2661 10 : pasSecHdrs[iLine].nDataOffset =
2662 10 : nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
2663 10 : pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
2664 :
2665 10 : numPointsTotal += numPoints;
2666 : }
2667 : else
2668 : {
2669 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2670 : "TABPolyline: Object contains an invalid Geometry!");
2671 0 : nStatus = -1;
2672 : }
2673 : }
2674 :
2675 8 : if (nStatus == 0)
2676 8 : nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
2677 : pasSecHdrs, bCompressed);
2678 :
2679 8 : CPLFree(pasSecHdrs);
2680 8 : pasSecHdrs = nullptr;
2681 :
2682 8 : if (nStatus != 0)
2683 0 : return nStatus; // Error has already been reported.
2684 :
2685 : /*-------------------------------------------------------------
2686 : * Then write the coordinates themselves...
2687 : *------------------------------------------------------------*/
2688 18 : for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
2689 : {
2690 10 : if (poMultiLine)
2691 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2692 :
2693 20 : if (poGeom &&
2694 10 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2695 : {
2696 10 : poLine = poGeom->toLineString();
2697 10 : GInt32 numPoints = poLine->getNumPoints();
2698 :
2699 30 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2700 : {
2701 20 : poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
2702 : nX, nY);
2703 20 : if ((nStatus = poCoordBlock->WriteIntCoord(
2704 20 : nX, nY, bCompressed)) != 0)
2705 : {
2706 : // Failed ... error message has already been produced
2707 0 : return nStatus;
2708 : }
2709 : }
2710 : }
2711 : else
2712 : {
2713 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2714 : "TABPolyline: Object contains an invalid Geometry!");
2715 0 : return -1;
2716 : }
2717 : }
2718 :
2719 8 : const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2720 :
2721 : /*-------------------------------------------------------------
2722 : * ... and finally copy info to poObjHdr
2723 : *------------------------------------------------------------*/
2724 8 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2725 :
2726 8 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2727 8 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2728 8 : poPLineHdr->m_numLineSections = numLines;
2729 :
2730 8 : poPLineHdr->m_bSmooth = m_bSmooth;
2731 :
2732 : // MBR
2733 8 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2734 :
2735 : // Polyline center/label point
2736 8 : double dX = 0.0;
2737 8 : double dY = 0.0;
2738 8 : if (GetCenter(dX, dY) != -1)
2739 : {
2740 8 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2741 8 : poPLineHdr->m_nLabelY);
2742 : }
2743 : else
2744 : {
2745 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
2746 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
2747 : }
2748 :
2749 : // Compressed coordinate origin (useful only in compressed case!)
2750 8 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2751 8 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2752 :
2753 8 : if (!bCoordBlockDataOnly)
2754 : {
2755 8 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2756 8 : poPLineHdr->m_nPenId =
2757 8 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2758 : }
2759 : }
2760 : else
2761 : {
2762 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2763 : "TABPolyline: Object contains an invalid Geometry!");
2764 0 : return -1;
2765 : }
2766 :
2767 406 : if (CPLGetLastErrorType() == CE_Failure)
2768 0 : return -1;
2769 :
2770 : /* Return a ref to coord block so that caller can continue writing
2771 : * after the end of this object (used by index splitting)
2772 : */
2773 406 : if (ppoCoordBlock)
2774 168 : *ppoCoordBlock = poCoordBlock;
2775 :
2776 406 : return 0;
2777 : }
2778 :
2779 : /**********************************************************************
2780 : * TABPolyline::GetStyleString() const
2781 : *
2782 : * Return style string for this feature.
2783 : *
2784 : * Style String is built only once during the first call to GetStyleString().
2785 : **********************************************************************/
2786 44 : const char *TABPolyline::GetStyleString() const
2787 : {
2788 44 : if (m_pszStyleString == nullptr)
2789 : {
2790 35 : m_pszStyleString = CPLStrdup(GetPenStyleString());
2791 : }
2792 :
2793 44 : return m_pszStyleString;
2794 : }
2795 :
2796 : /**********************************************************************
2797 : * TABPolyline::DumpMIF()
2798 : *
2799 : * Dump feature geometry in a format similar to .MIF PLINEs.
2800 : **********************************************************************/
2801 0 : void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
2802 : {
2803 0 : OGRMultiLineString *poMultiLine = nullptr;
2804 0 : OGRLineString *poLine = nullptr;
2805 : int i, numPoints;
2806 :
2807 0 : if (fpOut == nullptr)
2808 0 : fpOut = stdout;
2809 :
2810 : /*-----------------------------------------------------------------
2811 : * Fetch and validate geometry
2812 : *----------------------------------------------------------------*/
2813 0 : OGRGeometry *poGeom = GetGeometryRef();
2814 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2815 : {
2816 : /*-------------------------------------------------------------
2817 : * Generate output for simple polyline
2818 : *------------------------------------------------------------*/
2819 0 : poLine = poGeom->toLineString();
2820 0 : numPoints = poLine->getNumPoints();
2821 0 : fprintf(fpOut, "PLINE %d\n", numPoints);
2822 0 : for (i = 0; i < numPoints; i++)
2823 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
2824 : }
2825 0 : else if (poGeom &&
2826 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2827 : {
2828 : /*-------------------------------------------------------------
2829 : * Generate output for multiple polyline
2830 : *------------------------------------------------------------*/
2831 : int iLine, numLines;
2832 0 : poMultiLine = poGeom->toMultiLineString();
2833 0 : numLines = poMultiLine->getNumGeometries();
2834 0 : fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
2835 0 : for (iLine = 0; iLine < numLines; iLine++)
2836 : {
2837 0 : poGeom = poMultiLine->getGeometryRef(iLine);
2838 0 : if (poGeom &&
2839 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2840 : {
2841 0 : poLine = poGeom->toLineString();
2842 0 : numPoints = poLine->getNumPoints();
2843 0 : fprintf(fpOut, " %d\n", numPoints);
2844 0 : for (i = 0; i < numPoints; i++)
2845 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
2846 : poLine->getY(i));
2847 : }
2848 : else
2849 : {
2850 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2851 : "TABPolyline: Object contains an invalid Geometry!");
2852 0 : return;
2853 : }
2854 : }
2855 : }
2856 : else
2857 : {
2858 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2859 : "TABPolyline: Missing or Invalid Geometry!");
2860 0 : return;
2861 : }
2862 :
2863 0 : if (m_bCenterIsSet)
2864 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
2865 :
2866 : // Finish with PEN/BRUSH/etc. clauses
2867 0 : DumpPenDef();
2868 :
2869 0 : fflush(fpOut);
2870 : }
2871 :
2872 : /**********************************************************************
2873 : * TABPolyline::GetCenter()
2874 : *
2875 : * Returns the center point of the line. Compute one if it was not
2876 : * explicitly set:
2877 : *
2878 : * In MapInfo, for a simple or multiple polyline (pline), the center point
2879 : * in the object definition is supposed to be either the center point of
2880 : * the pline or the first section of a multiple pline (if an odd number of
2881 : * points in the pline or first section), or the midway point between the
2882 : * two central points (if an even number of points involved).
2883 : *
2884 : * Returns 0 on success, -1 on error.
2885 : **********************************************************************/
2886 383 : int TABPolyline::GetCenter(double &dX, double &dY)
2887 : {
2888 383 : if (!m_bCenterIsSet)
2889 : {
2890 215 : OGRLineString *poLine = nullptr;
2891 :
2892 215 : OGRGeometry *poGeom = GetGeometryRef();
2893 215 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2894 : {
2895 207 : poLine = poGeom->toLineString();
2896 : }
2897 16 : else if (poGeom &&
2898 8 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2899 : {
2900 8 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2901 8 : if (poMultiLine->getNumGeometries() > 0)
2902 8 : poLine = poMultiLine->getGeometryRef(0);
2903 : }
2904 :
2905 215 : if (poLine && poLine->getNumPoints() > 0)
2906 : {
2907 215 : int i = poLine->getNumPoints() / 2;
2908 215 : if (poLine->getNumPoints() % 2 == 0)
2909 : {
2910 : // Return the midway between the 2 center points
2911 15 : m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
2912 15 : m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
2913 : }
2914 : else
2915 : {
2916 : // Return the center point
2917 200 : m_dCenterX = poLine->getX(i);
2918 200 : m_dCenterY = poLine->getY(i);
2919 : }
2920 215 : m_bCenterIsSet = TRUE;
2921 : }
2922 : }
2923 :
2924 383 : if (!m_bCenterIsSet)
2925 0 : return -1;
2926 :
2927 383 : dX = m_dCenterX;
2928 383 : dY = m_dCenterY;
2929 383 : return 0;
2930 : }
2931 :
2932 : /**********************************************************************
2933 : * TABPolyline::SetCenter()
2934 : *
2935 : * Set the X,Y coordinates to use as center point for the line.
2936 : **********************************************************************/
2937 1561 : void TABPolyline::SetCenter(double dX, double dY)
2938 : {
2939 1561 : m_dCenterX = dX;
2940 1561 : m_dCenterY = dY;
2941 1561 : m_bCenterIsSet = TRUE;
2942 1561 : }
2943 :
2944 : /**********************************************************************
2945 : * TABPolyline::TwoPointLineAsPolyline()
2946 : *
2947 : * Returns the value of m_bWriteTwoPointLineAsPolyline
2948 : **********************************************************************/
2949 0 : GBool TABPolyline::TwoPointLineAsPolyline()
2950 : {
2951 0 : return m_bWriteTwoPointLineAsPolyline;
2952 : }
2953 :
2954 : /**********************************************************************
2955 : * TABPolyline::TwoPointLineAsPolyline()
2956 : *
2957 : * Sets the value of m_bWriteTwoPointLineAsPolyline
2958 : **********************************************************************/
2959 0 : void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
2960 : {
2961 0 : m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
2962 0 : }
2963 :
2964 : /*=====================================================================
2965 : * class TABRegion
2966 : *====================================================================*/
2967 :
2968 : /**********************************************************************
2969 : * TABRegion::TABRegion()
2970 : *
2971 : * Constructor.
2972 : **********************************************************************/
2973 1168 : TABRegion::TABRegion(OGRFeatureDefn *poDefnIn)
2974 : : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
2975 1168 : m_dCenterX(0.0), m_dCenterY(0.0)
2976 : {
2977 1168 : }
2978 :
2979 : /**********************************************************************
2980 : * TABRegion::~TABRegion()
2981 : *
2982 : * Destructor.
2983 : **********************************************************************/
2984 2336 : TABRegion::~TABRegion()
2985 : {
2986 2336 : }
2987 :
2988 : /**********************************************************************
2989 : * TABRegion::CloneTABFeature()
2990 : *
2991 : * Duplicate feature, including stuff specific to each TABFeature type.
2992 : *
2993 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
2994 : * then copies any members specific to its own type.
2995 : **********************************************************************/
2996 0 : TABFeature *TABRegion::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
2997 : {
2998 : /*-----------------------------------------------------------------
2999 : * Alloc new feature and copy the base stuff
3000 : *----------------------------------------------------------------*/
3001 0 : TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
3002 :
3003 0 : CopyTABFeatureBase(poNew);
3004 :
3005 : /*-----------------------------------------------------------------
3006 : * And members specific to this class
3007 : *----------------------------------------------------------------*/
3008 : // ITABFeaturePen
3009 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
3010 :
3011 : // ITABFeatureBrush
3012 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
3013 :
3014 0 : poNew->m_bSmooth = m_bSmooth;
3015 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
3016 0 : poNew->m_dCenterX = m_dCenterX;
3017 0 : poNew->m_dCenterY = m_dCenterY;
3018 :
3019 0 : return poNew;
3020 : }
3021 :
3022 : /**********************************************************************
3023 : * TABRegion::ValidateMapInfoType()
3024 : *
3025 : * Check the feature's geometry part and return the corresponding
3026 : * mapinfo object type code. The m_nMapInfoType member will also
3027 : * be updated for further calls to GetMapInfoType();
3028 : *
3029 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
3030 : * is expected for this object class.
3031 : **********************************************************************/
3032 88 : TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
3033 : {
3034 : /*-----------------------------------------------------------------
3035 : * Fetch and validate geometry
3036 : *----------------------------------------------------------------*/
3037 88 : OGRGeometry *poGeom = GetGeometryRef();
3038 94 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3039 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3040 : {
3041 88 : GInt32 numPointsTotal = 0;
3042 88 : GInt32 numRings = GetNumRings();
3043 176 : for (int i = 0; i < numRings; i++)
3044 : {
3045 88 : OGRLinearRing *poRing = GetRingRef(i);
3046 88 : if (poRing)
3047 88 : numPointsTotal += poRing->getNumPoints();
3048 : }
3049 88 : if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
3050 0 : m_nMapInfoType = TAB_GEOM_V800_REGION;
3051 88 : else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
3052 0 : m_nMapInfoType = TAB_GEOM_V450_REGION;
3053 : else
3054 88 : m_nMapInfoType = TAB_GEOM_REGION;
3055 : }
3056 : else
3057 : {
3058 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3059 : "TABRegion: Missing or Invalid Geometry!");
3060 0 : m_nMapInfoType = TAB_GEOM_NONE;
3061 : }
3062 :
3063 : /*-----------------------------------------------------------------
3064 : * Decide if coordinates should be compressed or not.
3065 : *----------------------------------------------------------------*/
3066 88 : ValidateCoordType(poMapFile);
3067 :
3068 88 : return m_nMapInfoType;
3069 : }
3070 :
3071 : /**********************************************************************
3072 : * TABRegion::ReadGeometryFromMAPFile()
3073 : *
3074 : * Fill the geometry and representation (color, etc...) part of the
3075 : * feature from the contents of the .MAP object pointed to by poMAPFile.
3076 : *
3077 : * It is assumed that poMAPFile currently points to the beginning of
3078 : * a map object.
3079 : *
3080 : * Returns 0 on success, -1 on error, in which case CPLError() will have
3081 : * been called.
3082 : **********************************************************************/
3083 514 : int TABRegion::ReadGeometryFromMAPFile(
3084 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3085 : GBool bCoordBlockDataOnly /*=FALSE*/,
3086 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3087 : {
3088 514 : double dXMin = 0.0;
3089 514 : double dYMin = 0.0;
3090 514 : double dXMax = 0.0;
3091 514 : double dYMax = 0.0;
3092 514 : OGRGeometry *poGeometry = nullptr;
3093 514 : TABMAPCoordBlock *poCoordBlock = nullptr;
3094 :
3095 : /*-----------------------------------------------------------------
3096 : * Fetch and validate geometry type
3097 : *----------------------------------------------------------------*/
3098 514 : m_nMapInfoType = poObjHdr->m_nType;
3099 :
3100 514 : if (m_nMapInfoType == TAB_GEOM_REGION ||
3101 459 : m_nMapInfoType == TAB_GEOM_REGION_C ||
3102 4 : m_nMapInfoType == TAB_GEOM_V450_REGION ||
3103 4 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3104 0 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3105 0 : m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3106 : {
3107 : /*=============================================================
3108 : * REGION (Similar to PLINE MULTIPLE)
3109 : *============================================================*/
3110 : GInt32 /* nCoordDataSize, */ numPointsTotal;
3111 514 : OGRMultiPolygon *poMultiPolygon = nullptr;
3112 514 : OGRPolygon *poPolygon = nullptr;
3113 514 : GBool bComprCoord = poObjHdr->IsCompressedType();
3114 514 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3115 :
3116 : /*-------------------------------------------------------------
3117 : * Copy data from poObjHdr
3118 : *------------------------------------------------------------*/
3119 514 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3120 :
3121 514 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
3122 : /* nCoordDataSize = poPLineHdr->m_nCoordDataSize; */
3123 514 : GInt32 numLineSections = poPLineHdr->m_numLineSections;
3124 514 : m_bSmooth = poPLineHdr->m_bSmooth;
3125 :
3126 : // Centroid/label point
3127 514 : double dX = 0.0;
3128 514 : double dY = 0.0;
3129 514 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
3130 : dX, dY);
3131 514 : SetCenter(dX, dY);
3132 :
3133 : // Compressed coordinate origin (useful only in compressed case!)
3134 514 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
3135 514 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
3136 :
3137 : // MBR
3138 514 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
3139 : dYMin);
3140 514 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
3141 : dYMax);
3142 :
3143 514 : if (!bCoordBlockDataOnly)
3144 : {
3145 514 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
3146 514 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
3147 514 : m_nBrushDefIndex = poPLineHdr->m_nBrushId; // Brush index
3148 514 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
3149 : }
3150 :
3151 : /*-------------------------------------------------------------
3152 : * Read data from the coord. block
3153 : *------------------------------------------------------------*/
3154 :
3155 514 : const int nMinSizeOfSection = 24;
3156 514 : if (numLineSections > INT_MAX / nMinSizeOfSection)
3157 : {
3158 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3159 0 : return -1;
3160 : }
3161 514 : const GUInt32 nMinimumBytesForSections =
3162 514 : nMinSizeOfSection * numLineSections;
3163 514 : if (nMinimumBytesForSections > 1024 * 1024 &&
3164 0 : nMinimumBytesForSections > poMapFile->GetFileSize())
3165 : {
3166 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3167 0 : return -1;
3168 : }
3169 :
3170 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3171 514 : VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
3172 514 : if (pasSecHdrs == nullptr)
3173 0 : return -1;
3174 :
3175 514 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3176 4 : poCoordBlock = *ppoCoordBlock;
3177 : else
3178 510 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
3179 :
3180 514 : if (poCoordBlock)
3181 514 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3182 :
3183 1028 : if (poCoordBlock == nullptr ||
3184 514 : poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
3185 : numLineSections, pasSecHdrs,
3186 : numPointsTotal) != 0)
3187 : {
3188 0 : CPLError(CE_Failure, CPLE_FileIO,
3189 : "Failed reading coordinate data at offset %d",
3190 : nCoordBlockPtr);
3191 0 : CPLFree(pasSecHdrs);
3192 0 : return -1;
3193 : }
3194 :
3195 514 : const GUInt32 nMinimumBytesForPoints =
3196 514 : (bComprCoord ? 4 : 8) * numPointsTotal;
3197 514 : if (nMinimumBytesForPoints > 1024 * 1024 &&
3198 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
3199 : {
3200 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
3201 0 : CPLFree(pasSecHdrs);
3202 0 : return -1;
3203 : }
3204 :
3205 : GInt32 *panXY = static_cast<GInt32 *>(
3206 514 : VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
3207 514 : if (panXY == nullptr)
3208 : {
3209 0 : CPLFree(pasSecHdrs);
3210 0 : return -1;
3211 : }
3212 :
3213 514 : if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
3214 : 0)
3215 : {
3216 0 : CPLError(CE_Failure, CPLE_FileIO,
3217 : "Failed reading coordinate data at offset %d",
3218 : nCoordBlockPtr);
3219 0 : CPLFree(pasSecHdrs);
3220 0 : CPLFree(panXY);
3221 0 : return -1;
3222 : }
3223 :
3224 : /*-------------------------------------------------------------
3225 : * Decide if we should return an OGRPolygon or an OGRMultiPolygon
3226 : * depending on the number of outer rings found in CoordSecHdr blocks.
3227 : * The CoodSecHdr block for each outer ring in the region has a flag
3228 : * indicating the number of inner rings that follow.
3229 : * In older versions of the format, the count of inner rings was
3230 : * always zero, so in this case we would always return MultiPolygons.
3231 : *
3232 : * Note: The current implementation assumes that there cannot be
3233 : * holes inside holes (i.e. multiple levels of inner rings)... if
3234 : * that case was encountered then we would return an OGRMultiPolygon
3235 : * in which the topological relationship between the rings would
3236 : * be lost.
3237 : *------------------------------------------------------------*/
3238 514 : int numOuterRings = 0;
3239 1028 : for (int iSection = 0; iSection < numLineSections; iSection++)
3240 : {
3241 : // Count this as an outer ring.
3242 514 : numOuterRings++;
3243 : // Skip inner rings... so loop continues on an outer ring.
3244 514 : iSection += pasSecHdrs[iSection].numHoles;
3245 : }
3246 :
3247 514 : if (numOuterRings > 1)
3248 : {
3249 0 : poMultiPolygon = new OGRMultiPolygon;
3250 0 : poGeometry = poMultiPolygon;
3251 : }
3252 : else
3253 : {
3254 514 : poGeometry = nullptr; // Will be set later
3255 : }
3256 :
3257 : /*-------------------------------------------------------------
3258 : * OK, build the OGRGeometry object.
3259 : *------------------------------------------------------------*/
3260 514 : int numHolesToRead = 0;
3261 514 : poPolygon = nullptr;
3262 1028 : for (int iSection = 0; iSection < numLineSections; iSection++)
3263 : {
3264 :
3265 514 : if (poPolygon == nullptr)
3266 514 : poPolygon = new OGRPolygon();
3267 :
3268 514 : if (numHolesToRead < 1)
3269 514 : numHolesToRead = pasSecHdrs[iSection].numHoles;
3270 : else
3271 0 : numHolesToRead--;
3272 :
3273 514 : int numSectionVertices = pasSecHdrs[iSection].numVertices;
3274 514 : GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
3275 :
3276 514 : OGRLinearRing *poRing = new OGRLinearRing();
3277 514 : poRing->setNumPoints(numSectionVertices);
3278 :
3279 13985 : for (int i = 0; i < numSectionVertices; i++)
3280 : {
3281 13471 : poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
3282 13471 : poRing->setPoint(i, dX, dY);
3283 13471 : pnXYPtr += 2;
3284 : }
3285 :
3286 514 : poPolygon->addRingDirectly(poRing);
3287 514 : poRing = nullptr;
3288 :
3289 514 : if (numHolesToRead < 1)
3290 : {
3291 514 : if (numOuterRings > 1)
3292 : {
3293 0 : poMultiPolygon->addGeometryDirectly(poPolygon);
3294 : }
3295 : else
3296 : {
3297 514 : poGeometry = poPolygon;
3298 514 : CPLAssert(iSection == numLineSections - 1);
3299 : }
3300 :
3301 514 : poPolygon = nullptr; // We'll alloc a new polygon next loop.
3302 : }
3303 : }
3304 514 : delete poPolygon; // should only trigger on corrupted files
3305 :
3306 514 : CPLFree(pasSecHdrs);
3307 514 : CPLFree(panXY);
3308 : }
3309 : else
3310 : {
3311 0 : CPLError(
3312 : CE_Failure, CPLE_AssertionFailed,
3313 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
3314 0 : m_nMapInfoType, m_nMapInfoType);
3315 0 : return -1;
3316 : }
3317 :
3318 514 : SetGeometryDirectly(poGeometry);
3319 :
3320 514 : SetMBR(dXMin, dYMin, dXMax, dYMax);
3321 514 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
3322 : poObjHdr->m_nMaxY);
3323 :
3324 : /* Return a ref to coord block so that caller can continue reading
3325 : * after the end of this object (used by TABCollection and index splitting)
3326 : */
3327 514 : if (ppoCoordBlock)
3328 4 : *ppoCoordBlock = poCoordBlock;
3329 :
3330 514 : return 0;
3331 : }
3332 :
3333 : /**********************************************************************
3334 : * TABRegion::WriteGeometryToMAPFile()
3335 : *
3336 : * Write the geometry and representation (color, etc...) part of the
3337 : * feature to the .MAP object pointed to by poMAPFile.
3338 : *
3339 : * It is assumed that poMAPFile currently points to a valid map object.
3340 : *
3341 : * Returns 0 on success, -1 on error, in which case CPLError() will have
3342 : * been called.
3343 : **********************************************************************/
3344 88 : int TABRegion::WriteGeometryToMAPFile(
3345 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3346 : GBool bCoordBlockDataOnly /*=FALSE*/,
3347 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3348 : {
3349 : /*-----------------------------------------------------------------
3350 : * We assume that ValidateMapInfoType() was called already and that
3351 : * the type in poObjHdr->m_nType is valid.
3352 : *----------------------------------------------------------------*/
3353 88 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
3354 :
3355 : /*-----------------------------------------------------------------
3356 : * Fetch and validate geometry
3357 : *----------------------------------------------------------------*/
3358 88 : OGRGeometry *poGeom = GetGeometryRef();
3359 88 : TABMAPCoordBlock *poCoordBlock = nullptr;
3360 :
3361 258 : if ((m_nMapInfoType == TAB_GEOM_REGION ||
3362 82 : m_nMapInfoType == TAB_GEOM_REGION_C ||
3363 0 : m_nMapInfoType == TAB_GEOM_V450_REGION ||
3364 0 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3365 0 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3366 88 : m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
3367 176 : poGeom &&
3368 88 : (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3369 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3370 : {
3371 : /*=============================================================
3372 : * REGIONs are similar to PLINE MULTIPLE
3373 : *
3374 : * We accept both OGRPolygons (with one or multiple rings) and
3375 : * OGRMultiPolygons as input.
3376 : *============================================================*/
3377 88 : GBool bCompressed = poObjHdr->IsCompressedType();
3378 :
3379 : /*-------------------------------------------------------------
3380 : * Process geometry first...
3381 : *------------------------------------------------------------*/
3382 88 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3383 0 : poCoordBlock = *ppoCoordBlock;
3384 : else
3385 88 : poCoordBlock = poMapFile->GetCurCoordBlock();
3386 88 : poCoordBlock->StartNewFeature();
3387 88 : GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
3388 88 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3389 :
3390 : #ifdef TABDUMP
3391 : printf(/*ok*/
3392 : "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
3393 : m_nComprOrgX, m_nComprOrgY);
3394 : #endif
3395 : /*-------------------------------------------------------------
3396 : * Fetch total number of rings and build array of coord
3397 : * sections headers.
3398 : *------------------------------------------------------------*/
3399 88 : TABMAPCoordSecHdr *pasSecHdrs = nullptr;
3400 88 : int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
3401 88 : int nStatus = numRingsTotal == 0 ? -1 : 0;
3402 :
3403 : /*-------------------------------------------------------------
3404 : * Write the Coord. Section Header
3405 : *------------------------------------------------------------*/
3406 88 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3407 :
3408 88 : if (nStatus == 0)
3409 88 : nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
3410 : pasSecHdrs, bCompressed);
3411 :
3412 88 : CPLFree(pasSecHdrs);
3413 88 : pasSecHdrs = nullptr;
3414 :
3415 88 : if (nStatus != 0)
3416 0 : return nStatus; // Error has already been reported.
3417 :
3418 : /*-------------------------------------------------------------
3419 : * Go through all the rings in our OGRMultiPolygon or OGRPolygon
3420 : * to write the coordinates themselves...
3421 : *------------------------------------------------------------*/
3422 :
3423 88 : GInt32 nX = 0, nY = 0;
3424 176 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3425 : {
3426 88 : OGRLinearRing *poRing = GetRingRef(iRing);
3427 88 : if (poRing == nullptr)
3428 : {
3429 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3430 : "TABRegion: Object Geometry contains NULL rings!");
3431 0 : return -1;
3432 : }
3433 :
3434 88 : int numPoints = poRing->getNumPoints();
3435 :
3436 2515 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
3437 : {
3438 2427 : poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
3439 : nY);
3440 2427 : if ((nStatus =
3441 2427 : poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
3442 : {
3443 : // Failed ... error message has already been produced
3444 0 : return nStatus;
3445 : }
3446 : }
3447 : } /* for iRing*/
3448 :
3449 88 : GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
3450 :
3451 : /*-------------------------------------------------------------
3452 : * ... and finally copy info to poObjHdr
3453 : *------------------------------------------------------------*/
3454 88 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3455 :
3456 88 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
3457 88 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
3458 88 : poPLineHdr->m_numLineSections = numRingsTotal;
3459 :
3460 88 : poPLineHdr->m_bSmooth = m_bSmooth;
3461 :
3462 : // MBR
3463 88 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
3464 :
3465 : // Region center/label point
3466 88 : double dX = 0.0;
3467 88 : double dY = 0.0;
3468 88 : if (GetCenter(dX, dY) != -1)
3469 : {
3470 88 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
3471 88 : poPLineHdr->m_nLabelY);
3472 : }
3473 : else
3474 : {
3475 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
3476 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
3477 : }
3478 :
3479 : // Compressed coordinate origin (useful only in compressed case!)
3480 88 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
3481 88 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
3482 :
3483 88 : if (!bCoordBlockDataOnly)
3484 : {
3485 88 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
3486 88 : poPLineHdr->m_nPenId =
3487 88 : static_cast<GByte>(m_nPenDefIndex); // Pen index
3488 :
3489 88 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
3490 88 : poPLineHdr->m_nBrushId =
3491 88 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
3492 : }
3493 : }
3494 : else
3495 : {
3496 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3497 : "TABRegion: Object contains an invalid Geometry!");
3498 0 : return -1;
3499 : }
3500 :
3501 88 : if (CPLGetLastErrorType() == CE_Failure)
3502 0 : return -1;
3503 :
3504 : /* Return a ref to coord block so that caller can continue writing
3505 : * after the end of this object (used by index splitting)
3506 : */
3507 88 : if (ppoCoordBlock)
3508 0 : *ppoCoordBlock = poCoordBlock;
3509 :
3510 88 : return 0;
3511 : }
3512 :
3513 : /**********************************************************************
3514 : * TABRegion::GetNumRings()
3515 : *
3516 : * Return the total number of rings in this object making it look like
3517 : * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3518 : * of rings... hides the complexity of handling OGRMultiPolygons vs
3519 : * OGRPolygons, etc.
3520 : *
3521 : * Returns 0 if the geometry contained in the object is invalid or missing.
3522 : **********************************************************************/
3523 109 : int TABRegion::GetNumRings()
3524 : {
3525 109 : return ComputeNumRings(nullptr, nullptr);
3526 : }
3527 :
3528 197 : int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
3529 : TABMAPFile *poMapFile)
3530 : {
3531 197 : int numRingsTotal = 0;
3532 197 : int iLastSect = 0;
3533 :
3534 197 : if (ppasSecHdrs)
3535 88 : *ppasSecHdrs = nullptr;
3536 :
3537 197 : OGRGeometry *poGeom = GetGeometryRef();
3538 :
3539 209 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3540 12 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3541 : {
3542 : /*-------------------------------------------------------------
3543 : * Calculate total number of rings...
3544 : *------------------------------------------------------------*/
3545 197 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3546 : {
3547 24 : for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
3548 : {
3549 12 : numRingsTotal += poPolygon->getNumInteriorRings() + 1;
3550 :
3551 12 : if (ppasSecHdrs && poMapFile)
3552 : {
3553 6 : if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3554 6 : iLastSect) != 0)
3555 0 : return 0; // An error happened, return count=0
3556 : }
3557 : } // for
3558 : }
3559 : else
3560 : {
3561 185 : OGRPolygon *poPolygon = poGeom->toPolygon();
3562 185 : numRingsTotal = poPolygon->getNumInteriorRings() + 1;
3563 :
3564 185 : if (ppasSecHdrs && poMapFile)
3565 : {
3566 82 : if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3567 82 : iLastSect) != 0)
3568 0 : return 0; // An error happened, return count=0
3569 : }
3570 : }
3571 : }
3572 :
3573 : /*-----------------------------------------------------------------
3574 : * If we're generating section header blocks, then init the
3575 : * coordinate offset values.
3576 : *
3577 : * In calculation of nDataOffset, we have to take into account that
3578 : * V450 header section uses int32 instead of int16 for numVertices
3579 : * and we add another 2 bytes to align with a 4 bytes boundary.
3580 : *------------------------------------------------------------*/
3581 197 : const int nTotalHdrSizeUncompressed =
3582 394 : (m_nMapInfoType == TAB_GEOM_V450_REGION ||
3583 197 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3584 197 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3585 197 : m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3586 394 : ? 28 * numRingsTotal
3587 : : 24 * numRingsTotal;
3588 :
3589 197 : if (ppasSecHdrs)
3590 : {
3591 88 : int numPointsTotal = 0;
3592 88 : CPLAssert(iLastSect == numRingsTotal);
3593 176 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3594 : {
3595 88 : (*ppasSecHdrs)[iRing].nDataOffset =
3596 88 : nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
3597 88 : (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
3598 :
3599 88 : numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
3600 : }
3601 : }
3602 :
3603 197 : return numRingsTotal;
3604 : }
3605 :
3606 : /**********************************************************************
3607 : * TABRegion::AppendSecHdrs()
3608 : *
3609 : * (Private method)
3610 : *
3611 : * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
3612 : **********************************************************************/
3613 88 : int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
3614 : TABMAPCoordSecHdr *&pasSecHdrs,
3615 : TABMAPFile *poMapFile, int &iLastRing)
3616 : {
3617 : /*-------------------------------------------------------------
3618 : * Add a pasSecHdrs[] entry for each ring in this polygon.
3619 : * Note that the structs won't be fully initialized.
3620 : *------------------------------------------------------------*/
3621 88 : int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
3622 :
3623 88 : pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3624 88 : CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
3625 : sizeof(TABMAPCoordSecHdr)));
3626 :
3627 176 : for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
3628 : {
3629 88 : OGRLinearRing *poRing = nullptr;
3630 88 : OGREnvelope sEnvelope;
3631 :
3632 88 : if (iRing == 0)
3633 88 : poRing = poPolygon->getExteriorRing();
3634 : else
3635 0 : poRing = poPolygon->getInteriorRing(iRing - 1);
3636 :
3637 88 : if (poRing == nullptr)
3638 : {
3639 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3640 : "Assertion Failed: Encountered NULL ring in OGRPolygon");
3641 0 : return -1;
3642 : }
3643 :
3644 88 : poRing->getEnvelope(&sEnvelope);
3645 :
3646 88 : pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
3647 :
3648 88 : if (iRing == 0)
3649 88 : pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
3650 : else
3651 0 : pasSecHdrs[iLastRing].numHoles = 0;
3652 :
3653 88 : poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
3654 88 : pasSecHdrs[iLastRing].nXMin,
3655 88 : pasSecHdrs[iLastRing].nYMin);
3656 88 : poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
3657 88 : pasSecHdrs[iLastRing].nXMax,
3658 88 : pasSecHdrs[iLastRing].nYMax);
3659 :
3660 88 : iLastRing++;
3661 : } /* for iRing*/
3662 :
3663 88 : return 0;
3664 : }
3665 :
3666 : /**********************************************************************
3667 : * TABRegion::GetRingRef()
3668 : *
3669 : * Returns a reference to the specified ring number making it look like
3670 : * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3671 : * of rings... hides the complexity of handling OGRMultiPolygons vs
3672 : * OGRPolygons, etc.
3673 : *
3674 : * Returns NULL if the geometry contained in the object is invalid or
3675 : * missing or if the specified ring index is invalid.
3676 : **********************************************************************/
3677 197 : OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
3678 : {
3679 197 : OGRLinearRing *poRing = nullptr;
3680 :
3681 197 : OGRGeometry *poGeom = GetGeometryRef();
3682 :
3683 209 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3684 12 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3685 : {
3686 : /*-------------------------------------------------------------
3687 : * Establish number of polygons based on geometry type
3688 : *------------------------------------------------------------*/
3689 197 : OGRMultiPolygon *poMultiPolygon = nullptr;
3690 197 : int iCurRing = 0;
3691 197 : int numOGRPolygons = 0;
3692 :
3693 197 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3694 : {
3695 12 : poMultiPolygon = poGeom->toMultiPolygon();
3696 12 : numOGRPolygons = poMultiPolygon->getNumGeometries();
3697 : }
3698 : else
3699 : {
3700 185 : numOGRPolygons = 1;
3701 : }
3702 :
3703 : /*-------------------------------------------------------------
3704 : * Loop through polygons until we find the requested ring.
3705 : *------------------------------------------------------------*/
3706 197 : iCurRing = 0;
3707 394 : for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
3708 : iPoly++)
3709 : {
3710 197 : OGRPolygon *poPolygon = nullptr;
3711 197 : if (poMultiPolygon)
3712 12 : poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3713 : else
3714 185 : poPolygon = poGeom->toPolygon();
3715 :
3716 197 : int numIntRings = poPolygon->getNumInteriorRings();
3717 :
3718 197 : if (iCurRing == nRequestedRingIndex)
3719 : {
3720 197 : poRing = poPolygon->getExteriorRing();
3721 : }
3722 0 : else if (nRequestedRingIndex > iCurRing &&
3723 0 : nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3724 : {
3725 0 : poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
3726 0 : (iCurRing + 1));
3727 : }
3728 197 : iCurRing += numIntRings + 1;
3729 : }
3730 : }
3731 :
3732 197 : return poRing;
3733 : }
3734 :
3735 : /**********************************************************************
3736 : * TABRegion::RingIsHole()
3737 : *
3738 : * Return false if the requested ring index is the first of a polygon
3739 : **********************************************************************/
3740 0 : GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
3741 : {
3742 0 : OGRGeometry *poGeom = GetGeometryRef();
3743 :
3744 0 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3745 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3746 : {
3747 : /*-------------------------------------------------------------
3748 : * Establish number of polygons based on geometry type
3749 : *------------------------------------------------------------*/
3750 0 : OGRMultiPolygon *poMultiPolygon = nullptr;
3751 0 : int iCurRing = 0;
3752 0 : int numOGRPolygons = 0;
3753 :
3754 0 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3755 : {
3756 0 : poMultiPolygon = poGeom->toMultiPolygon();
3757 0 : numOGRPolygons = poMultiPolygon->getNumGeometries();
3758 : }
3759 : else
3760 : {
3761 0 : numOGRPolygons = 1;
3762 : }
3763 :
3764 : /*-------------------------------------------------------------
3765 : * Loop through polygons until we find the requested ring.
3766 : *------------------------------------------------------------*/
3767 0 : iCurRing = 0;
3768 0 : for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
3769 : {
3770 0 : OGRPolygon *poPolygon = nullptr;
3771 0 : if (poMultiPolygon)
3772 0 : poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3773 : else
3774 0 : poPolygon = poGeom->toPolygon();
3775 :
3776 0 : int numIntRings = poPolygon->getNumInteriorRings();
3777 :
3778 0 : if (iCurRing == nRequestedRingIndex)
3779 : {
3780 0 : return FALSE;
3781 : }
3782 0 : else if (nRequestedRingIndex > iCurRing &&
3783 0 : nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3784 : {
3785 0 : return TRUE;
3786 : }
3787 0 : iCurRing += numIntRings + 1;
3788 : }
3789 : }
3790 :
3791 0 : return FALSE;
3792 : }
3793 :
3794 : /**********************************************************************
3795 : * TABRegion::GetStyleString() const
3796 : *
3797 : * Return style string for this feature.
3798 : *
3799 : * Style String is built only once during the first call to GetStyleString().
3800 : **********************************************************************/
3801 67 : const char *TABRegion::GetStyleString() const
3802 : {
3803 67 : if (m_pszStyleString == nullptr)
3804 : {
3805 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
3806 : // to use temporary buffers
3807 39 : char *pszPen = CPLStrdup(GetPenStyleString());
3808 39 : char *pszBrush = CPLStrdup(GetBrushStyleString());
3809 :
3810 39 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
3811 :
3812 39 : CPLFree(pszPen);
3813 39 : CPLFree(pszBrush);
3814 : }
3815 :
3816 67 : return m_pszStyleString;
3817 : }
3818 :
3819 : /**********************************************************************
3820 : * TABRegion::DumpMIF()
3821 : *
3822 : * Dump feature geometry in a format similar to .MIF REGIONs.
3823 : **********************************************************************/
3824 0 : void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
3825 : {
3826 0 : if (fpOut == nullptr)
3827 0 : fpOut = stdout;
3828 :
3829 : /*-----------------------------------------------------------------
3830 : * Fetch and validate geometry
3831 : *----------------------------------------------------------------*/
3832 0 : OGRGeometry *poGeom = GetGeometryRef();
3833 0 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3834 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3835 : {
3836 : /*-------------------------------------------------------------
3837 : * Generate output for region
3838 : *
3839 : * Note that we want to handle both OGRPolygons and OGRMultiPolygons
3840 : * that's why we use the GetNumRings()/GetRingRef() interface.
3841 : *------------------------------------------------------------*/
3842 0 : int numRingsTotal = GetNumRings();
3843 :
3844 0 : fprintf(fpOut, "REGION %d\n", numRingsTotal);
3845 :
3846 0 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3847 : {
3848 0 : OGRLinearRing *poRing = GetRingRef(iRing);
3849 :
3850 0 : if (poRing == nullptr)
3851 : {
3852 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3853 : "TABRegion: Object Geometry contains NULL rings!");
3854 0 : return;
3855 : }
3856 :
3857 0 : const int numPoints = poRing->getNumPoints();
3858 0 : fprintf(fpOut, " %d\n", numPoints);
3859 0 : for (int i = 0; i < numPoints; i++)
3860 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
3861 : poRing->getY(i));
3862 : }
3863 : }
3864 : else
3865 : {
3866 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3867 : "TABRegion: Missing or Invalid Geometry!");
3868 0 : return;
3869 : }
3870 :
3871 0 : if (m_bCenterIsSet)
3872 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
3873 :
3874 : // Finish with PEN/BRUSH/etc. clauses
3875 0 : DumpPenDef();
3876 0 : DumpBrushDef();
3877 :
3878 0 : fflush(fpOut);
3879 : }
3880 :
3881 : /**********************************************************************
3882 : * TABRegion::GetCenter()
3883 : *
3884 : * Returns the center/label point of the region.
3885 : * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
3886 : * before.
3887 : *
3888 : * Returns 0 on success, -1 on error.
3889 : **********************************************************************/
3890 88 : int TABRegion::GetCenter(double &dX, double &dY)
3891 : {
3892 88 : if (!m_bCenterIsSet)
3893 : {
3894 : /*-------------------------------------------------------------
3895 : * Calculate label point. If we have a multipolygon then we use
3896 : * the first OGRPolygon in the feature to calculate the point.
3897 : *------------------------------------------------------------*/
3898 88 : OGRGeometry *poGeom = GetGeometryRef();
3899 88 : if (poGeom == nullptr)
3900 0 : return -1;
3901 :
3902 88 : OGRPolygon *poPolygon = nullptr;
3903 :
3904 88 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3905 : {
3906 6 : OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
3907 6 : if (poMultiPolygon->getNumGeometries() > 0)
3908 6 : poPolygon = poMultiPolygon->getGeometryRef(0);
3909 : }
3910 82 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
3911 : {
3912 82 : poPolygon = poGeom->toPolygon();
3913 : }
3914 :
3915 88 : OGRPoint oLabelPoint;
3916 176 : if (poPolygon != nullptr &&
3917 88 : OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
3918 : {
3919 88 : m_dCenterX = oLabelPoint.getX();
3920 88 : m_dCenterY = oLabelPoint.getY();
3921 : }
3922 : else
3923 : {
3924 0 : OGREnvelope oEnv;
3925 0 : poGeom->getEnvelope(&oEnv);
3926 0 : m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
3927 0 : m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
3928 : }
3929 :
3930 88 : m_bCenterIsSet = TRUE;
3931 : }
3932 :
3933 88 : if (!m_bCenterIsSet)
3934 0 : return -1;
3935 :
3936 88 : dX = m_dCenterX;
3937 88 : dY = m_dCenterY;
3938 88 : return 0;
3939 : }
3940 :
3941 : /**********************************************************************
3942 : * TABRegion::SetCenter()
3943 : *
3944 : * Set the X,Y coordinates to use as center/label point for the region.
3945 : **********************************************************************/
3946 516 : void TABRegion::SetCenter(double dX, double dY)
3947 : {
3948 516 : m_dCenterX = dX;
3949 516 : m_dCenterY = dY;
3950 516 : m_bCenterIsSet = TRUE;
3951 516 : }
3952 :
3953 : /*=====================================================================
3954 : * class TABRectangle
3955 : *====================================================================*/
3956 :
3957 : /**********************************************************************
3958 : * TABRectangle::TABRectangle()
3959 : *
3960 : * Constructor.
3961 : **********************************************************************/
3962 949 : TABRectangle::TABRectangle(OGRFeatureDefn *poDefnIn)
3963 : : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
3964 949 : m_dRoundYRadius(0.0)
3965 : {
3966 949 : }
3967 :
3968 : /**********************************************************************
3969 : * TABRectangle::~TABRectangle()
3970 : *
3971 : * Destructor.
3972 : **********************************************************************/
3973 1898 : TABRectangle::~TABRectangle()
3974 : {
3975 1898 : }
3976 :
3977 : /**********************************************************************
3978 : * TABRectangle::CloneTABFeature()
3979 : *
3980 : * Duplicate feature, including stuff specific to each TABFeature type.
3981 : *
3982 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
3983 : * then copies any members specific to its own type.
3984 : **********************************************************************/
3985 0 : TABFeature *TABRectangle::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
3986 : {
3987 : /*-----------------------------------------------------------------
3988 : * Alloc new feature and copy the base stuff
3989 : *----------------------------------------------------------------*/
3990 : TABRectangle *poNew =
3991 0 : new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
3992 :
3993 0 : CopyTABFeatureBase(poNew);
3994 :
3995 : /*-----------------------------------------------------------------
3996 : * And members specific to this class
3997 : *----------------------------------------------------------------*/
3998 : // ITABFeaturePen
3999 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4000 :
4001 : // ITABFeatureBrush
4002 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4003 :
4004 0 : poNew->m_bRoundCorners = m_bRoundCorners;
4005 0 : poNew->m_dRoundXRadius = m_dRoundXRadius;
4006 0 : poNew->m_dRoundYRadius = m_dRoundYRadius;
4007 :
4008 0 : return poNew;
4009 : }
4010 :
4011 : /**********************************************************************
4012 : * TABRectangle::ValidateMapInfoType()
4013 : *
4014 : * Check the feature's geometry part and return the corresponding
4015 : * mapinfo object type code. The m_nMapInfoType member will also
4016 : * be updated for further calls to GetMapInfoType();
4017 : *
4018 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4019 : * is expected for this object class.
4020 : **********************************************************************/
4021 0 : TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4022 : {
4023 : /*-----------------------------------------------------------------
4024 : * Fetch and validate geometry
4025 : *----------------------------------------------------------------*/
4026 0 : OGRGeometry *poGeom = GetGeometryRef();
4027 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4028 : {
4029 0 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4030 0 : m_nMapInfoType = TAB_GEOM_ROUNDRECT;
4031 : else
4032 0 : m_nMapInfoType = TAB_GEOM_RECT;
4033 : }
4034 : else
4035 : {
4036 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4037 : "TABRectangle: Missing or Invalid Geometry!");
4038 0 : m_nMapInfoType = TAB_GEOM_NONE;
4039 : }
4040 :
4041 : /*-----------------------------------------------------------------
4042 : * Decide if coordinates should be compressed or not.
4043 : *----------------------------------------------------------------*/
4044 : // __TODO__ For now we always write uncompressed for this class...
4045 : // ValidateCoordType(poMapFile);
4046 0 : UpdateMBR(poMapFile);
4047 :
4048 0 : return m_nMapInfoType;
4049 : }
4050 :
4051 : /**********************************************************************
4052 : * TABRectangle::UpdateMBR()
4053 : *
4054 : * Update the feature MBR members using the geometry
4055 : *
4056 : * Returns 0 on success, or -1 if there is no geometry in object
4057 : **********************************************************************/
4058 0 : int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4059 : {
4060 0 : OGREnvelope sEnvelope;
4061 :
4062 : /*-----------------------------------------------------------------
4063 : * Fetch and validate geometry
4064 : *----------------------------------------------------------------*/
4065 0 : OGRGeometry *poGeom = GetGeometryRef();
4066 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4067 0 : poGeom->getEnvelope(&sEnvelope);
4068 : else
4069 : {
4070 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4071 : "TABRectangle: Missing or Invalid Geometry!");
4072 0 : return -1;
4073 : }
4074 :
4075 : /*-----------------------------------------------------------------
4076 : * Note that we will simply use the rectangle's MBR and don't really
4077 : * read the polygon geometry... this should be OK unless the
4078 : * polygon geometry was not really a rectangle.
4079 : *----------------------------------------------------------------*/
4080 0 : m_dXMin = sEnvelope.MinX;
4081 0 : m_dYMin = sEnvelope.MinY;
4082 0 : m_dXMax = sEnvelope.MaxX;
4083 0 : m_dYMax = sEnvelope.MaxY;
4084 :
4085 0 : if (poMapFile)
4086 : {
4087 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4088 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4089 : }
4090 :
4091 0 : return 0;
4092 : }
4093 :
4094 : /**********************************************************************
4095 : * TABRectangle::ReadGeometryFromMAPFile()
4096 : *
4097 : * Fill the geometry and representation (color, etc...) part of the
4098 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4099 : *
4100 : * It is assumed that poMAPFile currently points to the beginning of
4101 : * a map object.
4102 : *
4103 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4104 : * been called.
4105 : **********************************************************************/
4106 8 : int TABRectangle::ReadGeometryFromMAPFile(
4107 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4108 : GBool bCoordBlockDataOnly /*=FALSE*/,
4109 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4110 : {
4111 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4112 8 : if (bCoordBlockDataOnly)
4113 0 : return 0;
4114 :
4115 : /*-----------------------------------------------------------------
4116 : * Fetch and validate geometry type
4117 : *----------------------------------------------------------------*/
4118 8 : m_nMapInfoType = poObjHdr->m_nType;
4119 :
4120 8 : if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
4121 4 : m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
4122 0 : m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
4123 : {
4124 0 : CPLError(
4125 : CE_Failure, CPLE_AssertionFailed,
4126 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4127 0 : m_nMapInfoType, m_nMapInfoType);
4128 0 : return -1;
4129 : }
4130 :
4131 : /*-----------------------------------------------------------------
4132 : * Read object information
4133 : *----------------------------------------------------------------*/
4134 : TABMAPObjRectEllipse *poRectHdr =
4135 8 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4136 :
4137 : // Read the corners radius
4138 :
4139 8 : if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4140 4 : m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4141 : {
4142 : // Read the corner's diameters
4143 4 : poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
4144 4 : poRectHdr->m_nCornerHeight, m_dRoundXRadius,
4145 4 : m_dRoundYRadius);
4146 :
4147 : // Divide by 2 since we store the corner's radius
4148 4 : m_dRoundXRadius /= 2.0;
4149 4 : m_dRoundYRadius /= 2.0;
4150 :
4151 4 : m_bRoundCorners = TRUE;
4152 : }
4153 : else
4154 : {
4155 4 : m_bRoundCorners = FALSE;
4156 4 : m_dRoundXRadius = 0.0;
4157 4 : m_dRoundYRadius = 0.0;
4158 : }
4159 :
4160 : // A rectangle is defined by its MBR
4161 :
4162 8 : double dXMin = 0.0;
4163 8 : double dYMin = 0.0;
4164 8 : double dXMax = 0.0;
4165 8 : double dYMax = 0.0;
4166 8 : poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4167 : dYMin);
4168 8 : poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4169 : dYMax);
4170 :
4171 8 : m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index
4172 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4173 :
4174 8 : m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index
4175 8 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4176 :
4177 : /*-----------------------------------------------------------------
4178 : * Call SetMBR() and GetMBR() now to make sure that min values are
4179 : * really smaller than max values.
4180 : *----------------------------------------------------------------*/
4181 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
4182 8 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4183 :
4184 : /* Copy int MBR to feature class members */
4185 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4186 : poObjHdr->m_nMaxY);
4187 :
4188 : /*-----------------------------------------------------------------
4189 : * Create and fill geometry object
4190 : *----------------------------------------------------------------*/
4191 8 : OGRPolygon *poPolygon = new OGRPolygon;
4192 8 : OGRLinearRing *poRing = new OGRLinearRing();
4193 8 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4194 : {
4195 : /*-------------------------------------------------------------
4196 : * For rounded rectangles, we generate arcs with 45 line
4197 : * segments for each corner. We start with lower-left corner
4198 : * and proceed counterclockwise
4199 : * We also have to make sure that rounding radius is not too
4200 : * large for the MBR in the generated polygon... however, we
4201 : * always return the true X/Y radius (not adjusted) since this
4202 : * is the way MapInfo seems to do it when a radius bigger than
4203 : * the MBR is passed from TBA to MIF.
4204 : *------------------------------------------------------------*/
4205 : const double dXRadius =
4206 4 : std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
4207 : const double dYRadius =
4208 4 : std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
4209 4 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
4210 : dYRadius, M_PI, 3.0 * M_PI / 2.0);
4211 4 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
4212 : dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
4213 4 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
4214 : dYRadius, 0.0, M_PI / 2.0);
4215 4 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
4216 : dYRadius, M_PI / 2.0, M_PI);
4217 :
4218 4 : TABCloseRing(poRing);
4219 : }
4220 : else
4221 : {
4222 4 : poRing->addPoint(dXMin, dYMin);
4223 4 : poRing->addPoint(dXMax, dYMin);
4224 4 : poRing->addPoint(dXMax, dYMax);
4225 4 : poRing->addPoint(dXMin, dYMax);
4226 4 : poRing->addPoint(dXMin, dYMin);
4227 : }
4228 :
4229 8 : poPolygon->addRingDirectly(poRing);
4230 8 : SetGeometryDirectly(poPolygon);
4231 :
4232 8 : return 0;
4233 : }
4234 :
4235 : /**********************************************************************
4236 : * TABRectangle::WriteGeometryToMAPFile()
4237 : *
4238 : * Write the geometry and representation (color, etc...) part of the
4239 : * feature to the .MAP object pointed to by poMAPFile.
4240 : *
4241 : * It is assumed that poMAPFile currently points to a valid map object.
4242 : *
4243 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4244 : * been called.
4245 : **********************************************************************/
4246 0 : int TABRectangle::WriteGeometryToMAPFile(
4247 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4248 : GBool bCoordBlockDataOnly /*=FALSE*/,
4249 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4250 : {
4251 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4252 0 : if (bCoordBlockDataOnly)
4253 0 : return 0;
4254 :
4255 : /*-----------------------------------------------------------------
4256 : * We assume that ValidateMapInfoType() was called already and that
4257 : * the type in poObjHdr->m_nType is valid.
4258 : *----------------------------------------------------------------*/
4259 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4260 :
4261 : /*-----------------------------------------------------------------
4262 : * Fetch and validate geometry and update MBR
4263 : * Note that we will simply use the geometry's MBR and don't really
4264 : * read the polygon geometry... this should be OK unless the
4265 : * polygon geometry was not really a rectangle.
4266 : *----------------------------------------------------------------*/
4267 0 : if (UpdateMBR(poMapFile) != 0)
4268 0 : return -1; /* Error already reported */
4269 :
4270 : /*-----------------------------------------------------------------
4271 : * Copy object information
4272 : *----------------------------------------------------------------*/
4273 : TABMAPObjRectEllipse *poRectHdr =
4274 0 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4275 :
4276 0 : if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4277 0 : m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4278 : {
4279 0 : poMapFile->Coordsys2IntDist(
4280 0 : m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
4281 0 : poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
4282 : }
4283 : else
4284 : {
4285 0 : poRectHdr->m_nCornerWidth = 0;
4286 0 : poRectHdr->m_nCornerHeight = 0;
4287 : }
4288 :
4289 : // A rectangle is defined by its MBR (values were set in UpdateMBR())
4290 0 : poRectHdr->m_nMinX = m_nXMin;
4291 0 : poRectHdr->m_nMinY = m_nYMin;
4292 0 : poRectHdr->m_nMaxX = m_nXMax;
4293 0 : poRectHdr->m_nMaxY = m_nYMax;
4294 :
4295 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4296 0 : poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
4297 :
4298 0 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4299 0 : poRectHdr->m_nBrushId =
4300 0 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
4301 :
4302 0 : if (CPLGetLastErrorType() == CE_Failure)
4303 0 : return -1;
4304 :
4305 0 : return 0;
4306 : }
4307 :
4308 : /**********************************************************************
4309 : * TABRectangle::GetStyleString() const
4310 : *
4311 : * Return style string for this feature.
4312 : *
4313 : * Style String is built only once during the first call to GetStyleString().
4314 : **********************************************************************/
4315 18 : const char *TABRectangle::GetStyleString() const
4316 : {
4317 18 : if (m_pszStyleString == nullptr)
4318 : {
4319 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4320 : // to use temporary buffers
4321 10 : char *pszPen = CPLStrdup(GetPenStyleString());
4322 10 : char *pszBrush = CPLStrdup(GetBrushStyleString());
4323 :
4324 10 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4325 :
4326 10 : CPLFree(pszPen);
4327 10 : CPLFree(pszBrush);
4328 : }
4329 :
4330 18 : return m_pszStyleString;
4331 : }
4332 :
4333 : /**********************************************************************
4334 : * TABRectangle::DumpMIF()
4335 : *
4336 : * Dump feature geometry in a format similar to .MIF REGIONs.
4337 : **********************************************************************/
4338 0 : void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
4339 : {
4340 0 : if (fpOut == nullptr)
4341 0 : fpOut = stdout;
4342 :
4343 : /*-----------------------------------------------------------------
4344 : * Output RECT or ROUNDRECT parameters
4345 : *----------------------------------------------------------------*/
4346 0 : double dXMin = 0.0;
4347 0 : double dYMin = 0.0;
4348 0 : double dXMax = 0.0;
4349 0 : double dYMax = 0.0;
4350 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4351 :
4352 0 : if (m_bRoundCorners)
4353 0 : fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g %.15g %.15g)\n",
4354 : dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
4355 : else
4356 0 : fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4357 : dYMax);
4358 :
4359 : /*-----------------------------------------------------------------
4360 : * Fetch and validate geometry
4361 : *----------------------------------------------------------------*/
4362 0 : OGRGeometry *poGeom = GetGeometryRef();
4363 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4364 : {
4365 : /*-------------------------------------------------------------
4366 : * Generate rectangle output as a region
4367 : * We could also output as a RECT or ROUNDRECT in a real MIF generator
4368 : *------------------------------------------------------------*/
4369 0 : OGRPolygon *poPolygon = poGeom->toPolygon();
4370 0 : int numIntRings = poPolygon->getNumInteriorRings();
4371 0 : fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4372 : // In this loop, iRing=-1 for the outer ring.
4373 0 : for (int iRing = -1; iRing < numIntRings; iRing++)
4374 : {
4375 0 : OGRLinearRing *poRing = nullptr;
4376 :
4377 0 : if (iRing == -1)
4378 0 : poRing = poPolygon->getExteriorRing();
4379 : else
4380 0 : poRing = poPolygon->getInteriorRing(iRing);
4381 :
4382 0 : if (poRing == nullptr)
4383 : {
4384 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4385 : "TABRectangle: Object Geometry contains NULL rings!");
4386 0 : return;
4387 : }
4388 :
4389 0 : const int numPoints = poRing->getNumPoints();
4390 0 : fprintf(fpOut, " %d\n", numPoints);
4391 0 : for (int i = 0; i < numPoints; i++)
4392 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4393 : poRing->getY(i));
4394 : }
4395 : }
4396 : else
4397 : {
4398 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4399 : "TABRectangle: Missing or Invalid Geometry!");
4400 0 : return;
4401 : }
4402 :
4403 : // Finish with PEN/BRUSH/etc. clauses
4404 0 : DumpPenDef();
4405 0 : DumpBrushDef();
4406 :
4407 0 : fflush(fpOut);
4408 : }
4409 :
4410 : /*=====================================================================
4411 : * class TABEllipse
4412 : *====================================================================*/
4413 :
4414 : /**********************************************************************
4415 : * TABEllipse::TABEllipse()
4416 : *
4417 : * Constructor.
4418 : **********************************************************************/
4419 401 : TABEllipse::TABEllipse(OGRFeatureDefn *poDefnIn)
4420 : : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
4421 401 : m_dYRadius(0.0)
4422 : {
4423 401 : }
4424 :
4425 : /**********************************************************************
4426 : * TABEllipse::~TABEllipse()
4427 : *
4428 : * Destructor.
4429 : **********************************************************************/
4430 802 : TABEllipse::~TABEllipse()
4431 : {
4432 802 : }
4433 :
4434 : /**********************************************************************
4435 : * TABEllipse::CloneTABFeature()
4436 : *
4437 : * Duplicate feature, including stuff specific to each TABFeature type.
4438 : *
4439 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
4440 : * then copies any members specific to its own type.
4441 : **********************************************************************/
4442 0 : TABFeature *TABEllipse::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4443 : {
4444 : /*-----------------------------------------------------------------
4445 : * Alloc new feature and copy the base stuff
4446 : *----------------------------------------------------------------*/
4447 0 : TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
4448 :
4449 0 : CopyTABFeatureBase(poNew);
4450 :
4451 : /*-----------------------------------------------------------------
4452 : * And members specific to this class
4453 : *----------------------------------------------------------------*/
4454 : // ITABFeaturePen
4455 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4456 :
4457 : // ITABFeatureBrush
4458 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4459 :
4460 0 : poNew->m_dCenterX = m_dCenterX;
4461 0 : poNew->m_dCenterY = m_dCenterY;
4462 0 : poNew->m_dXRadius = m_dXRadius;
4463 0 : poNew->m_dYRadius = m_dYRadius;
4464 :
4465 0 : return poNew;
4466 : }
4467 :
4468 : /**********************************************************************
4469 : * TABEllipse::ValidateMapInfoType()
4470 : *
4471 : * Check the feature's geometry part and return the corresponding
4472 : * mapinfo object type code. The m_nMapInfoType member will also
4473 : * be updated for further calls to GetMapInfoType();
4474 : *
4475 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4476 : * is expected for this object class.
4477 : **********************************************************************/
4478 0 : TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4479 : {
4480 : /*-----------------------------------------------------------------
4481 : * Fetch and validate geometry
4482 : *----------------------------------------------------------------*/
4483 0 : OGRGeometry *poGeom = GetGeometryRef();
4484 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4485 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4486 : {
4487 0 : m_nMapInfoType = TAB_GEOM_ELLIPSE;
4488 : }
4489 : else
4490 : {
4491 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4492 : "TABEllipse: Missing or Invalid Geometry!");
4493 0 : m_nMapInfoType = TAB_GEOM_NONE;
4494 : }
4495 :
4496 : /*-----------------------------------------------------------------
4497 : * Decide if coordinates should be compressed or not.
4498 : *----------------------------------------------------------------*/
4499 : // __TODO__ For now we always write uncompressed for this class...
4500 : // ValidateCoordType(poMapFile);
4501 0 : UpdateMBR(poMapFile);
4502 :
4503 0 : return m_nMapInfoType;
4504 : }
4505 :
4506 : /**********************************************************************
4507 : * TABEllipse::UpdateMBR()
4508 : *
4509 : * Update the feature MBR members using the geometry
4510 : *
4511 : * Returns 0 on success, or -1 if there is no geometry in object
4512 : **********************************************************************/
4513 0 : int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4514 : {
4515 0 : OGREnvelope sEnvelope;
4516 :
4517 : /*-----------------------------------------------------------------
4518 : * Fetch and validate geometry... Polygon and point are accepted.
4519 : * Note that we will simply use the ellipse's MBR and don't really
4520 : * read the polygon geometry... this should be OK unless the
4521 : * polygon geometry was not really an ellipse.
4522 : *----------------------------------------------------------------*/
4523 0 : OGRGeometry *poGeom = GetGeometryRef();
4524 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4525 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4526 0 : poGeom->getEnvelope(&sEnvelope);
4527 : else
4528 : {
4529 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4530 : "TABEllipse: Missing or Invalid Geometry!");
4531 0 : return -1;
4532 : }
4533 :
4534 : /*-----------------------------------------------------------------
4535 : * We use the center of the MBR as the ellipse center, and the
4536 : * X/Y radius to define the MBR size. If X/Y radius are null then
4537 : * we'll try to use the MBR to recompute them.
4538 : *----------------------------------------------------------------*/
4539 0 : const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
4540 0 : const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
4541 0 : if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
4542 : {
4543 0 : m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
4544 0 : m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
4545 : }
4546 :
4547 0 : m_dXMin = dXCenter - m_dXRadius;
4548 0 : m_dYMin = dYCenter - m_dYRadius;
4549 0 : m_dXMax = dXCenter + m_dXRadius;
4550 0 : m_dYMax = dYCenter + m_dYRadius;
4551 :
4552 0 : if (poMapFile)
4553 : {
4554 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4555 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4556 : }
4557 :
4558 0 : return 0;
4559 : }
4560 :
4561 : /**********************************************************************
4562 : * TABEllipse::ReadGeometryFromMAPFile()
4563 : *
4564 : * Fill the geometry and representation (color, etc...) part of the
4565 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4566 : *
4567 : * It is assumed that poMAPFile currently points to the beginning of
4568 : * a map object.
4569 : *
4570 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4571 : * been called.
4572 : **********************************************************************/
4573 4 : int TABEllipse::ReadGeometryFromMAPFile(
4574 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4575 : GBool bCoordBlockDataOnly /*=FALSE*/,
4576 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4577 : {
4578 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4579 4 : if (bCoordBlockDataOnly)
4580 0 : return 0;
4581 :
4582 : /*-----------------------------------------------------------------
4583 : * Fetch and validate geometry type
4584 : *----------------------------------------------------------------*/
4585 4 : m_nMapInfoType = poObjHdr->m_nType;
4586 :
4587 4 : if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
4588 0 : m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
4589 : {
4590 0 : CPLError(
4591 : CE_Failure, CPLE_AssertionFailed,
4592 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4593 0 : m_nMapInfoType, m_nMapInfoType);
4594 0 : return -1;
4595 : }
4596 :
4597 : /*-----------------------------------------------------------------
4598 : * Read object information
4599 : *----------------------------------------------------------------*/
4600 : TABMAPObjRectEllipse *poRectHdr =
4601 4 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4602 :
4603 : // An ellipse is defined by its MBR
4604 :
4605 4 : double dXMin = 0.0;
4606 4 : double dYMin = 0.0;
4607 4 : double dXMax = 0.0;
4608 4 : double dYMax = 0.0;
4609 4 : poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4610 : dYMin);
4611 4 : poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4612 : dYMax);
4613 :
4614 4 : m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index
4615 4 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4616 :
4617 4 : m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index
4618 4 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4619 :
4620 : /*-----------------------------------------------------------------
4621 : * Save info about the ellipse def. inside class members
4622 : *----------------------------------------------------------------*/
4623 4 : m_dCenterX = (dXMin + dXMax) / 2.0;
4624 4 : m_dCenterY = (dYMin + dYMax) / 2.0;
4625 4 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
4626 4 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
4627 :
4628 4 : SetMBR(dXMin, dYMin, dXMax, dYMax);
4629 :
4630 4 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4631 : poObjHdr->m_nMaxY);
4632 :
4633 : /*-----------------------------------------------------------------
4634 : * Create and fill geometry object
4635 : *----------------------------------------------------------------*/
4636 4 : OGRPolygon *poPolygon = new OGRPolygon;
4637 4 : OGRLinearRing *poRing = new OGRLinearRing();
4638 :
4639 : /*-----------------------------------------------------------------
4640 : * For the OGR geometry, we generate an ellipse with 2 degrees line
4641 : * segments.
4642 : *----------------------------------------------------------------*/
4643 4 : TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
4644 : 0.0, 2.0 * M_PI);
4645 4 : TABCloseRing(poRing);
4646 :
4647 4 : poPolygon->addRingDirectly(poRing);
4648 4 : SetGeometryDirectly(poPolygon);
4649 :
4650 4 : return 0;
4651 : }
4652 :
4653 : /**********************************************************************
4654 : * TABEllipse::WriteGeometryToMAPFile()
4655 : *
4656 : * Write the geometry and representation (color, etc...) part of the
4657 : * feature to the .MAP object pointed to by poMAPFile.
4658 : *
4659 : * It is assumed that poMAPFile currently points to a valid map object.
4660 : *
4661 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4662 : * been called.
4663 : **********************************************************************/
4664 0 : int TABEllipse::WriteGeometryToMAPFile(
4665 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4666 : GBool bCoordBlockDataOnly /*=FALSE*/,
4667 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4668 : {
4669 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4670 0 : if (bCoordBlockDataOnly)
4671 0 : return 0;
4672 :
4673 : /*-----------------------------------------------------------------
4674 : * We assume that ValidateMapInfoType() was called already and that
4675 : * the type in poObjHdr->m_nType is valid.
4676 : *----------------------------------------------------------------*/
4677 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4678 :
4679 : /*-----------------------------------------------------------------
4680 : * Fetch and validate geometry... Polygon and point are accepted.
4681 : * Note that we will simply use the ellipse's MBR and don't really
4682 : * read the polygon geometry... this should be OK unless the
4683 : * polygon geometry was not really an ellipse.
4684 : *
4685 : * We use the center of the MBR as the ellipse center, and the
4686 : * X/Y radius to define the MBR size. If X/Y radius are null then
4687 : * we'll try to use the MBR to recompute them.
4688 : *----------------------------------------------------------------*/
4689 0 : if (UpdateMBR(poMapFile) != 0)
4690 0 : return -1; /* Error already reported */
4691 :
4692 : /*-----------------------------------------------------------------
4693 : * Copy object information
4694 : *----------------------------------------------------------------*/
4695 : TABMAPObjRectEllipse *poRectHdr =
4696 0 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4697 :
4698 : // Reset RoundRect Corner members... just in case (unused for ellipse)
4699 0 : poRectHdr->m_nCornerWidth = 0;
4700 0 : poRectHdr->m_nCornerHeight = 0;
4701 :
4702 : // An ellipse is defined by its MBR (values were set in UpdateMBR())
4703 0 : poRectHdr->m_nMinX = m_nXMin;
4704 0 : poRectHdr->m_nMinY = m_nYMin;
4705 0 : poRectHdr->m_nMaxX = m_nXMax;
4706 0 : poRectHdr->m_nMaxY = m_nYMax;
4707 :
4708 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4709 0 : poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
4710 :
4711 0 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4712 0 : poRectHdr->m_nBrushId =
4713 0 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
4714 :
4715 0 : if (CPLGetLastErrorType() == CE_Failure)
4716 0 : return -1;
4717 :
4718 0 : return 0;
4719 : }
4720 :
4721 : /**********************************************************************
4722 : * TABEllipse::GetStyleString() const
4723 : *
4724 : * Return style string for this feature.
4725 : *
4726 : * Style String is built only once during the first call to GetStyleString().
4727 : **********************************************************************/
4728 9 : const char *TABEllipse::GetStyleString() const
4729 : {
4730 9 : if (m_pszStyleString == nullptr)
4731 : {
4732 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4733 : // to use temporary buffers
4734 5 : char *pszPen = CPLStrdup(GetPenStyleString());
4735 5 : char *pszBrush = CPLStrdup(GetBrushStyleString());
4736 :
4737 5 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4738 :
4739 5 : CPLFree(pszPen);
4740 5 : CPLFree(pszBrush);
4741 : }
4742 :
4743 9 : return m_pszStyleString;
4744 : }
4745 :
4746 : /**********************************************************************
4747 : * TABEllipse::DumpMIF()
4748 : *
4749 : * Dump feature geometry in a format similar to .MIF REGIONs.
4750 : **********************************************************************/
4751 0 : void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
4752 : {
4753 0 : if (fpOut == nullptr)
4754 0 : fpOut = stdout;
4755 :
4756 : /*-----------------------------------------------------------------
4757 : * Output ELLIPSE parameters
4758 : *----------------------------------------------------------------*/
4759 0 : double dXMin = 0.0;
4760 0 : double dYMin = 0.0;
4761 0 : double dXMax = 0.0;
4762 0 : double dYMax = 0.0;
4763 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4764 0 : fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4765 : dYMax);
4766 :
4767 : /*-----------------------------------------------------------------
4768 : * Fetch and validate geometry
4769 : *----------------------------------------------------------------*/
4770 0 : OGRGeometry *poGeom = GetGeometryRef();
4771 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4772 : {
4773 : /*-------------------------------------------------------------
4774 : * Generate ellipse output as a region
4775 : * We could also output as an ELLIPSE in a real MIF generator
4776 : *------------------------------------------------------------*/
4777 0 : OGRPolygon *poPolygon = poGeom->toPolygon();
4778 0 : int numIntRings = poPolygon->getNumInteriorRings();
4779 0 : fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4780 : // In this loop, iRing=-1 for the outer ring.
4781 0 : for (int iRing = -1; iRing < numIntRings; iRing++)
4782 : {
4783 0 : OGRLinearRing *poRing = nullptr;
4784 :
4785 0 : if (iRing == -1)
4786 0 : poRing = poPolygon->getExteriorRing();
4787 : else
4788 0 : poRing = poPolygon->getInteriorRing(iRing);
4789 :
4790 0 : if (poRing == nullptr)
4791 : {
4792 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4793 : "TABEllipse: Object Geometry contains NULL rings!");
4794 0 : return;
4795 : }
4796 :
4797 0 : int numPoints = poRing->getNumPoints();
4798 0 : fprintf(fpOut, " %d\n", numPoints);
4799 0 : for (int i = 0; i < numPoints; i++)
4800 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4801 : poRing->getY(i));
4802 : }
4803 : }
4804 : else
4805 : {
4806 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4807 : "TABEllipse: Missing or Invalid Geometry!");
4808 0 : return;
4809 : }
4810 :
4811 : // Finish with PEN/BRUSH/etc. clauses
4812 0 : DumpPenDef();
4813 0 : DumpBrushDef();
4814 :
4815 0 : fflush(fpOut);
4816 : }
4817 :
4818 : /*=====================================================================
4819 : * class TABArc
4820 : *====================================================================*/
4821 :
4822 : /**********************************************************************
4823 : * TABArc::TABArc()
4824 : *
4825 : * Constructor.
4826 : **********************************************************************/
4827 680 : TABArc::TABArc(OGRFeatureDefn *poDefnIn)
4828 : : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
4829 680 : m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
4830 : {
4831 680 : }
4832 :
4833 : /**********************************************************************
4834 : * TABArc::~TABArc()
4835 : *
4836 : * Destructor.
4837 : **********************************************************************/
4838 1360 : TABArc::~TABArc()
4839 : {
4840 1360 : }
4841 :
4842 : /**********************************************************************
4843 : * TABArc::CloneTABFeature()
4844 : *
4845 : * Duplicate feature, including stuff specific to each TABFeature type.
4846 : *
4847 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
4848 : * then copies any members specific to its own type.
4849 : **********************************************************************/
4850 0 : TABFeature *TABArc::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4851 : {
4852 : /*-----------------------------------------------------------------
4853 : * Alloc new feature and copy the base stuff
4854 : *----------------------------------------------------------------*/
4855 0 : TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
4856 :
4857 0 : CopyTABFeatureBase(poNew);
4858 :
4859 : /*-----------------------------------------------------------------
4860 : * And members specific to this class
4861 : *----------------------------------------------------------------*/
4862 : // ITABFeaturePen
4863 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4864 :
4865 0 : poNew->SetStartAngle(GetStartAngle());
4866 0 : poNew->SetEndAngle(GetEndAngle());
4867 :
4868 0 : poNew->m_dCenterX = m_dCenterX;
4869 0 : poNew->m_dCenterY = m_dCenterY;
4870 0 : poNew->m_dXRadius = m_dXRadius;
4871 0 : poNew->m_dYRadius = m_dYRadius;
4872 :
4873 0 : return poNew;
4874 : }
4875 :
4876 : /**********************************************************************
4877 : * TABArc::ValidateMapInfoType()
4878 : *
4879 : * Check the feature's geometry part and return the corresponding
4880 : * mapinfo object type code. The m_nMapInfoType member will also
4881 : * be updated for further calls to GetMapInfoType();
4882 : *
4883 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4884 : * is expected for this object class.
4885 : **********************************************************************/
4886 0 : TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4887 : {
4888 : /*-----------------------------------------------------------------
4889 : * Fetch and validate geometry
4890 : *----------------------------------------------------------------*/
4891 0 : OGRGeometry *poGeom = GetGeometryRef();
4892 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
4893 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4894 : {
4895 0 : m_nMapInfoType = TAB_GEOM_ARC;
4896 : }
4897 : else
4898 : {
4899 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4900 : "TABArc: Missing or Invalid Geometry!");
4901 0 : m_nMapInfoType = TAB_GEOM_NONE;
4902 : }
4903 :
4904 : /*-----------------------------------------------------------------
4905 : * Decide if coordinates should be compressed or not.
4906 : *----------------------------------------------------------------*/
4907 : // __TODO__ For now we always write uncompressed for this class...
4908 : // ValidateCoordType(poMapFile);
4909 0 : UpdateMBR(poMapFile);
4910 :
4911 0 : return m_nMapInfoType;
4912 : }
4913 :
4914 : /**********************************************************************
4915 : * TABArc::UpdateMBR()
4916 : *
4917 : * Update the feature MBR members using the geometry
4918 : *
4919 : * Returns 0 on success, or -1 if there is no geometry in object
4920 : **********************************************************************/
4921 0 : int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4922 : {
4923 0 : OGREnvelope sEnvelope;
4924 :
4925 0 : OGRGeometry *poGeom = GetGeometryRef();
4926 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
4927 : {
4928 : /*-------------------------------------------------------------
4929 : * POLYGON geometry:
4930 : * Note that we will simply use the ellipse's MBR and don't really
4931 : * read the polygon geometry... this should be OK unless the
4932 : * polygon geometry was not really an ellipse.
4933 : * In the case of a polygon geometry. the m_dCenterX/Y values MUST
4934 : * have been set by the caller.
4935 : *------------------------------------------------------------*/
4936 0 : poGeom->getEnvelope(&sEnvelope);
4937 : }
4938 0 : else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4939 : {
4940 : /*-------------------------------------------------------------
4941 : * In the case of a POINT GEOMETRY, we will make sure the
4942 : * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
4943 : *
4944 : * In this case we have to reconstruct the arc inside a temporary
4945 : * geometry object in order to find its real MBR.
4946 : *------------------------------------------------------------*/
4947 0 : OGRPoint *poPoint = poGeom->toPoint();
4948 0 : m_dCenterX = poPoint->getX();
4949 0 : m_dCenterY = poPoint->getY();
4950 :
4951 0 : OGRLineString oTmpLine;
4952 0 : int numPts = 0;
4953 0 : if (m_dEndAngle < m_dStartAngle)
4954 0 : numPts = static_cast<int>(
4955 0 : std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
4956 : else
4957 0 : numPts = static_cast<int>(
4958 0 : std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
4959 0 : numPts = std::max(2, numPts);
4960 :
4961 0 : TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
4962 0 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
4963 0 : m_dEndAngle * M_PI / 180.0);
4964 :
4965 0 : oTmpLine.getEnvelope(&sEnvelope);
4966 : }
4967 : else
4968 : {
4969 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4970 : "TABArc: Missing or Invalid Geometry!");
4971 0 : return -1;
4972 : }
4973 :
4974 : // Update the Arc's MBR
4975 0 : m_dXMin = sEnvelope.MinX;
4976 0 : m_dYMin = sEnvelope.MinY;
4977 0 : m_dXMax = sEnvelope.MaxX;
4978 0 : m_dYMax = sEnvelope.MaxY;
4979 :
4980 0 : if (poMapFile)
4981 : {
4982 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4983 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4984 : }
4985 :
4986 0 : return 0;
4987 : }
4988 :
4989 : /**********************************************************************
4990 : * TABArc::ReadGeometryFromMAPFile()
4991 : *
4992 : * Fill the geometry and representation (color, etc...) part of the
4993 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4994 : *
4995 : * It is assumed that poMAPFile currently points to the beginning of
4996 : * a map object.
4997 : *
4998 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4999 : * been called.
5000 : **********************************************************************/
5001 8 : int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5002 : TABMAPObjHdr *poObjHdr,
5003 : GBool bCoordBlockDataOnly /*=FALSE*/,
5004 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5005 : {
5006 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5007 8 : if (bCoordBlockDataOnly)
5008 0 : return 0;
5009 :
5010 : /*-----------------------------------------------------------------
5011 : * Fetch and validate geometry type
5012 : *----------------------------------------------------------------*/
5013 8 : m_nMapInfoType = poObjHdr->m_nType;
5014 :
5015 8 : if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
5016 : {
5017 0 : CPLError(
5018 : CE_Failure, CPLE_AssertionFailed,
5019 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5020 0 : m_nMapInfoType, m_nMapInfoType);
5021 0 : return -1;
5022 : }
5023 :
5024 : /*-----------------------------------------------------------------
5025 : * Read object information
5026 : *----------------------------------------------------------------*/
5027 8 : TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5028 :
5029 : /*-------------------------------------------------------------
5030 : * Start/End angles
5031 : * Since the angles are specified for integer coordinates, and
5032 : * that these coordinates can have the X axis reversed, we have to
5033 : * adjust the angle values for the change in the X axis
5034 : * direction.
5035 : *
5036 : * This should be necessary only when X axis is flipped.
5037 : * __TODO__ Why is order of start/end values reversed as well???
5038 : *------------------------------------------------------------*/
5039 :
5040 : /*-------------------------------------------------------------
5041 : * OK, Arc angles again!!!!!!!!!!!!
5042 : * After some tests in 1999-11, it appeared that the angle values
5043 : * ALWAYS had to be flipped (read order= end angle followed by
5044 : * start angle), no matter which quadrant the file is in.
5045 : * This does not make any sense, so I suspect that there is something
5046 : * that we are missing here!
5047 : *
5048 : * 2000-01-14.... Again!!! Based on some sample data files:
5049 : * File Ver Quadr ReflXAxis Read_Order Adjust_Angle
5050 : * test_symb.tab 300 2 1 end,start X=yes Y=no
5051 : * alltypes.tab: 300 1 0 start,end X=no Y=no
5052 : * arcs.tab: 300 2 0 end,start X=yes Y=no
5053 : *
5054 : * Until we prove it wrong, the rule would be:
5055 : * -> Quadrant 1 and 3, angles order = start, end
5056 : * -> Quadrant 2 and 4, angles order = end, start
5057 : * + Always adjust angles for x and y axis based on quadrant.
5058 : *
5059 : * This was confirmed using some more files in which the quadrant was
5060 : * manually changed, but whether these are valid results is
5061 : * disputable.
5062 : *
5063 : * The ReflectXAxis flag seems to have no effect here...
5064 : *------------------------------------------------------------*/
5065 :
5066 : /*-------------------------------------------------------------
5067 : * In version 100 .tab files (version 400 .map), it is possible
5068 : * to have a quadrant value of 0 and it should be treated the
5069 : * same way as quadrant 3
5070 : *------------------------------------------------------------*/
5071 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
5072 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5073 0 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5074 : {
5075 : // Quadrants 1 and 3 ... read order = start, end
5076 8 : m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
5077 8 : m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
5078 : }
5079 : else
5080 : {
5081 : // Quadrants 2 and 4 ... read order = end, start
5082 0 : m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
5083 0 : m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
5084 : }
5085 :
5086 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
5087 16 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5088 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5089 : {
5090 : // X axis direction is flipped... adjust angle
5091 0 : m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
5092 0 : : (540.0 - m_dStartAngle);
5093 0 : m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
5094 0 : : (540.0 - m_dEndAngle);
5095 : }
5096 :
5097 8 : if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
5098 : {
5099 0 : CPLError(CE_Failure, CPLE_AppDefined,
5100 : "Wrong start and end angles: %f %f", m_dStartAngle,
5101 : m_dEndAngle);
5102 0 : return -1;
5103 : }
5104 :
5105 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5106 16 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
5107 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5108 : {
5109 : // Y axis direction is flipped... this reverses angle direction
5110 : // Unfortunately we never found any file that contains this case,
5111 : // but this should be the behavior to expect!!!
5112 : //
5113 : // 2000-01-14: some files in which quadrant was set to 3 and 4
5114 : // manually seemed to confirm that this is the right thing to do.
5115 0 : m_dStartAngle = 360.0 - m_dStartAngle;
5116 0 : m_dEndAngle = 360.0 - m_dEndAngle;
5117 : }
5118 :
5119 : // An arc is defined by its defining ellipse's MBR:
5120 :
5121 8 : double dXMin = 0.0;
5122 8 : double dYMin = 0.0;
5123 8 : double dXMax = 0.0;
5124 8 : double dYMax = 0.0;
5125 :
5126 8 : poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
5127 : poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
5128 8 : poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
5129 : poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
5130 :
5131 8 : m_dCenterX = (dXMin + dXMax) / 2.0;
5132 8 : m_dCenterY = (dYMin + dYMax) / 2.0;
5133 8 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
5134 8 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
5135 :
5136 : // Read the Arc's MBR and use that as this feature's MBR
5137 8 : poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
5138 8 : poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
5139 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
5140 :
5141 8 : m_nPenDefIndex = poArcHdr->m_nPenId; // Pen index
5142 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5143 :
5144 : /*-----------------------------------------------------------------
5145 : * Create and fill geometry object
5146 : * For the OGR geometry, we generate an arc with 2 degrees line
5147 : * segments.
5148 : *----------------------------------------------------------------*/
5149 8 : OGRLineString *poLine = new OGRLineString;
5150 :
5151 : const int numPts = std::max(
5152 16 : 2,
5153 8 : (m_dEndAngle < m_dStartAngle
5154 8 : ? static_cast<int>(
5155 0 : std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
5156 8 : : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
5157 8 : 1)));
5158 :
5159 8 : TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
5160 8 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
5161 8 : m_dEndAngle * M_PI / 180.0);
5162 :
5163 8 : SetGeometryDirectly(poLine);
5164 :
5165 8 : return 0;
5166 : }
5167 :
5168 : /**********************************************************************
5169 : * TABArc::WriteGeometryToMAPFile()
5170 : *
5171 : * Write the geometry and representation (color, etc...) part of the
5172 : * feature to the .MAP object pointed to by poMAPFile.
5173 : *
5174 : * It is assumed that poMAPFile currently points to a valid map object.
5175 : *
5176 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5177 : * been called.
5178 : **********************************************************************/
5179 0 : int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5180 : TABMAPObjHdr *poObjHdr,
5181 : GBool bCoordBlockDataOnly /*=FALSE*/,
5182 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5183 : {
5184 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5185 0 : if (bCoordBlockDataOnly)
5186 0 : return 0;
5187 :
5188 : /*-----------------------------------------------------------------
5189 : * We assume that ValidateMapInfoType() was called already and that
5190 : * the type in poObjHdr->m_nType is valid.
5191 : *----------------------------------------------------------------*/
5192 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5193 :
5194 : /*-----------------------------------------------------------------
5195 : * Fetch and validate geometry
5196 : * In the case of ARCs, this is all done inside UpdateMBR()
5197 : *----------------------------------------------------------------*/
5198 0 : if (UpdateMBR(poMapFile) != 0)
5199 0 : return -1; /* Error already reported */
5200 :
5201 : /*-----------------------------------------------------------------
5202 : * Copy object information
5203 : *----------------------------------------------------------------*/
5204 0 : TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5205 :
5206 : /*-------------------------------------------------------------
5207 : * Start/End angles
5208 : * Since we ALWAYS produce files in quadrant 1 then we can
5209 : * ignore the special angle conversion required by flipped axis.
5210 : *
5211 : * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
5212 : *------------------------------------------------------------*/
5213 0 : CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
5214 :
5215 0 : poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
5216 0 : poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
5217 :
5218 : // An arc is defined by its defining ellipse's MBR:
5219 0 : poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5220 0 : poArcHdr->m_nArcEllipseMinX,
5221 0 : poArcHdr->m_nArcEllipseMinY);
5222 0 : poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5223 0 : poArcHdr->m_nArcEllipseMaxX,
5224 0 : poArcHdr->m_nArcEllipseMaxY);
5225 :
5226 : // Pass the Arc's actual MBR (values were set in UpdateMBR())
5227 0 : poArcHdr->m_nMinX = m_nXMin;
5228 0 : poArcHdr->m_nMinY = m_nYMin;
5229 0 : poArcHdr->m_nMaxX = m_nXMax;
5230 0 : poArcHdr->m_nMaxY = m_nYMax;
5231 :
5232 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5233 0 : poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
5234 :
5235 0 : if (CPLGetLastErrorType() == CE_Failure)
5236 0 : return -1;
5237 :
5238 0 : return 0;
5239 : }
5240 :
5241 : /**********************************************************************
5242 : * TABArc::SetStart/EndAngle()
5243 : *
5244 : * Set the start/end angle values in degrees, making sure the values are
5245 : * always in the range [0..360]
5246 : **********************************************************************/
5247 0 : void TABArc::SetStartAngle(double dAngle)
5248 : {
5249 0 : dAngle = fmod(dAngle, 360.0);
5250 0 : if (dAngle < 0.0)
5251 0 : dAngle += 360.0;
5252 :
5253 0 : m_dStartAngle = dAngle;
5254 0 : }
5255 :
5256 0 : void TABArc::SetEndAngle(double dAngle)
5257 : {
5258 0 : dAngle = fmod(dAngle, 360.0);
5259 0 : if (dAngle < 0.0)
5260 0 : dAngle += 360.0;
5261 :
5262 0 : m_dEndAngle = dAngle;
5263 0 : }
5264 :
5265 : /**********************************************************************
5266 : * TABArc::GetStyleString() const
5267 : *
5268 : * Return style string for this feature.
5269 : *
5270 : * Style String is built only once during the first call to GetStyleString().
5271 : **********************************************************************/
5272 14 : const char *TABArc::GetStyleString() const
5273 : {
5274 14 : if (m_pszStyleString == nullptr)
5275 : {
5276 10 : m_pszStyleString = CPLStrdup(GetPenStyleString());
5277 : }
5278 :
5279 14 : return m_pszStyleString;
5280 : }
5281 :
5282 : /**********************************************************************
5283 : * TABArc::DumpMIF()
5284 : *
5285 : * Dump feature geometry in a format similar to .MIF REGIONs.
5286 : **********************************************************************/
5287 0 : void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
5288 : {
5289 0 : if (fpOut == nullptr)
5290 0 : fpOut = stdout;
5291 :
5292 : /*-----------------------------------------------------------------
5293 : * Output ARC parameters
5294 : *----------------------------------------------------------------*/
5295 0 : fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g %d %d)\n",
5296 0 : m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5297 0 : m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5298 0 : static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
5299 :
5300 : /*-----------------------------------------------------------------
5301 : * Fetch and validate geometry
5302 : *----------------------------------------------------------------*/
5303 0 : OGRGeometry *poGeom = GetGeometryRef();
5304 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
5305 : {
5306 : /*-------------------------------------------------------------
5307 : * Generate arc output as a simple polyline
5308 : * We could also output as an ELLIPSE in a real MIF generator
5309 : *------------------------------------------------------------*/
5310 0 : OGRLineString *poLine = poGeom->toLineString();
5311 0 : const int numPoints = poLine->getNumPoints();
5312 0 : fprintf(fpOut, "PLINE %d\n", numPoints);
5313 0 : for (int i = 0; i < numPoints; i++)
5314 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
5315 : }
5316 : else
5317 : {
5318 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5319 : "TABArc: Missing or Invalid Geometry!");
5320 0 : return;
5321 : }
5322 :
5323 : // Finish with PEN/BRUSH/etc. clauses
5324 0 : DumpPenDef();
5325 :
5326 0 : fflush(fpOut);
5327 : }
5328 :
5329 : /*=====================================================================
5330 : * class TABText
5331 : *====================================================================*/
5332 :
5333 : /**********************************************************************
5334 : * TABText::TABText()
5335 : *
5336 : * Constructor.
5337 : **********************************************************************/
5338 309 : TABText::TABText(OGRFeatureDefn *poDefnIn)
5339 : : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
5340 : m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
5341 : m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
5342 : m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
5343 309 : m_nFontStyle(0)
5344 : {
5345 309 : }
5346 :
5347 : /**********************************************************************
5348 : * TABText::~TABText()
5349 : *
5350 : * Destructor.
5351 : **********************************************************************/
5352 618 : TABText::~TABText()
5353 : {
5354 309 : CPLFree(m_pszString);
5355 618 : }
5356 :
5357 : /**********************************************************************
5358 : * TABText::CloneTABFeature()
5359 : *
5360 : * Duplicate feature, including stuff specific to each TABFeature type.
5361 : *
5362 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
5363 : * then copies any members specific to its own type.
5364 : **********************************************************************/
5365 0 : TABFeature *TABText::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
5366 : {
5367 : /*-----------------------------------------------------------------
5368 : * Alloc new feature and copy the base stuff
5369 : *----------------------------------------------------------------*/
5370 0 : TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
5371 :
5372 0 : CopyTABFeatureBase(poNew);
5373 :
5374 : /*-----------------------------------------------------------------
5375 : * And members specific to this class
5376 : *----------------------------------------------------------------*/
5377 : // ITABFeaturePen
5378 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
5379 :
5380 : // ITABFeatureFont
5381 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
5382 :
5383 0 : poNew->SetTextString(GetTextString());
5384 0 : poNew->SetTextAngle(GetTextAngle());
5385 0 : poNew->SetTextBoxHeight(GetTextBoxHeight());
5386 0 : poNew->SetTextBoxWidth(GetTextBoxWidth());
5387 0 : poNew->SetFontStyleTABValue(GetFontStyleTABValue());
5388 0 : poNew->SetFontBGColor(GetFontBGColor());
5389 0 : poNew->SetFontFGColor(GetFontFGColor());
5390 0 : poNew->SetFontOColor(GetFontOColor());
5391 0 : poNew->SetFontSColor(GetFontSColor());
5392 :
5393 0 : poNew->SetTextJustification(GetTextJustification());
5394 0 : poNew->SetTextSpacing(GetTextSpacing());
5395 : // Note: Text arrow/line coordinates are not transported... but
5396 : // we ignore them most of the time anyways.
5397 0 : poNew->SetTextLineType(TABTLNoLine);
5398 :
5399 0 : return poNew;
5400 : }
5401 :
5402 : /**********************************************************************
5403 : * TABText::ValidateMapInfoType()
5404 : *
5405 : * Check the feature's geometry part and return the corresponding
5406 : * mapinfo object type code. The m_nMapInfoType member will also
5407 : * be updated for further calls to GetMapInfoType();
5408 : *
5409 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
5410 : * is expected for this object class.
5411 : **********************************************************************/
5412 4 : TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
5413 : {
5414 : /*-----------------------------------------------------------------
5415 : * Fetch and validate geometry
5416 : *----------------------------------------------------------------*/
5417 4 : OGRGeometry *poGeom = GetGeometryRef();
5418 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5419 : {
5420 4 : m_nMapInfoType = TAB_GEOM_TEXT;
5421 : }
5422 : else
5423 : {
5424 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5425 : "TABText: Missing or Invalid Geometry!");
5426 0 : m_nMapInfoType = TAB_GEOM_NONE;
5427 : }
5428 :
5429 : /*-----------------------------------------------------------------
5430 : * Decide if coordinates should be compressed or not.
5431 : *----------------------------------------------------------------*/
5432 : // __TODO__ For now we always write uncompressed for this class...
5433 : // ValidateCoordType(poMapFile);
5434 4 : UpdateMBR(poMapFile);
5435 :
5436 4 : return m_nMapInfoType;
5437 : }
5438 :
5439 : /**********************************************************************
5440 : * TABText::ReadGeometryFromMAPFile()
5441 : *
5442 : * Fill the geometry and representation (color, etc...) part of the
5443 : * feature from the contents of the .MAP object pointed to by poMAPFile.
5444 : *
5445 : * It is assumed that poMAPFile currently points to the beginning of
5446 : * a map object.
5447 : *
5448 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5449 : * been called.
5450 : **********************************************************************/
5451 8 : int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5452 : TABMAPObjHdr *poObjHdr,
5453 : GBool bCoordBlockDataOnly /*=FALSE*/,
5454 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5455 : {
5456 : /*-----------------------------------------------------------------
5457 : * Fetch and validate geometry type
5458 : *----------------------------------------------------------------*/
5459 8 : m_nMapInfoType = poObjHdr->m_nType;
5460 :
5461 8 : if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
5462 : {
5463 0 : CPLError(
5464 : CE_Failure, CPLE_AssertionFailed,
5465 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5466 0 : m_nMapInfoType, m_nMapInfoType);
5467 0 : return -1;
5468 : }
5469 :
5470 : /*=============================================================
5471 : * TEXT
5472 : *============================================================*/
5473 :
5474 : /*-----------------------------------------------------------------
5475 : * Read object information
5476 : *----------------------------------------------------------------*/
5477 8 : TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5478 :
5479 8 : const GInt32 nCoordBlockPtr =
5480 : poTextHdr->m_nCoordBlockPtr; // String position
5481 8 : const int nStringLen = poTextHdr->m_nCoordDataSize; // String length
5482 8 : m_nTextAlignment = poTextHdr->m_nTextAlignment; // just./spacing/arrow
5483 :
5484 : /*-------------------------------------------------------------
5485 : * Text Angle, in tenths of degree.
5486 : * Contrary to arc start/end angles, no conversion based on
5487 : * origin quadrant is required here.
5488 : *------------------------------------------------------------*/
5489 8 : m_dAngle = poTextHdr->m_nAngle / 10.0;
5490 :
5491 8 : m_nFontStyle = poTextHdr->m_nFontStyle; // Font style
5492 :
5493 8 : m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
5494 8 : poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
5495 8 : m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
5496 8 : poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
5497 8 : m_rgbOutline = m_rgbBackground;
5498 : // In MapInfo, the shadow color is always gray (128,128,128)
5499 8 : m_rgbShadow = 0x808080;
5500 :
5501 : // arrow endpoint
5502 8 : poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
5503 8 : m_dfLineEndX, m_dfLineEndY);
5504 8 : m_bLineEndSet = TRUE;
5505 :
5506 : // Text Height
5507 8 : double dJunk = 0.0;
5508 8 : poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
5509 :
5510 8 : if (!bCoordBlockDataOnly)
5511 : {
5512 8 : m_nFontDefIndex = poTextHdr->m_nFontId; // Font name index
5513 8 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
5514 : }
5515 :
5516 : // MBR after rotation
5517 8 : double dXMin = 0.0;
5518 8 : double dYMin = 0.0;
5519 8 : double dXMax = 0.0;
5520 8 : double dYMax = 0.0;
5521 8 : poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
5522 : dYMin);
5523 8 : poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
5524 : dYMax);
5525 :
5526 8 : if (!bCoordBlockDataOnly)
5527 : {
5528 8 : m_nPenDefIndex = poTextHdr->m_nPenId; // Pen index for line
5529 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5530 : }
5531 :
5532 : /*-------------------------------------------------------------
5533 : * Read text string from the coord. block
5534 : * Note that the string may contain binary '\n' and '\\' chars
5535 : * that we keep to an unescaped form internally. This is to
5536 : * be like OGR drivers. See bug 1107 for details.
5537 : *------------------------------------------------------------*/
5538 : char *pszTmpString =
5539 8 : static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
5540 :
5541 8 : if (nStringLen > 0)
5542 : {
5543 8 : TABMAPCoordBlock *poCoordBlock = nullptr;
5544 :
5545 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5546 0 : poCoordBlock = *ppoCoordBlock;
5547 : else
5548 8 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
5549 16 : if (poCoordBlock == nullptr ||
5550 8 : poCoordBlock->ReadBytes(
5551 : nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
5552 : {
5553 0 : CPLError(CE_Failure, CPLE_FileIO,
5554 : "Failed reading text string at offset %d", nCoordBlockPtr);
5555 0 : CPLFree(pszTmpString);
5556 0 : return -1;
5557 : }
5558 :
5559 : /* Return a ref to coord block so that caller can continue reading
5560 : * after the end of this object (used by index splitting)
5561 : */
5562 8 : if (ppoCoordBlock)
5563 0 : *ppoCoordBlock = poCoordBlock;
5564 : }
5565 :
5566 8 : pszTmpString[nStringLen] = '\0';
5567 :
5568 8 : if (!poMapFile->GetEncoding().empty())
5569 : {
5570 : char *pszUtf8String =
5571 1 : CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
5572 1 : CPLFree(pszTmpString);
5573 1 : pszTmpString = pszUtf8String;
5574 : }
5575 :
5576 8 : CPLFree(m_pszString);
5577 8 : m_pszString = pszTmpString; // This string was Escaped before 20050714
5578 :
5579 : /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
5580 : */
5581 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
5582 8 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5583 :
5584 : /* Copy int MBR to feature class members */
5585 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
5586 : poObjHdr->m_nMaxY);
5587 :
5588 : /*-----------------------------------------------------------------
5589 : * Create an OGRPoint Geometry...
5590 : * The point X,Y values will be the coords of the lower-left corner before
5591 : * rotation is applied. (Note that the rotation in MapInfo is done around
5592 : * the upper-left corner)
5593 : * We need to calculate the true lower left corner of the text based
5594 : * on the MBR after rotation, the text height and the rotation angle.
5595 : *----------------------------------------------------------------*/
5596 8 : double dSin = sin(m_dAngle * M_PI / 180.0);
5597 8 : double dCos = cos(m_dAngle * M_PI / 180.0);
5598 8 : double dX = 0.0;
5599 8 : double dY = 0.0;
5600 8 : if (dSin > 0.0 && dCos > 0.0)
5601 : {
5602 7 : dX = dXMin + m_dHeight * dSin;
5603 7 : dY = dYMin;
5604 : }
5605 1 : else if (dSin > 0.0 && dCos < 0.0)
5606 : {
5607 0 : dX = dXMax;
5608 0 : dY = dYMin - m_dHeight * dCos;
5609 : }
5610 1 : else if (dSin < 0.0 && dCos < 0.0)
5611 : {
5612 0 : dX = dXMax + m_dHeight * dSin;
5613 0 : dY = dYMax;
5614 : }
5615 : else // dSin < 0 && dCos > 0
5616 : {
5617 1 : dX = dXMin;
5618 1 : dY = dYMax - m_dHeight * dCos;
5619 : }
5620 :
5621 8 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
5622 :
5623 8 : SetGeometryDirectly(poGeometry);
5624 :
5625 : /*-----------------------------------------------------------------
5626 : * Compute Text Width: the width of the Text MBR before rotation
5627 : * in ground units... unfortunately this value is not stored in the
5628 : * file, so we have to compute it with the MBR after rotation and
5629 : * the height of the MBR before rotation:
5630 : * With W = Width of MBR before rotation
5631 : * H = Height of MBR before rotation
5632 : * dX = Width of MBR after rotation
5633 : * dY = Height of MBR after rotation
5634 : * teta = rotation angle
5635 : *
5636 : * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
5637 : * W = H * (dX - H * sin(teta)) / (H * cos(teta))
5638 : *
5639 : * and for other teta values, use:
5640 : * W = H * (dY - H * cos(teta)) / (H * sin(teta))
5641 : *----------------------------------------------------------------*/
5642 8 : dSin = std::abs(dSin);
5643 8 : dCos = std::abs(dCos);
5644 8 : if (m_dHeight == 0.0)
5645 0 : m_dWidth = 0.0;
5646 8 : else if (dCos > dSin)
5647 8 : m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
5648 8 : (m_dHeight * dCos);
5649 : else
5650 0 : m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
5651 0 : (m_dHeight * dSin);
5652 8 : m_dWidth = std::abs(m_dWidth);
5653 :
5654 8 : return 0;
5655 : }
5656 :
5657 : /**********************************************************************
5658 : * TABText::WriteGeometryToMAPFile()
5659 : *
5660 : * Write the geometry and representation (color, etc...) part of the
5661 : * feature to the .MAP object pointed to by poMAPFile.
5662 : *
5663 : * It is assumed that poMAPFile currently points to a valid map object.
5664 : *
5665 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5666 : * been called.
5667 : **********************************************************************/
5668 4 : int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5669 : TABMAPObjHdr *poObjHdr,
5670 : GBool bCoordBlockDataOnly /*=FALSE*/,
5671 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5672 : {
5673 : GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
5674 :
5675 : /*-----------------------------------------------------------------
5676 : * We assume that ValidateMapInfoType() was called already and that
5677 : * the type in poObjHdr->m_nType is valid.
5678 : *----------------------------------------------------------------*/
5679 4 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5680 :
5681 : /*-----------------------------------------------------------------
5682 : * Fetch and validate geometry
5683 : *----------------------------------------------------------------*/
5684 4 : OGRGeometry *poGeom = GetGeometryRef();
5685 4 : OGRPoint *poPoint = nullptr;
5686 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5687 4 : poPoint = poGeom->toPoint();
5688 : else
5689 : {
5690 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5691 : "TABText: Missing or Invalid Geometry!");
5692 0 : return -1;
5693 : }
5694 :
5695 4 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
5696 :
5697 : /*-----------------------------------------------------------------
5698 : * Write string to a coord block first...
5699 : * Note that the string may contain unescaped '\n' and '\\'
5700 : * that we have to keep like that for the MAP file.
5701 : * See MapTools bug 1107 for more details.
5702 : *----------------------------------------------------------------*/
5703 4 : TABMAPCoordBlock *poCoordBlock = nullptr;
5704 4 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5705 0 : poCoordBlock = *ppoCoordBlock;
5706 : else
5707 4 : poCoordBlock = poMapFile->GetCurCoordBlock();
5708 4 : poCoordBlock->StartNewFeature();
5709 4 : GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
5710 :
5711 : // This string was escaped before 20050714
5712 8 : CPLString oTmpString(m_pszString ? m_pszString : "");
5713 4 : if (!poMapFile->GetEncoding().empty())
5714 : {
5715 0 : oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
5716 : }
5717 :
5718 4 : int nStringLen = static_cast<int>(oTmpString.length());
5719 :
5720 4 : if (nStringLen > 0)
5721 : {
5722 3 : poCoordBlock->WriteBytes(
5723 3 : nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
5724 : }
5725 : else
5726 : {
5727 1 : nCoordBlockPtr = 0;
5728 : }
5729 :
5730 : /*-----------------------------------------------------------------
5731 : * Copy object information
5732 : *----------------------------------------------------------------*/
5733 4 : TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5734 :
5735 4 : poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr; // String position
5736 4 : poTextHdr->m_nCoordDataSize = nStringLen; // String length
5737 4 : poTextHdr->m_nTextAlignment = m_nTextAlignment; // just./spacing/arrow
5738 :
5739 : /*-----------------------------------------------------------------
5740 : * Text Angle, (written in tenths of degrees)
5741 : * Contrary to arc start/end angles, no conversion based on
5742 : * origin quadrant is required here.
5743 : *----------------------------------------------------------------*/
5744 4 : poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
5745 :
5746 4 : poTextHdr->m_nFontStyle = m_nFontStyle; // Font style/effect
5747 :
5748 4 : poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
5749 4 : poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
5750 4 : poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
5751 :
5752 4 : poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
5753 4 : poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
5754 4 : poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
5755 :
5756 : /*-----------------------------------------------------------------
5757 : * The OGRPoint's X,Y values were the coords of the lower-left corner
5758 : * before rotation was applied. (Note that the rotation in MapInfo is
5759 : * done around the upper-left corner)
5760 : * The Feature's MBR is the MBR of the text after rotation... that's
5761 : * what MapInfo uses to define the text location.
5762 : *----------------------------------------------------------------*/
5763 4 : double dXMin = 0.0;
5764 4 : double dYMin = 0.0;
5765 4 : double dXMax = 0.0;
5766 4 : double dYMax = 0.0;
5767 : // Make sure Feature MBR is in sync with other params
5768 :
5769 4 : UpdateMBR();
5770 4 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5771 :
5772 4 : poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
5773 4 : poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
5774 :
5775 : // Label line end point
5776 4 : double dX = 0.0;
5777 4 : double dY = 0.0;
5778 4 : GetTextLineEndPoint(dX, dY); // Make sure a default line end point is set
5779 4 : poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
5780 4 : poTextHdr->m_nLineEndY);
5781 :
5782 : // Text Height
5783 4 : poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
5784 4 : poTextHdr->m_nHeight = nY;
5785 :
5786 4 : if (!bCoordBlockDataOnly)
5787 : {
5788 : // Font name
5789 4 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
5790 4 : poTextHdr->m_nFontId =
5791 4 : static_cast<GByte>(m_nFontDefIndex); // Font name index
5792 : }
5793 :
5794 : // MBR after rotation
5795 4 : poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
5796 :
5797 4 : if (!bCoordBlockDataOnly)
5798 : {
5799 4 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5800 4 : poTextHdr->m_nPenId =
5801 4 : static_cast<GByte>(m_nPenDefIndex); // Pen index for line/arrow
5802 : }
5803 :
5804 4 : if (CPLGetLastErrorType() == CE_Failure)
5805 0 : return -1;
5806 :
5807 : /* Return a ref to coord block so that caller can continue writing
5808 : * after the end of this object (used by index splitting)
5809 : */
5810 4 : if (ppoCoordBlock)
5811 0 : *ppoCoordBlock = poCoordBlock;
5812 :
5813 4 : return 0;
5814 : }
5815 :
5816 : /**********************************************************************
5817 : * TABText::GetTextString()
5818 : *
5819 : * Return ref to text string value.
5820 : *
5821 : * Returned string is a reference to the internal string buffer and should
5822 : * not be modified or freed by the caller.
5823 : **********************************************************************/
5824 300 : const char *TABText::GetTextString() const
5825 : {
5826 300 : if (m_pszString == nullptr)
5827 0 : return "";
5828 :
5829 300 : return m_pszString;
5830 : }
5831 :
5832 : /**********************************************************************
5833 : * TABText::SetTextString()
5834 : *
5835 : * Set new text string value.
5836 : *
5837 : * Note: The text string may contain "\n" chars or "\\" chars
5838 : * and we expect to receive them in a 2 chars escaped form as
5839 : * described in the MIF format specs.
5840 : **********************************************************************/
5841 3 : void TABText::SetTextString(const char *pszNewStr)
5842 : {
5843 3 : CPLFree(m_pszString);
5844 3 : m_pszString = CPLStrdup(pszNewStr);
5845 3 : }
5846 :
5847 : /**********************************************************************
5848 : * TABText::GetTextAngle()
5849 : *
5850 : * Return text angle in degrees.
5851 : **********************************************************************/
5852 10 : double TABText::GetTextAngle() const
5853 : {
5854 10 : return m_dAngle;
5855 : }
5856 :
5857 211 : void TABText::SetTextAngle(double dAngle)
5858 : {
5859 : // Make sure angle is in the range [0..360]
5860 211 : dAngle = fmod(dAngle, 360.0);
5861 211 : if (dAngle < 0.0)
5862 0 : dAngle += 360.0;
5863 211 : m_dAngle = dAngle;
5864 211 : UpdateMBR();
5865 211 : }
5866 :
5867 : /**********************************************************************
5868 : * TABText::GetTextBoxHeight()
5869 : *
5870 : * Return text height in Y axis coord. units of the text box before rotation.
5871 : **********************************************************************/
5872 10 : double TABText::GetTextBoxHeight() const
5873 : {
5874 10 : return m_dHeight;
5875 : }
5876 :
5877 3 : void TABText::SetTextBoxHeight(double dHeight)
5878 : {
5879 3 : m_dHeight = dHeight;
5880 3 : UpdateMBR();
5881 3 : }
5882 :
5883 : /**********************************************************************
5884 : * TABText::GetTextBoxWidth()
5885 : *
5886 : * Return text width in X axis coord. units. of the text box before rotation.
5887 : *
5888 : * If value has not been set, then we force a default value that assumes
5889 : * that one char's box width is 60% of its height... and we ignore
5890 : * the multiline case. This should not matter when the user PROPERLY sets
5891 : * the value.
5892 : **********************************************************************/
5893 12 : double TABText::GetTextBoxWidth() const
5894 : {
5895 12 : if (m_dWidth == 0.0 && m_pszString)
5896 : {
5897 3 : m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
5898 : }
5899 12 : return m_dWidth;
5900 : }
5901 :
5902 0 : void TABText::SetTextBoxWidth(double dWidth)
5903 : {
5904 0 : m_dWidth = dWidth;
5905 0 : UpdateMBR();
5906 0 : }
5907 :
5908 : /**********************************************************************
5909 : * TABText::GetTextLineEndPoint()
5910 : *
5911 : * Return X,Y coordinates of the text label line end point.
5912 : * Default is the center of the text MBR.
5913 : **********************************************************************/
5914 4 : void TABText::GetTextLineEndPoint(double &dX, double &dY)
5915 : {
5916 4 : if (!m_bLineEndSet)
5917 : {
5918 : // Set default location at center of text MBR
5919 4 : double dXMin = 0.0;
5920 4 : double dYMin = 0.0;
5921 4 : double dXMax = 0.0;
5922 4 : double dYMax = 0.0;
5923 4 : UpdateMBR();
5924 4 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5925 4 : m_dfLineEndX = (dXMin + dXMax) / 2.0;
5926 4 : m_dfLineEndY = (dYMin + dYMax) / 2.0;
5927 4 : m_bLineEndSet = TRUE;
5928 : }
5929 :
5930 : // Return values
5931 4 : dX = m_dfLineEndX;
5932 4 : dY = m_dfLineEndY;
5933 4 : }
5934 :
5935 178 : void TABText::SetTextLineEndPoint(double dX, double dY)
5936 : {
5937 178 : m_dfLineEndX = dX;
5938 178 : m_dfLineEndY = dY;
5939 178 : m_bLineEndSet = TRUE;
5940 178 : }
5941 :
5942 : /**********************************************************************
5943 : * TABText::UpdateMBR()
5944 : *
5945 : * Update the feature MBR using the text origin (OGRPoint geometry), the
5946 : * rotation angle, and the Width/height before rotation.
5947 : *
5948 : * This function cannot perform properly unless all the above have been set.
5949 : *
5950 : * Returns 0 on success, or -1 if there is no geometry in object
5951 : **********************************************************************/
5952 226 : int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
5953 : {
5954 226 : OGRGeometry *poGeom = GetGeometryRef();
5955 226 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5956 : {
5957 12 : OGRPoint *poPoint = poGeom->toPoint();
5958 :
5959 12 : const double dX0 = poPoint->getX();
5960 12 : const double dY0 = poPoint->getY();
5961 :
5962 12 : const double dSin = sin(m_dAngle * M_PI / 180.0);
5963 12 : const double dCos = cos(m_dAngle * M_PI / 180.0);
5964 :
5965 12 : GetTextBoxWidth(); // Force default width value if necessary.
5966 :
5967 12 : const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
5968 12 : const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
5969 :
5970 12 : SetMBR(dX0, dY0, dX0, dY0);
5971 60 : for (int i = 0; i < 4; i++)
5972 : {
5973 : // Rotate one of the box corners
5974 48 : const double dX1 =
5975 48 : dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
5976 48 : const double dY1 =
5977 48 : dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
5978 :
5979 : // And update feature MBR with rotated coordinate
5980 48 : if (dX1 < m_dXMin)
5981 9 : m_dXMin = dX1;
5982 48 : if (dX1 > m_dXMax)
5983 9 : m_dXMax = dX1;
5984 48 : if (dY1 < m_dYMin)
5985 0 : m_dYMin = dY1;
5986 48 : if (dY1 > m_dYMax)
5987 18 : m_dYMax = dY1;
5988 : }
5989 :
5990 12 : if (poMapFile)
5991 : {
5992 4 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
5993 4 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
5994 : }
5995 :
5996 12 : return 0;
5997 : }
5998 :
5999 214 : return -1;
6000 : }
6001 :
6002 : /**********************************************************************
6003 : * TABText::GetFontBGColor()
6004 : *
6005 : * Return background color.
6006 : **********************************************************************/
6007 8 : GInt32 TABText::GetFontBGColor() const
6008 : {
6009 8 : return m_rgbBackground;
6010 : }
6011 :
6012 258 : void TABText::SetFontBGColor(GInt32 rgbColor)
6013 : {
6014 258 : m_rgbBackground = rgbColor;
6015 258 : }
6016 :
6017 : /**********************************************************************
6018 : * TABText::GetFontOColor()
6019 : *
6020 : * Return outline color.
6021 : **********************************************************************/
6022 1 : GInt32 TABText::GetFontOColor() const
6023 : {
6024 1 : return m_rgbOutline;
6025 : }
6026 :
6027 1 : void TABText::SetFontOColor(GInt32 rgbColor)
6028 : {
6029 1 : m_rgbOutline = rgbColor;
6030 1 : }
6031 :
6032 : /**********************************************************************
6033 : * TABText::GetFontSColor()
6034 : *
6035 : * Return shadow color.
6036 : **********************************************************************/
6037 0 : GInt32 TABText::GetFontSColor() const
6038 : {
6039 0 : return m_rgbShadow;
6040 : }
6041 :
6042 0 : void TABText::SetFontSColor(GInt32 rgbColor)
6043 : {
6044 0 : m_rgbShadow = rgbColor;
6045 0 : }
6046 :
6047 : /**********************************************************************
6048 : * TABText::GetFontFGColor()
6049 : *
6050 : * Return foreground color.
6051 : **********************************************************************/
6052 10 : GInt32 TABText::GetFontFGColor() const
6053 : {
6054 10 : return m_rgbForeground;
6055 : }
6056 :
6057 265 : void TABText::SetFontFGColor(GInt32 rgbColor)
6058 : {
6059 265 : m_rgbForeground = rgbColor;
6060 265 : }
6061 :
6062 : /**********************************************************************
6063 : * TABText::GetTextJustification()
6064 : *
6065 : * Return text justification. Default is TABTJLeft
6066 : **********************************************************************/
6067 10 : TABTextJust TABText::GetTextJustification() const
6068 : {
6069 10 : TABTextJust eJust = TABTJLeft;
6070 :
6071 10 : if (m_nTextAlignment & 0x0200)
6072 9 : eJust = TABTJCenter;
6073 1 : else if (m_nTextAlignment & 0x0400)
6074 0 : eJust = TABTJRight;
6075 :
6076 10 : return eJust;
6077 : }
6078 :
6079 222 : void TABText::SetTextJustification(TABTextJust eJustification)
6080 : {
6081 : // Flush current value... default is TABTJLeft
6082 222 : m_nTextAlignment &= ~0x0600;
6083 : // ... and set new one.
6084 222 : if (eJustification == TABTJCenter)
6085 222 : m_nTextAlignment |= 0x0200;
6086 0 : else if (eJustification == TABTJRight)
6087 0 : m_nTextAlignment |= 0x0400;
6088 222 : }
6089 :
6090 : /**********************************************************************
6091 : * TABText::GetTextSpacing()
6092 : *
6093 : * Return text vertical spacing factor. Default is TABTSSingle
6094 : **********************************************************************/
6095 0 : TABTextSpacing TABText::GetTextSpacing() const
6096 : {
6097 0 : TABTextSpacing eSpacing = TABTSSingle;
6098 :
6099 0 : if (m_nTextAlignment & 0x0800)
6100 0 : eSpacing = TABTS1_5;
6101 0 : else if (m_nTextAlignment & 0x1000)
6102 0 : eSpacing = TABTSDouble;
6103 :
6104 0 : return eSpacing;
6105 : }
6106 :
6107 237 : void TABText::SetTextSpacing(TABTextSpacing eSpacing)
6108 : {
6109 : // Flush current value... default is TABTSSingle
6110 237 : m_nTextAlignment &= ~0x1800;
6111 : // ... and set new one.
6112 237 : if (eSpacing == TABTS1_5)
6113 0 : m_nTextAlignment |= 0x0800;
6114 237 : else if (eSpacing == TABTSDouble)
6115 237 : m_nTextAlignment |= 0x1000;
6116 237 : }
6117 :
6118 : /**********************************************************************
6119 : * TABText::GetTextLineType()
6120 : *
6121 : * Return text line (arrow) type. Default is TABTLNoLine
6122 : **********************************************************************/
6123 0 : TABTextLineType TABText::GetTextLineType() const
6124 : {
6125 0 : TABTextLineType eLine = TABTLNoLine;
6126 :
6127 0 : if (m_nTextAlignment & 0x2000)
6128 0 : eLine = TABTLSimple;
6129 0 : else if (m_nTextAlignment & 0x4000)
6130 0 : eLine = TABTLArrow;
6131 :
6132 0 : return eLine;
6133 : }
6134 :
6135 178 : void TABText::SetTextLineType(TABTextLineType eLineType)
6136 : {
6137 : // Flush current value... default is TABTLNoLine
6138 178 : m_nTextAlignment &= ~0x6000;
6139 : // ... and set new one.
6140 178 : if (eLineType == TABTLSimple)
6141 178 : m_nTextAlignment |= 0x2000;
6142 0 : else if (eLineType == TABTLArrow)
6143 0 : m_nTextAlignment |= 0x4000;
6144 178 : }
6145 :
6146 : /**********************************************************************
6147 : * TABText::QueryFontStyle()
6148 : *
6149 : * Return TRUE if the specified font style attribute is turned ON,
6150 : * or FALSE otherwise. See enum TABFontStyle for the list of styles
6151 : * that can be queried on.
6152 : **********************************************************************/
6153 385 : GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
6154 : {
6155 385 : return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
6156 : }
6157 :
6158 264 : void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
6159 : {
6160 264 : if (bStyleOn)
6161 264 : m_nFontStyle |= static_cast<int>(eStyleToToggle);
6162 : else
6163 0 : m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
6164 264 : }
6165 :
6166 : /**********************************************************************
6167 : * TABText::GetFontStyleMIFValue()
6168 : *
6169 : * Return the Font Style value for this object using the style values
6170 : * that are used in a MIF FONT() clause. See MIF specs (appendix A).
6171 : *
6172 : * The reason why we have to differentiate between the TAB and the MIF font
6173 : * style values is that in TAB, TABFSBox is included in the style value
6174 : * as code 0x100, but in MIF it is not included, instead it is implied by
6175 : * the presence of the BG color in the FONT() clause (the BG color is
6176 : * present only when TABFSBox or TABFSHalo is set).
6177 : * This also has the effect of shifting all the other style values > 0x100
6178 : * by 1 byte.
6179 : **********************************************************************/
6180 0 : int TABText::GetFontStyleMIFValue() const
6181 : {
6182 : // The conversion is simply to remove bit 0x100 from the value and shift
6183 : // down all values past this bit.
6184 0 : return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
6185 : }
6186 :
6187 261 : void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
6188 : {
6189 261 : m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
6190 : // When BG color is set, then either BOX or HALO should be set.
6191 261 : if (bBGColorSet && !QueryFontStyle(TABFSHalo))
6192 254 : ToggleFontStyle(TABFSBox, TRUE);
6193 261 : }
6194 :
6195 10 : int TABText::IsFontBGColorUsed() const
6196 : {
6197 : // Font BG color is used only when BOX is set.
6198 10 : return QueryFontStyle(TABFSBox);
6199 : }
6200 :
6201 10 : int TABText::IsFontOColorUsed() const
6202 : {
6203 : // Font outline color is used only when HALO is set.
6204 10 : return QueryFontStyle(TABFSHalo);
6205 : }
6206 :
6207 10 : int TABText::IsFontSColorUsed() const
6208 : {
6209 : // Font shadow color is used only when Shadow is set.
6210 10 : return QueryFontStyle(TABFSShadow);
6211 : }
6212 :
6213 10 : int TABText::IsFontBold() const
6214 : {
6215 : // Font bold is used only when Bold is set.
6216 10 : return QueryFontStyle(TABFSBold);
6217 : }
6218 :
6219 10 : int TABText::IsFontItalic() const
6220 : {
6221 : // Font italic is used only when Italic is set.
6222 10 : return QueryFontStyle(TABFSItalic);
6223 : }
6224 :
6225 10 : int TABText::IsFontUnderline() const
6226 : {
6227 : // Font underline is used only when Underline is set.
6228 10 : return QueryFontStyle(TABFSUnderline);
6229 : }
6230 :
6231 : /**********************************************************************
6232 : * TABText::GetLabelStyleString()
6233 : *
6234 : * This is not the correct location, it should be in ITABFeatureFont,
6235 : * but it is really more easy to put it here. This fct return a complete
6236 : * string for the representation with the string to display
6237 : **********************************************************************/
6238 10 : const char *TABText::GetLabelStyleString() const
6239 : {
6240 10 : const char *pszStyle = nullptr;
6241 10 : int nStringLen = static_cast<int>(strlen(GetTextString()));
6242 : // ALL Caps, Extpanded need to modify the string value
6243 : char *pszTextString =
6244 10 : static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
6245 : /* char szPattern[20]; */
6246 10 : int nJustification = 1;
6247 :
6248 10 : strcpy(pszTextString, GetTextString());
6249 : /* szPattern[0] = '\0'; */
6250 :
6251 10 : switch (GetTextJustification())
6252 : {
6253 9 : case TABTJCenter:
6254 9 : nJustification = 2;
6255 9 : break;
6256 0 : case TABTJRight:
6257 0 : nJustification = 3;
6258 0 : break;
6259 1 : case TABTJLeft:
6260 : default:
6261 1 : nJustification = 1;
6262 1 : break;
6263 : }
6264 :
6265 : // Compute real font size, taking number of lines ("\\n", "\n") and line
6266 : // spacing into account.
6267 10 : int numLines = 1;
6268 61 : for (int i = 0; pszTextString[i];
6269 51 : numLines +=
6270 102 : ((pszTextString[i] == '\n' ||
6271 51 : (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
6272 0 : pszTextString[i + 1] != '\0'),
6273 : ++i)
6274 : ;
6275 :
6276 10 : double dHeight = GetTextBoxHeight() / numLines;
6277 :
6278 : // In all cases, take out 20% of font height to account for line spacing
6279 10 : if (numLines > 1)
6280 : {
6281 0 : switch (GetTextSpacing())
6282 : {
6283 0 : case TABTS1_5:
6284 0 : dHeight *= (0.80 * 0.69);
6285 0 : break;
6286 0 : case TABTSDouble:
6287 0 : dHeight *= (0.66 * 0.69);
6288 0 : break;
6289 0 : default:
6290 0 : dHeight *= 0.69;
6291 : }
6292 : }
6293 : else
6294 : {
6295 10 : dHeight *= 0.69;
6296 : }
6297 :
6298 10 : if (QueryFontStyle(TABFSAllCaps))
6299 0 : for (int i = 0; pszTextString[i]; ++i)
6300 0 : if (isalpha(static_cast<unsigned char>(pszTextString[i])))
6301 0 : pszTextString[i] = static_cast<char>(
6302 0 : CPLToupper(static_cast<unsigned char>(pszTextString[i])));
6303 :
6304 : /* Escape the double quote chars and expand the text */
6305 10 : char *pszTmpTextString = nullptr;
6306 :
6307 10 : if (QueryFontStyle(TABFSExpanded))
6308 : pszTmpTextString = static_cast<char *>(
6309 0 : CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
6310 : else
6311 : pszTmpTextString = static_cast<char *>(
6312 10 : CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
6313 :
6314 10 : int j = 0;
6315 61 : for (int i = 0; i < nStringLen; ++i, ++j)
6316 : {
6317 51 : if (pszTextString[i] == '"')
6318 : {
6319 0 : pszTmpTextString[j] = '\\';
6320 0 : pszTmpTextString[j + 1] = pszTextString[i];
6321 0 : ++j;
6322 : }
6323 : else
6324 51 : pszTmpTextString[j] = pszTextString[i];
6325 :
6326 51 : if (QueryFontStyle(TABFSExpanded))
6327 : {
6328 0 : pszTmpTextString[j + 1] = ' ';
6329 0 : ++j;
6330 : }
6331 : }
6332 :
6333 10 : pszTmpTextString[j] = '\0';
6334 10 : CPLFree(pszTextString);
6335 : pszTextString = static_cast<char *>(
6336 10 : CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
6337 10 : strcpy(pszTextString, pszTmpTextString);
6338 10 : CPLFree(pszTmpTextString);
6339 :
6340 : const char *pszBGColor =
6341 10 : IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
6342 : const char *pszOColor =
6343 10 : IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
6344 : const char *pszSColor =
6345 10 : IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
6346 10 : const char *pszBold = IsFontBold() ? ",bo:1" : "";
6347 10 : const char *pszItalic = IsFontItalic() ? ",it:1" : "";
6348 10 : const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
6349 :
6350 10 : pszStyle = CPLSPrintf(
6351 : "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
6352 : pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
6353 : pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
6354 : GetFontNameRef());
6355 :
6356 10 : CPLFree(pszTextString);
6357 10 : return pszStyle;
6358 : }
6359 :
6360 : /**********************************************************************
6361 : * TABText::GetStyleString() const
6362 : *
6363 : * Return style string for this feature.
6364 : *
6365 : * Style String is built only once during the first call to GetStyleString().
6366 : **********************************************************************/
6367 10 : const char *TABText::GetStyleString() const
6368 : {
6369 10 : if (m_pszStyleString == nullptr)
6370 : {
6371 10 : m_pszStyleString = CPLStrdup(GetLabelStyleString());
6372 : }
6373 :
6374 10 : return m_pszStyleString;
6375 : }
6376 :
6377 4 : void TABText::SetLabelFromStyleString(const char *pszStyleString)
6378 : {
6379 : // Use the Style Manager to retrieve all the information we need.
6380 4 : auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
6381 0 : std::unique_ptr<OGRStyleTool> poStylePart;
6382 :
6383 : // Init the StyleMgr with the StyleString.
6384 4 : poStyleMgr->InitStyleString(pszStyleString);
6385 :
6386 : // Retrieve the Symbol info.
6387 4 : const int numParts = poStyleMgr->GetPartCount();
6388 4 : for (int i = 0; i < numParts; i++)
6389 : {
6390 4 : poStylePart.reset(poStyleMgr->GetPart(i));
6391 4 : if (poStylePart == nullptr)
6392 : {
6393 0 : continue;
6394 : }
6395 :
6396 4 : if (poStylePart->GetType() == OGRSTCLabel)
6397 : {
6398 4 : break;
6399 : }
6400 : else
6401 : {
6402 0 : poStylePart.reset();
6403 : }
6404 : }
6405 :
6406 : // If the no Symbol found, do nothing.
6407 4 : if (poStylePart == nullptr)
6408 : {
6409 0 : return;
6410 : }
6411 :
6412 4 : auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
6413 :
6414 4 : GBool bIsNull = 0;
6415 4 : const char *pszText = poLabelStyle->TextString(bIsNull);
6416 4 : if (!bIsNull && pszText)
6417 : {
6418 3 : SetTextString(pszText);
6419 :
6420 3 : poLabelStyle->SetUnit(OGRSTUMM);
6421 3 : double dfSize = poLabelStyle->Size(bIsNull);
6422 3 : if (!bIsNull)
6423 : {
6424 3 : dfSize /= 1000;
6425 :
6426 : // Compute text box height, taking number of lines ("\\n", "\n") and
6427 : // line spacing into account.
6428 3 : int numLines = 1;
6429 18 : for (int i = 0; pszText[i];
6430 45 : numLines += ((pszText[i] == '\n' ||
6431 15 : (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
6432 0 : pszText[i + 1] != '\0'),
6433 : ++i)
6434 : ;
6435 :
6436 : // Cf GetLabelStyleString() for 0.69. We should likely also take
6437 : // into account line spacing if we knew how to compute it.
6438 3 : SetTextBoxHeight(dfSize / 0.69 * numLines);
6439 : }
6440 : }
6441 :
6442 4 : if (poLabelStyle->Bold(bIsNull))
6443 3 : ToggleFontStyle(TABFSBold, true);
6444 :
6445 4 : if (poLabelStyle->Italic(bIsNull))
6446 1 : ToggleFontStyle(TABFSItalic, true);
6447 :
6448 4 : if (poLabelStyle->Underline(bIsNull))
6449 1 : ToggleFontStyle(TABFSUnderline, true);
6450 :
6451 4 : const char *pszFontName = poLabelStyle->FontName(bIsNull);
6452 4 : if (!bIsNull && pszFontName)
6453 4 : SetFontName(pszFontName);
6454 :
6455 : // Set the ForeColor
6456 4 : const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
6457 4 : if (bIsNull)
6458 0 : pszForeColor = nullptr;
6459 4 : if (pszForeColor)
6460 : {
6461 4 : if (pszForeColor[0] == '#')
6462 4 : pszForeColor++;
6463 8 : CPLString osForeColor(pszForeColor);
6464 4 : if (strlen(pszForeColor) > 6)
6465 1 : osForeColor.resize(6);
6466 4 : const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
6467 4 : SetFontFGColor(static_cast<GInt32>(nColor));
6468 : }
6469 :
6470 : // Set the BackgroundColor
6471 4 : const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
6472 4 : if (bIsNull)
6473 0 : pszBackColor = nullptr;
6474 4 : if (pszBackColor)
6475 : {
6476 4 : if (pszBackColor[0] == '#')
6477 4 : pszBackColor++;
6478 8 : CPLString osBackColor(pszBackColor);
6479 4 : if (strlen(pszBackColor) > 6)
6480 1 : osBackColor.resize(6);
6481 4 : const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
6482 4 : ToggleFontStyle(TABFSBox, true);
6483 4 : SetFontBGColor(static_cast<GInt32>(nColor));
6484 : }
6485 :
6486 : // Set the OutlineColor
6487 4 : const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
6488 4 : if (bIsNull)
6489 3 : pszOutlineColor = nullptr;
6490 4 : if (pszOutlineColor)
6491 : {
6492 1 : if (pszOutlineColor[0] == '#')
6493 1 : pszOutlineColor++;
6494 2 : CPLString osOutlineColor(pszOutlineColor);
6495 1 : if (strlen(pszOutlineColor) > 6)
6496 0 : osOutlineColor.resize(6);
6497 : const int nColor =
6498 1 : static_cast<int>(strtol(osOutlineColor, nullptr, 16));
6499 1 : ToggleFontStyle(TABFSHalo, true);
6500 1 : SetFontOColor(static_cast<GInt32>(nColor));
6501 : }
6502 :
6503 : #if 0
6504 : // Commented out since it is hardcoded to 0x808080.
6505 : // Set the ShadowColor
6506 : const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
6507 : if(bIsNull) pszShadowColor = nullptr;
6508 : if(pszShadowColor)
6509 : {
6510 : if(pszShadowColor[0] == '#')
6511 : pszShadowColor++;
6512 : CPLString osShadowColor(pszShadowColor);
6513 : if( strlen(pszShadowColor) > 6 )
6514 : osShadowColor.resize(6);
6515 : const int nColor =
6516 : static_cast<int>(strtol(osShadowColor, nullptr, 16));
6517 : ToggleFontStyle(TABFSShadow, true);
6518 : SetFontSColor(static_cast<GInt32>(nColor));
6519 : }
6520 : #endif
6521 :
6522 4 : const double dfAngle = poLabelStyle->Angle(bIsNull);
6523 4 : if (!bIsNull)
6524 3 : SetTextAngle(dfAngle);
6525 :
6526 4 : const int nAnchor = poLabelStyle->Anchor(bIsNull);
6527 4 : if (!bIsNull)
6528 : {
6529 3 : switch ((nAnchor - 1) % 3)
6530 : {
6531 0 : case 0:
6532 0 : SetTextJustification(TABTJLeft);
6533 0 : break;
6534 3 : case 1:
6535 3 : SetTextJustification(TABTJCenter);
6536 3 : break;
6537 0 : default /* 2 */:
6538 0 : SetTextJustification(TABTJRight);
6539 0 : break;
6540 : }
6541 : }
6542 : }
6543 :
6544 : /**********************************************************************
6545 : * TABText::DumpMIF()
6546 : *
6547 : * Dump feature geometry in a format similar to .MIF REGIONs.
6548 : **********************************************************************/
6549 0 : void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
6550 : {
6551 0 : if (fpOut == nullptr)
6552 0 : fpOut = stdout;
6553 :
6554 : /*-----------------------------------------------------------------
6555 : * Fetch and validate geometry
6556 : *----------------------------------------------------------------*/
6557 0 : OGRGeometry *poGeom = GetGeometryRef();
6558 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6559 : {
6560 : /*-------------------------------------------------------------
6561 : * Generate output for text object
6562 : *------------------------------------------------------------*/
6563 0 : OGRPoint *poPoint = poGeom->toPoint();
6564 :
6565 0 : fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
6566 0 : m_pszString ? m_pszString : "", poPoint->getX(),
6567 : poPoint->getY());
6568 :
6569 0 : fprintf(fpOut, " m_pszString = '%s'\n", m_pszString);
6570 0 : fprintf(fpOut, " m_dAngle = %.15g\n", m_dAngle);
6571 0 : fprintf(fpOut, " m_dHeight = %.15g\n", m_dHeight);
6572 0 : fprintf(fpOut, " m_rgbForeground = 0x%6.6x (%d)\n", m_rgbForeground,
6573 : m_rgbForeground);
6574 0 : fprintf(fpOut, " m_rgbBackground = 0x%6.6x (%d)\n", m_rgbBackground,
6575 : m_rgbBackground);
6576 0 : fprintf(fpOut, " m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
6577 0 : fprintf(fpOut, " m_nFontStyle = 0x%4.4x\n", m_nFontStyle);
6578 : }
6579 : else
6580 : {
6581 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6582 : "TABText: Missing or Invalid Geometry!");
6583 0 : return;
6584 : }
6585 :
6586 : // Finish with PEN/BRUSH/etc. clauses
6587 0 : DumpPenDef();
6588 0 : DumpFontDef();
6589 :
6590 0 : fflush(fpOut);
6591 : }
6592 :
6593 : /*=====================================================================
6594 : * class TABMultiPoint
6595 : *====================================================================*/
6596 :
6597 : /**********************************************************************
6598 : * TABMultiPoint::TABMultiPoint()
6599 : *
6600 : * Constructor.
6601 : **********************************************************************/
6602 191 : TABMultiPoint::TABMultiPoint(OGRFeatureDefn *poDefnIn)
6603 : : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
6604 191 : m_dCenterY(0.0)
6605 : {
6606 191 : }
6607 :
6608 : /**********************************************************************
6609 : * TABMultiPoint::~TABMultiPoint()
6610 : *
6611 : * Destructor.
6612 : **********************************************************************/
6613 382 : TABMultiPoint::~TABMultiPoint()
6614 : {
6615 382 : }
6616 :
6617 : /**********************************************************************
6618 : * TABMultiPoint::CloneTABFeature()
6619 : *
6620 : * Duplicate feature, including stuff specific to each TABFeature type.
6621 : *
6622 : * This method calls the generic TABFeature::CloneTABFeature() and
6623 : * then copies any members specific to its own type.
6624 : **********************************************************************/
6625 0 : TABFeature *TABMultiPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
6626 : {
6627 : /*-----------------------------------------------------------------
6628 : * Alloc new feature and copy the base stuff
6629 : *----------------------------------------------------------------*/
6630 : TABMultiPoint *poNew =
6631 0 : new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
6632 :
6633 0 : CopyTABFeatureBase(poNew);
6634 :
6635 : /*-----------------------------------------------------------------
6636 : * And members specific to this class
6637 : *----------------------------------------------------------------*/
6638 : // ITABFeatureSymbol
6639 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
6640 :
6641 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
6642 0 : poNew->m_dCenterX = m_dCenterX;
6643 0 : poNew->m_dCenterY = m_dCenterY;
6644 :
6645 0 : return poNew;
6646 : }
6647 :
6648 : /**********************************************************************
6649 : * TABMultiPoint::ValidateMapInfoType()
6650 : *
6651 : * Check the feature's geometry part and return the corresponding
6652 : * mapinfo object type code. The m_nMapInfoType member will also
6653 : * be updated for further calls to GetMapInfoType();
6654 : *
6655 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
6656 : * is expected for this object class.
6657 : **********************************************************************/
6658 0 : TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
6659 : {
6660 : /*-----------------------------------------------------------------
6661 : * Fetch and validate geometry
6662 : *----------------------------------------------------------------*/
6663 0 : OGRGeometry *poGeom = GetGeometryRef();
6664 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6665 : {
6666 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6667 :
6668 0 : if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
6669 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
6670 : else
6671 0 : m_nMapInfoType = TAB_GEOM_MULTIPOINT;
6672 : }
6673 : else
6674 : {
6675 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6676 : "TABMultiPoint: Missing or Invalid Geometry!");
6677 0 : m_nMapInfoType = TAB_GEOM_NONE;
6678 : }
6679 :
6680 : /*-----------------------------------------------------------------
6681 : * Decide if coordinates should be compressed or not.
6682 : *----------------------------------------------------------------*/
6683 0 : ValidateCoordType(poMapFile);
6684 :
6685 0 : return m_nMapInfoType;
6686 : }
6687 :
6688 : /**********************************************************************
6689 : * TABMultiPoint::ReadGeometryFromMAPFile()
6690 : *
6691 : * Fill the geometry and representation (color, etc...) part of the
6692 : * feature from the contents of the .MAP object pointed to by poMAPFile.
6693 : *
6694 : * It is assumed that poMAPFile currently points to the beginning of
6695 : * a map object.
6696 : *
6697 : * Returns 0 on success, -1 on error, in which case CPLError() will have
6698 : * been called.
6699 : **********************************************************************/
6700 8 : int TABMultiPoint::ReadGeometryFromMAPFile(
6701 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6702 : GBool bCoordBlockDataOnly /*=FALSE*/,
6703 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6704 : {
6705 8 : double dXMin = 0.0;
6706 8 : double dYMin = 0.0;
6707 8 : double dXMax = 0.0;
6708 8 : double dYMax = 0.0;
6709 8 : OGRGeometry *poGeometry = nullptr;
6710 8 : GBool bComprCoord = poObjHdr->IsCompressedType();
6711 8 : TABMAPCoordBlock *poCoordBlock = nullptr;
6712 :
6713 : /*-----------------------------------------------------------------
6714 : * Fetch and validate geometry type
6715 : *----------------------------------------------------------------*/
6716 8 : m_nMapInfoType = poObjHdr->m_nType;
6717 :
6718 : /*-----------------------------------------------------------------
6719 : * Read object information
6720 : *----------------------------------------------------------------*/
6721 8 : if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
6722 8 : m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
6723 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
6724 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
6725 : {
6726 : /*-------------------------------------------------------------
6727 : * Copy data from poObjHdr
6728 : *------------------------------------------------------------*/
6729 : TABMAPObjMultiPoint *poMPointHdr =
6730 8 : cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6731 :
6732 8 : const GUInt32 nMinimumBytesForPoints =
6733 8 : (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
6734 8 : if (nMinimumBytesForPoints > 1024 * 1024 &&
6735 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
6736 : {
6737 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
6738 0 : return -1;
6739 : }
6740 :
6741 : // MBR
6742 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
6743 : dXMin, dYMin);
6744 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
6745 : dXMax, dYMax);
6746 :
6747 8 : if (!bCoordBlockDataOnly)
6748 : {
6749 8 : m_nSymbolDefIndex = poMPointHdr->m_nSymbolId; // Symbol index
6750 8 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
6751 : }
6752 :
6753 8 : double dX = 0.0;
6754 8 : double dY = 0.0;
6755 : // Centroid/label point
6756 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
6757 : dX, dY);
6758 8 : SetCenter(dX, dY);
6759 :
6760 : // Compressed coordinate origin (useful only in compressed case!)
6761 8 : m_nComprOrgX = poMPointHdr->m_nComprOrgX;
6762 8 : m_nComprOrgY = poMPointHdr->m_nComprOrgY;
6763 :
6764 : /*-------------------------------------------------------------
6765 : * Read Point Coordinates
6766 : *------------------------------------------------------------*/
6767 8 : OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
6768 8 : poGeometry = poMultiPoint;
6769 :
6770 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6771 4 : poCoordBlock = *ppoCoordBlock;
6772 : else
6773 : poCoordBlock =
6774 4 : poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
6775 8 : if (poCoordBlock == nullptr)
6776 : {
6777 0 : delete poGeometry;
6778 0 : return -1;
6779 : }
6780 8 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6781 :
6782 24 : for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6783 : {
6784 16 : GInt32 nX = 0;
6785 16 : GInt32 nY = 0;
6786 16 : if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
6787 : {
6788 0 : CPLError(CE_Failure, CPLE_FileIO,
6789 : "Failed reading coordinate data at offset %d",
6790 : poMPointHdr->m_nCoordBlockPtr);
6791 0 : delete poGeometry;
6792 0 : return -1;
6793 : }
6794 :
6795 16 : poMapFile->Int2Coordsys(nX, nY, dX, dY);
6796 16 : OGRPoint *poPoint = new OGRPoint(dX, dY);
6797 :
6798 16 : if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
6799 : {
6800 0 : CPLAssert(false); // Just in case lower-level lib is modified
6801 : }
6802 8 : }
6803 : }
6804 : else
6805 : {
6806 0 : CPLError(
6807 : CE_Failure, CPLE_AssertionFailed,
6808 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
6809 0 : m_nMapInfoType, m_nMapInfoType);
6810 0 : return -1;
6811 : }
6812 :
6813 8 : SetGeometryDirectly(poGeometry);
6814 :
6815 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
6816 :
6817 : /* Copy int MBR to feature class members */
6818 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
6819 : poObjHdr->m_nMaxY);
6820 :
6821 : /* Return a ref to coord block so that caller can continue reading
6822 : * after the end of this object (used by TABCollection and index splitting)
6823 : */
6824 8 : if (ppoCoordBlock)
6825 4 : *ppoCoordBlock = poCoordBlock;
6826 :
6827 8 : return 0;
6828 : }
6829 :
6830 : /**********************************************************************
6831 : * TABMultiPoint::WriteGeometryToMAPFile()
6832 : *
6833 : * Write the geometry and representation (color, etc...) part of the
6834 : * feature to the .MAP object pointed to by poMAPFile.
6835 : *
6836 : * It is assumed that poMAPFile currently points to a valid map object.
6837 : *
6838 : * Returns 0 on success, -1 on error, in which case CPLError() will have
6839 : * been called.
6840 : **********************************************************************/
6841 0 : int TABMultiPoint::WriteGeometryToMAPFile(
6842 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6843 : GBool bCoordBlockDataOnly /*=FALSE*/,
6844 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6845 : {
6846 : GInt32 nX, nY;
6847 :
6848 : /*-----------------------------------------------------------------
6849 : * We assume that ValidateMapInfoType() was called already and that
6850 : * the type in poObjHdr->m_nType is valid.
6851 : *----------------------------------------------------------------*/
6852 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
6853 :
6854 : TABMAPObjMultiPoint *poMPointHdr =
6855 0 : cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6856 :
6857 : /*-----------------------------------------------------------------
6858 : * Fetch and validate geometry
6859 : *----------------------------------------------------------------*/
6860 0 : OGRGeometry *poGeom = GetGeometryRef();
6861 0 : OGRMultiPoint *poMPoint = nullptr;
6862 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6863 0 : poMPoint = poGeom->toMultiPoint();
6864 : else
6865 : {
6866 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6867 : "TABMultiPoint: Missing or Invalid Geometry!");
6868 0 : return -1;
6869 : }
6870 :
6871 0 : poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
6872 :
6873 : /*-----------------------------------------------------------------
6874 : * Write data to coordinate block
6875 : *----------------------------------------------------------------*/
6876 0 : const GBool bCompressed = poObjHdr->IsCompressedType();
6877 :
6878 0 : TABMAPCoordBlock *poCoordBlock = nullptr;
6879 0 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6880 0 : poCoordBlock = *ppoCoordBlock;
6881 : else
6882 0 : poCoordBlock = poMapFile->GetCurCoordBlock();
6883 0 : poCoordBlock->StartNewFeature();
6884 0 : poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
6885 0 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6886 :
6887 0 : for (int iPoint = 0, nStatus = 0;
6888 0 : nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6889 : {
6890 0 : poGeom = poMPoint->getGeometryRef(iPoint);
6891 :
6892 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6893 : {
6894 0 : OGRPoint *poPoint = poGeom->toPoint();
6895 :
6896 0 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
6897 0 : if (iPoint == 0)
6898 : {
6899 : // Default to the first point, we may use explicit value below
6900 0 : poMPointHdr->m_nLabelX = nX;
6901 0 : poMPointHdr->m_nLabelY = nY;
6902 : }
6903 :
6904 0 : if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
6905 : 0)
6906 : {
6907 : // Failed ... error message has already been produced
6908 0 : return nStatus;
6909 : }
6910 : }
6911 : else
6912 : {
6913 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6914 : "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
6915 0 : return -1;
6916 : }
6917 : }
6918 :
6919 : /*-----------------------------------------------------------------
6920 : * Copy object information
6921 : *----------------------------------------------------------------*/
6922 :
6923 : // Compressed coordinate origin (useful only in compressed case!)
6924 0 : poMPointHdr->m_nComprOrgX = m_nComprOrgX;
6925 0 : poMPointHdr->m_nComprOrgY = m_nComprOrgY;
6926 :
6927 0 : poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
6928 0 : poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
6929 :
6930 : // Center/label point (default value already set above)
6931 0 : double dX = 0.0;
6932 0 : double dY = 0.0;
6933 0 : if (GetCenter(dX, dY) != -1)
6934 : {
6935 0 : poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
6936 0 : poMPointHdr->m_nLabelY);
6937 : }
6938 :
6939 0 : if (!bCoordBlockDataOnly)
6940 : {
6941 0 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
6942 0 : poMPointHdr->m_nSymbolId =
6943 0 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
6944 : }
6945 :
6946 0 : if (CPLGetLastErrorType() == CE_Failure)
6947 0 : return -1;
6948 :
6949 : /* Return a ref to coord block so that caller can continue writing
6950 : * after the end of this object (used by index splitting)
6951 : */
6952 0 : if (ppoCoordBlock)
6953 0 : *ppoCoordBlock = poCoordBlock;
6954 :
6955 0 : return 0;
6956 : }
6957 :
6958 : /**********************************************************************
6959 : * TABMultiPoint::GetXY()
6960 : *
6961 : * Return this point's X,Y coordinates.
6962 : **********************************************************************/
6963 0 : int TABMultiPoint::GetXY(int i, double &dX, double &dY)
6964 : {
6965 : /*-----------------------------------------------------------------
6966 : * Fetch and validate geometry
6967 : *----------------------------------------------------------------*/
6968 0 : OGRGeometry *poGeom = GetGeometryRef();
6969 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6970 : {
6971 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6972 :
6973 0 : if (i >= 0 && i < poMPoint->getNumGeometries() &&
6974 0 : (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
6975 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6976 : {
6977 0 : OGRPoint *poPoint = poGeom->toPoint();
6978 :
6979 0 : dX = poPoint->getX();
6980 0 : dY = poPoint->getY();
6981 : }
6982 : }
6983 : else
6984 : {
6985 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6986 : "TABMultiPoint: Missing or Invalid Geometry!");
6987 0 : dX = 0.0;
6988 0 : dY = 0.0;
6989 0 : return -1;
6990 : }
6991 :
6992 0 : return 0;
6993 : }
6994 :
6995 : /**********************************************************************
6996 : * TABMultiPoint::GetNumPoints()
6997 : *
6998 : * Return the number of points in this multipoint object
6999 : **********************************************************************/
7000 0 : int TABMultiPoint::GetNumPoints()
7001 : {
7002 : /*-----------------------------------------------------------------
7003 : * Fetch and validate geometry
7004 : *----------------------------------------------------------------*/
7005 0 : OGRGeometry *poGeom = GetGeometryRef();
7006 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7007 : {
7008 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
7009 :
7010 0 : return poMPoint->getNumGeometries();
7011 : }
7012 : else
7013 : {
7014 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7015 : "TABMultiPoint: Missing or Invalid Geometry!");
7016 0 : return 0;
7017 : }
7018 : }
7019 :
7020 : /**********************************************************************
7021 : * TABMultiPoint::GetStyleString() const
7022 : *
7023 : * Return style string for this feature.
7024 : *
7025 : * Style String is built only once during the first call to GetStyleString().
7026 : **********************************************************************/
7027 3 : const char *TABMultiPoint::GetStyleString() const
7028 : {
7029 3 : if (m_pszStyleString == nullptr)
7030 : {
7031 3 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
7032 : }
7033 :
7034 3 : return m_pszStyleString;
7035 : }
7036 :
7037 : /**********************************************************************
7038 : * TABMultiPoint::GetCenter()
7039 : *
7040 : * Returns the center point (or label point?) of the object. Compute one
7041 : * if it was not explicitly set:
7042 : *
7043 : * The default seems to be to use the first point in the collection as
7044 : * the center.. so we'll use that.
7045 : *
7046 : * Returns 0 on success, -1 on error.
7047 : **********************************************************************/
7048 0 : int TABMultiPoint::GetCenter(double &dX, double &dY)
7049 : {
7050 0 : if (!m_bCenterIsSet && GetNumPoints() > 0)
7051 : {
7052 : // The default seems to be to use the first point in the collection
7053 : // as the center... so we'll use that.
7054 0 : if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
7055 0 : m_bCenterIsSet = TRUE;
7056 : }
7057 :
7058 0 : if (!m_bCenterIsSet)
7059 0 : return -1;
7060 :
7061 0 : dX = m_dCenterX;
7062 0 : dY = m_dCenterY;
7063 0 : return 0;
7064 : }
7065 :
7066 : /**********************************************************************
7067 : * TABMultiPoint::SetCenter()
7068 : *
7069 : * Set the X,Y coordinates to use as center point (or label point?)
7070 : **********************************************************************/
7071 173 : void TABMultiPoint::SetCenter(double dX, double dY)
7072 : {
7073 173 : m_dCenterX = dX;
7074 173 : m_dCenterY = dY;
7075 173 : m_bCenterIsSet = TRUE;
7076 173 : }
7077 :
7078 : /**********************************************************************
7079 : * TABMultiPoint::DumpMIF()
7080 : *
7081 : * Dump feature geometry in a format similar to .MIF POINTs.
7082 : **********************************************************************/
7083 0 : void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
7084 : {
7085 0 : if (fpOut == nullptr)
7086 0 : fpOut = stdout;
7087 :
7088 : /*-----------------------------------------------------------------
7089 : * Fetch and validate geometry
7090 : *----------------------------------------------------------------*/
7091 0 : OGRGeometry *poGeom = GetGeometryRef();
7092 0 : OGRMultiPoint *poMPoint = nullptr;
7093 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7094 0 : poMPoint = poGeom->toMultiPoint();
7095 : else
7096 : {
7097 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7098 : "TABMultiPoint: Missing or Invalid Geometry!");
7099 0 : return;
7100 : }
7101 :
7102 : /*-----------------------------------------------------------------
7103 : * Generate output
7104 : *----------------------------------------------------------------*/
7105 0 : fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
7106 :
7107 0 : for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
7108 : {
7109 0 : poGeom = poMPoint->getGeometryRef(iPoint);
7110 :
7111 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
7112 : {
7113 0 : OGRPoint *poPoint = poGeom->toPoint();
7114 0 : fprintf(fpOut, " %.15g %.15g\n", poPoint->getX(), poPoint->getY());
7115 : }
7116 : else
7117 : {
7118 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7119 : "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
7120 0 : return;
7121 : }
7122 : }
7123 :
7124 0 : DumpSymbolDef(fpOut);
7125 :
7126 0 : if (m_bCenterIsSet)
7127 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
7128 :
7129 0 : fflush(fpOut);
7130 : }
7131 :
7132 : /*=====================================================================
7133 : * class TABCollection
7134 : *====================================================================*/
7135 :
7136 : /**********************************************************************
7137 : * TABCollection::TABCollection()
7138 : *
7139 : * Constructor.
7140 : **********************************************************************/
7141 103 : TABCollection::TABCollection(OGRFeatureDefn *poDefnIn)
7142 : : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
7143 103 : m_poMpoint(nullptr)
7144 : {
7145 103 : }
7146 :
7147 : /**********************************************************************
7148 : * TABCollection::~TABCollection()
7149 : *
7150 : * Destructor.
7151 : **********************************************************************/
7152 206 : TABCollection::~TABCollection()
7153 : {
7154 103 : EmptyCollection();
7155 206 : }
7156 :
7157 : /**********************************************************************
7158 : * TABCollection::EmptyCollection()
7159 : *
7160 : * Delete/free all collection components.
7161 : **********************************************************************/
7162 204 : void TABCollection::EmptyCollection()
7163 : {
7164 :
7165 204 : if (m_poRegion)
7166 : {
7167 54 : delete m_poRegion;
7168 54 : m_poRegion = nullptr;
7169 : }
7170 :
7171 204 : if (m_poPline)
7172 : {
7173 37 : delete m_poPline;
7174 37 : m_poPline = nullptr;
7175 : }
7176 :
7177 204 : if (m_poMpoint)
7178 : {
7179 6 : delete m_poMpoint;
7180 6 : m_poMpoint = nullptr;
7181 : }
7182 :
7183 : // Empty OGR Geometry Collection as well
7184 204 : SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
7185 204 : }
7186 :
7187 : /**********************************************************************
7188 : * TABCollection::CloneTABFeature()
7189 : *
7190 : * Duplicate feature, including stuff specific to each TABFeature type.
7191 : *
7192 : * This method calls the generic TABFeature::CloneTABFeature() and
7193 : * then copies any members specific to its own type.
7194 : **********************************************************************/
7195 0 : TABFeature *TABCollection::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
7196 : {
7197 : /*-----------------------------------------------------------------
7198 : * Alloc new feature and copy the base stuff
7199 : *----------------------------------------------------------------*/
7200 : TABCollection *poNew =
7201 0 : new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
7202 :
7203 0 : CopyTABFeatureBase(poNew);
7204 :
7205 : /*-----------------------------------------------------------------
7206 : * And members specific to this class
7207 : *----------------------------------------------------------------*/
7208 :
7209 0 : if (m_poRegion)
7210 0 : poNew->SetRegionDirectly(
7211 0 : cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
7212 :
7213 0 : if (m_poPline)
7214 0 : poNew->SetPolylineDirectly(
7215 0 : cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
7216 :
7217 0 : if (m_poMpoint)
7218 0 : poNew->SetMultiPointDirectly(
7219 0 : cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
7220 :
7221 0 : return poNew;
7222 : }
7223 :
7224 : /**********************************************************************
7225 : * TABCollection::ValidateMapInfoType()
7226 : *
7227 : * Check the feature's geometry part and return the corresponding
7228 : * mapinfo object type code. The m_nMapInfoType member will also
7229 : * be updated for further calls to GetMapInfoType();
7230 : *
7231 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
7232 : * is expected for this object class.
7233 : **********************************************************************/
7234 0 : TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
7235 : {
7236 0 : int nRegionType = TAB_GEOM_NONE;
7237 0 : int nPLineType = TAB_GEOM_NONE;
7238 0 : int nMPointType = TAB_GEOM_NONE;
7239 0 : int nVersion = 650;
7240 :
7241 : /*-----------------------------------------------------------------
7242 : * Fetch and validate geometry
7243 : *----------------------------------------------------------------*/
7244 0 : OGRGeometry *poGeom = GetGeometryRef();
7245 0 : if (poGeom &&
7246 0 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
7247 : {
7248 0 : m_nMapInfoType = TAB_GEOM_COLLECTION;
7249 : }
7250 : else
7251 : {
7252 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7253 : "TABCollection: Missing or Invalid Geometry!");
7254 0 : m_nMapInfoType = TAB_GEOM_NONE;
7255 : }
7256 :
7257 : /*-----------------------------------------------------------------
7258 : * Decide if coordinates should be compressed or not.
7259 : *----------------------------------------------------------------*/
7260 0 : GBool bComprCoord = ValidateCoordType(poMapFile);
7261 :
7262 : /*-----------------------------------------------------------------
7263 : * Since all members of the collection share the same compressed coord
7264 : * origin, we should force the compressed origin in all components
7265 : * to be the same.
7266 : * This also implies that ValidateMapInfoType() should *NOT* be called
7267 : * again until the collection components are written by WriteGeom...()
7268 : *----------------------------------------------------------------*/
7269 :
7270 : // First pass to figure collection type...
7271 0 : if (m_poRegion)
7272 : {
7273 0 : m_poRegion->ValidateCoordType(poMapFile);
7274 0 : nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
7275 0 : if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
7276 0 : nVersion = TAB_GEOM_GET_VERSION(nRegionType);
7277 : }
7278 :
7279 0 : if (m_poPline)
7280 : {
7281 0 : m_poPline->ValidateCoordType(poMapFile);
7282 0 : nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
7283 0 : if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
7284 0 : nVersion = TAB_GEOM_GET_VERSION(nPLineType);
7285 : }
7286 :
7287 0 : if (m_poMpoint)
7288 : {
7289 0 : m_poMpoint->ValidateCoordType(poMapFile);
7290 0 : nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
7291 0 : if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
7292 0 : nVersion = TAB_GEOM_GET_VERSION(nMPointType);
7293 : }
7294 :
7295 : // Need to upgrade native type of collection?
7296 0 : if (nVersion == 800)
7297 : {
7298 0 : m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
7299 : }
7300 :
7301 : // Make another pass updating native type and coordinates type and origin
7302 : // of each component
7303 0 : if (m_poRegion && nRegionType != TAB_GEOM_NONE)
7304 : {
7305 0 : GInt32 nXMin = 0;
7306 0 : GInt32 nYMin = 0;
7307 0 : GInt32 nXMax = 0;
7308 0 : GInt32 nYMax = 0;
7309 0 : m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7310 0 : m_poRegion->ForceCoordTypeAndOrigin(
7311 : (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
7312 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7313 : nYMax);
7314 : }
7315 :
7316 0 : if (m_poPline && nPLineType != TAB_GEOM_NONE)
7317 : {
7318 : GInt32 nXMin, nYMin, nXMax, nYMax;
7319 0 : m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7320 0 : m_poPline->ForceCoordTypeAndOrigin(
7321 : (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
7322 : : TAB_GEOM_V450_MULTIPLINE),
7323 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7324 : nYMax);
7325 : }
7326 :
7327 0 : if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
7328 : {
7329 : GInt32 nXMin, nYMin, nXMax, nYMax;
7330 0 : m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7331 0 : m_poMpoint->ForceCoordTypeAndOrigin(
7332 : (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
7333 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7334 : nYMax);
7335 : }
7336 :
7337 0 : return m_nMapInfoType;
7338 : }
7339 :
7340 : /**********************************************************************
7341 : * TABCollection::ReadLabelAndMBR()
7342 : *
7343 : * Reads the label and MBR elements of the header of a collection component
7344 : *
7345 : * Returns 0 on success, -1 on failure.
7346 : **********************************************************************/
7347 12 : int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7348 : GBool bComprCoord, GInt32 nComprOrgX,
7349 : GInt32 nComprOrgY, GInt32 &pnMinX,
7350 : GInt32 &pnMinY, GInt32 &pnMaxX,
7351 : GInt32 &pnMaxY, GInt32 &pnLabelX,
7352 : GInt32 &pnLabelY)
7353 : {
7354 : //
7355 : // The sections in the collection's coord blocks start with center/label
7356 : // point + MBR that are normally found in the object data blocks
7357 : // of regular region/pline/mulitpoint objects.
7358 : //
7359 :
7360 12 : if (bComprCoord)
7361 : {
7362 : // Region center/label point, relative to compr. coord. origin
7363 : // No it is not relative to the Object block center
7364 12 : pnLabelX = poCoordBlock->ReadInt16();
7365 12 : pnLabelY = poCoordBlock->ReadInt16();
7366 :
7367 12 : TABSaturatedAdd(pnLabelX, nComprOrgX);
7368 12 : TABSaturatedAdd(pnLabelY, nComprOrgY);
7369 :
7370 12 : pnMinX = poCoordBlock->ReadInt16(); // Read MBR
7371 12 : pnMinY = poCoordBlock->ReadInt16();
7372 12 : pnMaxX = poCoordBlock->ReadInt16();
7373 12 : pnMaxY = poCoordBlock->ReadInt16();
7374 12 : TABSaturatedAdd(pnMinX, nComprOrgX);
7375 12 : TABSaturatedAdd(pnMinY, nComprOrgY);
7376 12 : TABSaturatedAdd(pnMaxX, nComprOrgX);
7377 12 : TABSaturatedAdd(pnMaxY, nComprOrgY);
7378 : }
7379 : else
7380 : {
7381 : // Region center/label point, relative to compr. coord. origin
7382 : // No it is not relative to the Object block center
7383 0 : pnLabelX = poCoordBlock->ReadInt32();
7384 0 : pnLabelY = poCoordBlock->ReadInt32();
7385 :
7386 0 : pnMinX = poCoordBlock->ReadInt32(); // Read MBR
7387 0 : pnMinY = poCoordBlock->ReadInt32();
7388 0 : pnMaxX = poCoordBlock->ReadInt32();
7389 0 : pnMaxY = poCoordBlock->ReadInt32();
7390 : }
7391 :
7392 12 : return 0;
7393 : }
7394 :
7395 : /**********************************************************************
7396 : * TABCollection::WriteLabelAndMBR()
7397 : *
7398 : * Writes the label and MBR elements of the header of a collection component
7399 : *
7400 : * Returns 0 on success, -1 on failure.
7401 : **********************************************************************/
7402 0 : int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7403 : GBool bComprCoord, GInt32 nMinX,
7404 : GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
7405 : GInt32 nLabelX, GInt32 nLabelY)
7406 : {
7407 : //
7408 : // The sections in the collection's coord blocks start with center/label
7409 : // point + MBR that are normally found in the object data blocks
7410 : // of regular region/pline/mulitpoint objects.
7411 : //
7412 :
7413 0 : int nStatus = 0;
7414 0 : if ((nStatus =
7415 0 : poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
7416 0 : (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
7417 0 : 0 ||
7418 0 : (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
7419 : {
7420 : // Failed ... error message has already been produced
7421 0 : return nStatus;
7422 : }
7423 :
7424 0 : return 0;
7425 : }
7426 :
7427 : /**********************************************************************
7428 : * TABCollection::ReadGeometryFromMAPFile()
7429 : *
7430 : * Fill the geometry and representation (color, etc...) part of the
7431 : * feature from the contents of the .MAP object pointed to by poMAPFile.
7432 : *
7433 : * It is assumed that poMAPFile currently points to the beginning of
7434 : * a map object.
7435 : *
7436 : * Returns 0 on success, -1 on error, in which case CPLError() will have
7437 : * been called.
7438 : **********************************************************************/
7439 4 : int TABCollection::ReadGeometryFromMAPFile(
7440 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7441 : GBool bCoordBlockDataOnly /*=FALSE*/,
7442 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7443 : {
7444 4 : const GBool bComprCoord = poObjHdr->IsCompressedType();
7445 :
7446 : /*-----------------------------------------------------------------
7447 : * Fetch and validate geometry type
7448 : *----------------------------------------------------------------*/
7449 4 : m_nMapInfoType = poObjHdr->m_nType;
7450 :
7451 4 : if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
7452 4 : m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
7453 0 : m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
7454 0 : m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
7455 : {
7456 0 : CPLError(
7457 : CE_Failure, CPLE_AssertionFailed,
7458 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
7459 0 : m_nMapInfoType, m_nMapInfoType);
7460 0 : return -1;
7461 : }
7462 :
7463 4 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7464 :
7465 : // Make sure collection is empty
7466 4 : EmptyCollection();
7467 :
7468 : /*-------------------------------------------------------------
7469 : * Copy data from poObjHdr
7470 : *------------------------------------------------------------*/
7471 : TABMAPObjCollection *poCollHdr =
7472 4 : cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7473 :
7474 : // MBR
7475 4 : double dXMin = 0.0;
7476 4 : double dYMin = 0.0;
7477 4 : double dXMax = 0.0;
7478 4 : double dYMax = 0.0;
7479 4 : poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
7480 : dYMin);
7481 4 : poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
7482 : dYMax);
7483 :
7484 4 : SetMBR(dXMin, dYMin, dXMax, dYMax);
7485 :
7486 4 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
7487 : poObjHdr->m_nMaxY);
7488 :
7489 4 : int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
7490 4 : TABMAPCoordBlock *poCoordBlock = nullptr;
7491 4 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7492 0 : poCoordBlock = *ppoCoordBlock;
7493 : else
7494 4 : poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
7495 :
7496 : // Compressed coordinate origin (useful only in compressed case!)
7497 4 : m_nComprOrgX = poCollHdr->m_nComprOrgX;
7498 4 : m_nComprOrgY = poCollHdr->m_nComprOrgY;
7499 :
7500 : /*-----------------------------------------------------------------
7501 : * Region Component
7502 : *----------------------------------------------------------------*/
7503 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
7504 : {
7505 : //
7506 : // Build fake coord section header to pass to TABRegion::ReadGeom...()
7507 : //
7508 4 : TABMAPObjPLine oRegionHdr;
7509 :
7510 4 : oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7511 4 : oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7512 :
7513 : //
7514 : // The region section in the coord block starts with center/label
7515 : // point + MBR that are normally found in the object data blocks
7516 : // of regular region objects.
7517 : //
7518 :
7519 : // In V800 the mini-header starts with a copy of num_parts
7520 4 : if (nVersion >= 800)
7521 : {
7522 : // int numParts = poCoordBlock->ReadInt32();
7523 0 : CPLAssert(poCoordBlock->ReadInt32() ==
7524 : poCollHdr->m_nNumRegSections);
7525 : }
7526 :
7527 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
7528 : oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
7529 : oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
7530 : oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
7531 : oRegionHdr.m_nLabelY);
7532 :
7533 : // Set CoordBlockPtr so that TABRegion continues reading here
7534 4 : oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7535 :
7536 4 : if (bComprCoord)
7537 4 : oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
7538 : else
7539 0 : oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
7540 4 : if (nVersion == 800)
7541 0 : oRegionHdr.m_nType = static_cast<TABGeomType>(
7542 0 : oRegionHdr.m_nType +
7543 : (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
7544 :
7545 4 : oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
7546 4 : oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
7547 4 : oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
7548 4 : oRegionHdr.m_bSmooth = 0; // TODO
7549 :
7550 : //
7551 : // Use a TABRegion to read/store the Region coord data
7552 : //
7553 4 : m_poRegion = new TABRegion(GetDefnRef());
7554 4 : if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
7555 : bCoordBlockDataOnly,
7556 4 : &poCoordBlock) != 0)
7557 0 : return -1;
7558 :
7559 : // Set new coord block ptr for next object
7560 : /*if (poCoordBlock)
7561 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7562 : }
7563 :
7564 : /*-----------------------------------------------------------------
7565 : * PLine Component
7566 : *----------------------------------------------------------------*/
7567 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
7568 : {
7569 : //
7570 : // Build fake coord section header to pass to TABPolyline::ReadGeom..()
7571 : //
7572 4 : TABMAPObjPLine oPLineHdr;
7573 :
7574 4 : oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7575 4 : oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7576 :
7577 : //
7578 : // The pline section in the coord block starts with center/label
7579 : // point + MBR that are normally found in the object data blocks
7580 : // of regular pline objects.
7581 : //
7582 :
7583 : // In V800 the mini-header starts with a copy of num_parts
7584 4 : if (nVersion >= 800)
7585 : {
7586 : // int numParts = poCoordBlock->ReadInt32();
7587 0 : CPLAssert(poCoordBlock->ReadInt32() ==
7588 : poCollHdr->m_nNumPLineSections);
7589 : }
7590 :
7591 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
7592 : oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
7593 : oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
7594 : oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
7595 :
7596 : // Set CoordBlockPtr so that TABRegion continues reading here
7597 4 : oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7598 :
7599 4 : if (bComprCoord)
7600 4 : oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
7601 : else
7602 0 : oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
7603 4 : if (nVersion == 800)
7604 0 : oPLineHdr.m_nType = static_cast<TABGeomType>(
7605 0 : oPLineHdr.m_nType +
7606 : (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
7607 :
7608 4 : oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
7609 4 : oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
7610 4 : oPLineHdr.m_bSmooth = 0; // TODO
7611 :
7612 : //
7613 : // Use a TABPolyline to read/store the Polyline coord data
7614 : //
7615 4 : m_poPline = new TABPolyline(GetDefnRef());
7616 4 : if (m_poPline->ReadGeometryFromMAPFile(
7617 4 : poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7618 0 : return -1;
7619 :
7620 : // Set new coord block ptr for next object
7621 : /*if (poCoordBlock)
7622 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7623 : }
7624 :
7625 : /*-----------------------------------------------------------------
7626 : * MultiPoint Component
7627 : *----------------------------------------------------------------*/
7628 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
7629 : {
7630 : //
7631 : // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
7632 : //
7633 4 : TABMAPObjMultiPoint oMPointHdr;
7634 :
7635 4 : oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7636 4 : oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7637 :
7638 : //
7639 : // The pline section in the coord block starts with center/label
7640 : // point + MBR that are normally found in the object data blocks
7641 : // of regular pline objects.
7642 : //
7643 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
7644 : oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
7645 : oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
7646 : oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
7647 : oMPointHdr.m_nLabelY);
7648 :
7649 : // Set CoordBlockPtr so that TABRegion continues reading here
7650 4 : oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7651 :
7652 4 : if (bComprCoord)
7653 4 : oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
7654 : else
7655 0 : oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
7656 4 : if (nVersion == 800)
7657 0 : oMPointHdr.m_nType = static_cast<TABGeomType>(
7658 0 : oMPointHdr.m_nType +
7659 : (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
7660 :
7661 4 : oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
7662 4 : oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
7663 :
7664 : //
7665 : // Use a TABMultiPoint to read/store the coord data
7666 : //
7667 4 : m_poMpoint = new TABMultiPoint(GetDefnRef());
7668 4 : if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
7669 : bCoordBlockDataOnly,
7670 4 : &poCoordBlock) != 0)
7671 0 : return -1;
7672 :
7673 : // Set new coord block ptr for next object (not really useful here)
7674 : /*if (poCoordBlock)
7675 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7676 : }
7677 :
7678 : /*-----------------------------------------------------------------
7679 : * Set the main OGRFeature Geometry
7680 : * (this is actually duplicating geometries from each member)
7681 : *----------------------------------------------------------------*/
7682 4 : if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
7683 0 : return -1;
7684 :
7685 : /* Return a ref to coord block so that caller can continue reading
7686 : * after the end of this object (used by index splitting)
7687 : */
7688 4 : if (ppoCoordBlock)
7689 0 : *ppoCoordBlock = poCoordBlock;
7690 :
7691 4 : return 0;
7692 : }
7693 :
7694 : /**********************************************************************
7695 : * TABCollection::WriteGeometryToMAPFile()
7696 : *
7697 : * Write the geometry and representation (color, etc...) part of the
7698 : * feature to the .MAP object pointed to by poMAPFile.
7699 : *
7700 : * It is assumed that poMAPFile currently points to a valid map object.
7701 : *
7702 : * Returns 0 on success, -1 on error, in which case CPLError() will have
7703 : * been called.
7704 : **********************************************************************/
7705 0 : int TABCollection::WriteGeometryToMAPFile(
7706 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7707 : GBool bCoordBlockDataOnly /*=FALSE*/,
7708 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7709 : {
7710 : /*-----------------------------------------------------------------
7711 : * Note that the current implementation does not allow setting the
7712 : * Geometry via OGRFeature::SetGeometry(). The geometries must be set
7713 : * via the SetRegion/Pline/MpointDirectly() methods which will take
7714 : * care of keeping the OGRFeature's geometry in sync.
7715 : *
7716 : * TODO: If we ever want to support sync'ing changes from the OGRFeature's
7717 : * geometry to the m_poRegion/Pline/Mpoint then a call should be added
7718 : * here, or perhaps in ValidateMapInfoType(), or even better in
7719 : * custom TABCollection::SetGeometry*()... but then this last option
7720 : * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
7721 : *----------------------------------------------------------------*/
7722 :
7723 : /*-----------------------------------------------------------------
7724 : * We assume that ValidateMapInfoType() was called already and that
7725 : * the type in poObjHdr->m_nType is valid.
7726 : *----------------------------------------------------------------*/
7727 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
7728 :
7729 : TABMAPObjCollection *poCollHdr =
7730 0 : cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7731 :
7732 : /*-----------------------------------------------------------------
7733 : * Write data to coordinate block for each component...
7734 : *
7735 : * Note that at this point, the caller (TABFile) has called
7736 : * TABCollection::ValidateMapInfoType() which in turn has called
7737 : * each component's respective ValidateMapInfoType() and
7738 : * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
7739 : * their respective WriteGeometryToMapFile() called.
7740 : *----------------------------------------------------------------*/
7741 0 : const GBool bCompressed = poObjHdr->IsCompressedType();
7742 : // TODO: ??? Do we need to track overall collection coord data size???
7743 0 : int nTotalFeatureDataSize = 0;
7744 :
7745 0 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7746 :
7747 0 : TABMAPCoordBlock *poCoordBlock = nullptr;
7748 0 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7749 0 : poCoordBlock = *ppoCoordBlock;
7750 : else
7751 0 : poCoordBlock = poMapFile->GetCurCoordBlock();
7752 0 : poCoordBlock->StartNewFeature();
7753 0 : poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7754 0 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
7755 :
7756 : /*-----------------------------------------------------------------
7757 : * Region component
7758 : *----------------------------------------------------------------*/
7759 0 : if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
7760 : {
7761 0 : CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
7762 : m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
7763 : m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
7764 : m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
7765 :
7766 0 : TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
7767 0 : TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
7768 :
7769 : // Update count of objects by type in header
7770 0 : if (!bCoordBlockDataOnly)
7771 0 : poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
7772 :
7773 : // Write a placeholder for centroid/label point and MBR mini-header
7774 : // and we'll come back later to write the real values.
7775 : //
7776 : // Note that the call to WriteGeometryToMAPFile() below will call
7777 : // StartNewFeature() as well, so we need to track the current
7778 : // value before calling it
7779 :
7780 0 : poCoordBlock->StartNewFeature();
7781 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7782 :
7783 : // In V800 the mini-header starts with a copy of num_parts
7784 0 : if (nVersion >= 800)
7785 : {
7786 0 : poCoordBlock->WriteInt32(0);
7787 : }
7788 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7789 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7790 :
7791 0 : if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
7792 : bCoordBlockDataOnly,
7793 0 : &poCoordBlock) != 0)
7794 : {
7795 0 : CPLError(CE_Failure, CPLE_FileIO,
7796 : "Failed writing Region part in collection.");
7797 0 : delete poRegionHdr;
7798 0 : return -1;
7799 : }
7800 :
7801 0 : nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
7802 :
7803 : // Come back to write the real values in the mini-header
7804 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7805 0 : poCoordBlock->StartNewFeature();
7806 :
7807 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7808 : {
7809 0 : delete poRegionHdr;
7810 0 : return -1;
7811 : }
7812 :
7813 : // In V800 the mini-header starts with a copy of num_parts
7814 0 : if (nVersion >= 800)
7815 : {
7816 0 : poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
7817 : }
7818 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
7819 : poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
7820 : poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
7821 : poRegionHdr->m_nLabelY);
7822 :
7823 : // And finally move the pointer back to the end of this component
7824 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7825 : {
7826 0 : delete poRegionHdr;
7827 0 : return -1;
7828 : }
7829 :
7830 : // Copy other header members to the main collection header
7831 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7832 : // mini-header???
7833 0 : poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
7834 0 : poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
7835 :
7836 0 : if (!bCoordBlockDataOnly)
7837 : {
7838 0 : poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
7839 0 : poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
7840 : // TODO: Smooth flag = poRegionHdr->m_bSmooth;
7841 : }
7842 :
7843 0 : delete poRegionHdr;
7844 : }
7845 : else
7846 : {
7847 : // No Region component. Set corresponding header fields to 0
7848 :
7849 0 : poCollHdr->m_nRegionDataSize = 0;
7850 0 : poCollHdr->m_nNumRegSections = 0;
7851 0 : poCollHdr->m_nRegionPenId = 0;
7852 0 : poCollHdr->m_nRegionBrushId = 0;
7853 : }
7854 :
7855 : /*-----------------------------------------------------------------
7856 : * PLine component
7857 : *----------------------------------------------------------------*/
7858 0 : if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
7859 : {
7860 0 : CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
7861 : m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
7862 : m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
7863 : m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
7864 :
7865 0 : TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
7866 0 : TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
7867 :
7868 : // Update count of objects by type in header
7869 0 : if (!bCoordBlockDataOnly)
7870 0 : poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
7871 :
7872 : // Write a placeholder for centroid/label point and MBR mini-header
7873 : // and we'll come back later to write the real values.
7874 : //
7875 : // Note that the call to WriteGeometryToMAPFile() below will call
7876 : // StartNewFeature() as well, so we need to track the current
7877 : // value before calling it
7878 :
7879 0 : poCoordBlock->StartNewFeature();
7880 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7881 :
7882 : // In V800 the mini-header starts with a copy of num_parts
7883 0 : if (nVersion >= 800)
7884 : {
7885 0 : poCoordBlock->WriteInt32(0);
7886 : }
7887 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7888 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7889 :
7890 0 : if (m_poPline->WriteGeometryToMAPFile(
7891 0 : poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7892 : {
7893 0 : CPLError(CE_Failure, CPLE_FileIO,
7894 : "Failed writing Region part in collection.");
7895 0 : delete poPlineHdr;
7896 0 : return -1;
7897 : }
7898 :
7899 0 : nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
7900 :
7901 : // Come back to write the real values in the mini-header
7902 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7903 0 : poCoordBlock->StartNewFeature();
7904 :
7905 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7906 : {
7907 0 : delete poPlineHdr;
7908 0 : return -1;
7909 : }
7910 :
7911 : // In V800 the mini-header starts with a copy of num_parts
7912 0 : if (nVersion >= 800)
7913 : {
7914 0 : poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
7915 : }
7916 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
7917 : poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
7918 : poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
7919 : poPlineHdr->m_nLabelY);
7920 :
7921 : // And finally move the pointer back to the end of this component
7922 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7923 : {
7924 0 : delete poPlineHdr;
7925 0 : return -1;
7926 : }
7927 :
7928 : // Copy other header members to the main collection header
7929 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7930 : // mini-header???
7931 0 : poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
7932 0 : poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
7933 0 : if (!bCoordBlockDataOnly)
7934 : {
7935 0 : poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
7936 : // TODO: Smooth flag = poPlineHdr->m_bSmooth;
7937 : }
7938 :
7939 0 : delete poPlineHdr;
7940 : }
7941 : else
7942 : {
7943 : // No Polyline component. Set corresponding header fields to 0
7944 :
7945 0 : poCollHdr->m_nPolylineDataSize = 0;
7946 0 : poCollHdr->m_nNumPLineSections = 0;
7947 0 : poCollHdr->m_nPolylinePenId = 0;
7948 : }
7949 :
7950 : /*-----------------------------------------------------------------
7951 : * MultiPoint component
7952 : *----------------------------------------------------------------*/
7953 0 : if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
7954 : {
7955 0 : CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
7956 : m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
7957 : m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
7958 : m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
7959 :
7960 : TABMAPObjMultiPoint *poMpointHdr =
7961 0 : cpl::down_cast<TABMAPObjMultiPoint *>(
7962 0 : TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
7963 :
7964 : // Update count of objects by type in header
7965 0 : if (!bCoordBlockDataOnly)
7966 0 : poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
7967 :
7968 : // Write a placeholder for centroid/label point and MBR mini-header
7969 : // and we'll come back later to write the real values.
7970 : //
7971 : // Note that the call to WriteGeometryToMAPFile() below will call
7972 : // StartNewFeature() as well, so we need to track the current
7973 : // value before calling it
7974 :
7975 0 : poCoordBlock->StartNewFeature();
7976 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7977 :
7978 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7979 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7980 :
7981 0 : if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
7982 : bCoordBlockDataOnly,
7983 0 : &poCoordBlock) != 0)
7984 : {
7985 0 : CPLError(CE_Failure, CPLE_FileIO,
7986 : "Failed writing Region part in collection.");
7987 0 : delete poMpointHdr;
7988 0 : return -1;
7989 : }
7990 :
7991 0 : nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
7992 :
7993 : // Come back to write the real values in the mini-header
7994 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7995 0 : poCoordBlock->StartNewFeature();
7996 :
7997 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7998 : {
7999 0 : delete poMpointHdr;
8000 0 : return -1;
8001 : }
8002 :
8003 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
8004 : poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
8005 : poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
8006 : poMpointHdr->m_nLabelY);
8007 :
8008 : // And finally move the pointer back to the end of this component
8009 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
8010 : {
8011 0 : delete poMpointHdr;
8012 0 : return -1;
8013 : }
8014 :
8015 : // Copy other header members to the main collection header
8016 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
8017 : // mini-header???
8018 0 : poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
8019 0 : poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
8020 0 : if (!bCoordBlockDataOnly)
8021 : {
8022 0 : poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
8023 : }
8024 :
8025 0 : delete poMpointHdr;
8026 : }
8027 : else
8028 : {
8029 : // No Multipoint component. Set corresponding header fields to 0
8030 :
8031 0 : poCollHdr->m_nMPointDataSize = 0;
8032 0 : poCollHdr->m_nNumMultiPoints = 0;
8033 0 : poCollHdr->m_nMultiPointSymbolId = 0;
8034 : }
8035 :
8036 : /*-----------------------------------------------------------------
8037 : * Copy object information
8038 : *----------------------------------------------------------------*/
8039 :
8040 : // Compressed coordinate origin (useful only in compressed case!)
8041 0 : poCollHdr->m_nComprOrgX = m_nComprOrgX;
8042 0 : poCollHdr->m_nComprOrgY = m_nComprOrgY;
8043 :
8044 0 : poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
8045 :
8046 0 : poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
8047 :
8048 0 : if (CPLGetLastErrorType() == CE_Failure)
8049 0 : return -1;
8050 :
8051 : /* Return a ref to coord block so that caller can continue writing
8052 : * after the end of this object (used by index splitting)
8053 : */
8054 0 : if (ppoCoordBlock)
8055 0 : *ppoCoordBlock = poCoordBlock;
8056 :
8057 0 : return 0;
8058 : }
8059 :
8060 : /**********************************************************************
8061 : * TABCollection::SyncOGRGeometryCollection()
8062 : *
8063 : * Copy the region/pline/multipoint's geometries to the OGRFeature's
8064 : * geometry.
8065 : **********************************************************************/
8066 208 : int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
8067 : GBool bSyncPline,
8068 : GBool bSyncMpoint)
8069 : {
8070 208 : OGRGeometry *poThisGeom = GetGeometryRef();
8071 208 : OGRGeometryCollection *poGeomColl = nullptr;
8072 :
8073 : // poGeometry is defined in the OGRFeature class
8074 208 : if (poThisGeom == nullptr)
8075 : {
8076 103 : poGeomColl = new OGRGeometryCollection();
8077 : }
8078 105 : else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
8079 : {
8080 105 : poGeomColl = poThisGeom->toGeometryCollection();
8081 : }
8082 : else
8083 : {
8084 0 : CPLError(
8085 : CE_Failure, CPLE_AssertionFailed,
8086 : "TABCollection: Invalid Geometry. Type must be OGRCollection.");
8087 0 : return -1;
8088 : }
8089 :
8090 : /*-----------------------------------------------------------------
8091 : * Start by removing geometries that need to be replaced
8092 : * In theory there should be a single geometry of each type, but
8093 : * just in case, we'll loop over the whole collection and delete all
8094 : * instances of each type if there are some.
8095 : *----------------------------------------------------------------*/
8096 208 : int numGeometries = poGeomColl->getNumGeometries();
8097 220 : for (int i = 0; i < numGeometries; i++)
8098 : {
8099 12 : OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
8100 12 : if (!poGeom)
8101 0 : continue;
8102 :
8103 24 : if ((bSyncRegion &&
8104 12 : (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
8105 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
8106 6 : (bSyncPline &&
8107 6 : (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
8108 30 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
8109 6 : (bSyncMpoint &&
8110 6 : (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
8111 : {
8112 : // Remove this geometry
8113 12 : poGeomColl->removeGeometry(i);
8114 :
8115 : // Unless this was the last geometry, we need to restart
8116 : // scanning the collection since we modified it
8117 12 : if (i != numGeometries - 1)
8118 : {
8119 6 : i = 0;
8120 6 : numGeometries = poGeomColl->getNumGeometries();
8121 : }
8122 : }
8123 : }
8124 :
8125 : /*-----------------------------------------------------------------
8126 : * Copy TAB Feature geometries to OGRGeometryCollection
8127 : *----------------------------------------------------------------*/
8128 208 : if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
8129 4 : poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
8130 :
8131 208 : if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
8132 4 : poGeomColl->addGeometry(m_poPline->GetGeometryRef());
8133 :
8134 208 : if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
8135 4 : poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
8136 :
8137 208 : if (poThisGeom == nullptr)
8138 103 : SetGeometryDirectly(poGeomColl);
8139 :
8140 208 : return 0;
8141 : }
8142 :
8143 : /**********************************************************************
8144 : * TABCollection::SetRegionDirectly()
8145 : *
8146 : * Set the region component of the collection, deleting the current
8147 : * region component if there is one. The object is then owned by the
8148 : * TABCollection object. Passing NULL just deletes it.
8149 : *
8150 : * Note that an intentional side-effect is that calling this method
8151 : * with the same poRegion pointer that is already owned by this object
8152 : * will force resync'ing the OGR Geometry member.
8153 : **********************************************************************/
8154 0 : int TABCollection::SetRegionDirectly(TABRegion *poRegion)
8155 : {
8156 0 : if (m_poRegion && m_poRegion != poRegion)
8157 0 : delete m_poRegion;
8158 0 : m_poRegion = poRegion;
8159 :
8160 : // Update OGRGeometryCollection component as well
8161 0 : return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
8162 : }
8163 :
8164 : /**********************************************************************
8165 : * TABCollection::SetPolylineDirectly()
8166 : *
8167 : * Set the polyline component of the collection, deleting the current
8168 : * polyline component if there is one. The object is then owned by the
8169 : * TABCollection object. Passing NULL just deletes it.
8170 : *
8171 : * Note that an intentional side-effect is that calling this method
8172 : * with the same poPline pointer that is already owned by this object
8173 : * will force resync'ing the OGR Geometry member.
8174 : **********************************************************************/
8175 0 : int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
8176 : {
8177 0 : if (m_poPline && m_poPline != poPline)
8178 0 : delete m_poPline;
8179 0 : m_poPline = poPline;
8180 :
8181 : // Update OGRGeometryCollection component as well
8182 0 : return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
8183 : }
8184 :
8185 : /**********************************************************************
8186 : * TABCollection::SetMultiPointDirectly()
8187 : *
8188 : * Set the multipoint component of the collection, deleting the current
8189 : * multipoint component if there is one. The object is then owned by the
8190 : * TABCollection object. Passing NULL just deletes it.
8191 : *
8192 : * Note that an intentional side-effect is that calling this method
8193 : * with the same poMpoint pointer that is already owned by this object
8194 : * will force resync'ing the OGR Geometry member.
8195 : **********************************************************************/
8196 0 : int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
8197 : {
8198 0 : if (m_poMpoint && m_poMpoint != poMpoint)
8199 0 : delete m_poMpoint;
8200 0 : m_poMpoint = poMpoint;
8201 :
8202 : // Update OGRGeometryCollection component as well
8203 0 : return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
8204 : }
8205 :
8206 : /**********************************************************************
8207 : * TABCollection::GetStyleString() const
8208 : *
8209 : * Return style string for this feature.
8210 : *
8211 : * Style String is built only once during the first call to GetStyleString().
8212 : **********************************************************************/
8213 3 : const char *TABCollection::GetStyleString() const
8214 : {
8215 3 : if (m_pszStyleString == nullptr)
8216 : {
8217 3 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
8218 : }
8219 :
8220 3 : return m_pszStyleString;
8221 : }
8222 :
8223 : /**********************************************************************
8224 : * TABCollection::DumpMIF()
8225 : *
8226 : * Dump feature geometry
8227 : **********************************************************************/
8228 0 : void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
8229 : {
8230 0 : if (fpOut == nullptr)
8231 0 : fpOut = stdout;
8232 :
8233 : /*-----------------------------------------------------------------
8234 : * Generate output
8235 : *----------------------------------------------------------------*/
8236 0 : int numParts = 0;
8237 0 : if (m_poRegion)
8238 0 : numParts++;
8239 0 : if (m_poPline)
8240 0 : numParts++;
8241 0 : if (m_poMpoint)
8242 0 : numParts++;
8243 :
8244 0 : fprintf(fpOut, "COLLECTION %d\n", numParts);
8245 :
8246 0 : if (m_poRegion)
8247 0 : m_poRegion->DumpMIF(fpOut);
8248 :
8249 0 : if (m_poPline)
8250 0 : m_poPline->DumpMIF(fpOut);
8251 :
8252 0 : if (m_poMpoint)
8253 0 : m_poMpoint->DumpMIF(fpOut);
8254 :
8255 0 : DumpSymbolDef(fpOut);
8256 :
8257 0 : fflush(fpOut);
8258 0 : }
8259 :
8260 : /*=====================================================================
8261 : * class TABDebugFeature
8262 : *====================================================================*/
8263 :
8264 : /**********************************************************************
8265 : * TABDebugFeature::TABDebugFeature()
8266 : *
8267 : * Constructor.
8268 : **********************************************************************/
8269 0 : TABDebugFeature::TABDebugFeature(OGRFeatureDefn *poDefnIn)
8270 0 : : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
8271 : {
8272 0 : memset(m_abyBuf, 0, sizeof(m_abyBuf));
8273 0 : }
8274 :
8275 : /**********************************************************************
8276 : * TABDebugFeature::~TABDebugFeature()
8277 : *
8278 : * Destructor.
8279 : **********************************************************************/
8280 0 : TABDebugFeature::~TABDebugFeature()
8281 : {
8282 0 : }
8283 :
8284 : /**********************************************************************
8285 : * TABDebugFeature::ReadGeometryFromMAPFile()
8286 : *
8287 : * Fill the geometry and representation (color, etc...) part of the
8288 : * feature from the contents of the .MAP object pointed to by poMAPFile.
8289 : *
8290 : * It is assumed that poMAPFile currently points to the beginning of
8291 : * a map object.
8292 : *
8293 : * Returns 0 on success, -1 on error, in which case CPLError() will have
8294 : * been called.
8295 : **********************************************************************/
8296 0 : int TABDebugFeature::ReadGeometryFromMAPFile(
8297 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
8298 : GBool /*bCoordBlockDataOnly=FALSE*/,
8299 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8300 : {
8301 : /*-----------------------------------------------------------------
8302 : * Fetch geometry type
8303 : *----------------------------------------------------------------*/
8304 0 : m_nMapInfoType = poObjHdr->m_nType;
8305 :
8306 0 : TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
8307 0 : TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
8308 :
8309 : /*-----------------------------------------------------------------
8310 : * If object type has coords in a type 3 block, then its position
8311 : * follows
8312 : *----------------------------------------------------------------*/
8313 0 : if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
8314 : {
8315 0 : m_nCoordDataPtr = poObjBlock->ReadInt32();
8316 0 : m_nCoordDataSize = poObjBlock->ReadInt32();
8317 : }
8318 : else
8319 : {
8320 0 : m_nCoordDataPtr = -1;
8321 0 : m_nCoordDataSize = 0;
8322 : }
8323 :
8324 0 : m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
8325 0 : if (m_nSize > 0)
8326 : {
8327 0 : poObjBlock->GotoByteRel(-5); // Go back to beginning of header
8328 0 : poObjBlock->ReadBytes(
8329 0 : std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
8330 : }
8331 :
8332 0 : return 0;
8333 : }
8334 :
8335 : /**********************************************************************
8336 : * TABDebugFeature::WriteGeometryToMAPFile()
8337 : *
8338 : * Write the geometry and representation (color, etc...) part of the
8339 : * feature to the .MAP object pointed to by poMAPFile.
8340 : *
8341 : * It is assumed that poMAPFile currently points to a valid map object.
8342 : *
8343 : * Returns 0 on success, -1 on error, in which case CPLError() will have
8344 : * been called.
8345 : **********************************************************************/
8346 0 : int TABDebugFeature::WriteGeometryToMAPFile(
8347 : TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
8348 : GBool /*bCoordBlockDataOnly=FALSE*/,
8349 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8350 : {
8351 : // Nothing to do here!
8352 :
8353 0 : CPLError(CE_Failure, CPLE_NotSupported,
8354 : "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
8355 :
8356 0 : return -1;
8357 : }
8358 :
8359 : /**********************************************************************
8360 : * TABDebugFeature::DumpMIF()
8361 : *
8362 : * Dump feature contents... available only in DEBUG mode.
8363 : **********************************************************************/
8364 0 : void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
8365 : {
8366 0 : if (fpOut == nullptr)
8367 0 : fpOut = stdout;
8368 :
8369 0 : fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
8370 0 : GetMapInfoType());
8371 0 : fprintf(fpOut, " Object size: %d bytes\n", m_nSize);
8372 0 : fprintf(fpOut, " m_nCoordDataPtr = %d\n", m_nCoordDataPtr);
8373 0 : fprintf(fpOut, " m_nCoordDataSize = %d\n", m_nCoordDataSize);
8374 0 : fprintf(fpOut, " ");
8375 :
8376 0 : for (int i = 0; i < m_nSize; i++)
8377 0 : fprintf(fpOut, " %2.2x", m_abyBuf[i]);
8378 :
8379 0 : fprintf(fpOut, " \n");
8380 :
8381 0 : fflush(fpOut);
8382 0 : }
8383 :
8384 : /*=====================================================================
8385 : * class ITABFeaturePen
8386 : *====================================================================*/
8387 :
8388 : /**********************************************************************
8389 : * ITABFeaturePen::ITABFeaturePen()
8390 : **********************************************************************/
8391 :
8392 : // MI default is PEN(1, 2, 0)
8393 : static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
8394 :
8395 7760 : ITABFeaturePen::ITABFeaturePen()
8396 7760 : : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
8397 : {
8398 7760 : }
8399 :
8400 : /**********************************************************************
8401 : * ITABFeaturePen::GetPenWidthPixel()
8402 : * ITABFeaturePen::SetPenWidthPixel()
8403 : * ITABFeaturePen::GetPenWidthPoint()
8404 : * ITABFeaturePen::SetPenWidthPoint()
8405 : *
8406 : * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
8407 : * in points (value from 0.1 to 203.7 points). The default pen width
8408 : * in MapInfo is 1 pixel. Pen width in points exist only in file version 450.
8409 : *
8410 : * The following methods hide the way the pen width is stored in the files.
8411 : *
8412 : * In order to establish if a given pen def had its width specified in
8413 : * pixels or in points, one should first call GetPenWidthPoint(), and if
8414 : * it returns 0 then the Pixel width should be used instead:
8415 : * if (GetPenWidthPoint() == 0)
8416 : * ... use pen width in points ...
8417 : * else
8418 : * ... use Pixel width from GetPenWidthPixel()
8419 : *
8420 : * Note that the reverse is not true: the default pixel width is always 1,
8421 : * even when the pen width was actually set in points.
8422 : **********************************************************************/
8423 :
8424 99 : GByte ITABFeaturePen::GetPenWidthPixel() const
8425 : {
8426 99 : return m_sPenDef.nPixelWidth;
8427 : }
8428 :
8429 29 : void ITABFeaturePen::SetPenWidthPixel(GByte val)
8430 : {
8431 29 : const GByte nPixelWidthMin = 1;
8432 29 : const GByte nPixelWidthMax = 7;
8433 29 : m_sPenDef.nPixelWidth =
8434 29 : std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
8435 29 : m_sPenDef.nPointWidth = 0;
8436 29 : }
8437 :
8438 0 : double ITABFeaturePen::GetPenWidthPoint() const
8439 : {
8440 : // We store point width internally as tenths of points
8441 0 : return m_sPenDef.nPointWidth / 10.0;
8442 : }
8443 :
8444 0 : void ITABFeaturePen::SetPenWidthPoint(double val)
8445 : {
8446 0 : m_sPenDef.nPointWidth =
8447 0 : std::min(std::max(static_cast<int>(val * 10), 1), 2037);
8448 0 : m_sPenDef.nPixelWidth = 1;
8449 0 : }
8450 :
8451 : /**********************************************************************
8452 : * ITABFeaturePen::GetPenWidthMIF()
8453 : * ITABFeaturePen::SetPenWidthMIF()
8454 : *
8455 : * The MIF representation for pen width is either a value from 1 to 7
8456 : * for a pen width in pixels, or a value from 11 to 2047 for a pen
8457 : * width in points = 10 + (point_width*10)
8458 : **********************************************************************/
8459 21 : int ITABFeaturePen::GetPenWidthMIF() const
8460 : {
8461 21 : return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
8462 21 : : m_sPenDef.nPixelWidth);
8463 : }
8464 :
8465 2423 : void ITABFeaturePen::SetPenWidthMIF(int val)
8466 : {
8467 2423 : if (val > 10)
8468 : {
8469 0 : m_sPenDef.nPointWidth = std::min((val - 10), 2037);
8470 0 : m_sPenDef.nPixelWidth = 0;
8471 : }
8472 : else
8473 : {
8474 2423 : m_sPenDef.nPixelWidth =
8475 2423 : static_cast<GByte>(std::min(std::max(val, 1), 7));
8476 2423 : m_sPenDef.nPointWidth = 0;
8477 : }
8478 2423 : }
8479 :
8480 : /**********************************************************************
8481 : * ITABFeaturePen::GetPenStyleString()
8482 : *
8483 : * Return a PEN() string. All representations info for the pen are here.
8484 : **********************************************************************/
8485 99 : const char *ITABFeaturePen::GetPenStyleString() const
8486 : {
8487 99 : const char *pszStyle = nullptr;
8488 99 : int nOGRStyle = 0;
8489 : char szPattern[20];
8490 :
8491 99 : szPattern[0] = '\0';
8492 :
8493 : // For now, I only add the 25 first styles
8494 99 : switch (GetPenPattern())
8495 : {
8496 0 : case 1:
8497 0 : nOGRStyle = 1;
8498 0 : break;
8499 99 : case 2:
8500 99 : nOGRStyle = 0;
8501 99 : break;
8502 0 : case 3:
8503 0 : nOGRStyle = 3;
8504 0 : strcpy(szPattern, "1 1");
8505 0 : break;
8506 0 : case 4:
8507 0 : nOGRStyle = 3;
8508 0 : strcpy(szPattern, "2 1");
8509 0 : break;
8510 0 : case 5:
8511 0 : nOGRStyle = 3;
8512 0 : strcpy(szPattern, "3 1");
8513 0 : break;
8514 0 : case 6:
8515 0 : nOGRStyle = 3;
8516 0 : strcpy(szPattern, "6 1");
8517 0 : break;
8518 0 : case 7:
8519 0 : nOGRStyle = 4;
8520 0 : strcpy(szPattern, "12 2");
8521 0 : break;
8522 0 : case 8:
8523 0 : nOGRStyle = 4;
8524 0 : strcpy(szPattern, "24 4");
8525 0 : break;
8526 0 : case 9:
8527 0 : nOGRStyle = 3;
8528 0 : strcpy(szPattern, "4 3");
8529 0 : break;
8530 0 : case 10:
8531 0 : nOGRStyle = 5;
8532 0 : strcpy(szPattern, "1 4");
8533 0 : break;
8534 0 : case 11:
8535 0 : nOGRStyle = 3;
8536 0 : strcpy(szPattern, "4 6");
8537 0 : break;
8538 0 : case 12:
8539 0 : nOGRStyle = 3;
8540 0 : strcpy(szPattern, "6 4");
8541 0 : break;
8542 0 : case 13:
8543 0 : nOGRStyle = 4;
8544 0 : strcpy(szPattern, "12 12");
8545 0 : break;
8546 0 : case 14:
8547 0 : nOGRStyle = 6;
8548 0 : strcpy(szPattern, "8 2 1 2");
8549 0 : break;
8550 0 : case 15:
8551 0 : nOGRStyle = 6;
8552 0 : strcpy(szPattern, "12 1 1 1");
8553 0 : break;
8554 0 : case 16:
8555 0 : nOGRStyle = 6;
8556 0 : strcpy(szPattern, "12 1 3 1");
8557 0 : break;
8558 0 : case 17:
8559 0 : nOGRStyle = 6;
8560 0 : strcpy(szPattern, "24 6 4 6");
8561 0 : break;
8562 0 : case 18:
8563 0 : nOGRStyle = 7;
8564 0 : strcpy(szPattern, "24 3 3 3 3 3");
8565 0 : break;
8566 0 : case 19:
8567 0 : nOGRStyle = 7;
8568 0 : strcpy(szPattern, "24 3 3 3 3 3 3 3");
8569 0 : break;
8570 0 : case 20:
8571 0 : nOGRStyle = 7;
8572 0 : strcpy(szPattern, "6 3 1 3 1 3");
8573 0 : break;
8574 0 : case 21:
8575 0 : nOGRStyle = 7;
8576 0 : strcpy(szPattern, "12 2 1 2 1 2");
8577 0 : break;
8578 0 : case 22:
8579 0 : nOGRStyle = 7;
8580 0 : strcpy(szPattern, "12 2 1 2 1 2 1 2");
8581 0 : break;
8582 0 : case 23:
8583 0 : nOGRStyle = 6;
8584 0 : strcpy(szPattern, "4 1 1 1");
8585 0 : break;
8586 0 : case 24:
8587 0 : nOGRStyle = 7;
8588 0 : strcpy(szPattern, "4 1 1 1 1");
8589 0 : break;
8590 0 : case 25:
8591 0 : nOGRStyle = 6;
8592 0 : strcpy(szPattern, "4 1 1 1 2 1 1 1");
8593 0 : break;
8594 :
8595 0 : default:
8596 0 : nOGRStyle = 0;
8597 0 : break;
8598 : }
8599 :
8600 : // note - MapInfo renders all lines using a round pen cap and round pen join
8601 : // which are not the default values for OGR pen cap/join styles. So we need
8602 : // to explicitly include the cap/j parameters in these strings
8603 99 : if (strlen(szPattern) != 0)
8604 : {
8605 0 : if (m_sPenDef.nPointWidth > 0)
8606 0 : pszStyle = CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
8607 : "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8608 0 : static_cast<int>(GetPenWidthPoint()),
8609 0 : m_sPenDef.rgbColor, GetPenPattern(),
8610 : nOGRStyle, szPattern);
8611 : else
8612 0 : pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
8613 : "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8614 0 : GetPenWidthPixel(), m_sPenDef.rgbColor,
8615 0 : GetPenPattern(), nOGRStyle, szPattern);
8616 : }
8617 : else
8618 : {
8619 99 : if (m_sPenDef.nPointWidth > 0)
8620 : pszStyle =
8621 0 : CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\""
8622 : "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8623 0 : static_cast<int>(GetPenWidthPoint()),
8624 0 : m_sPenDef.rgbColor, GetPenPattern(), nOGRStyle);
8625 : else
8626 99 : pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
8627 : "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8628 99 : GetPenWidthPixel(), m_sPenDef.rgbColor,
8629 99 : GetPenPattern(), nOGRStyle);
8630 : }
8631 :
8632 99 : return pszStyle;
8633 : }
8634 :
8635 : /**********************************************************************
8636 : * ITABFeaturePen::SetPenFromStyleString()
8637 : *
8638 : * Init the Pen properties from a style string.
8639 : **********************************************************************/
8640 32 : void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
8641 : {
8642 32 : GBool bIsNull = 0;
8643 :
8644 : // Use the Style Manager to retrieve all the information we need.
8645 32 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8646 32 : OGRStyleTool *poStylePart = nullptr;
8647 :
8648 : // Init the StyleMgr with the StyleString.
8649 32 : poStyleMgr->InitStyleString(pszStyleString);
8650 :
8651 : // Retrieve the Pen info.
8652 32 : const int numParts = poStyleMgr->GetPartCount();
8653 51 : for (int i = 0; i < numParts; i++)
8654 : {
8655 49 : poStylePart = poStyleMgr->GetPart(i);
8656 49 : if (poStylePart == nullptr)
8657 0 : continue;
8658 :
8659 49 : if (poStylePart->GetType() == OGRSTCPen)
8660 : {
8661 30 : break;
8662 : }
8663 : else
8664 : {
8665 19 : delete poStylePart;
8666 19 : poStylePart = nullptr;
8667 : }
8668 : }
8669 :
8670 : // If the no Pen found, do nothing.
8671 32 : if (poStylePart == nullptr)
8672 : {
8673 2 : delete poStyleMgr;
8674 2 : return;
8675 : }
8676 :
8677 30 : OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
8678 :
8679 : // With Pen, we always want to output points or pixels (which are the same,
8680 : // so just use points).
8681 : //
8682 : // It's very important to set the output unit of the feature.
8683 : // The default value is meter. If we don't do it all numerical values
8684 : // will be assumed to be converted from the input unit to meter when we
8685 : // will get them via GetParam...() functions.
8686 : // See OGRStyleTool::Parse() for more details.
8687 30 : poPenStyle->SetUnit(OGRSTUPoints, 1);
8688 :
8689 : // Get the Pen Id or pattern
8690 30 : const char *pszPenName = poPenStyle->Id(bIsNull);
8691 30 : if (bIsNull)
8692 1 : pszPenName = nullptr;
8693 :
8694 : // Set the width
8695 30 : if (poPenStyle->Width(bIsNull) != 0.0)
8696 : {
8697 29 : const double nPenWidth = poPenStyle->Width(bIsNull);
8698 : // Width < 10 is a pixel
8699 29 : if (nPenWidth > 10)
8700 0 : SetPenWidthPoint(nPenWidth);
8701 : else
8702 29 : SetPenWidthPixel(static_cast<GByte>(nPenWidth));
8703 : }
8704 :
8705 : // Set the color
8706 30 : const char *pszPenColor = poPenStyle->Color(bIsNull);
8707 30 : if (pszPenColor != nullptr)
8708 : {
8709 30 : if (pszPenColor[0] == '#')
8710 30 : pszPenColor++;
8711 : // The Pen color is an Hexa string that need to be convert in a int
8712 : const GInt32 nPenColor =
8713 30 : static_cast<int>(strtol(pszPenColor, nullptr, 16));
8714 30 : SetPenColor(nPenColor);
8715 : }
8716 :
8717 30 : const char *pszPenPattern = nullptr;
8718 :
8719 : // Set the Id of the Pen, use Pattern if necessary.
8720 30 : if (pszPenName &&
8721 29 : (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
8722 : {
8723 29 : const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
8724 29 : if (pszPenId != nullptr)
8725 : {
8726 29 : const int nPenId = atoi(pszPenId + 12);
8727 29 : SetPenPattern(static_cast<GByte>(nPenId));
8728 : }
8729 : else
8730 : {
8731 0 : pszPenId = strstr(pszPenName, "ogr-pen-");
8732 0 : if (pszPenId != nullptr)
8733 : {
8734 0 : int nPenId = atoi(pszPenId + 8);
8735 0 : if (nPenId == 0)
8736 0 : nPenId = 2;
8737 0 : SetPenPattern(static_cast<GByte>(nPenId));
8738 : }
8739 29 : }
8740 : }
8741 : else
8742 : {
8743 : // If no Pen Id, use the Pen Pattern to retrieve the Id.
8744 1 : pszPenPattern = poPenStyle->Pattern(bIsNull);
8745 1 : if (bIsNull)
8746 1 : pszPenPattern = nullptr;
8747 : else
8748 : {
8749 0 : if (strcmp(pszPenPattern, "1 1") == 0)
8750 0 : SetPenPattern(3);
8751 0 : else if (strcmp(pszPenPattern, "2 1") == 0)
8752 0 : SetPenPattern(4);
8753 0 : else if (strcmp(pszPenPattern, "3 1") == 0)
8754 0 : SetPenPattern(5);
8755 0 : else if (strcmp(pszPenPattern, "6 1") == 0)
8756 0 : SetPenPattern(6);
8757 0 : else if (strcmp(pszPenPattern, "12 2") == 0)
8758 0 : SetPenPattern(7);
8759 0 : else if (strcmp(pszPenPattern, "24 4") == 0)
8760 0 : SetPenPattern(8);
8761 0 : else if (strcmp(pszPenPattern, "4 3") == 0)
8762 0 : SetPenPattern(9);
8763 0 : else if (strcmp(pszPenPattern, "1 4") == 0)
8764 0 : SetPenPattern(10);
8765 0 : else if (strcmp(pszPenPattern, "4 6") == 0)
8766 0 : SetPenPattern(11);
8767 0 : else if (strcmp(pszPenPattern, "6 4") == 0)
8768 0 : SetPenPattern(12);
8769 0 : else if (strcmp(pszPenPattern, "12 12") == 0)
8770 0 : SetPenPattern(13);
8771 0 : else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
8772 0 : SetPenPattern(14);
8773 0 : else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
8774 0 : SetPenPattern(15);
8775 0 : else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
8776 0 : SetPenPattern(16);
8777 0 : else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
8778 0 : SetPenPattern(17);
8779 0 : else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
8780 0 : SetPenPattern(18);
8781 0 : else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
8782 0 : SetPenPattern(19);
8783 0 : else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
8784 0 : SetPenPattern(20);
8785 0 : else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
8786 0 : SetPenPattern(21);
8787 0 : else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
8788 0 : SetPenPattern(22);
8789 0 : else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
8790 0 : SetPenPattern(23);
8791 0 : else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
8792 0 : SetPenPattern(24);
8793 0 : else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
8794 0 : SetPenPattern(25);
8795 : }
8796 : }
8797 :
8798 30 : delete poStyleMgr;
8799 30 : delete poStylePart;
8800 :
8801 30 : return;
8802 : }
8803 :
8804 : /**********************************************************************
8805 : * ITABFeaturePen::DumpPenDef()
8806 : *
8807 : * Dump pen definition information.
8808 : **********************************************************************/
8809 0 : void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
8810 : {
8811 0 : if (fpOut == nullptr)
8812 0 : fpOut = stdout;
8813 :
8814 0 : fprintf(fpOut, " m_nPenDefIndex = %d\n", m_nPenDefIndex);
8815 0 : fprintf(fpOut, " m_sPenDef.nRefCount = %d\n", m_sPenDef.nRefCount);
8816 0 : fprintf(fpOut, " m_sPenDef.nPixelWidth = %u\n", m_sPenDef.nPixelWidth);
8817 0 : fprintf(fpOut, " m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
8818 0 : fprintf(fpOut, " m_sPenDef.nPointWidth = %d\n", m_sPenDef.nPointWidth);
8819 0 : fprintf(fpOut, " m_sPenDef.rgbColor = 0x%6.6x (%d)\n",
8820 : m_sPenDef.rgbColor, m_sPenDef.rgbColor);
8821 :
8822 0 : fflush(fpOut);
8823 0 : }
8824 :
8825 : /*=====================================================================
8826 : * class ITABFeatureBrush
8827 : *====================================================================*/
8828 :
8829 : /**********************************************************************
8830 : * ITABFeatureBrush::ITABFeatureBrush()
8831 : **********************************************************************/
8832 :
8833 : // MI default is BRUSH(2, 16777215, 16777215)
8834 : static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
8835 :
8836 2518 : ITABFeatureBrush::ITABFeatureBrush()
8837 2518 : : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
8838 : {
8839 2518 : }
8840 :
8841 : /**********************************************************************
8842 : * ITABFeatureBrush::GetBrushStyleString()
8843 : *
8844 : * Return a Brush() string. All representations info for the Brush are here.
8845 : **********************************************************************/
8846 54 : const char *ITABFeatureBrush::GetBrushStyleString() const
8847 : {
8848 54 : const char *pszStyle = nullptr;
8849 54 : int nOGRStyle = 0;
8850 : /* char szPattern[20]; */
8851 : //* szPattern[0] = '\0'; */
8852 :
8853 54 : if (m_sBrushDef.nFillPattern == 1)
8854 53 : nOGRStyle = 1;
8855 1 : else if (m_sBrushDef.nFillPattern == 3)
8856 0 : nOGRStyle = 2;
8857 1 : else if (m_sBrushDef.nFillPattern == 4)
8858 0 : nOGRStyle = 3;
8859 1 : else if (m_sBrushDef.nFillPattern == 5)
8860 0 : nOGRStyle = 5;
8861 1 : else if (m_sBrushDef.nFillPattern == 6)
8862 0 : nOGRStyle = 4;
8863 1 : else if (m_sBrushDef.nFillPattern == 7)
8864 0 : nOGRStyle = 6;
8865 1 : else if (m_sBrushDef.nFillPattern == 8)
8866 0 : nOGRStyle = 7;
8867 :
8868 54 : if (GetBrushTransparent())
8869 : {
8870 : /* Omit BG Color for transparent brushes */
8871 2 : pszStyle = CPLSPrintf(
8872 : "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8873 2 : m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
8874 : }
8875 : else
8876 : {
8877 52 : pszStyle = CPLSPrintf(
8878 : "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8879 52 : m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
8880 52 : m_sBrushDef.nFillPattern, nOGRStyle);
8881 : }
8882 :
8883 54 : return pszStyle;
8884 : }
8885 :
8886 : /**********************************************************************
8887 : * ITABFeatureBrush::SetBrushFromStyleString()
8888 : *
8889 : * Set all Brush elements from a StyleString.
8890 : * Use StyleMgr to do so.
8891 : **********************************************************************/
8892 19 : void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
8893 : {
8894 19 : GBool bIsNull = 0;
8895 :
8896 : // Use the Style Manager to retrieve all the information we need.
8897 19 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8898 19 : OGRStyleTool *poStylePart = nullptr;
8899 :
8900 : // Init the StyleMgr with the StyleString.
8901 19 : poStyleMgr->InitStyleString(pszStyleString);
8902 :
8903 : // Retrieve the Brush info.
8904 19 : const int numParts = poStyleMgr->GetPartCount();
8905 19 : for (int i = 0; i < numParts; i++)
8906 : {
8907 19 : poStylePart = poStyleMgr->GetPart(i);
8908 19 : if (poStylePart == nullptr)
8909 0 : continue;
8910 :
8911 19 : if (poStylePart->GetType() == OGRSTCBrush)
8912 : {
8913 19 : break;
8914 : }
8915 : else
8916 : {
8917 0 : delete poStylePart;
8918 0 : poStylePart = nullptr;
8919 : }
8920 : }
8921 :
8922 : // If the no Brush found, do nothing.
8923 19 : if (poStylePart == nullptr)
8924 : {
8925 0 : delete poStyleMgr;
8926 0 : return;
8927 : }
8928 :
8929 19 : OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
8930 :
8931 : // Set the Brush Id (FillPattern)
8932 19 : const char *pszBrushId = poBrushStyle->Id(bIsNull);
8933 19 : if (bIsNull)
8934 2 : pszBrushId = nullptr;
8935 19 : bool bHasBrushId = false;
8936 :
8937 19 : if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
8938 0 : strstr(pszBrushId, "ogr-brush-")))
8939 : {
8940 17 : if (strstr(pszBrushId, "mapinfo-brush-"))
8941 : {
8942 17 : const int nBrushId = atoi(pszBrushId + 14);
8943 17 : SetBrushPattern(static_cast<GByte>(nBrushId));
8944 17 : bHasBrushId = true;
8945 : }
8946 0 : else if (strstr(pszBrushId, "ogr-brush-"))
8947 : {
8948 0 : int nBrushId = atoi(pszBrushId + 10);
8949 0 : if (nBrushId > 1)
8950 0 : nBrushId++;
8951 0 : SetBrushPattern(static_cast<GByte>(nBrushId));
8952 0 : bHasBrushId = true;
8953 : }
8954 : }
8955 :
8956 : // Set the BackColor, if not set, then it is transparent
8957 19 : const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
8958 19 : if (bIsNull)
8959 1 : pszBrushColor = nullptr;
8960 :
8961 19 : if (pszBrushColor)
8962 : {
8963 18 : if (pszBrushColor[0] == '#')
8964 18 : pszBrushColor++;
8965 18 : if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
8966 1 : pszBrushColor[7] == '0')
8967 : {
8968 1 : SetBrushTransparent(1);
8969 : }
8970 : else
8971 : {
8972 34 : CPLString osBrushColor(pszBrushColor);
8973 17 : if (strlen(pszBrushColor) > 6)
8974 0 : osBrushColor.resize(6);
8975 : const int nBrushColor =
8976 17 : static_cast<int>(strtol(osBrushColor, nullptr, 16));
8977 17 : SetBrushBGColor(static_cast<GInt32>(nBrushColor));
8978 : }
8979 : }
8980 : else
8981 : {
8982 1 : SetBrushTransparent(1);
8983 : }
8984 :
8985 : // Set the ForeColor
8986 19 : pszBrushColor = poBrushStyle->ForeColor(bIsNull);
8987 19 : if (bIsNull)
8988 0 : pszBrushColor = nullptr;
8989 :
8990 19 : if (pszBrushColor)
8991 : {
8992 19 : if (pszBrushColor[0] == '#')
8993 19 : pszBrushColor++;
8994 19 : if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
8995 1 : pszBrushColor[7] == '0')
8996 : {
8997 1 : if (!bHasBrushId)
8998 1 : SetBrushPattern(static_cast<GByte>(1)); // No-fill
8999 : }
9000 : else
9001 : {
9002 18 : if (!bHasBrushId)
9003 1 : SetBrushPattern(static_cast<GByte>(2)); // Solid-fill
9004 : }
9005 :
9006 38 : CPLString osBrushColor(pszBrushColor);
9007 19 : if (strlen(pszBrushColor) > 6)
9008 1 : osBrushColor.resize(6);
9009 : const int nBrushColor =
9010 19 : static_cast<int>(strtol(osBrushColor, nullptr, 16));
9011 19 : SetBrushFGColor(static_cast<GInt32>(nBrushColor));
9012 : }
9013 :
9014 19 : delete poStyleMgr;
9015 19 : delete poStylePart;
9016 :
9017 19 : return;
9018 : }
9019 :
9020 : /**********************************************************************
9021 : * ITABFeatureBrush::DumpBrushDef()
9022 : *
9023 : * Dump Brush definition information.
9024 : **********************************************************************/
9025 0 : void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
9026 : {
9027 0 : if (fpOut == nullptr)
9028 0 : fpOut = stdout;
9029 :
9030 0 : fprintf(fpOut, " m_nBrushDefIndex = %d\n", m_nBrushDefIndex);
9031 0 : fprintf(fpOut, " m_sBrushDef.nRefCount = %d\n", m_sBrushDef.nRefCount);
9032 0 : fprintf(fpOut, " m_sBrushDef.nFillPattern = %d\n",
9033 0 : static_cast<int>(m_sBrushDef.nFillPattern));
9034 0 : fprintf(fpOut, " m_sBrushDef.bTransparentFill = %d\n",
9035 0 : static_cast<int>(m_sBrushDef.bTransparentFill));
9036 0 : fprintf(fpOut, " m_sBrushDef.rgbFGColor = 0x%6.6x (%d)\n",
9037 : m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
9038 0 : fprintf(fpOut, " m_sBrushDef.rgbBGColor = 0x%6.6x (%d)\n",
9039 : m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
9040 :
9041 0 : fflush(fpOut);
9042 0 : }
9043 :
9044 : /*=====================================================================
9045 : * class ITABFeatureFont
9046 : *====================================================================*/
9047 :
9048 : /**********************************************************************
9049 : * ITABFeatureFont::ITABFeatureFont()
9050 : **********************************************************************/
9051 :
9052 : // MI default is Font("Arial", 0, 0, 0)
9053 : static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
9054 :
9055 1638 : ITABFeatureFont::ITABFeatureFont()
9056 1638 : : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
9057 : {
9058 1638 : }
9059 :
9060 : /**********************************************************************
9061 : * ITABFeatureFont::SetFontName()
9062 : **********************************************************************/
9063 1582 : void ITABFeatureFont::SetFontName(const char *pszName)
9064 : {
9065 1582 : strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
9066 1582 : m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
9067 1582 : }
9068 :
9069 : /**********************************************************************
9070 : * ITABFeatureFont::DumpFontDef()
9071 : *
9072 : * Dump Font definition information.
9073 : **********************************************************************/
9074 0 : void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
9075 : {
9076 0 : if (fpOut == nullptr)
9077 0 : fpOut = stdout;
9078 :
9079 0 : fprintf(fpOut, " m_nFontDefIndex = %d\n", m_nFontDefIndex);
9080 0 : fprintf(fpOut, " m_sFontDef.nRefCount = %d\n", m_sFontDef.nRefCount);
9081 0 : fprintf(fpOut, " m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
9082 :
9083 0 : fflush(fpOut);
9084 0 : }
9085 :
9086 : /*=====================================================================
9087 : * class ITABFeatureSymbol
9088 : *====================================================================*/
9089 :
9090 : /**********************************************************************
9091 : * ITABFeatureSymbol::ITABFeatureSymbol()
9092 : **********************************************************************/
9093 :
9094 : // MI default is Symbol(35, 0, 12)
9095 : static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
9096 :
9097 544458 : ITABFeatureSymbol::ITABFeatureSymbol()
9098 544458 : : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
9099 : {
9100 544458 : }
9101 :
9102 : /**********************************************************************
9103 : * ITABFeatureSymbol::GetSymbolStyleString()
9104 : *
9105 : * Return a Symbol() string. All representations info for the Symbol are here.
9106 : **********************************************************************/
9107 33 : const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
9108 : {
9109 33 : const char *pszStyle = nullptr;
9110 33 : int nOGRStyle = 0;
9111 : /* char szPattern[20]; */
9112 33 : int nAngle = 0;
9113 : /* szPattern[0] = '\0'; */
9114 :
9115 33 : switch (m_sSymbolDef.nSymbolNo)
9116 : {
9117 0 : case 31:
9118 : // this is actually a "null" symbol in MapInfo!
9119 0 : nOGRStyle = 0;
9120 0 : break;
9121 0 : case 32: // filled square
9122 0 : nOGRStyle = 5;
9123 0 : break;
9124 0 : case 33: // filled diamond
9125 0 : nAngle = 45;
9126 0 : nOGRStyle = 5;
9127 0 : break;
9128 0 : case 34: // filled circle
9129 0 : nOGRStyle = 3;
9130 0 : break;
9131 33 : case 35: // filled star
9132 33 : nOGRStyle = 9;
9133 33 : break;
9134 0 : case 36: // filled upward pointing triangle
9135 0 : nOGRStyle = 7;
9136 0 : break;
9137 0 : case 37: // filled downward pointing triangle
9138 0 : nAngle = 180;
9139 0 : nOGRStyle = 7;
9140 0 : break;
9141 0 : case 38: // hollow square
9142 0 : nOGRStyle = 4;
9143 0 : break;
9144 0 : case 39: // hollow diamond
9145 0 : nAngle = 45;
9146 0 : nOGRStyle = 4;
9147 0 : break;
9148 0 : case 40: // hollow circle
9149 0 : nOGRStyle = 2;
9150 0 : break;
9151 0 : case 41: // hollow star
9152 0 : nOGRStyle = 8;
9153 0 : break;
9154 0 : case 42: // hollow upward pointing triangle
9155 0 : nOGRStyle = 6;
9156 0 : break;
9157 0 : case 43: // hollow downward pointing triangle
9158 0 : nAngle = 180;
9159 0 : nOGRStyle = 6;
9160 0 : break;
9161 0 : case 44: // filled square (with shadow)
9162 0 : nOGRStyle = 5;
9163 0 : break;
9164 0 : case 45: // filled upward triangle (with shadow)
9165 0 : nOGRStyle = 7;
9166 0 : break;
9167 0 : case 46: // filled circle (with shadow)
9168 0 : nOGRStyle = 3;
9169 0 : break;
9170 0 : case 49: // crossed lines
9171 0 : nOGRStyle = 0;
9172 0 : break;
9173 0 : case 50: // X crossed lines
9174 0 : nOGRStyle = 1;
9175 0 : break;
9176 : }
9177 :
9178 33 : nAngle += static_cast<int>(dfAngle);
9179 :
9180 66 : pszStyle = CPLSPrintf(
9181 : "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
9182 33 : m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
9183 : nOGRStyle);
9184 :
9185 33 : return pszStyle;
9186 : }
9187 :
9188 : /**********************************************************************
9189 : * ITABFeatureSymbol::SetSymbolFromStyleString()
9190 : *
9191 : * Set all Symbol var from a OGRStyleSymbol.
9192 : **********************************************************************/
9193 109 : void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
9194 : {
9195 109 : GBool bIsNull = 0;
9196 :
9197 : // Set the Symbol Id (SymbolNo)
9198 109 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9199 109 : if (bIsNull)
9200 0 : pszSymbolId = nullptr;
9201 :
9202 109 : if (pszSymbolId)
9203 : {
9204 109 : if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
9205 : {
9206 105 : const int nSymbolId = atoi(pszSymbolId + 12);
9207 105 : SetSymbolNo(static_cast<GByte>(nSymbolId));
9208 : }
9209 4 : else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
9210 : {
9211 0 : const int nSymbolId = atoi(pszSymbolId + 8);
9212 :
9213 : // The OGR symbol is not the MapInfo one
9214 : // Here's some mapping
9215 0 : switch (nSymbolId)
9216 : {
9217 0 : case 0:
9218 0 : SetSymbolNo(49);
9219 0 : break;
9220 0 : case 1:
9221 0 : SetSymbolNo(50);
9222 0 : break;
9223 0 : case 2:
9224 0 : SetSymbolNo(40);
9225 0 : break;
9226 0 : case 3:
9227 0 : SetSymbolNo(34);
9228 0 : break;
9229 0 : case 4:
9230 0 : SetSymbolNo(38);
9231 0 : break;
9232 0 : case 5:
9233 0 : SetSymbolNo(32);
9234 0 : break;
9235 0 : case 6:
9236 0 : SetSymbolNo(42);
9237 0 : break;
9238 0 : case 7:
9239 0 : SetSymbolNo(36);
9240 0 : break;
9241 0 : case 8:
9242 0 : SetSymbolNo(41);
9243 0 : break;
9244 0 : case 9:
9245 0 : SetSymbolNo(35);
9246 0 : break;
9247 0 : case 10: // vertical bar -- no mapinfo equivalent, so use
9248 : // crosshairs as closest match
9249 0 : SetSymbolNo(49);
9250 0 : break;
9251 : }
9252 : }
9253 : }
9254 :
9255 : // Set SymbolSize
9256 109 : const double dSymbolSize = poSymbolStyle->Size(bIsNull);
9257 109 : if (dSymbolSize != 0.0)
9258 : {
9259 109 : SetSymbolSize(static_cast<GInt16>(dSymbolSize));
9260 : }
9261 :
9262 : // Set Symbol Color
9263 109 : const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
9264 109 : if (pszSymbolColor)
9265 : {
9266 109 : if (pszSymbolColor[0] == '#')
9267 109 : pszSymbolColor++;
9268 : int nSymbolColor =
9269 109 : static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
9270 109 : SetSymbolColor(static_cast<GInt32>(nSymbolColor));
9271 : }
9272 109 : }
9273 :
9274 : /**********************************************************************
9275 : * ITABFeatureSymbol::SetSymbolFromStyleString()
9276 : *
9277 : * Set all Symbol var from a StyleString. Use StyleMgr to do so.
9278 : **********************************************************************/
9279 109 : void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
9280 : {
9281 : // Use the Style Manager to retrieve all the information we need.
9282 109 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9283 109 : OGRStyleTool *poStylePart = nullptr;
9284 :
9285 : // Init the StyleMgr with the StyleString.
9286 109 : poStyleMgr->InitStyleString(pszStyleString);
9287 :
9288 : // Retrieve the Symbol info.
9289 109 : const int numParts = poStyleMgr->GetPartCount();
9290 109 : for (int i = 0; i < numParts; i++)
9291 : {
9292 109 : poStylePart = poStyleMgr->GetPart(i);
9293 109 : if (poStylePart == nullptr)
9294 0 : continue;
9295 :
9296 109 : if (poStylePart->GetType() == OGRSTCSymbol)
9297 : {
9298 109 : break;
9299 : }
9300 : else
9301 : {
9302 0 : delete poStylePart;
9303 0 : poStylePart = nullptr;
9304 : }
9305 : }
9306 :
9307 : // If the no Symbol found, do nothing.
9308 109 : if (poStylePart == nullptr)
9309 : {
9310 0 : delete poStyleMgr;
9311 0 : return;
9312 : }
9313 :
9314 : OGRStyleSymbol *poSymbolStyle =
9315 109 : cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9316 :
9317 : // With Symbol, we always want to output points
9318 : //
9319 : // It's very important to set the output unit of the feature.
9320 : // The default value is meter. If we don't do it all numerical values
9321 : // will be assumed to be converted from the input unit to meter when we
9322 : // will get them via GetParam...() functions.
9323 : // See OGRStyleTool::Parse() for more details.
9324 109 : poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
9325 :
9326 109 : SetSymbolFromStyle(poSymbolStyle);
9327 :
9328 109 : delete poStyleMgr;
9329 109 : delete poStylePart;
9330 :
9331 109 : return;
9332 : }
9333 :
9334 : /**********************************************************************
9335 : * ITABFeatureSymbol::GetSymbolFeatureClass()
9336 : *
9337 : * Return the feature class needed to represent the style string.
9338 : **********************************************************************/
9339 : TABFeatureClass
9340 109 : ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
9341 : {
9342 : // Use the Style Manager to retrieve all the information we need.
9343 109 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9344 109 : OGRStyleTool *poStylePart = nullptr;
9345 :
9346 : // Init the StyleMgr with the StyleString.
9347 109 : poStyleMgr->InitStyleString(pszStyleString);
9348 :
9349 : // Retrieve the Symbol info.
9350 109 : const int numParts = poStyleMgr->GetPartCount();
9351 109 : for (int i = 0; i < numParts; i++)
9352 : {
9353 109 : poStylePart = poStyleMgr->GetPart(i);
9354 109 : if (poStylePart == nullptr)
9355 : {
9356 0 : continue;
9357 : }
9358 :
9359 109 : if (poStylePart->GetType() == OGRSTCSymbol)
9360 : {
9361 109 : break;
9362 : }
9363 : else
9364 : {
9365 0 : delete poStylePart;
9366 0 : poStylePart = nullptr;
9367 : }
9368 : }
9369 :
9370 109 : TABFeatureClass result = TABFCPoint;
9371 :
9372 : // If the no Symbol found, do nothing.
9373 109 : if (poStylePart == nullptr)
9374 : {
9375 0 : delete poStyleMgr;
9376 0 : return result;
9377 : }
9378 :
9379 : OGRStyleSymbol *poSymbolStyle =
9380 109 : cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9381 :
9382 109 : GBool bIsNull = 0;
9383 :
9384 : // Set the Symbol Id (SymbolNo)
9385 109 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9386 109 : if (bIsNull)
9387 0 : pszSymbolId = nullptr;
9388 :
9389 109 : if (pszSymbolId)
9390 : {
9391 109 : if (STARTS_WITH(pszSymbolId, "font-sym-"))
9392 : {
9393 2 : result = TABFCFontPoint;
9394 : }
9395 107 : else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
9396 : {
9397 2 : result = TABFCCustomPoint;
9398 : }
9399 : }
9400 :
9401 109 : delete poStyleMgr;
9402 109 : delete poStylePart;
9403 :
9404 109 : return result;
9405 : }
9406 :
9407 : /**********************************************************************
9408 : * ITABFeatureSymbol::DumpSymbolDef()
9409 : *
9410 : * Dump Symbol definition information.
9411 : **********************************************************************/
9412 0 : void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
9413 : {
9414 0 : if (fpOut == nullptr)
9415 0 : fpOut = stdout;
9416 :
9417 0 : fprintf(fpOut, " m_nSymbolDefIndex = %d\n", m_nSymbolDefIndex);
9418 0 : fprintf(fpOut, " m_sSymbolDef.nRefCount = %d\n", m_sSymbolDef.nRefCount);
9419 0 : fprintf(fpOut, " m_sSymbolDef.nSymbolNo = %d\n", m_sSymbolDef.nSymbolNo);
9420 0 : fprintf(fpOut, " m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
9421 0 : fprintf(fpOut, " m_sSymbolDef._unknown_ = %d\n",
9422 0 : static_cast<int>(m_sSymbolDef._nUnknownValue_));
9423 0 : fprintf(fpOut, " m_sSymbolDef.rgbColor = 0x%6.6x (%d)\n",
9424 : m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
9425 :
9426 0 : fflush(fpOut);
9427 0 : }
|