Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_feature.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the feature classes specific to MapInfo files.
7 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
8 : *
9 : **********************************************************************
10 : * Copyright (c) 1999-2002, Daniel Morissette
11 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : **********************************************************************/
15 :
16 : #include "cpl_port.h"
17 : #include "mitab.h"
18 : #include "mitab_geometry.h"
19 : #include "mitab_utils.h"
20 :
21 : #include <cctype>
22 : #include <cmath>
23 : #include <cstdio>
24 : #include <cstdlib>
25 : #include <cstring>
26 : #include <algorithm>
27 : #include <utility>
28 :
29 : #include "cpl_conv.h"
30 : #include "cpl_error.h"
31 : #include "cpl_string.h"
32 : #include "cpl_vsi.h"
33 : #include "mitab.h"
34 : #include "mitab_geometry.h"
35 : #include "mitab_priv.h"
36 : #include "mitab_utils.h"
37 : #include "ogr_core.h"
38 : #include "ogr_feature.h"
39 : #include "ogr_featurestyle.h"
40 : #include "ogr_geometry.h"
41 :
42 : /*=====================================================================
43 : * class TABFeature
44 : *====================================================================*/
45 :
46 : /**********************************************************************
47 : * TABFeature::TABFeature()
48 : *
49 : * Constructor.
50 : **********************************************************************/
51 574275 : TABFeature::TABFeature(const OGRFeatureDefn *poDefnIn)
52 : : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
53 : m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
54 574275 : m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
55 : {
56 574275 : }
57 :
58 : /**********************************************************************
59 : * TABFeature::~TABFeature()
60 : *
61 : * Destructor.
62 : **********************************************************************/
63 574540 : TABFeature::~TABFeature()
64 : {
65 574540 : }
66 :
67 : /**********************************************************************
68 : * TABFeature::CreateFromMapInfoType()
69 : *
70 : * Factory that creates a TABFeature of the right class for the specified
71 : * MapInfo Type
72 : *
73 : **********************************************************************/
74 551382 : TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
75 : OGRFeatureDefn *poDefn)
76 : {
77 551382 : TABFeature *poFeature = nullptr;
78 :
79 : /*-----------------------------------------------------------------
80 : * Create new feature object of the right type
81 : *----------------------------------------------------------------*/
82 551382 : switch (nMapInfoType)
83 : {
84 155 : case TAB_GEOM_NONE:
85 155 : poFeature = new TABFeature(poDefn);
86 155 : break;
87 549085 : case TAB_GEOM_SYMBOL_C:
88 : case TAB_GEOM_SYMBOL:
89 549085 : poFeature = new TABPoint(poDefn);
90 549085 : break;
91 6 : case TAB_GEOM_FONTSYMBOL_C:
92 : case TAB_GEOM_FONTSYMBOL:
93 6 : poFeature = new TABFontPoint(poDefn);
94 6 : break;
95 6 : case TAB_GEOM_CUSTOMSYMBOL_C:
96 : case TAB_GEOM_CUSTOMSYMBOL:
97 6 : poFeature = new TABCustomPoint(poDefn);
98 6 : break;
99 1573 : case TAB_GEOM_LINE_C:
100 : case TAB_GEOM_LINE:
101 : case TAB_GEOM_PLINE_C:
102 : case TAB_GEOM_PLINE:
103 : case TAB_GEOM_MULTIPLINE_C:
104 : case TAB_GEOM_MULTIPLINE:
105 : case TAB_GEOM_V450_MULTIPLINE_C:
106 : case TAB_GEOM_V450_MULTIPLINE:
107 : case TAB_GEOM_V800_MULTIPLINE_C:
108 : case TAB_GEOM_V800_MULTIPLINE:
109 1573 : poFeature = new TABPolyline(poDefn);
110 1573 : break;
111 8 : case TAB_GEOM_ARC_C:
112 : case TAB_GEOM_ARC:
113 8 : poFeature = new TABArc(poDefn);
114 8 : break;
115 :
116 521 : case TAB_GEOM_REGION_C:
117 : case TAB_GEOM_REGION:
118 : case TAB_GEOM_V450_REGION_C:
119 : case TAB_GEOM_V450_REGION:
120 : case TAB_GEOM_V800_REGION_C:
121 : case TAB_GEOM_V800_REGION:
122 521 : poFeature = new TABRegion(poDefn);
123 521 : break;
124 8 : case TAB_GEOM_RECT_C:
125 : case TAB_GEOM_RECT:
126 : case TAB_GEOM_ROUNDRECT_C:
127 : case TAB_GEOM_ROUNDRECT:
128 8 : poFeature = new TABRectangle(poDefn);
129 8 : break;
130 4 : case TAB_GEOM_ELLIPSE_C:
131 : case TAB_GEOM_ELLIPSE:
132 4 : poFeature = new TABEllipse(poDefn);
133 4 : break;
134 8 : case TAB_GEOM_TEXT_C:
135 : case TAB_GEOM_TEXT:
136 8 : poFeature = new TABText(poDefn);
137 8 : break;
138 4 : case TAB_GEOM_MULTIPOINT_C:
139 : case TAB_GEOM_MULTIPOINT:
140 : case TAB_GEOM_V800_MULTIPOINT_C:
141 : case TAB_GEOM_V800_MULTIPOINT:
142 4 : poFeature = new TABMultiPoint(poDefn);
143 4 : break;
144 4 : case TAB_GEOM_COLLECTION_C:
145 : case TAB_GEOM_COLLECTION:
146 : case TAB_GEOM_V800_COLLECTION_C:
147 : case TAB_GEOM_V800_COLLECTION:
148 4 : poFeature = new TABCollection(poDefn);
149 4 : break;
150 0 : default:
151 : /*-------------------------------------------------------------
152 : * Unsupported feature type... we still return a valid feature
153 : * with NONE geometry after producing a Warning.
154 : * Callers can trap that case by checking CPLGetLastErrorNo()
155 : * against TAB_WarningFeatureTypeNotSupported
156 : *------------------------------------------------------------*/
157 : // poFeature = new TABDebugFeature(poDefn);
158 0 : poFeature = new TABFeature(poDefn);
159 :
160 0 : CPLError(
161 : CE_Warning,
162 : static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
163 : "Unsupported object type %d (0x%2.2x). Feature will be "
164 : "returned with NONE geometry.",
165 : nMapInfoType, nMapInfoType);
166 : }
167 :
168 551382 : return poFeature;
169 : }
170 :
171 : /**********************************************************************
172 : * TABFeature::CopyTABFeatureBase()
173 : *
174 : * Used by CloneTABFeature() to copy the basic (fields, geometry, etc.)
175 : * TABFeature members.
176 : *
177 : * The newly created feature is owned by the caller, and will have its own
178 : * reference to the OGRFeatureDefn.
179 : *
180 : * It is possible to create the clone with a different OGRFeatureDefn,
181 : * in this case, the fields won't be copied of course.
182 : *
183 : **********************************************************************/
184 2 : void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature)
185 : {
186 : /*-----------------------------------------------------------------
187 : * Copy fields only if OGRFeatureDefn is the same
188 : *----------------------------------------------------------------*/
189 2 : const OGRFeatureDefn *poThisDefnRef = GetDefnRef();
190 :
191 2 : if (poThisDefnRef == poDestFeature->GetDefnRef())
192 : {
193 0 : for (int i = 0; i < poThisDefnRef->GetFieldCount(); i++)
194 : {
195 0 : poDestFeature->SetField(i, GetRawFieldRef(i));
196 : }
197 : }
198 :
199 : /*-----------------------------------------------------------------
200 : * Copy the geometry
201 : *----------------------------------------------------------------*/
202 2 : poDestFeature->SetGeometry(GetGeometryRef());
203 :
204 2 : double dXMin = 0.0;
205 2 : double dYMin = 0.0;
206 2 : double dXMax = 0.0;
207 2 : double dYMax = 0.0;
208 2 : GetMBR(dXMin, dYMin, dXMax, dYMax);
209 2 : poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
210 :
211 2 : GInt32 nXMin = 0;
212 2 : GInt32 nYMin = 0;
213 2 : GInt32 nXMax = 0;
214 2 : GInt32 nYMax = 0;
215 2 : GetIntMBR(nXMin, nYMin, nXMax, nYMax);
216 2 : poDestFeature->SetIntMBR(nXMin, nYMin, nXMax, nYMax);
217 :
218 : // m_nMapInfoType is not carried but it is not required anyways.
219 : // it will default to TAB_GEOM_NONE
220 2 : }
221 :
222 : /**********************************************************************
223 : * TABFeature::CloneTABFeature()
224 : *
225 : * Duplicate feature, including stuff specific to each TABFeature type.
226 : *
227 : * The newly created feature is owned by the caller, and will have its own
228 : * reference to the OGRFeatureDefn.
229 : *
230 : * It is possible to create the clone with a different OGRFeatureDefn,
231 : * in this case, the fields won't be copied of course.
232 : *
233 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
234 : * then copies any members specific to its own type.
235 : **********************************************************************/
236 : TABFeature *
237 0 : TABFeature::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
238 : {
239 : /*-----------------------------------------------------------------
240 : * Alloc new feature and copy the base stuff
241 : *----------------------------------------------------------------*/
242 0 : TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
243 :
244 0 : CopyTABFeatureBase(poNew);
245 :
246 : /*-----------------------------------------------------------------
247 : * And members specific to this class
248 : *----------------------------------------------------------------*/
249 : // Nothing to do for this class
250 :
251 0 : return poNew;
252 : }
253 :
254 : /**********************************************************************
255 : * TABFeature::SetMBR()
256 : *
257 : * Set the values for the MBR corners for this feature.
258 : **********************************************************************/
259 558529 : void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
260 : {
261 558529 : m_dXMin = std::min(dXMin, dXMax);
262 558529 : m_dYMin = std::min(dYMin, dYMax);
263 558529 : m_dXMax = std::max(dXMin, dXMax);
264 558529 : m_dYMax = std::max(dYMin, dYMax);
265 558529 : }
266 :
267 : /**********************************************************************
268 : * TABFeature::GetMBR()
269 : *
270 : * Return the values for the MBR corners for this feature.
271 : **********************************************************************/
272 1227 : void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
273 : double &dYMax)
274 : {
275 1227 : dXMin = m_dXMin;
276 1227 : dYMin = m_dYMin;
277 1227 : dXMax = m_dXMax;
278 1227 : dYMax = m_dYMax;
279 1227 : }
280 :
281 : /**********************************************************************
282 : * TABFeature::SetIntMBR()
283 : *
284 : * Set the integer coordinates values of the MBR of this feature.
285 : **********************************************************************/
286 551233 : void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
287 : GInt32 nYMax)
288 : {
289 551233 : m_nXMin = nXMin;
290 551233 : m_nYMin = nYMin;
291 551233 : m_nXMax = nXMax;
292 551233 : m_nYMax = nYMax;
293 551233 : }
294 :
295 : /**********************************************************************
296 : * TABFeature::GetIntMBR()
297 : *
298 : * Return the integer coordinates values of the MBR of this feature.
299 : **********************************************************************/
300 15023 : void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
301 : GInt32 &nYMax)
302 : {
303 15023 : nXMin = m_nXMin;
304 15023 : nYMin = m_nYMin;
305 15023 : nXMax = m_nXMax;
306 15023 : nYMax = m_nYMax;
307 15023 : }
308 :
309 : /**********************************************************************
310 : * TABFeature::ReadRecordFromDATFile()
311 : *
312 : * Fill the fields part of the feature from the contents of the
313 : * table record pointed to by poDATFile.
314 : *
315 : * It is assumed that poDATFile currently points to the beginning of
316 : * the table record and that this feature's OGRFeatureDefn has been
317 : * properly initialized for this table.
318 : **********************************************************************/
319 551214 : int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
320 : {
321 551214 : CPLAssert(poDATFile);
322 :
323 551214 : const int numFields = poDATFile->GetNumFields();
324 :
325 1104240 : for (int iField = 0; iField < numFields; iField++)
326 : {
327 553026 : switch (poDATFile->GetFieldType(iField))
328 : {
329 1320 : case TABFChar:
330 : {
331 1320 : int iWidth(poDATFile->GetFieldWidth(iField));
332 2640 : CPLString osValue(poDATFile->ReadCharField(iWidth));
333 :
334 1320 : if (!poDATFile->GetEncoding().empty())
335 : {
336 110 : osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
337 : }
338 1320 : SetField(iField, osValue);
339 1320 : break;
340 : }
341 28 : case TABFDecimal:
342 : {
343 28 : const double dValue = poDATFile->ReadDecimalField(
344 : poDATFile->GetFieldWidth(iField));
345 28 : SetField(iField, dValue);
346 28 : break;
347 : }
348 550994 : case TABFInteger:
349 : {
350 550994 : const int nValue = poDATFile->ReadIntegerField(
351 : poDATFile->GetFieldWidth(iField));
352 550994 : SetField(iField, nValue);
353 550994 : break;
354 : }
355 4 : case TABFSmallInt:
356 : {
357 4 : const int nValue = poDATFile->ReadSmallIntField(
358 4 : poDATFile->GetFieldWidth(iField));
359 4 : SetField(iField, nValue);
360 4 : break;
361 : }
362 6 : case TABFLargeInt:
363 : {
364 6 : const GInt64 nValue = poDATFile->ReadLargeIntField(
365 : poDATFile->GetFieldWidth(iField));
366 6 : SetField(iField, nValue);
367 6 : break;
368 : }
369 609 : case TABFFloat:
370 : {
371 : const double dValue =
372 609 : poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
373 609 : SetField(iField, dValue);
374 609 : break;
375 : }
376 6 : case TABFLogical:
377 : {
378 6 : const bool bValue = poDATFile->ReadLogicalField(
379 : poDATFile->GetFieldWidth(iField));
380 6 : SetField(iField, bValue ? 1 : 0);
381 6 : break;
382 : }
383 29 : case TABFDate:
384 : {
385 : #ifdef MITAB_USE_OFTDATETIME
386 29 : int nYear = 0;
387 29 : int nMonth = 0;
388 29 : int nDay = 0;
389 29 : const int status = poDATFile->ReadDateField(
390 : poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
391 29 : if (status == 0)
392 : {
393 12 : SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
394 : }
395 : #else
396 : const char *pszValue =
397 : poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
398 : SetField(iField, pszValue);
399 : #endif
400 29 : break;
401 : }
402 7 : case TABFTime:
403 : {
404 : #ifdef MITAB_USE_OFTDATETIME
405 7 : int nHour = 0;
406 7 : int nMin = 0;
407 7 : int nMS = 0;
408 7 : int nSec = 0;
409 : const int status =
410 7 : poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
411 : &nHour, &nMin, &nSec, &nMS);
412 7 : if (status == 0)
413 : {
414 6 : int nYear = 0;
415 6 : int nMonth = 0;
416 6 : int nDay = 0;
417 6 : SetField(iField, nYear, nMonth, nDay, nHour, nMin,
418 6 : nSec + nMS / 1000.0f, 0);
419 : }
420 : #else
421 : const char *pszValue =
422 : poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
423 : SetField(iField, pszValue);
424 : #endif
425 7 : break;
426 : }
427 23 : case TABFDateTime:
428 : {
429 : #ifdef MITAB_USE_OFTDATETIME
430 23 : int nYear = 0;
431 23 : int nMonth = 0;
432 23 : int nDay = 0;
433 23 : int nHour = 0;
434 23 : int nMin = 0;
435 23 : int nMS = 0;
436 23 : int nSec = 0;
437 23 : const int status = poDATFile->ReadDateTimeField(
438 : poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
439 : &nHour, &nMin, &nSec, &nMS);
440 23 : if (status == 0)
441 : {
442 6 : SetField(iField, nYear, nMonth, nDay, nHour, nMin,
443 6 : nSec + nMS / 1000.0f, 0);
444 : }
445 : #else
446 : const char *pszValue = poDATFile->ReadDateTimeField(
447 : poDATFile->GetFieldWidth(iField));
448 : SetField(iField, pszValue);
449 : #endif
450 23 : break;
451 : }
452 0 : default:
453 : // Other type??? Impossible!
454 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
455 : "Unsupported field type for field %d!", iField);
456 : }
457 : }
458 :
459 551214 : return 0;
460 : }
461 :
462 : /**********************************************************************
463 : * TABFeature::WriteRecordToDATFile()
464 : *
465 : * Write the attribute part of the feature to the .DAT file.
466 : *
467 : * It is assumed that poDATFile currently points to the beginning of
468 : * the table record and that this feature's OGRFeatureDefn has been
469 : * properly initialized for this table.
470 : *
471 : * Returns 0 on success, -1 on error.
472 : **********************************************************************/
473 15087 : int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
474 : TABINDFile *poINDFile, int *panIndexNo)
475 : {
476 : #ifdef MITAB_USE_OFTDATETIME
477 15087 : int nYear = 0;
478 15087 : int nMon = 0;
479 15087 : int nDay = 0;
480 15087 : int nHour = 0;
481 15087 : int nMin = 0;
482 15087 : int nTZFlag = 0;
483 15087 : float fSec = 0.0f;
484 : #endif
485 :
486 15087 : CPLAssert(poDATFile);
487 :
488 15087 : const int numFields = poDATFile->GetNumFields();
489 :
490 15087 : poDATFile->MarkRecordAsExisting();
491 :
492 15087 : int nStatus = 0;
493 30858 : for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
494 : {
495 : // Hack for "extra" introduced field.
496 15771 : if (iField >= GetDefnRef()->GetFieldCount())
497 : {
498 1 : CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
499 : iField == 0);
500 1 : nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
501 : poINDFile, 0);
502 1 : continue;
503 : }
504 15770 : CPLAssert(panIndexNo != nullptr);
505 :
506 15770 : switch (poDATFile->GetFieldType(iField))
507 : {
508 377 : case TABFChar:
509 : {
510 377 : CPLString osValue(GetFieldAsString(iField));
511 377 : if (!poDATFile->GetEncoding().empty())
512 : {
513 30 : osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
514 : }
515 377 : nStatus = poDATFile->WriteCharField(
516 : osValue, poDATFile->GetFieldWidth(iField), poINDFile,
517 377 : panIndexNo[iField]);
518 : }
519 377 : break;
520 5 : case TABFDecimal:
521 5 : nStatus = poDATFile->WriteDecimalField(
522 : GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
523 : poDATFile->GetFieldPrecision(iField), poINDFile,
524 5 : panIndexNo[iField]);
525 5 : break;
526 14993 : case TABFInteger:
527 14993 : nStatus = poDATFile->WriteIntegerField(
528 14993 : GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
529 14993 : break;
530 0 : case TABFSmallInt:
531 0 : nStatus = poDATFile->WriteSmallIntField(
532 0 : static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
533 0 : panIndexNo[iField]);
534 0 : break;
535 2 : case TABFLargeInt:
536 6 : nStatus = poDATFile->WriteLargeIntField(
537 2 : static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
538 2 : panIndexNo[iField]);
539 2 : break;
540 237 : case TABFFloat:
541 237 : nStatus = poDATFile->WriteFloatField(
542 237 : GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
543 237 : break;
544 2 : case TABFLogical:
545 : nStatus =
546 2 : poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
547 2 : poINDFile, panIndexNo[iField]);
548 2 : break;
549 76 : case TABFDate:
550 : #ifdef MITAB_USE_OFTDATETIME
551 76 : if (IsFieldSetAndNotNull(iField))
552 : {
553 59 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
554 : &nMin, &fSec, &nTZFlag);
555 : }
556 : else
557 : {
558 17 : nYear = 0;
559 17 : nMon = 0;
560 17 : nDay = 0;
561 : }
562 :
563 152 : nStatus = poDATFile->WriteDateField(
564 76 : nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
565 : #else
566 : nStatus = poDATFile->WriteDateField(
567 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
568 : #endif
569 76 : break;
570 3 : case TABFTime:
571 : #ifdef MITAB_USE_OFTDATETIME
572 3 : if (IsFieldSetAndNotNull(iField))
573 : {
574 2 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
575 : &nMin, &fSec, &nTZFlag);
576 : }
577 : else
578 : {
579 : // Put negative values, so that WriteTimeField() forges
580 : // a negative value, and ultimately write -1 in the binary
581 : // field
582 1 : nHour = -1;
583 1 : nMin = -1;
584 1 : fSec = -1;
585 : }
586 3 : nStatus = poDATFile->WriteTimeField(
587 : nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
588 3 : poINDFile, panIndexNo[iField]);
589 :
590 : #else
591 : nStatus = poDATFile->WriteTimeField(
592 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
593 : #endif
594 3 : break;
595 75 : case TABFDateTime:
596 : #ifdef MITAB_USE_OFTDATETIME
597 75 : if (IsFieldSetAndNotNull(iField))
598 : {
599 58 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
600 : &nMin, &fSec, &nTZFlag);
601 : }
602 : else
603 : {
604 17 : nYear = 0;
605 17 : nMon = 0;
606 17 : nDay = 0;
607 17 : nHour = 0;
608 17 : nMin = 0;
609 17 : fSec = 0;
610 : }
611 :
612 75 : nStatus = poDATFile->WriteDateTimeField(
613 : nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
614 75 : OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
615 : #else
616 : nStatus = poDATFile->WriteDateTimeField(
617 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
618 : #endif
619 75 : break;
620 0 : default:
621 : // Other type??? Impossible!
622 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
623 : "Unsupported field type!");
624 : }
625 : }
626 :
627 15087 : if (nStatus != 0)
628 1 : return nStatus;
629 :
630 15086 : if (poDATFile->CommitRecordToFile() != 0)
631 0 : return -1;
632 :
633 15086 : return 0;
634 : }
635 :
636 : /**********************************************************************
637 : * TABFeature::ReadGeometryFromMAPFile()
638 : *
639 : * In derived classes, this method should be reimplemented to
640 : * fill the geometry and representation (color, etc...) part of the
641 : * feature from the contents of the .MAP object pointed to by poMAPFile.
642 : *
643 : * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
644 : * currently points to the beginning of a map object.
645 : *
646 : * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
647 : * the CoordBlock data during splitting of object blocks. In this case we
648 : * need to process only the information related to the CoordBlock. One
649 : * important thing to avoid is reading/writing pen/brush/symbol definitions
650 : * as that would screw up their ref counters.
651 : *
652 : * ppoCoordBlock is used by TABCollection and by index splitting code
653 : * to provide a CoordBlock to use instead of the one from the poMAPFile and
654 : * return the current pointer at the end of the call.
655 : *
656 : * The current implementation does nothing since instances of TABFeature
657 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
658 : *
659 : * Returns 0 on success, -1 on error, in which case CPLError() will have
660 : * been called.
661 : **********************************************************************/
662 155 : int TABFeature::ReadGeometryFromMAPFile(
663 : TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
664 : GBool /*bCoordBlockDataOnly=FALSE*/,
665 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
666 : {
667 : // Nothing to do. Instances of TABFeature objects contain no geometry.
668 155 : return 0;
669 : }
670 :
671 : /**********************************************************************
672 : * TABFeature::UpdateMBR()
673 : *
674 : * Fetch envelope of poGeom and update MBR.
675 : * Integer coord MBR is updated only if poMapFile is not NULL.
676 : *
677 : * Returns 0 on success, or -1 if there is no geometry in object
678 : **********************************************************************/
679 15017 : int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
680 : {
681 15017 : OGRGeometry *poGeom = GetGeometryRef();
682 :
683 15017 : if (poGeom)
684 : {
685 15017 : OGREnvelope oEnv;
686 15017 : poGeom->getEnvelope(&oEnv);
687 :
688 15017 : m_dXMin = oEnv.MinX;
689 15017 : m_dYMin = oEnv.MinY;
690 15017 : m_dXMax = oEnv.MaxX;
691 15017 : m_dYMax = oEnv.MaxY;
692 :
693 15017 : if (poMapFile)
694 : {
695 15017 : poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
696 15017 : poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
697 : // Coordsy2Int can transform a min value to a max one and vice
698 : // versa.
699 15017 : if (m_nXMin > m_nXMax)
700 : {
701 0 : std::swap(m_nXMin, m_nXMax);
702 : }
703 15017 : if (m_nYMin > m_nYMax)
704 : {
705 0 : std::swap(m_nYMin, m_nYMax);
706 : }
707 : }
708 :
709 15017 : return 0;
710 : }
711 :
712 0 : return -1;
713 : }
714 :
715 : /**********************************************************************
716 : * TABFeature::ValidateCoordType()
717 : *
718 : * Checks the feature envelope to establish if the feature should be
719 : * written using Compressed coordinates or not and adjust m_nMapInfoType
720 : * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
721 : * m_nXMax, m_nYMax
722 : *
723 : * This function should be used only by the ValidateMapInfoType()
724 : * implementations.
725 : *
726 : * Returns TRUE if coord. should be compressed, FALSE otherwise
727 : **********************************************************************/
728 303 : GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
729 : {
730 303 : GBool bCompr = FALSE;
731 :
732 : /*-------------------------------------------------------------
733 : * Decide if coordinates should be compressed or not.
734 : *------------------------------------------------------------*/
735 303 : if (UpdateMBR(poMapFile) == 0)
736 : {
737 : /* Test for max range < 65535 here instead of < 65536 to avoid
738 : * compressed coordinate overflows in some boundary situations
739 : */
740 303 : if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
741 294 : (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
742 : {
743 294 : bCompr = TRUE;
744 : }
745 303 : m_nComprOrgX =
746 303 : static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
747 303 : m_nComprOrgY =
748 303 : static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
749 : }
750 :
751 : /*-------------------------------------------------------------
752 : * Adjust native type
753 : *------------------------------------------------------------*/
754 303 : if (bCompr && ((m_nMapInfoType % 3) == 2))
755 294 : m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
756 : 1); // compr = 1, 4, 7, ...
757 9 : else if (!bCompr && ((m_nMapInfoType % 3) == 1))
758 0 : m_nMapInfoType = static_cast<TABGeomType>(
759 0 : m_nMapInfoType + 1); // non-compr = 2, 5, 8, ...
760 :
761 303 : return bCompr;
762 : }
763 :
764 : /**********************************************************************
765 : * TABFeature::ForceCoordTypeAndOrigin()
766 : *
767 : * This function is used by TABCollection::ValidateMapInfoType() to force
768 : * the coord type and compressed origin of all members of a collection
769 : * to be the same. (A replacement for ValidateCoordType() for this
770 : * specific case)
771 : **********************************************************************/
772 0 : void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
773 : GInt32 nComprOrgX, GInt32 nComprOrgY,
774 : GInt32 nXMin, GInt32 nYMin,
775 : GInt32 nXMax, GInt32 nYMax)
776 : {
777 : /*-------------------------------------------------------------
778 : * Set Compressed Origin and adjust native type
779 : *------------------------------------------------------------*/
780 0 : m_nComprOrgX = nComprOrgX;
781 0 : m_nComprOrgY = nComprOrgY;
782 :
783 0 : m_nMapInfoType = nMapInfoType;
784 :
785 0 : if (bCompr && ((m_nMapInfoType % 3) == 2))
786 0 : m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
787 : 1); // compr = 1, 4, 7, ...
788 0 : else if (!bCompr && ((m_nMapInfoType % 3) == 1))
789 0 : m_nMapInfoType = static_cast<TABGeomType>(
790 0 : m_nMapInfoType + 1); // non-compr = 2, 5, 8, ...
791 :
792 0 : m_nXMin = nXMin;
793 0 : m_nYMin = nYMin;
794 0 : m_nXMax = nXMax;
795 0 : m_nYMax = nYMax;
796 0 : }
797 :
798 : /**********************************************************************
799 : * TABFeature::WriteGeometryToMAPFile()
800 : *
801 : *
802 : * In derived classes, this method should be reimplemented to
803 : * write the geometry and representation (color, etc...) part of the
804 : * feature to the .MAP object pointed to by poMAPFile.
805 : *
806 : * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
807 : * currently points to a valid map object.
808 : *
809 : * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
810 : * the CoordBlock data during splitting of object blocks. In this case we
811 : * need to process only the information related to the CoordBlock. One
812 : * important thing to avoid is reading/writing pen/brush/symbol definitions
813 : * as that would screw up their ref counters.
814 : *
815 : * ppoCoordBlock is used by TABCollection and by index splitting code
816 : * to provide a CoordBlock to use instead of the one from the poMAPFile and
817 : * return the current pointer at the end of the call.
818 : *
819 : * The current implementation does nothing since instances of TABFeature
820 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
821 : *
822 : * Returns 0 on success, -1 on error, in which case CPLError() will have
823 : * been called.
824 : **********************************************************************/
825 60 : int TABFeature::WriteGeometryToMAPFile(
826 : TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
827 : GBool /*bCoordBlockDataOnly=FALSE*/,
828 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
829 : {
830 : /*-----------------------------------------------------------------
831 : * Nothing to do... instances of TABFeature objects contain no geometry.
832 : *----------------------------------------------------------------*/
833 :
834 60 : return 0;
835 : }
836 :
837 : /**********************************************************************
838 : * TABFeature::DumpMID()
839 : *
840 : * Dump feature attributes in a format similar to .MID data records.
841 : **********************************************************************/
842 0 : void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
843 : {
844 0 : const OGRFeatureDefn *l_poDefn = GetDefnRef();
845 :
846 0 : if (fpOut == nullptr)
847 0 : fpOut = stdout;
848 :
849 0 : for (int iField = 0; iField < l_poDefn->GetFieldCount(); iField++)
850 : {
851 0 : const OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
852 :
853 0 : fprintf(fpOut, " %s (%s) = %s\n", poFDefn->GetNameRef(),
854 : OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
855 : GetFieldAsString(iField));
856 : }
857 :
858 0 : fflush(fpOut);
859 0 : }
860 :
861 : /**********************************************************************
862 : * TABFeature::DumpMIF()
863 : *
864 : * Dump feature geometry in a format similar to .MIF files.
865 : **********************************************************************/
866 0 : void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
867 : {
868 0 : if (fpOut == nullptr)
869 0 : fpOut = stdout;
870 :
871 : /*-----------------------------------------------------------------
872 : * Generate output... not much to do, feature contains no geometry.
873 : *----------------------------------------------------------------*/
874 0 : fprintf(fpOut, "NONE\n");
875 :
876 0 : fflush(fpOut);
877 0 : }
878 :
879 : /*=====================================================================
880 : * class TABPoint
881 : *====================================================================*/
882 :
883 : /**********************************************************************
884 : * TABPoint::TABPoint()
885 : *
886 : * Constructor.
887 : **********************************************************************/
888 565939 : TABPoint::TABPoint(const OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
889 : {
890 565939 : }
891 :
892 : /**********************************************************************
893 : * TABPoint::~TABPoint()
894 : *
895 : * Destructor.
896 : **********************************************************************/
897 1130549 : TABPoint::~TABPoint()
898 : {
899 1130549 : }
900 :
901 : /**********************************************************************
902 : * TABPoint::CloneTABFeature()
903 : *
904 : * Duplicate feature, including stuff specific to each TABFeature type.
905 : *
906 : * This method calls the generic TABFeature::CloneTABFeature() and
907 : * then copies any members specific to its own type.
908 : **********************************************************************/
909 2 : TABFeature *TABPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
910 : {
911 : /*-----------------------------------------------------------------
912 : * Alloc new feature and copy the base stuff
913 : *----------------------------------------------------------------*/
914 2 : TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
915 :
916 2 : CopyTABFeatureBase(poNew);
917 :
918 : /*-----------------------------------------------------------------
919 : * And members specific to this class
920 : *----------------------------------------------------------------*/
921 : // ITABFeatureSymbol
922 2 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
923 :
924 2 : return poNew;
925 : }
926 :
927 : /**********************************************************************
928 : * TABPoint::ValidateMapInfoType()
929 : *
930 : * Check the feature's geometry part and return the corresponding
931 : * mapinfo object type code. The m_nMapInfoType member will also
932 : * be updated for further calls to GetMapInfoType();
933 : *
934 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
935 : * is expected for this object class.
936 : **********************************************************************/
937 14691 : TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
938 : {
939 : /*-----------------------------------------------------------------
940 : * Fetch and validate geometry
941 : * __TODO__ For now we always write in uncompressed format (until we
942 : * find that this is not correct... note that at this point the
943 : * decision to use compressed/uncompressed will likely be based on
944 : * the distance between the point and the object block center in
945 : * integer coordinates being > 32767 or not... remains to be verified)
946 : *----------------------------------------------------------------*/
947 14691 : OGRGeometry *poGeom = GetGeometryRef();
948 14691 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
949 : {
950 14691 : switch (GetFeatureClass())
951 : {
952 2 : case TABFCFontPoint:
953 2 : m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
954 2 : break;
955 2 : case TABFCCustomPoint:
956 2 : m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
957 2 : break;
958 14687 : case TABFCPoint:
959 : default:
960 14687 : m_nMapInfoType = TAB_GEOM_SYMBOL;
961 14687 : break;
962 : }
963 : }
964 : else
965 : {
966 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
967 : "TABPoint: Missing or Invalid Geometry!");
968 0 : m_nMapInfoType = TAB_GEOM_NONE;
969 : }
970 :
971 14691 : UpdateMBR(poMapFile);
972 :
973 14691 : return m_nMapInfoType;
974 : }
975 :
976 : /**********************************************************************
977 : * TABPoint::ReadGeometryFromMAPFile()
978 : *
979 : * Fill the geometry and representation (color, etc...) part of the
980 : * feature from the contents of the .MAP object pointed to by poMAPFile.
981 : *
982 : * It is assumed that poMAPFile currently points to the beginning of
983 : * a map object.
984 : *
985 : * Returns 0 on success, -1 on error, in which case CPLError() will have
986 : * been called.
987 : **********************************************************************/
988 549085 : int TABPoint::ReadGeometryFromMAPFile(
989 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
990 : GBool bCoordBlockDataOnly /*=FALSE*/,
991 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
992 : {
993 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
994 549085 : if (bCoordBlockDataOnly)
995 0 : return 0;
996 :
997 : /*-----------------------------------------------------------------
998 : * Fetch and validate geometry type
999 : *----------------------------------------------------------------*/
1000 549085 : m_nMapInfoType = poObjHdr->m_nType;
1001 :
1002 549085 : if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
1003 13872 : m_nMapInfoType != TAB_GEOM_SYMBOL_C)
1004 : {
1005 0 : CPLError(
1006 : CE_Failure, CPLE_AssertionFailed,
1007 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1008 0 : m_nMapInfoType, m_nMapInfoType);
1009 0 : return -1;
1010 : }
1011 :
1012 : /*-----------------------------------------------------------------
1013 : * Read object information
1014 : *----------------------------------------------------------------*/
1015 549085 : TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1016 :
1017 549085 : m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index
1018 :
1019 549085 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1020 :
1021 : /*-----------------------------------------------------------------
1022 : * Create and fill geometry object
1023 : *----------------------------------------------------------------*/
1024 549085 : double dX = 0.0;
1025 549085 : double dY = 0.0;
1026 :
1027 549085 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1028 549085 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1029 :
1030 549085 : SetGeometryDirectly(poGeometry);
1031 :
1032 549085 : SetMBR(dX, dY, dX, dY);
1033 549085 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1034 : poObjHdr->m_nMaxY);
1035 :
1036 549085 : return 0;
1037 : }
1038 :
1039 : /**********************************************************************
1040 : * TABPoint::WriteGeometryToMAPFile()
1041 : *
1042 : * Write the geometry and representation (color, etc...) part of the
1043 : * feature to the .MAP object pointed to by poMAPFile.
1044 : *
1045 : * It is assumed that poMAPFile currently points to a valid map object.
1046 : *
1047 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1048 : * been called.
1049 : **********************************************************************/
1050 14687 : int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
1051 : TABMAPObjHdr *poObjHdr,
1052 : GBool bCoordBlockDataOnly /*=FALSE*/,
1053 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1054 : {
1055 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1056 14687 : if (bCoordBlockDataOnly)
1057 0 : return 0;
1058 :
1059 : /*-----------------------------------------------------------------
1060 : * We assume that ValidateMapInfoType() was called already and that
1061 : * the type in poObjHdr->m_nType is valid.
1062 : *----------------------------------------------------------------*/
1063 14687 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1064 :
1065 : /*-----------------------------------------------------------------
1066 : * Fetch and validate geometry
1067 : *----------------------------------------------------------------*/
1068 14687 : OGRGeometry *poGeom = GetGeometryRef();
1069 14687 : OGRPoint *poPoint = nullptr;
1070 14687 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1071 14687 : poPoint = poGeom->toPoint();
1072 : else
1073 : {
1074 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1075 : "TABPoint: Missing or Invalid Geometry!");
1076 0 : return -1;
1077 : }
1078 :
1079 14687 : GInt32 nX = 0;
1080 14687 : GInt32 nY = 0;
1081 14687 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1082 :
1083 : /*-----------------------------------------------------------------
1084 : * Copy object information
1085 : *----------------------------------------------------------------*/
1086 14687 : TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1087 :
1088 14687 : poPointHdr->m_nX = nX;
1089 14687 : poPointHdr->m_nY = nY;
1090 14687 : poPointHdr->SetMBR(nX, nY, nX, nY);
1091 :
1092 14687 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1093 14687 : poPointHdr->m_nSymbolId =
1094 14687 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
1095 :
1096 14687 : if (CPLGetLastErrorType() == CE_Failure)
1097 0 : return -1;
1098 :
1099 14687 : return 0;
1100 : }
1101 :
1102 : /**********************************************************************
1103 : * TABPoint::GetX()
1104 : *
1105 : * Return this point's X coordinate.
1106 : **********************************************************************/
1107 0 : double TABPoint::GetX()
1108 : {
1109 :
1110 : /*-----------------------------------------------------------------
1111 : * Fetch and validate geometry
1112 : *----------------------------------------------------------------*/
1113 0 : OGRGeometry *poGeom = GetGeometryRef();
1114 0 : OGRPoint *poPoint = nullptr;
1115 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1116 0 : poPoint = poGeom->toPoint();
1117 : else
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1120 : "TABPoint: Missing or Invalid Geometry!");
1121 0 : return 0.0;
1122 : }
1123 :
1124 0 : return poPoint->getX();
1125 : }
1126 :
1127 : /**********************************************************************
1128 : * TABPoint::GetY()
1129 : *
1130 : * Return this point's Y coordinate.
1131 : **********************************************************************/
1132 0 : double TABPoint::GetY()
1133 : {
1134 : /*-----------------------------------------------------------------
1135 : * Fetch and validate geometry
1136 : *----------------------------------------------------------------*/
1137 0 : OGRGeometry *poGeom = GetGeometryRef();
1138 0 : OGRPoint *poPoint = nullptr;
1139 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1140 0 : poPoint = poGeom->toPoint();
1141 : else
1142 : {
1143 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1144 : "TABPoint: Missing or Invalid Geometry!");
1145 0 : return 0.0;
1146 : }
1147 :
1148 0 : return poPoint->getY();
1149 : }
1150 :
1151 : /**********************************************************************
1152 : * TABPoint::GetStyleString() const
1153 : *
1154 : * Return style string for this feature.
1155 : *
1156 : * Style String is built only once during the first call to GetStyleString().
1157 : **********************************************************************/
1158 239 : const char *TABPoint::GetStyleString() const
1159 : {
1160 239 : if (m_pszStyleString == nullptr)
1161 : {
1162 27 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1163 : }
1164 :
1165 239 : return m_pszStyleString;
1166 : }
1167 :
1168 : /**********************************************************************
1169 : * TABPoint::DumpMIF()
1170 : *
1171 : * Dump feature geometry in a format similar to .MIF POINTs.
1172 : **********************************************************************/
1173 0 : void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
1174 : {
1175 0 : if (fpOut == nullptr)
1176 0 : fpOut = stdout;
1177 :
1178 : /*-----------------------------------------------------------------
1179 : * Fetch and validate geometry
1180 : *----------------------------------------------------------------*/
1181 0 : OGRGeometry *poGeom = GetGeometryRef();
1182 0 : OGRPoint *poPoint = nullptr;
1183 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1184 0 : poPoint = poGeom->toPoint();
1185 : else
1186 : {
1187 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1188 : "TABPoint: Missing or Invalid Geometry!");
1189 0 : return;
1190 : }
1191 :
1192 : /*-----------------------------------------------------------------
1193 : * Generate output
1194 : *----------------------------------------------------------------*/
1195 0 : fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
1196 :
1197 0 : DumpSymbolDef(fpOut);
1198 :
1199 : /*-----------------------------------------------------------------
1200 : * Handle stuff specific to derived classes
1201 : *----------------------------------------------------------------*/
1202 : // cppcheck-suppress knownConditionTrueFalse
1203 0 : if (GetFeatureClass() == TABFCFontPoint)
1204 : {
1205 0 : TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
1206 0 : fprintf(fpOut, " m_nFontStyle = 0x%2.2x (%d)\n",
1207 : poFeature->GetFontStyleTABValue(),
1208 : poFeature->GetFontStyleTABValue());
1209 :
1210 0 : poFeature->DumpFontDef(fpOut);
1211 : }
1212 : // cppcheck-suppress knownConditionTrueFalse
1213 0 : if (GetFeatureClass() == TABFCCustomPoint)
1214 : {
1215 0 : TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
1216 :
1217 0 : fprintf(fpOut, " m_nUnknown_ = 0x%2.2x (%d)\n",
1218 0 : poFeature->m_nUnknown_, poFeature->m_nUnknown_);
1219 0 : fprintf(fpOut, " m_nCustomStyle = 0x%2.2x (%d)\n",
1220 0 : poFeature->GetCustomSymbolStyle(),
1221 0 : poFeature->GetCustomSymbolStyle());
1222 :
1223 0 : poFeature->DumpFontDef(fpOut);
1224 : }
1225 :
1226 0 : fflush(fpOut);
1227 : }
1228 :
1229 : /*=====================================================================
1230 : * class TABFontPoint
1231 : *====================================================================*/
1232 :
1233 : /**********************************************************************
1234 : * TABFontPoint::TABFontPoint()
1235 : *
1236 : * Constructor.
1237 : **********************************************************************/
1238 643 : TABFontPoint::TABFontPoint(const OGRFeatureDefn *poDefnIn)
1239 643 : : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
1240 : {
1241 643 : }
1242 :
1243 : /**********************************************************************
1244 : * TABFontPoint::~TABFontPoint()
1245 : *
1246 : * Destructor.
1247 : **********************************************************************/
1248 1286 : TABFontPoint::~TABFontPoint()
1249 : {
1250 1286 : }
1251 :
1252 : /**********************************************************************
1253 : * TABFontPoint::CloneTABFeature()
1254 : *
1255 : * Duplicate feature, including stuff specific to each TABFeature type.
1256 : *
1257 : * This method calls the generic TABFeature::CloneTABFeature() and
1258 : * then copies any members specific to its own type.
1259 : **********************************************************************/
1260 : TABFeature *
1261 0 : TABFontPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
1262 : {
1263 : /*-----------------------------------------------------------------
1264 : * Alloc new feature and copy the base stuff
1265 : *----------------------------------------------------------------*/
1266 : TABFontPoint *poNew =
1267 0 : new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
1268 :
1269 0 : CopyTABFeatureBase(poNew);
1270 :
1271 : /*-----------------------------------------------------------------
1272 : * And members specific to this class
1273 : *----------------------------------------------------------------*/
1274 : // ITABFeatureSymbol
1275 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1276 :
1277 : // ITABFeatureFont
1278 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
1279 :
1280 0 : poNew->SetSymbolAngle(GetSymbolAngle());
1281 0 : poNew->SetFontStyleTABValue(GetFontStyleTABValue());
1282 :
1283 0 : return poNew;
1284 : }
1285 :
1286 : /**********************************************************************
1287 : * TABFontPoint::ReadGeometryFromMAPFile()
1288 : *
1289 : * Fill the geometry and representation (color, etc...) part of the
1290 : * feature from the contents of the .MAP object pointed to by poMAPFile.
1291 : *
1292 : * It is assumed that poMAPFile currently points to the beginning of
1293 : * a map object.
1294 : *
1295 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1296 : * been called.
1297 : **********************************************************************/
1298 6 : int TABFontPoint::ReadGeometryFromMAPFile(
1299 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1300 : GBool bCoordBlockDataOnly /*=FALSE*/,
1301 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1302 : {
1303 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1304 6 : if (bCoordBlockDataOnly)
1305 0 : return 0;
1306 :
1307 : /*-----------------------------------------------------------------
1308 : * Fetch and validate geometry type
1309 : *----------------------------------------------------------------*/
1310 6 : m_nMapInfoType = poObjHdr->m_nType;
1311 :
1312 6 : if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
1313 0 : m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
1314 : {
1315 0 : CPLError(
1316 : CE_Failure, CPLE_AssertionFailed,
1317 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1318 0 : m_nMapInfoType, m_nMapInfoType);
1319 0 : return -1;
1320 : }
1321 :
1322 : /*-----------------------------------------------------------------
1323 : * Read object information
1324 : * NOTE: This symbol type does not contain a reference to a
1325 : * SymbolDef block in the file, but we still use the m_sSymbolDef
1326 : * structure to store the information inside the class so that the
1327 : * ITABFeatureSymbol methods work properly for the class user.
1328 : *----------------------------------------------------------------*/
1329 : TABMAPObjFontPoint *poPointHdr =
1330 6 : cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1331 :
1332 6 : m_nSymbolDefIndex = -1;
1333 6 : m_sSymbolDef.nRefCount = 0;
1334 :
1335 6 : m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId; // shape
1336 6 : m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize; // point size
1337 :
1338 6 : m_nFontStyle = poPointHdr->m_nFontStyle; // font style
1339 :
1340 6 : m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
1341 6 : poPointHdr->m_nG * 256 + poPointHdr->m_nB;
1342 :
1343 : /*-------------------------------------------------------------
1344 : * Symbol Angle, in tenths of degree.
1345 : * Contrary to arc start/end angles, no conversion based on
1346 : * origin quadrant is required here.
1347 : *------------------------------------------------------------*/
1348 6 : m_dAngle = poPointHdr->m_nAngle / 10.0;
1349 :
1350 6 : m_nFontDefIndex = poPointHdr->m_nFontId; // Font name index
1351 :
1352 6 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1353 :
1354 : /*-----------------------------------------------------------------
1355 : * Create and fill geometry object
1356 : *----------------------------------------------------------------*/
1357 6 : double dX = 0.0;
1358 6 : double dY = 0.0;
1359 6 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1360 6 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1361 :
1362 6 : SetGeometryDirectly(poGeometry);
1363 :
1364 6 : SetMBR(dX, dY, dX, dY);
1365 6 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1366 : poObjHdr->m_nMaxY);
1367 :
1368 6 : return 0;
1369 : }
1370 :
1371 : /**********************************************************************
1372 : * TABFontPoint::WriteGeometryToMAPFile()
1373 : *
1374 : * Write the geometry and representation (color, etc...) part of the
1375 : * feature to the .MAP object pointed to by poMAPFile.
1376 : *
1377 : * It is assumed that poMAPFile currently points to a valid map object.
1378 : *
1379 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1380 : * been called.
1381 : **********************************************************************/
1382 2 : int TABFontPoint::WriteGeometryToMAPFile(
1383 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1384 : GBool bCoordBlockDataOnly /*=FALSE*/,
1385 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1386 : {
1387 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1388 2 : if (bCoordBlockDataOnly)
1389 0 : return 0;
1390 :
1391 : /*-----------------------------------------------------------------
1392 : * We assume that ValidateMapInfoType() was called already and that
1393 : * the type in poObjHdr->m_nType is valid.
1394 : *----------------------------------------------------------------*/
1395 2 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1396 :
1397 : /*-----------------------------------------------------------------
1398 : * Fetch and validate geometry
1399 : *----------------------------------------------------------------*/
1400 2 : OGRGeometry *poGeom = GetGeometryRef();
1401 2 : OGRPoint *poPoint = nullptr;
1402 2 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1403 2 : poPoint = poGeom->toPoint();
1404 : else
1405 : {
1406 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1407 : "TABFontPoint: Missing or Invalid Geometry!");
1408 0 : return -1;
1409 : }
1410 :
1411 2 : GInt32 nX = 0;
1412 2 : GInt32 nY = 0;
1413 2 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1414 :
1415 : /*-----------------------------------------------------------------
1416 : * Copy object information
1417 : * NOTE: This symbol type does not contain a reference to a
1418 : * SymbolDef block in the file, but we still use the m_sSymbolDef
1419 : * structure to store the information inside the class so that the
1420 : * ITABFeatureSymbol methods work properly for the class user.
1421 : *----------------------------------------------------------------*/
1422 : TABMAPObjFontPoint *poPointHdr =
1423 2 : cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1424 :
1425 2 : poPointHdr->m_nX = nX;
1426 2 : poPointHdr->m_nY = nY;
1427 2 : poPointHdr->SetMBR(nX, nY, nX, nY);
1428 :
1429 2 : poPointHdr->m_nSymbolId =
1430 2 : static_cast<GByte>(m_sSymbolDef.nSymbolNo); // shape
1431 2 : poPointHdr->m_nPointSize =
1432 2 : static_cast<GByte>(m_sSymbolDef.nPointSize); // point size
1433 2 : poPointHdr->m_nFontStyle = m_nFontStyle; // font style
1434 :
1435 2 : poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
1436 2 : poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
1437 2 : poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
1438 :
1439 : /*-------------------------------------------------------------
1440 : * Symbol Angle, in tenths of degree.
1441 : * Contrary to arc start/end angles, no conversion based on
1442 : * origin quadrant is required here.
1443 : *------------------------------------------------------------*/
1444 2 : poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
1445 :
1446 : // Write Font Def
1447 2 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1448 2 : poPointHdr->m_nFontId =
1449 2 : static_cast<GByte>(m_nFontDefIndex); // Font name index
1450 :
1451 2 : if (CPLGetLastErrorType() == CE_Failure)
1452 0 : return -1;
1453 :
1454 2 : return 0;
1455 : }
1456 :
1457 : /**********************************************************************
1458 : * TABFontPoint::QueryFontStyle()
1459 : *
1460 : * Return TRUE if the specified font style attribute is turned ON,
1461 : * or FALSE otherwise. See enum TABFontStyle for the list of styles
1462 : * that can be queried on.
1463 : **********************************************************************/
1464 0 : GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
1465 : {
1466 0 : return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
1467 : }
1468 :
1469 0 : void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
1470 : {
1471 0 : if (bStyleOn)
1472 0 : m_nFontStyle |= static_cast<int>(eStyleToToggle);
1473 : else
1474 0 : m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
1475 0 : }
1476 :
1477 : /**********************************************************************
1478 : * TABFontPoint::GetFontStyleMIFValue()
1479 : *
1480 : * Return the Font Style value for this object using the style values
1481 : * that are used in a MIF FONT() clause. See MIF specs (appendix A).
1482 : *
1483 : * The reason why we have to differentiate between the TAB and the MIF font
1484 : * style values is that in TAB, TABFSBox is included in the style value
1485 : * as code 0x100, but in MIF it is not included, instead it is implied by
1486 : * the presence of the BG color in the FONT() clause (the BG color is
1487 : * present only when TABFSBox or TABFSHalo is set).
1488 : * This also has the effect of shifting all the other style values > 0x100
1489 : * by 1 byte.
1490 : *
1491 : * NOTE: Even if there is no BG color for font symbols, we inherit this
1492 : * problem because Font Point styles use the same codes as Text Font styles.
1493 : **********************************************************************/
1494 0 : int TABFontPoint::GetFontStyleMIFValue()
1495 : {
1496 : // The conversion is simply to remove bit 0x100 from the value and shift
1497 : // down all values past this bit.
1498 0 : return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
1499 : }
1500 :
1501 635 : void TABFontPoint::SetFontStyleMIFValue(int nStyle)
1502 : {
1503 635 : m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
1504 635 : }
1505 :
1506 : /**********************************************************************
1507 : * TABFontPoint::SetSymbolAngle()
1508 : *
1509 : * Set the symbol angle value in degrees, making sure the value is
1510 : * always in the range [0..360]
1511 : **********************************************************************/
1512 635 : void TABFontPoint::SetSymbolAngle(double dAngle)
1513 : {
1514 635 : dAngle = fmod(dAngle, 360.0);
1515 635 : if (dAngle < 0.0)
1516 0 : dAngle += 360.0;
1517 :
1518 635 : m_dAngle = dAngle;
1519 635 : }
1520 :
1521 : /**********************************************************************
1522 : * TABFontPoint::GetSymbolStyleString()
1523 : *
1524 : * Return a Symbol() string. All representations info for the Symbol are here.
1525 : **********************************************************************/
1526 7 : const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
1527 : {
1528 : /* Get the SymbolStyleString, and add the outline Color
1529 : (halo/border in MapInfo Symbol terminology) */
1530 7 : const char *outlineColor = nullptr;
1531 7 : if (m_nFontStyle & 16)
1532 0 : outlineColor = ",o:#000000";
1533 7 : else if (m_nFontStyle & 512)
1534 0 : outlineColor = ",o:#ffffff";
1535 : else
1536 7 : outlineColor = "";
1537 :
1538 7 : int nAngle = static_cast<int>(dfAngle);
1539 : const char *pszStyle;
1540 :
1541 7 : pszStyle = CPLSPrintf(
1542 : "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
1543 7 : nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
1544 7 : m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
1545 7 : return pszStyle;
1546 : }
1547 :
1548 : /**********************************************************************
1549 : * TABFontPoint::GetStyleString() const
1550 : *
1551 : * Return style string for this feature.
1552 : *
1553 : * Style String is built only once during the first call to GetStyleString().
1554 : **********************************************************************/
1555 9 : const char *TABFontPoint::GetStyleString() const
1556 : {
1557 9 : if (m_pszStyleString == nullptr)
1558 : {
1559 7 : m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
1560 : }
1561 :
1562 9 : return m_pszStyleString;
1563 : }
1564 :
1565 : /**********************************************************************
1566 : * TABFontPoint::SetSymbolFromStyle()
1567 : *
1568 : * Set all Symbol var from a OGRStyleSymbol.
1569 : **********************************************************************/
1570 2 : void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1571 : {
1572 2 : ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1573 :
1574 2 : GBool bIsNull = 0;
1575 :
1576 : // Try to set font glyph number
1577 2 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1578 2 : if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
1579 : {
1580 2 : const int nSymbolId = atoi(pszSymbolId + 9);
1581 2 : SetSymbolNo(static_cast<GInt16>(nSymbolId));
1582 : }
1583 :
1584 2 : const char *pszFontName = poSymbolStyle->FontName(bIsNull);
1585 2 : if ((!bIsNull) && pszFontName)
1586 : {
1587 2 : SetFontName(pszFontName);
1588 : }
1589 2 : }
1590 :
1591 : /*=====================================================================
1592 : * class TABCustomPoint
1593 : *====================================================================*/
1594 :
1595 : /**********************************************************************
1596 : * TABCustomPoint::TABCustomPoint()
1597 : *
1598 : * Constructor.
1599 : **********************************************************************/
1600 686 : TABCustomPoint::TABCustomPoint(const OGRFeatureDefn *poDefnIn)
1601 686 : : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
1602 : {
1603 686 : }
1604 :
1605 : /**********************************************************************
1606 : * TABCustomPoint::~TABCustomPoint()
1607 : *
1608 : * Destructor.
1609 : **********************************************************************/
1610 1372 : TABCustomPoint::~TABCustomPoint()
1611 : {
1612 1372 : }
1613 :
1614 : /**********************************************************************
1615 : * TABCustomPoint::CloneTABFeature()
1616 : *
1617 : * Duplicate feature, including stuff specific to each TABFeature type.
1618 : *
1619 : * This method calls the generic TABFeature::CloneTABFeature() and
1620 : * then copies any members specific to its own type.
1621 : **********************************************************************/
1622 : TABFeature *
1623 0 : TABCustomPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
1624 : {
1625 : /*-----------------------------------------------------------------
1626 : * Alloc new feature and copy the base stuff
1627 : *----------------------------------------------------------------*/
1628 : TABCustomPoint *poNew =
1629 0 : new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
1630 :
1631 0 : CopyTABFeatureBase(poNew);
1632 :
1633 : /*-----------------------------------------------------------------
1634 : * And members specific to this class
1635 : *----------------------------------------------------------------*/
1636 : // ITABFeatureSymbol
1637 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1638 :
1639 : // ITABFeatureFont
1640 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
1641 :
1642 0 : poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
1643 :
1644 0 : return poNew;
1645 : }
1646 :
1647 : /**********************************************************************
1648 : * TABCustomPoint::ReadGeometryFromMAPFile()
1649 : *
1650 : * Fill the geometry and representation (color, etc...) part of the
1651 : * feature from the contents of the .MAP object pointed to by poMAPFile.
1652 : *
1653 : * It is assumed that poMAPFile currently points to the beginning of
1654 : * a map object.
1655 : *
1656 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1657 : * been called.
1658 : **********************************************************************/
1659 6 : int TABCustomPoint::ReadGeometryFromMAPFile(
1660 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1661 : GBool bCoordBlockDataOnly /*=FALSE*/,
1662 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1663 : {
1664 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1665 6 : if (bCoordBlockDataOnly)
1666 0 : return 0;
1667 :
1668 : /*-----------------------------------------------------------------
1669 : * Fetch and validate geometry type
1670 : *----------------------------------------------------------------*/
1671 6 : m_nMapInfoType = poObjHdr->m_nType;
1672 :
1673 6 : if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
1674 0 : m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
1675 : {
1676 0 : CPLError(
1677 : CE_Failure, CPLE_AssertionFailed,
1678 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1679 0 : m_nMapInfoType, m_nMapInfoType);
1680 0 : return -1;
1681 : }
1682 :
1683 : /*-----------------------------------------------------------------
1684 : * Read object information
1685 : *----------------------------------------------------------------*/
1686 : TABMAPObjCustomPoint *poPointHdr =
1687 6 : cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1688 :
1689 6 : m_nUnknown_ = poPointHdr->m_nUnknown_; // ???
1690 6 : m_nCustomStyle = poPointHdr->m_nCustomStyle; // 0x01=Show BG,
1691 : // 0x02=Apply Color
1692 :
1693 6 : m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index
1694 6 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1695 :
1696 6 : m_nFontDefIndex = poPointHdr->m_nFontId; // Font index
1697 6 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1698 :
1699 : /*-----------------------------------------------------------------
1700 : * Create and fill geometry object
1701 : *----------------------------------------------------------------*/
1702 6 : double dX = 0.0;
1703 6 : double dY = 0.0;
1704 6 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1705 6 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1706 :
1707 6 : SetGeometryDirectly(poGeometry);
1708 :
1709 6 : SetMBR(dX, dY, dX, dY);
1710 6 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1711 : poObjHdr->m_nMaxY);
1712 :
1713 6 : return 0;
1714 : }
1715 :
1716 : /**********************************************************************
1717 : * TABCustomPoint::WriteGeometryToMAPFile()
1718 : *
1719 : * Write the geometry and representation (color, etc...) part of the
1720 : * feature to the .MAP object pointed to by poMAPFile.
1721 : *
1722 : * It is assumed that poMAPFile currently points to a valid map object.
1723 : *
1724 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1725 : * been called.
1726 : **********************************************************************/
1727 2 : int TABCustomPoint::WriteGeometryToMAPFile(
1728 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1729 : GBool bCoordBlockDataOnly /*=FALSE*/,
1730 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1731 : {
1732 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1733 2 : if (bCoordBlockDataOnly)
1734 0 : return 0;
1735 :
1736 : /*-----------------------------------------------------------------
1737 : * We assume that ValidateMapInfoType() was called already and that
1738 : * the type in poObjHdr->m_nType is valid.
1739 : *----------------------------------------------------------------*/
1740 2 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1741 :
1742 : /*-----------------------------------------------------------------
1743 : * Fetch and validate geometry
1744 : *----------------------------------------------------------------*/
1745 2 : OGRGeometry *poGeom = GetGeometryRef();
1746 2 : OGRPoint *poPoint = nullptr;
1747 2 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1748 2 : poPoint = poGeom->toPoint();
1749 : else
1750 : {
1751 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1752 : "TABCustomPoint: Missing or Invalid Geometry!");
1753 0 : return -1;
1754 : }
1755 :
1756 2 : GInt32 nX = 0;
1757 2 : GInt32 nY = 0;
1758 2 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1759 :
1760 : /*-----------------------------------------------------------------
1761 : * Copy object information
1762 : *----------------------------------------------------------------*/
1763 : TABMAPObjCustomPoint *poPointHdr =
1764 2 : cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1765 :
1766 2 : poPointHdr->m_nX = nX;
1767 2 : poPointHdr->m_nY = nY;
1768 2 : poPointHdr->SetMBR(nX, nY, nX, nY);
1769 2 : poPointHdr->m_nUnknown_ = m_nUnknown_;
1770 2 : poPointHdr->m_nCustomStyle = m_nCustomStyle; // 0x01=Show BG,
1771 : // 0x02=Apply Color
1772 :
1773 2 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1774 2 : poPointHdr->m_nSymbolId =
1775 2 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
1776 :
1777 2 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1778 2 : poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex); // Font index
1779 :
1780 2 : if (CPLGetLastErrorType() == CE_Failure)
1781 0 : return -1;
1782 :
1783 2 : return 0;
1784 : }
1785 :
1786 : /**********************************************************************
1787 : * TABCustomPoint::GetSymbolStyleString()
1788 : *
1789 : * Return a Symbol() string. All representations info for the Symbol are here.
1790 : **********************************************************************/
1791 7 : const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
1792 : {
1793 : /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
1794 : * "apply color". */
1795 7 : const char *color = nullptr;
1796 7 : if (m_nCustomStyle & 0x02)
1797 7 : color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
1798 : else
1799 0 : color = "";
1800 :
1801 7 : int nAngle = static_cast<int>(dfAngle);
1802 : const char *pszStyle;
1803 7 : const std::string osExt = CPLGetExtensionSafe(GetSymbolNameRef());
1804 7 : char szLowerExt[8] = "";
1805 7 : const char *pszPtr = osExt.c_str();
1806 : int i;
1807 :
1808 7 : for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
1809 : {
1810 0 : szLowerExt[i] =
1811 0 : static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
1812 : }
1813 7 : szLowerExt[i] = '\0';
1814 :
1815 7 : pszStyle = CPLSPrintf(
1816 : "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
1817 7 : nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
1818 : GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
1819 14 : return pszStyle;
1820 : }
1821 :
1822 : /**********************************************************************
1823 : * TABCustomPoint::SetSymbolFromStyle()
1824 : *
1825 : * Set all Symbol var from a OGRStyleSymbol.
1826 : **********************************************************************/
1827 2 : void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1828 : {
1829 2 : ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1830 :
1831 2 : GBool bIsNull = 0;
1832 :
1833 : // Try to set font glyph number
1834 2 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1835 2 : if ((!bIsNull) && pszSymbolId &&
1836 2 : STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
1837 : {
1838 2 : const int nSymbolStyle = atoi(pszSymbolId + 19);
1839 2 : SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
1840 :
1841 2 : const char *pszPtr = pszSymbolId + 19;
1842 4 : while (*pszPtr != '-')
1843 : {
1844 2 : pszPtr++;
1845 : }
1846 2 : pszPtr++;
1847 :
1848 2 : char szSymbolName[256] = "";
1849 : int i;
1850 2 : for (i = 0;
1851 8 : i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
1852 : i++, pszPtr++)
1853 : {
1854 6 : szSymbolName[i] = *pszPtr;
1855 : }
1856 2 : szSymbolName[i] = '\0';
1857 2 : SetSymbolName(szSymbolName);
1858 : }
1859 2 : }
1860 :
1861 : /**********************************************************************
1862 : * TABCustomPoint::GetStyleString() const
1863 : *
1864 : * Return style string for this feature.
1865 : *
1866 : * Style String is built only once during the first call to GetStyleString().
1867 : **********************************************************************/
1868 9 : const char *TABCustomPoint::GetStyleString() const
1869 : {
1870 9 : if (m_pszStyleString == nullptr)
1871 : {
1872 7 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1873 : }
1874 :
1875 9 : return m_pszStyleString;
1876 : }
1877 :
1878 : /*=====================================================================
1879 : * class TABPolyline
1880 : *====================================================================*/
1881 :
1882 : /**********************************************************************
1883 : * TABPolyline::TABPolyline()
1884 : *
1885 : * Constructor.
1886 : **********************************************************************/
1887 4248 : TABPolyline::TABPolyline(const OGRFeatureDefn *poDefnIn)
1888 : : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
1889 4248 : m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
1890 : {
1891 4248 : }
1892 :
1893 : /**********************************************************************
1894 : * TABPolyline::~TABPolyline()
1895 : *
1896 : * Destructor.
1897 : **********************************************************************/
1898 8496 : TABPolyline::~TABPolyline()
1899 : {
1900 8496 : }
1901 :
1902 : /**********************************************************************
1903 : * TABPolyline::CloneTABFeature()
1904 : *
1905 : * Duplicate feature, including stuff specific to each TABFeature type.
1906 : *
1907 : * This method calls the generic TABFeature::CloneTABFeature() and
1908 : * then copies any members specific to its own type.
1909 : **********************************************************************/
1910 : TABFeature *
1911 0 : TABPolyline::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
1912 : {
1913 : /*-----------------------------------------------------------------
1914 : * Alloc new feature and copy the base stuff
1915 : *----------------------------------------------------------------*/
1916 0 : TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
1917 :
1918 0 : CopyTABFeatureBase(poNew);
1919 :
1920 : /*-----------------------------------------------------------------
1921 : * And members specific to this class
1922 : *----------------------------------------------------------------*/
1923 : // ITABFeaturePen
1924 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
1925 :
1926 0 : poNew->m_bSmooth = m_bSmooth;
1927 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
1928 0 : poNew->m_dCenterX = m_dCenterX;
1929 0 : poNew->m_dCenterY = m_dCenterY;
1930 :
1931 0 : return poNew;
1932 : }
1933 :
1934 : /**********************************************************************
1935 : * TABPolyline::GetNumParts()
1936 : *
1937 : * Return the total number of parts in this object.
1938 : *
1939 : * Returns 0 if the geometry contained in the object is invalid or missing.
1940 : **********************************************************************/
1941 0 : int TABPolyline::GetNumParts()
1942 : {
1943 0 : int numParts = 0;
1944 :
1945 0 : OGRGeometry *poGeom = GetGeometryRef();
1946 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1947 : {
1948 : /*-------------------------------------------------------------
1949 : * Simple polyline
1950 : *------------------------------------------------------------*/
1951 0 : numParts = 1;
1952 : }
1953 0 : else if (poGeom &&
1954 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1955 : {
1956 : /*-------------------------------------------------------------
1957 : * Multiple polyline
1958 : *------------------------------------------------------------*/
1959 0 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1960 0 : numParts = poMultiLine->getNumGeometries();
1961 : }
1962 :
1963 0 : return numParts;
1964 : }
1965 :
1966 : /**********************************************************************
1967 : * TABPolyline::GetPartRef()
1968 : *
1969 : * Returns a reference to the specified OGRLineString number, hiding the
1970 : * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
1971 : *
1972 : * Returns NULL if the geometry contained in the object is invalid or
1973 : * missing or if the specified part index is invalid.
1974 : **********************************************************************/
1975 0 : OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
1976 : {
1977 0 : OGRGeometry *poGeom = GetGeometryRef();
1978 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1979 : nPartIndex == 0)
1980 : {
1981 : /*-------------------------------------------------------------
1982 : * Simple polyline
1983 : *------------------------------------------------------------*/
1984 0 : return poGeom->toLineString();
1985 : }
1986 0 : else if (poGeom &&
1987 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1988 : {
1989 : /*-------------------------------------------------------------
1990 : * Multiple polyline
1991 : *------------------------------------------------------------*/
1992 0 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1993 0 : if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
1994 : {
1995 0 : return poMultiLine->getGeometryRef(nPartIndex);
1996 : }
1997 : else
1998 0 : return nullptr;
1999 : }
2000 :
2001 0 : return nullptr;
2002 : }
2003 :
2004 : /**********************************************************************
2005 : * TABPolyline::ValidateMapInfoType()
2006 : *
2007 : * Check the feature's geometry part and return the corresponding
2008 : * mapinfo object type code. The m_nMapInfoType member will also
2009 : * be updated for further calls to GetMapInfoType();
2010 : *
2011 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
2012 : * is expected for this object class.
2013 : **********************************************************************/
2014 238 : TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
2015 : {
2016 : /*-----------------------------------------------------------------
2017 : * Fetch and validate geometry
2018 : *----------------------------------------------------------------*/
2019 238 : OGRGeometry *poGeom = GetGeometryRef();
2020 238 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2021 : {
2022 : /*-------------------------------------------------------------
2023 : * Simple polyline
2024 : *------------------------------------------------------------*/
2025 230 : OGRLineString *poLine = poGeom->toLineString();
2026 230 : if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
2027 : {
2028 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2029 : }
2030 230 : else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
2031 : {
2032 0 : m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2033 : }
2034 230 : else if (poLine->getNumPoints() > 2)
2035 : {
2036 207 : m_nMapInfoType = TAB_GEOM_PLINE;
2037 : }
2038 46 : else if ((poLine->getNumPoints() == 2) &&
2039 23 : (m_bWriteTwoPointLineAsPolyline == TRUE))
2040 : {
2041 0 : m_nMapInfoType = TAB_GEOM_PLINE;
2042 : }
2043 46 : else if ((poLine->getNumPoints() == 2) &&
2044 23 : (m_bWriteTwoPointLineAsPolyline == FALSE))
2045 : {
2046 23 : m_nMapInfoType = TAB_GEOM_LINE;
2047 : }
2048 : else
2049 : {
2050 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2051 : "TABPolyline: Geometry must contain at least 2 points.");
2052 0 : m_nMapInfoType = TAB_GEOM_NONE;
2053 : }
2054 : }
2055 16 : else if (poGeom &&
2056 8 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2057 : {
2058 : /*-------------------------------------------------------------
2059 : * Multiple polyline... validate all components
2060 : *------------------------------------------------------------*/
2061 8 : GInt32 numPointsTotal = 0;
2062 8 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2063 8 : int numLines = poMultiLine->getNumGeometries();
2064 :
2065 8 : m_nMapInfoType = TAB_GEOM_MULTIPLINE;
2066 :
2067 18 : for (int iLine = 0; iLine < numLines; iLine++)
2068 : {
2069 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2070 20 : if (poGeom == nullptr ||
2071 10 : wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2072 : {
2073 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2074 : "TABPolyline: Object contains an invalid Geometry!");
2075 0 : m_nMapInfoType = TAB_GEOM_NONE;
2076 0 : numPointsTotal = 0;
2077 0 : break;
2078 : }
2079 10 : OGRLineString *poLine = poGeom->toLineString();
2080 10 : numPointsTotal += poLine->getNumPoints();
2081 : }
2082 :
2083 8 : if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
2084 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2085 8 : else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
2086 0 : m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2087 : }
2088 : else
2089 : {
2090 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2091 : "TABPolyline: Missing or Invalid Geometry!");
2092 0 : m_nMapInfoType = TAB_GEOM_NONE;
2093 : }
2094 :
2095 : /*-----------------------------------------------------------------
2096 : * Decide if coordinates should be compressed or not.
2097 : *
2098 : * __TODO__ We never write type LINE (2 points line) as compressed
2099 : * for the moment. If we ever do it, then the decision to write
2100 : * a 2 point line in compressed coordinates or not should take into
2101 : * account the location of the object block MBR, so this would be
2102 : * better handled directly by TABMAPObjLine::WriteObject() since the
2103 : * object block center is not known until it is written to disk.
2104 : *----------------------------------------------------------------*/
2105 238 : if (m_nMapInfoType != TAB_GEOM_LINE)
2106 : {
2107 215 : ValidateCoordType(poMapFile);
2108 : }
2109 : else
2110 : {
2111 23 : UpdateMBR(poMapFile);
2112 : }
2113 :
2114 238 : return m_nMapInfoType;
2115 : }
2116 :
2117 : /**********************************************************************
2118 : * TABPolyline::ReadGeometryFromMAPFile()
2119 : *
2120 : * Fill the geometry and representation (color, etc...) part of the
2121 : * feature from the contents of the .MAP object pointed to by poMAPFile.
2122 : *
2123 : * It is assumed that poMAPFile currently points to the beginning of
2124 : * a map object.
2125 : *
2126 : * Returns 0 on success, -1 on error, in which case CPLError() will have
2127 : * been called.
2128 : **********************************************************************/
2129 1577 : int TABPolyline::ReadGeometryFromMAPFile(
2130 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2131 : GBool bCoordBlockDataOnly /*=FALSE*/,
2132 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2133 : {
2134 1577 : GInt32 nX = 0;
2135 1577 : GInt32 nY = 0;
2136 1577 : double dX = 0.0;
2137 1577 : double dY = 0.0;
2138 1577 : double dXMin = 0.0;
2139 1577 : double dYMin = 0.0;
2140 1577 : double dXMax = 0.0;
2141 1577 : double dYMax = 0.0;
2142 1577 : OGRGeometry *poGeometry = nullptr;
2143 1577 : GBool bComprCoord = poObjHdr->IsCompressedType();
2144 1577 : TABMAPCoordBlock *poCoordBlock = nullptr;
2145 :
2146 : /*-----------------------------------------------------------------
2147 : * Fetch and validate geometry type
2148 : *----------------------------------------------------------------*/
2149 1577 : m_nMapInfoType = poObjHdr->m_nType;
2150 :
2151 1577 : if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
2152 : {
2153 : /*=============================================================
2154 : * LINE (2 vertices)
2155 : *============================================================*/
2156 21 : TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2157 :
2158 21 : m_bSmooth = FALSE;
2159 :
2160 21 : auto poLine = new OGRLineString();
2161 21 : poGeometry = poLine;
2162 21 : poLine->setNumPoints(2);
2163 :
2164 21 : poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
2165 : dYMin);
2166 21 : poLine->setPoint(0, dXMin, dYMin);
2167 :
2168 21 : poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
2169 : dYMax);
2170 21 : poLine->setPoint(1, dXMax, dYMax);
2171 :
2172 21 : if (!bCoordBlockDataOnly)
2173 : {
2174 21 : m_nPenDefIndex = poLineHdr->m_nPenId; // Pen index
2175 21 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2176 21 : }
2177 : }
2178 1556 : else if (m_nMapInfoType == TAB_GEOM_PLINE ||
2179 1551 : m_nMapInfoType == TAB_GEOM_PLINE_C)
2180 : {
2181 : /*=============================================================
2182 : * PLINE ( > 2 vertices)
2183 : *============================================================*/
2184 :
2185 : /*-------------------------------------------------------------
2186 : * Copy data from poObjHdr
2187 : *------------------------------------------------------------*/
2188 1546 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2189 :
2190 1546 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2191 1546 : const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
2192 1546 : if (nCoordDataSize > 1024 * 1024 &&
2193 0 : nCoordDataSize > poMapFile->GetFileSize())
2194 : {
2195 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
2196 : nCoordDataSize);
2197 0 : return -1;
2198 : }
2199 : // numLineSections = poPLineHdr->m_numLineSections; // Always 1
2200 1546 : m_bSmooth = poPLineHdr->m_bSmooth;
2201 :
2202 : // Centroid/label point
2203 1546 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2204 : dX, dY);
2205 1546 : SetCenter(dX, dY);
2206 :
2207 : // Compressed coordinate origin (useful only in compressed case!)
2208 1546 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2209 1546 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2210 :
2211 : // MBR
2212 1546 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2213 : dYMin);
2214 1546 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2215 : dYMax);
2216 :
2217 1546 : if (!bCoordBlockDataOnly)
2218 : {
2219 1378 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
2220 1378 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2221 : }
2222 :
2223 : /*-------------------------------------------------------------
2224 : * Create Geometry and read coordinates
2225 : *------------------------------------------------------------*/
2226 1546 : const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
2227 :
2228 1546 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2229 168 : poCoordBlock = *ppoCoordBlock;
2230 : else
2231 1378 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2232 1546 : if (poCoordBlock == nullptr)
2233 : {
2234 0 : CPLError(CE_Failure, CPLE_FileIO,
2235 : "Can't access coordinate block at offset %d",
2236 : nCoordBlockPtr);
2237 0 : return -1;
2238 : }
2239 :
2240 1546 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2241 :
2242 1546 : auto poLine = new OGRLineString();
2243 1546 : poGeometry = poLine;
2244 1546 : poLine->setNumPoints(numPoints);
2245 :
2246 1546 : int nStatus = 0;
2247 11886 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2248 : {
2249 10340 : nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
2250 10340 : if (nStatus != 0)
2251 0 : break;
2252 10340 : poMapFile->Int2Coordsys(nX, nY, dX, dY);
2253 10340 : poLine->setPoint(i, dX, dY);
2254 : }
2255 :
2256 1546 : if (nStatus != 0)
2257 : {
2258 : // Failed ... error message has already been produced
2259 0 : delete poGeometry;
2260 0 : return nStatus;
2261 1546 : }
2262 : }
2263 10 : else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2264 10 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2265 4 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2266 4 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2267 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2268 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
2269 : {
2270 : /*=============================================================
2271 : * PLINE MULTIPLE
2272 : *============================================================*/
2273 10 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2274 :
2275 : /*-------------------------------------------------------------
2276 : * Copy data from poObjHdr
2277 : *------------------------------------------------------------*/
2278 10 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2279 :
2280 10 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2281 : /* GInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize; */
2282 10 : GInt32 numLineSections = poPLineHdr->m_numLineSections;
2283 10 : m_bSmooth = poPLineHdr->m_bSmooth;
2284 :
2285 : // Centroid/label point
2286 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2287 : dX, dY);
2288 10 : SetCenter(dX, dY);
2289 :
2290 : // Compressed coordinate origin (useful only in compressed case!)
2291 10 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2292 10 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2293 :
2294 : // MBR
2295 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2296 : dYMin);
2297 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2298 : dYMax);
2299 :
2300 10 : if (!bCoordBlockDataOnly)
2301 : {
2302 10 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
2303 10 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2304 : }
2305 :
2306 10 : const int nMinSizeOfSection = 24;
2307 10 : if (numLineSections > INT_MAX / nMinSizeOfSection)
2308 : {
2309 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2310 0 : return -1;
2311 : }
2312 10 : const GUInt32 nMinimumBytesForSections =
2313 10 : nMinSizeOfSection * numLineSections;
2314 10 : if (nMinimumBytesForSections > 1024 * 1024 &&
2315 0 : nMinimumBytesForSections > poMapFile->GetFileSize())
2316 : {
2317 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2318 0 : return -1;
2319 : }
2320 :
2321 : /*-------------------------------------------------------------
2322 : * Read data from the coord. block
2323 : *------------------------------------------------------------*/
2324 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2325 10 : VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
2326 10 : if (pasSecHdrs == nullptr)
2327 0 : return -1;
2328 :
2329 10 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2330 4 : poCoordBlock = *ppoCoordBlock;
2331 : else
2332 6 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2333 :
2334 10 : GInt32 numPointsTotal = 0;
2335 20 : if (poCoordBlock == nullptr ||
2336 10 : poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
2337 : numLineSections, pasSecHdrs,
2338 : numPointsTotal) != 0)
2339 : {
2340 0 : CPLError(CE_Failure, CPLE_FileIO,
2341 : "Failed reading coordinate data at offset %d",
2342 : nCoordBlockPtr);
2343 0 : CPLFree(pasSecHdrs);
2344 0 : return -1;
2345 : }
2346 :
2347 10 : const GUInt32 nMinimumBytesForPoints =
2348 10 : (bComprCoord ? 4 : 8) * numPointsTotal;
2349 10 : if (nMinimumBytesForPoints > 1024 * 1024 &&
2350 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
2351 : {
2352 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
2353 0 : CPLFree(pasSecHdrs);
2354 0 : return -1;
2355 : }
2356 :
2357 10 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2358 :
2359 : GInt32 *panXY = static_cast<GInt32 *>(
2360 10 : VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
2361 10 : if (panXY == nullptr)
2362 : {
2363 0 : CPLFree(pasSecHdrs);
2364 0 : return -1;
2365 : }
2366 :
2367 10 : if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
2368 : 0)
2369 : {
2370 0 : CPLError(CE_Failure, CPLE_FileIO,
2371 : "Failed reading coordinate data at offset %d",
2372 : nCoordBlockPtr);
2373 0 : CPLFree(pasSecHdrs);
2374 0 : CPLFree(panXY);
2375 0 : return -1;
2376 : }
2377 :
2378 : /*-------------------------------------------------------------
2379 : * Create a Geometry collection with one line geometry for
2380 : * each coordinates section
2381 : * If object contains only one section, then return a simple LineString
2382 : *------------------------------------------------------------*/
2383 10 : OGRMultiLineString *poMultiLine = nullptr;
2384 10 : if (numLineSections > 1)
2385 : {
2386 6 : poMultiLine = new OGRMultiLineString();
2387 6 : poGeometry = poMultiLine;
2388 : }
2389 :
2390 26 : for (int iSection = 0; iSection < numLineSections; iSection++)
2391 : {
2392 16 : const int numSectionVertices = pasSecHdrs[iSection].numVertices;
2393 16 : GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
2394 :
2395 16 : auto poLine = new OGRLineString();
2396 16 : poLine->setNumPoints(numSectionVertices);
2397 :
2398 48 : for (int i = 0; i < numSectionVertices; i++)
2399 : {
2400 32 : poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
2401 32 : poLine->setPoint(i, dX, dY);
2402 32 : pnXYPtr += 2;
2403 : }
2404 :
2405 16 : if (poGeometry == nullptr)
2406 4 : poGeometry = poLine;
2407 12 : else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
2408 : {
2409 0 : CPLAssert(false); // Just in case lower-level lib is modified
2410 : }
2411 : }
2412 :
2413 10 : CPLFree(pasSecHdrs);
2414 10 : CPLFree(panXY);
2415 : }
2416 : else
2417 : {
2418 0 : CPLError(
2419 : CE_Failure, CPLE_AssertionFailed,
2420 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
2421 0 : m_nMapInfoType, m_nMapInfoType);
2422 0 : return -1;
2423 : }
2424 :
2425 1577 : SetGeometryDirectly(poGeometry);
2426 :
2427 1577 : SetMBR(dXMin, dYMin, dXMax, dYMax);
2428 1577 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
2429 : poObjHdr->m_nMaxY);
2430 :
2431 : /* Return a ref to coord block so that caller can continue reading
2432 : * after the end of this object (used by TABCollection and index splitting)
2433 : */
2434 1577 : if (ppoCoordBlock)
2435 172 : *ppoCoordBlock = poCoordBlock;
2436 :
2437 1577 : return 0;
2438 : }
2439 :
2440 : /**********************************************************************
2441 : * TABPolyline::WriteGeometryToMAPFile()
2442 : *
2443 : * Write the geometry and representation (color, etc...) part of the
2444 : * feature to the .MAP object pointed to by poMAPFile.
2445 : *
2446 : * It is assumed that poMAPFile currently points to a valid map object.
2447 : *
2448 : * Returns 0 on success, -1 on error, in which case CPLError() will have
2449 : * been called.
2450 : **********************************************************************/
2451 406 : int TABPolyline::WriteGeometryToMAPFile(
2452 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2453 : GBool bCoordBlockDataOnly /*=FALSE*/,
2454 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2455 : {
2456 406 : GInt32 nX = 0;
2457 406 : GInt32 nY = 0;
2458 406 : OGRLineString *poLine = nullptr;
2459 406 : TABMAPCoordBlock *poCoordBlock = nullptr;
2460 :
2461 : /*-----------------------------------------------------------------
2462 : * We assume that ValidateMapInfoType() was called already and that
2463 : * the type in poObjHdr->m_nType is valid.
2464 : *----------------------------------------------------------------*/
2465 406 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
2466 406 : CPLErrorReset();
2467 :
2468 : /*-----------------------------------------------------------------
2469 : * Fetch and validate geometry
2470 : *----------------------------------------------------------------*/
2471 406 : OGRGeometry *poGeom = GetGeometryRef();
2472 :
2473 1195 : if ((m_nMapInfoType == TAB_GEOM_LINE ||
2474 406 : m_nMapInfoType == TAB_GEOM_LINE_C) &&
2475 835 : poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
2476 23 : (poLine = poGeom->toLineString())->getNumPoints() == 2)
2477 : {
2478 : /*=============================================================
2479 : * LINE (2 vertices)
2480 : *============================================================*/
2481 23 : TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2482 :
2483 23 : poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
2484 23 : poLineHdr->m_nX1, poLineHdr->m_nY1);
2485 23 : poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
2486 23 : poLineHdr->m_nX2, poLineHdr->m_nY2);
2487 23 : poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
2488 : poLineHdr->m_nY2);
2489 :
2490 23 : if (!bCoordBlockDataOnly)
2491 : {
2492 23 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2493 23 : poLineHdr->m_nPenId =
2494 23 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2495 : }
2496 : }
2497 1146 : else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
2498 383 : m_nMapInfoType == TAB_GEOM_PLINE_C) &&
2499 766 : poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2500 : {
2501 : /*=============================================================
2502 : * PLINE ( > 2 vertices and less than 32767 vertices)
2503 : *============================================================*/
2504 375 : GBool bCompressed = poObjHdr->IsCompressedType();
2505 :
2506 : /*-------------------------------------------------------------
2507 : * Process geometry first...
2508 : *------------------------------------------------------------*/
2509 375 : poLine = poGeom->toLineString();
2510 375 : const int numPoints = poLine->getNumPoints();
2511 375 : CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
2512 :
2513 375 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2514 168 : poCoordBlock = *ppoCoordBlock;
2515 : else
2516 207 : poCoordBlock = poMapFile->GetCurCoordBlock();
2517 375 : poCoordBlock->StartNewFeature();
2518 375 : const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2519 375 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2520 :
2521 375 : int nStatus = 0;
2522 5207 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2523 : {
2524 4832 : poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
2525 4832 : if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
2526 : 0)
2527 : {
2528 : // Failed ... error message has already been produced
2529 0 : return nStatus;
2530 : }
2531 : }
2532 :
2533 375 : const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2534 :
2535 : /*-------------------------------------------------------------
2536 : * Copy info to poObjHdr
2537 : *------------------------------------------------------------*/
2538 375 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2539 :
2540 375 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2541 375 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2542 375 : poPLineHdr->m_numLineSections = 1;
2543 :
2544 375 : poPLineHdr->m_bSmooth = m_bSmooth;
2545 :
2546 : // MBR
2547 375 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2548 :
2549 : // Polyline center/label point
2550 375 : double dX = 0.0;
2551 375 : double dY = 0.0;
2552 375 : if (GetCenter(dX, dY) != -1)
2553 : {
2554 375 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2555 375 : poPLineHdr->m_nLabelY);
2556 : }
2557 : else
2558 : {
2559 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
2560 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
2561 : }
2562 :
2563 : // Compressed coordinate origin (useful only in compressed case!)
2564 375 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2565 375 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2566 :
2567 375 : if (!bCoordBlockDataOnly)
2568 : {
2569 207 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2570 207 : poPLineHdr->m_nPenId =
2571 207 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2572 : }
2573 : }
2574 24 : else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2575 8 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2576 0 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2577 0 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2578 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2579 8 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
2580 16 : poGeom &&
2581 8 : (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
2582 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
2583 : {
2584 : /*=============================================================
2585 : * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
2586 : *============================================================*/
2587 :
2588 8 : CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2589 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2590 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2591 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2592 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2593 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
2594 :
2595 8 : int nStatus = 0;
2596 8 : OGREnvelope sEnvelope;
2597 8 : GBool bCompressed = poObjHdr->IsCompressedType();
2598 :
2599 : /*-------------------------------------------------------------
2600 : * Process geometry first...
2601 : *------------------------------------------------------------*/
2602 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2603 0 : poCoordBlock = *ppoCoordBlock;
2604 : else
2605 8 : poCoordBlock = poMapFile->GetCurCoordBlock();
2606 8 : poCoordBlock->StartNewFeature();
2607 8 : const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2608 8 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2609 :
2610 8 : OGRMultiLineString *poMultiLine = nullptr;
2611 8 : GInt32 numLines = 1;
2612 8 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2613 : {
2614 8 : poMultiLine = poGeom->toMultiLineString();
2615 8 : numLines = poMultiLine->getNumGeometries();
2616 : }
2617 : // else
2618 : // {
2619 : // poMultiLine = NULL;
2620 : // numLines = 1;
2621 : // }
2622 :
2623 : /*-------------------------------------------------------------
2624 : * Build and write array of coord sections headers
2625 : *------------------------------------------------------------*/
2626 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2627 8 : VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
2628 8 : if (pasSecHdrs == nullptr)
2629 : {
2630 0 : return -1;
2631 : }
2632 :
2633 : /*-------------------------------------------------------------
2634 : * In calculation of nDataOffset, we have to take into account that
2635 : * V450 header section uses int32 instead of int16 for numVertices
2636 : * and we add another 2 bytes to align with a 4 bytes boundary.
2637 : *------------------------------------------------------------*/
2638 8 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2639 :
2640 8 : const int nTotalHdrSizeUncompressed =
2641 8 : (nVersion >= 450 ? 28 : 24) * numLines;
2642 :
2643 8 : GInt32 numPointsTotal = 0;
2644 18 : for (int iLine = 0; iLine < numLines; iLine++)
2645 : {
2646 10 : if (poMultiLine)
2647 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2648 :
2649 20 : if (poGeom &&
2650 10 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2651 : {
2652 10 : poLine = poGeom->toLineString();
2653 10 : const GInt32 numPoints = poLine->getNumPoints();
2654 10 : poLine->getEnvelope(&sEnvelope);
2655 :
2656 10 : pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
2657 10 : pasSecHdrs[iLine].numHoles = 0; // It is a line!
2658 :
2659 10 : poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
2660 10 : pasSecHdrs[iLine].nXMin,
2661 10 : pasSecHdrs[iLine].nYMin);
2662 10 : poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
2663 10 : pasSecHdrs[iLine].nXMax,
2664 10 : pasSecHdrs[iLine].nYMax);
2665 10 : pasSecHdrs[iLine].nDataOffset =
2666 10 : nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
2667 10 : pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
2668 :
2669 10 : numPointsTotal += numPoints;
2670 : }
2671 : else
2672 : {
2673 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2674 : "TABPolyline: Object contains an invalid Geometry!");
2675 0 : nStatus = -1;
2676 : }
2677 : }
2678 :
2679 8 : if (nStatus == 0)
2680 8 : nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
2681 : pasSecHdrs, bCompressed);
2682 :
2683 8 : CPLFree(pasSecHdrs);
2684 8 : pasSecHdrs = nullptr;
2685 :
2686 8 : if (nStatus != 0)
2687 0 : return nStatus; // Error has already been reported.
2688 :
2689 : /*-------------------------------------------------------------
2690 : * Then write the coordinates themselves...
2691 : *------------------------------------------------------------*/
2692 18 : for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
2693 : {
2694 10 : if (poMultiLine)
2695 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2696 :
2697 20 : if (poGeom &&
2698 10 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2699 : {
2700 10 : poLine = poGeom->toLineString();
2701 10 : GInt32 numPoints = poLine->getNumPoints();
2702 :
2703 30 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2704 : {
2705 20 : poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
2706 : nX, nY);
2707 20 : if ((nStatus = poCoordBlock->WriteIntCoord(
2708 20 : nX, nY, bCompressed)) != 0)
2709 : {
2710 : // Failed ... error message has already been produced
2711 0 : return nStatus;
2712 : }
2713 : }
2714 : }
2715 : else
2716 : {
2717 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2718 : "TABPolyline: Object contains an invalid Geometry!");
2719 0 : return -1;
2720 : }
2721 : }
2722 :
2723 8 : const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2724 :
2725 : /*-------------------------------------------------------------
2726 : * ... and finally copy info to poObjHdr
2727 : *------------------------------------------------------------*/
2728 8 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2729 :
2730 8 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2731 8 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2732 8 : poPLineHdr->m_numLineSections = numLines;
2733 :
2734 8 : poPLineHdr->m_bSmooth = m_bSmooth;
2735 :
2736 : // MBR
2737 8 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2738 :
2739 : // Polyline center/label point
2740 8 : double dX = 0.0;
2741 8 : double dY = 0.0;
2742 8 : if (GetCenter(dX, dY) != -1)
2743 : {
2744 8 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2745 8 : poPLineHdr->m_nLabelY);
2746 : }
2747 : else
2748 : {
2749 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
2750 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
2751 : }
2752 :
2753 : // Compressed coordinate origin (useful only in compressed case!)
2754 8 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2755 8 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2756 :
2757 8 : if (!bCoordBlockDataOnly)
2758 : {
2759 8 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2760 8 : poPLineHdr->m_nPenId =
2761 8 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2762 : }
2763 : }
2764 : else
2765 : {
2766 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2767 : "TABPolyline: Object contains an invalid Geometry!");
2768 0 : return -1;
2769 : }
2770 :
2771 406 : if (CPLGetLastErrorType() == CE_Failure)
2772 0 : return -1;
2773 :
2774 : /* Return a ref to coord block so that caller can continue writing
2775 : * after the end of this object (used by index splitting)
2776 : */
2777 406 : if (ppoCoordBlock)
2778 168 : *ppoCoordBlock = poCoordBlock;
2779 :
2780 406 : return 0;
2781 : }
2782 :
2783 : /**********************************************************************
2784 : * TABPolyline::GetStyleString() const
2785 : *
2786 : * Return style string for this feature.
2787 : *
2788 : * Style String is built only once during the first call to GetStyleString().
2789 : **********************************************************************/
2790 44 : const char *TABPolyline::GetStyleString() const
2791 : {
2792 44 : if (m_pszStyleString == nullptr)
2793 : {
2794 35 : m_pszStyleString = CPLStrdup(GetPenStyleString());
2795 : }
2796 :
2797 44 : return m_pszStyleString;
2798 : }
2799 :
2800 : /**********************************************************************
2801 : * TABPolyline::DumpMIF()
2802 : *
2803 : * Dump feature geometry in a format similar to .MIF PLINEs.
2804 : **********************************************************************/
2805 0 : void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
2806 : {
2807 0 : OGRMultiLineString *poMultiLine = nullptr;
2808 0 : OGRLineString *poLine = nullptr;
2809 : int i, numPoints;
2810 :
2811 0 : if (fpOut == nullptr)
2812 0 : fpOut = stdout;
2813 :
2814 : /*-----------------------------------------------------------------
2815 : * Fetch and validate geometry
2816 : *----------------------------------------------------------------*/
2817 0 : OGRGeometry *poGeom = GetGeometryRef();
2818 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2819 : {
2820 : /*-------------------------------------------------------------
2821 : * Generate output for simple polyline
2822 : *------------------------------------------------------------*/
2823 0 : poLine = poGeom->toLineString();
2824 0 : numPoints = poLine->getNumPoints();
2825 0 : fprintf(fpOut, "PLINE %d\n", numPoints);
2826 0 : for (i = 0; i < numPoints; i++)
2827 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
2828 : }
2829 0 : else if (poGeom &&
2830 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2831 : {
2832 : /*-------------------------------------------------------------
2833 : * Generate output for multiple polyline
2834 : *------------------------------------------------------------*/
2835 : int iLine, numLines;
2836 0 : poMultiLine = poGeom->toMultiLineString();
2837 0 : numLines = poMultiLine->getNumGeometries();
2838 0 : fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
2839 0 : for (iLine = 0; iLine < numLines; iLine++)
2840 : {
2841 0 : poGeom = poMultiLine->getGeometryRef(iLine);
2842 0 : if (poGeom &&
2843 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2844 : {
2845 0 : poLine = poGeom->toLineString();
2846 0 : numPoints = poLine->getNumPoints();
2847 0 : fprintf(fpOut, " %d\n", numPoints);
2848 0 : for (i = 0; i < numPoints; i++)
2849 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
2850 : poLine->getY(i));
2851 : }
2852 : else
2853 : {
2854 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2855 : "TABPolyline: Object contains an invalid Geometry!");
2856 0 : return;
2857 : }
2858 : }
2859 : }
2860 : else
2861 : {
2862 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2863 : "TABPolyline: Missing or Invalid Geometry!");
2864 0 : return;
2865 : }
2866 :
2867 0 : if (m_bCenterIsSet)
2868 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
2869 :
2870 : // Finish with PEN/BRUSH/etc. clauses
2871 0 : DumpPenDef();
2872 :
2873 0 : fflush(fpOut);
2874 : }
2875 :
2876 : /**********************************************************************
2877 : * TABPolyline::GetCenter()
2878 : *
2879 : * Returns the center point of the line. Compute one if it was not
2880 : * explicitly set:
2881 : *
2882 : * In MapInfo, for a simple or multiple polyline (pline), the center point
2883 : * in the object definition is supposed to be either the center point of
2884 : * the pline or the first section of a multiple pline (if an odd number of
2885 : * points in the pline or first section), or the midway point between the
2886 : * two central points (if an even number of points involved).
2887 : *
2888 : * Returns 0 on success, -1 on error.
2889 : **********************************************************************/
2890 383 : int TABPolyline::GetCenter(double &dX, double &dY)
2891 : {
2892 383 : if (!m_bCenterIsSet)
2893 : {
2894 215 : OGRLineString *poLine = nullptr;
2895 :
2896 215 : OGRGeometry *poGeom = GetGeometryRef();
2897 215 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2898 : {
2899 207 : poLine = poGeom->toLineString();
2900 : }
2901 16 : else if (poGeom &&
2902 8 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2903 : {
2904 8 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2905 8 : if (poMultiLine->getNumGeometries() > 0)
2906 8 : poLine = poMultiLine->getGeometryRef(0);
2907 : }
2908 :
2909 215 : if (poLine && poLine->getNumPoints() > 0)
2910 : {
2911 215 : int i = poLine->getNumPoints() / 2;
2912 215 : if (poLine->getNumPoints() % 2 == 0)
2913 : {
2914 : // Return the midway between the 2 center points
2915 15 : m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
2916 15 : m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
2917 : }
2918 : else
2919 : {
2920 : // Return the center point
2921 200 : m_dCenterX = poLine->getX(i);
2922 200 : m_dCenterY = poLine->getY(i);
2923 : }
2924 215 : m_bCenterIsSet = TRUE;
2925 : }
2926 : }
2927 :
2928 383 : if (!m_bCenterIsSet)
2929 0 : return -1;
2930 :
2931 383 : dX = m_dCenterX;
2932 383 : dY = m_dCenterY;
2933 383 : return 0;
2934 : }
2935 :
2936 : /**********************************************************************
2937 : * TABPolyline::SetCenter()
2938 : *
2939 : * Set the X,Y coordinates to use as center point for the line.
2940 : **********************************************************************/
2941 1556 : void TABPolyline::SetCenter(double dX, double dY)
2942 : {
2943 1556 : m_dCenterX = dX;
2944 1556 : m_dCenterY = dY;
2945 1556 : m_bCenterIsSet = TRUE;
2946 1556 : }
2947 :
2948 : /**********************************************************************
2949 : * TABPolyline::TwoPointLineAsPolyline()
2950 : *
2951 : * Returns the value of m_bWriteTwoPointLineAsPolyline
2952 : **********************************************************************/
2953 0 : GBool TABPolyline::TwoPointLineAsPolyline()
2954 : {
2955 0 : return m_bWriteTwoPointLineAsPolyline;
2956 : }
2957 :
2958 : /**********************************************************************
2959 : * TABPolyline::TwoPointLineAsPolyline()
2960 : *
2961 : * Sets the value of m_bWriteTwoPointLineAsPolyline
2962 : **********************************************************************/
2963 0 : void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
2964 : {
2965 0 : m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
2966 0 : }
2967 :
2968 : /*=====================================================================
2969 : * class TABRegion
2970 : *====================================================================*/
2971 :
2972 : /**********************************************************************
2973 : * TABRegion::TABRegion()
2974 : *
2975 : * Constructor.
2976 : **********************************************************************/
2977 1190 : TABRegion::TABRegion(const OGRFeatureDefn *poDefnIn)
2978 : : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
2979 1190 : m_dCenterX(0.0), m_dCenterY(0.0)
2980 : {
2981 1190 : }
2982 :
2983 : /**********************************************************************
2984 : * TABRegion::~TABRegion()
2985 : *
2986 : * Destructor.
2987 : **********************************************************************/
2988 2380 : TABRegion::~TABRegion()
2989 : {
2990 2380 : }
2991 :
2992 : /**********************************************************************
2993 : * TABRegion::CloneTABFeature()
2994 : *
2995 : * Duplicate feature, including stuff specific to each TABFeature type.
2996 : *
2997 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
2998 : * then copies any members specific to its own type.
2999 : **********************************************************************/
3000 : TABFeature *
3001 0 : TABRegion::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
3002 : {
3003 : /*-----------------------------------------------------------------
3004 : * Alloc new feature and copy the base stuff
3005 : *----------------------------------------------------------------*/
3006 0 : TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
3007 :
3008 0 : CopyTABFeatureBase(poNew);
3009 :
3010 : /*-----------------------------------------------------------------
3011 : * And members specific to this class
3012 : *----------------------------------------------------------------*/
3013 : // ITABFeaturePen
3014 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
3015 :
3016 : // ITABFeatureBrush
3017 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
3018 :
3019 0 : poNew->m_bSmooth = m_bSmooth;
3020 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
3021 0 : poNew->m_dCenterX = m_dCenterX;
3022 0 : poNew->m_dCenterY = m_dCenterY;
3023 :
3024 0 : return poNew;
3025 : }
3026 :
3027 : /**********************************************************************
3028 : * TABRegion::ValidateMapInfoType()
3029 : *
3030 : * Check the feature's geometry part and return the corresponding
3031 : * mapinfo object type code. The m_nMapInfoType member will also
3032 : * be updated for further calls to GetMapInfoType();
3033 : *
3034 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
3035 : * is expected for this object class.
3036 : **********************************************************************/
3037 88 : TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
3038 : {
3039 : /*-----------------------------------------------------------------
3040 : * Fetch and validate geometry
3041 : *----------------------------------------------------------------*/
3042 88 : OGRGeometry *poGeom = GetGeometryRef();
3043 94 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3044 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3045 : {
3046 88 : GInt32 numPointsTotal = 0;
3047 88 : GInt32 numRings = GetNumRings();
3048 176 : for (int i = 0; i < numRings; i++)
3049 : {
3050 88 : OGRLinearRing *poRing = GetRingRef(i);
3051 88 : if (poRing)
3052 88 : numPointsTotal += poRing->getNumPoints();
3053 : }
3054 88 : if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
3055 0 : m_nMapInfoType = TAB_GEOM_V800_REGION;
3056 88 : else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
3057 0 : m_nMapInfoType = TAB_GEOM_V450_REGION;
3058 : else
3059 88 : m_nMapInfoType = TAB_GEOM_REGION;
3060 : }
3061 : else
3062 : {
3063 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3064 : "TABRegion: Missing or Invalid Geometry!");
3065 0 : m_nMapInfoType = TAB_GEOM_NONE;
3066 : }
3067 :
3068 : /*-----------------------------------------------------------------
3069 : * Decide if coordinates should be compressed or not.
3070 : *----------------------------------------------------------------*/
3071 88 : ValidateCoordType(poMapFile);
3072 :
3073 88 : return m_nMapInfoType;
3074 : }
3075 :
3076 : /**********************************************************************
3077 : * TABRegion::ReadGeometryFromMAPFile()
3078 : *
3079 : * Fill the geometry and representation (color, etc...) part of the
3080 : * feature from the contents of the .MAP object pointed to by poMAPFile.
3081 : *
3082 : * It is assumed that poMAPFile currently points to the beginning of
3083 : * a map object.
3084 : *
3085 : * Returns 0 on success, -1 on error, in which case CPLError() will have
3086 : * been called.
3087 : **********************************************************************/
3088 525 : int TABRegion::ReadGeometryFromMAPFile(
3089 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3090 : GBool bCoordBlockDataOnly /*=FALSE*/,
3091 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3092 : {
3093 525 : double dXMin = 0.0;
3094 525 : double dYMin = 0.0;
3095 525 : double dXMax = 0.0;
3096 525 : double dYMax = 0.0;
3097 525 : OGRGeometry *poGeometry = nullptr;
3098 525 : TABMAPCoordBlock *poCoordBlock = nullptr;
3099 :
3100 : /*-----------------------------------------------------------------
3101 : * Fetch and validate geometry type
3102 : *----------------------------------------------------------------*/
3103 525 : m_nMapInfoType = poObjHdr->m_nType;
3104 :
3105 525 : if (m_nMapInfoType == TAB_GEOM_REGION ||
3106 469 : m_nMapInfoType == TAB_GEOM_REGION_C ||
3107 4 : m_nMapInfoType == TAB_GEOM_V450_REGION ||
3108 4 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3109 0 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3110 0 : m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3111 : {
3112 : /*=============================================================
3113 : * REGION (Similar to PLINE MULTIPLE)
3114 : *============================================================*/
3115 : GInt32 /* nCoordDataSize, */ numPointsTotal;
3116 525 : OGRMultiPolygon *poMultiPolygon = nullptr;
3117 525 : OGRPolygon *poPolygon = nullptr;
3118 525 : GBool bComprCoord = poObjHdr->IsCompressedType();
3119 525 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3120 :
3121 : /*-------------------------------------------------------------
3122 : * Copy data from poObjHdr
3123 : *------------------------------------------------------------*/
3124 525 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3125 :
3126 525 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
3127 : /* nCoordDataSize = poPLineHdr->m_nCoordDataSize; */
3128 525 : GInt32 numLineSections = poPLineHdr->m_numLineSections;
3129 525 : m_bSmooth = poPLineHdr->m_bSmooth;
3130 :
3131 : // Centroid/label point
3132 525 : double dX = 0.0;
3133 525 : double dY = 0.0;
3134 525 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
3135 : dX, dY);
3136 525 : SetCenter(dX, dY);
3137 :
3138 : // Compressed coordinate origin (useful only in compressed case!)
3139 525 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
3140 525 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
3141 :
3142 : // MBR
3143 525 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
3144 : dYMin);
3145 525 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
3146 : dYMax);
3147 :
3148 525 : if (!bCoordBlockDataOnly)
3149 : {
3150 525 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
3151 525 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
3152 525 : m_nBrushDefIndex = poPLineHdr->m_nBrushId; // Brush index
3153 525 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
3154 : }
3155 :
3156 : /*-------------------------------------------------------------
3157 : * Read data from the coord. block
3158 : *------------------------------------------------------------*/
3159 :
3160 525 : const int nMinSizeOfSection = 24;
3161 525 : if (numLineSections > INT_MAX / nMinSizeOfSection)
3162 : {
3163 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3164 0 : return -1;
3165 : }
3166 525 : const GUInt32 nMinimumBytesForSections =
3167 525 : nMinSizeOfSection * numLineSections;
3168 525 : if (nMinimumBytesForSections > 1024 * 1024 &&
3169 0 : nMinimumBytesForSections > poMapFile->GetFileSize())
3170 : {
3171 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3172 0 : return -1;
3173 : }
3174 :
3175 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3176 525 : VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
3177 525 : if (pasSecHdrs == nullptr)
3178 0 : return -1;
3179 :
3180 525 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3181 4 : poCoordBlock = *ppoCoordBlock;
3182 : else
3183 521 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
3184 :
3185 525 : if (poCoordBlock)
3186 525 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3187 :
3188 1050 : if (poCoordBlock == nullptr ||
3189 525 : poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
3190 : numLineSections, pasSecHdrs,
3191 : numPointsTotal) != 0)
3192 : {
3193 0 : CPLError(CE_Failure, CPLE_FileIO,
3194 : "Failed reading coordinate data at offset %d",
3195 : nCoordBlockPtr);
3196 0 : CPLFree(pasSecHdrs);
3197 0 : return -1;
3198 : }
3199 :
3200 525 : const GUInt32 nMinimumBytesForPoints =
3201 525 : (bComprCoord ? 4 : 8) * numPointsTotal;
3202 525 : if (nMinimumBytesForPoints > 1024 * 1024 &&
3203 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
3204 : {
3205 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
3206 0 : CPLFree(pasSecHdrs);
3207 0 : return -1;
3208 : }
3209 :
3210 : GInt32 *panXY = static_cast<GInt32 *>(
3211 525 : VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
3212 525 : if (panXY == nullptr)
3213 : {
3214 0 : CPLFree(pasSecHdrs);
3215 0 : return -1;
3216 : }
3217 :
3218 525 : if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
3219 : 0)
3220 : {
3221 0 : CPLError(CE_Failure, CPLE_FileIO,
3222 : "Failed reading coordinate data at offset %d",
3223 : nCoordBlockPtr);
3224 0 : CPLFree(pasSecHdrs);
3225 0 : CPLFree(panXY);
3226 0 : return -1;
3227 : }
3228 :
3229 : /*-------------------------------------------------------------
3230 : * Decide if we should return an OGRPolygon or an OGRMultiPolygon
3231 : * depending on the number of outer rings found in CoordSecHdr blocks.
3232 : * The CoodSecHdr block for each outer ring in the region has a flag
3233 : * indicating the number of inner rings that follow.
3234 : * In older versions of the format, the count of inner rings was
3235 : * always zero, so in this case we would always return MultiPolygons.
3236 : *
3237 : * Note: The current implementation assumes that there cannot be
3238 : * holes inside holes (i.e. multiple levels of inner rings)... if
3239 : * that case was encountered then we would return an OGRMultiPolygon
3240 : * in which the topological relationship between the rings would
3241 : * be lost.
3242 : *------------------------------------------------------------*/
3243 525 : int numOuterRings = 0;
3244 1050 : for (int iSection = 0; iSection < numLineSections; iSection++)
3245 : {
3246 : // Count this as an outer ring.
3247 525 : numOuterRings++;
3248 : // Skip inner rings... so loop continues on an outer ring.
3249 525 : iSection += pasSecHdrs[iSection].numHoles;
3250 : }
3251 :
3252 525 : if (numOuterRings > 1)
3253 : {
3254 0 : poMultiPolygon = new OGRMultiPolygon;
3255 0 : poGeometry = poMultiPolygon;
3256 : }
3257 : else
3258 : {
3259 525 : poGeometry = nullptr; // Will be set later
3260 : }
3261 :
3262 : /*-------------------------------------------------------------
3263 : * OK, build the OGRGeometry object.
3264 : *------------------------------------------------------------*/
3265 525 : int numHolesToRead = 0;
3266 525 : poPolygon = nullptr;
3267 1050 : for (int iSection = 0; iSection < numLineSections; iSection++)
3268 : {
3269 :
3270 525 : if (poPolygon == nullptr)
3271 525 : poPolygon = new OGRPolygon();
3272 :
3273 525 : if (numHolesToRead < 1)
3274 525 : numHolesToRead = pasSecHdrs[iSection].numHoles;
3275 : else
3276 0 : numHolesToRead--;
3277 :
3278 525 : int numSectionVertices = pasSecHdrs[iSection].numVertices;
3279 525 : GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
3280 :
3281 525 : OGRLinearRing *poRing = new OGRLinearRing();
3282 525 : poRing->setNumPoints(numSectionVertices);
3283 :
3284 14261 : for (int i = 0; i < numSectionVertices; i++)
3285 : {
3286 13736 : poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
3287 13736 : poRing->setPoint(i, dX, dY);
3288 13736 : pnXYPtr += 2;
3289 : }
3290 :
3291 525 : poPolygon->addRingDirectly(poRing);
3292 525 : poRing = nullptr;
3293 :
3294 525 : if (numHolesToRead < 1)
3295 : {
3296 525 : if (numOuterRings > 1)
3297 : {
3298 0 : poMultiPolygon->addGeometryDirectly(poPolygon);
3299 : }
3300 : else
3301 : {
3302 525 : poGeometry = poPolygon;
3303 525 : CPLAssert(iSection == numLineSections - 1);
3304 : }
3305 :
3306 525 : poPolygon = nullptr; // We'll alloc a new polygon next loop.
3307 : }
3308 : }
3309 525 : delete poPolygon; // should only trigger on corrupted files
3310 :
3311 525 : CPLFree(pasSecHdrs);
3312 525 : CPLFree(panXY);
3313 : }
3314 : else
3315 : {
3316 0 : CPLError(
3317 : CE_Failure, CPLE_AssertionFailed,
3318 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
3319 0 : m_nMapInfoType, m_nMapInfoType);
3320 0 : return -1;
3321 : }
3322 :
3323 525 : SetGeometryDirectly(poGeometry);
3324 :
3325 525 : SetMBR(dXMin, dYMin, dXMax, dYMax);
3326 525 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
3327 : poObjHdr->m_nMaxY);
3328 :
3329 : /* Return a ref to coord block so that caller can continue reading
3330 : * after the end of this object (used by TABCollection and index splitting)
3331 : */
3332 525 : if (ppoCoordBlock)
3333 4 : *ppoCoordBlock = poCoordBlock;
3334 :
3335 525 : return 0;
3336 : }
3337 :
3338 : /**********************************************************************
3339 : * TABRegion::WriteGeometryToMAPFile()
3340 : *
3341 : * Write the geometry and representation (color, etc...) part of the
3342 : * feature to the .MAP object pointed to by poMAPFile.
3343 : *
3344 : * It is assumed that poMAPFile currently points to a valid map object.
3345 : *
3346 : * Returns 0 on success, -1 on error, in which case CPLError() will have
3347 : * been called.
3348 : **********************************************************************/
3349 88 : int TABRegion::WriteGeometryToMAPFile(
3350 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3351 : GBool bCoordBlockDataOnly /*=FALSE*/,
3352 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3353 : {
3354 : /*-----------------------------------------------------------------
3355 : * We assume that ValidateMapInfoType() was called already and that
3356 : * the type in poObjHdr->m_nType is valid.
3357 : *----------------------------------------------------------------*/
3358 88 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
3359 :
3360 : /*-----------------------------------------------------------------
3361 : * Fetch and validate geometry
3362 : *----------------------------------------------------------------*/
3363 88 : OGRGeometry *poGeom = GetGeometryRef();
3364 88 : TABMAPCoordBlock *poCoordBlock = nullptr;
3365 :
3366 258 : if ((m_nMapInfoType == TAB_GEOM_REGION ||
3367 82 : m_nMapInfoType == TAB_GEOM_REGION_C ||
3368 0 : m_nMapInfoType == TAB_GEOM_V450_REGION ||
3369 0 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3370 0 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3371 88 : m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
3372 176 : poGeom &&
3373 88 : (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3374 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3375 : {
3376 : /*=============================================================
3377 : * REGIONs are similar to PLINE MULTIPLE
3378 : *
3379 : * We accept both OGRPolygons (with one or multiple rings) and
3380 : * OGRMultiPolygons as input.
3381 : *============================================================*/
3382 88 : GBool bCompressed = poObjHdr->IsCompressedType();
3383 :
3384 : /*-------------------------------------------------------------
3385 : * Process geometry first...
3386 : *------------------------------------------------------------*/
3387 88 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3388 0 : poCoordBlock = *ppoCoordBlock;
3389 : else
3390 88 : poCoordBlock = poMapFile->GetCurCoordBlock();
3391 88 : poCoordBlock->StartNewFeature();
3392 88 : GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
3393 88 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3394 :
3395 : #ifdef TABDUMP
3396 : printf(/*ok*/
3397 : "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
3398 : m_nComprOrgX, m_nComprOrgY);
3399 : #endif
3400 : /*-------------------------------------------------------------
3401 : * Fetch total number of rings and build array of coord
3402 : * sections headers.
3403 : *------------------------------------------------------------*/
3404 88 : TABMAPCoordSecHdr *pasSecHdrs = nullptr;
3405 88 : int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
3406 88 : int nStatus = numRingsTotal == 0 ? -1 : 0;
3407 :
3408 : /*-------------------------------------------------------------
3409 : * Write the Coord. Section Header
3410 : *------------------------------------------------------------*/
3411 88 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3412 :
3413 88 : if (nStatus == 0)
3414 88 : nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
3415 : pasSecHdrs, bCompressed);
3416 :
3417 88 : CPLFree(pasSecHdrs);
3418 88 : pasSecHdrs = nullptr;
3419 :
3420 88 : if (nStatus != 0)
3421 0 : return nStatus; // Error has already been reported.
3422 :
3423 : /*-------------------------------------------------------------
3424 : * Go through all the rings in our OGRMultiPolygon or OGRPolygon
3425 : * to write the coordinates themselves...
3426 : *------------------------------------------------------------*/
3427 :
3428 88 : GInt32 nX = 0, nY = 0;
3429 176 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3430 : {
3431 88 : OGRLinearRing *poRing = GetRingRef(iRing);
3432 88 : if (poRing == nullptr)
3433 : {
3434 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3435 : "TABRegion: Object Geometry contains NULL rings!");
3436 0 : return -1;
3437 : }
3438 :
3439 88 : int numPoints = poRing->getNumPoints();
3440 :
3441 2515 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
3442 : {
3443 2427 : poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
3444 : nY);
3445 2427 : if ((nStatus =
3446 2427 : poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
3447 : {
3448 : // Failed ... error message has already been produced
3449 0 : return nStatus;
3450 : }
3451 : }
3452 : } /* for iRing*/
3453 :
3454 88 : GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
3455 :
3456 : /*-------------------------------------------------------------
3457 : * ... and finally copy info to poObjHdr
3458 : *------------------------------------------------------------*/
3459 88 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3460 :
3461 88 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
3462 88 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
3463 88 : poPLineHdr->m_numLineSections = numRingsTotal;
3464 :
3465 88 : poPLineHdr->m_bSmooth = m_bSmooth;
3466 :
3467 : // MBR
3468 88 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
3469 :
3470 : // Region center/label point
3471 88 : double dX = 0.0;
3472 88 : double dY = 0.0;
3473 88 : if (GetCenter(dX, dY) != -1)
3474 : {
3475 88 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
3476 88 : poPLineHdr->m_nLabelY);
3477 : }
3478 : else
3479 : {
3480 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
3481 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
3482 : }
3483 :
3484 : // Compressed coordinate origin (useful only in compressed case!)
3485 88 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
3486 88 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
3487 :
3488 88 : if (!bCoordBlockDataOnly)
3489 : {
3490 88 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
3491 88 : poPLineHdr->m_nPenId =
3492 88 : static_cast<GByte>(m_nPenDefIndex); // Pen index
3493 :
3494 88 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
3495 88 : poPLineHdr->m_nBrushId =
3496 88 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
3497 : }
3498 : }
3499 : else
3500 : {
3501 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3502 : "TABRegion: Object contains an invalid Geometry!");
3503 0 : return -1;
3504 : }
3505 :
3506 88 : if (CPLGetLastErrorType() == CE_Failure)
3507 0 : return -1;
3508 :
3509 : /* Return a ref to coord block so that caller can continue writing
3510 : * after the end of this object (used by index splitting)
3511 : */
3512 88 : if (ppoCoordBlock)
3513 0 : *ppoCoordBlock = poCoordBlock;
3514 :
3515 88 : return 0;
3516 : }
3517 :
3518 : /**********************************************************************
3519 : * TABRegion::GetNumRings()
3520 : *
3521 : * Return the total number of rings in this object making it look like
3522 : * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3523 : * of rings... hides the complexity of handling OGRMultiPolygons vs
3524 : * OGRPolygons, etc.
3525 : *
3526 : * Returns 0 if the geometry contained in the object is invalid or missing.
3527 : **********************************************************************/
3528 109 : int TABRegion::GetNumRings()
3529 : {
3530 109 : return ComputeNumRings(nullptr, nullptr);
3531 : }
3532 :
3533 197 : int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
3534 : TABMAPFile *poMapFile)
3535 : {
3536 197 : int numRingsTotal = 0;
3537 197 : int iLastSect = 0;
3538 :
3539 197 : if (ppasSecHdrs)
3540 88 : *ppasSecHdrs = nullptr;
3541 :
3542 197 : OGRGeometry *poGeom = GetGeometryRef();
3543 :
3544 209 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3545 12 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3546 : {
3547 : /*-------------------------------------------------------------
3548 : * Calculate total number of rings...
3549 : *------------------------------------------------------------*/
3550 197 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3551 : {
3552 24 : for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
3553 : {
3554 12 : numRingsTotal += poPolygon->getNumInteriorRings() + 1;
3555 :
3556 12 : if (ppasSecHdrs && poMapFile)
3557 : {
3558 6 : if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3559 6 : iLastSect) != 0)
3560 0 : return 0; // An error happened, return count=0
3561 : }
3562 : } // for
3563 : }
3564 : else
3565 : {
3566 185 : OGRPolygon *poPolygon = poGeom->toPolygon();
3567 185 : numRingsTotal = poPolygon->getNumInteriorRings() + 1;
3568 :
3569 185 : if (ppasSecHdrs && poMapFile)
3570 : {
3571 82 : if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3572 82 : iLastSect) != 0)
3573 0 : return 0; // An error happened, return count=0
3574 : }
3575 : }
3576 : }
3577 :
3578 : /*-----------------------------------------------------------------
3579 : * If we're generating section header blocks, then init the
3580 : * coordinate offset values.
3581 : *
3582 : * In calculation of nDataOffset, we have to take into account that
3583 : * V450 header section uses int32 instead of int16 for numVertices
3584 : * and we add another 2 bytes to align with a 4 bytes boundary.
3585 : *------------------------------------------------------------*/
3586 197 : const int nTotalHdrSizeUncompressed =
3587 394 : (m_nMapInfoType == TAB_GEOM_V450_REGION ||
3588 197 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3589 197 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3590 197 : m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3591 394 : ? 28 * numRingsTotal
3592 : : 24 * numRingsTotal;
3593 :
3594 197 : if (ppasSecHdrs)
3595 : {
3596 88 : int numPointsTotal = 0;
3597 88 : CPLAssert(iLastSect == numRingsTotal);
3598 176 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3599 : {
3600 88 : (*ppasSecHdrs)[iRing].nDataOffset =
3601 88 : nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
3602 88 : (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
3603 :
3604 88 : numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
3605 : }
3606 : }
3607 :
3608 197 : return numRingsTotal;
3609 : }
3610 :
3611 : /**********************************************************************
3612 : * TABRegion::AppendSecHdrs()
3613 : *
3614 : * (Private method)
3615 : *
3616 : * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
3617 : **********************************************************************/
3618 88 : int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
3619 : TABMAPCoordSecHdr *&pasSecHdrs,
3620 : TABMAPFile *poMapFile, int &iLastRing)
3621 : {
3622 : /*-------------------------------------------------------------
3623 : * Add a pasSecHdrs[] entry for each ring in this polygon.
3624 : * Note that the structs won't be fully initialized.
3625 : *------------------------------------------------------------*/
3626 88 : int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
3627 :
3628 88 : pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3629 88 : CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
3630 : sizeof(TABMAPCoordSecHdr)));
3631 :
3632 176 : for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
3633 : {
3634 88 : OGRLinearRing *poRing = nullptr;
3635 88 : OGREnvelope sEnvelope;
3636 :
3637 88 : if (iRing == 0)
3638 88 : poRing = poPolygon->getExteriorRing();
3639 : else
3640 0 : poRing = poPolygon->getInteriorRing(iRing - 1);
3641 :
3642 88 : if (poRing == nullptr)
3643 : {
3644 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3645 : "Assertion Failed: Encountered NULL ring in OGRPolygon");
3646 0 : return -1;
3647 : }
3648 :
3649 88 : poRing->getEnvelope(&sEnvelope);
3650 :
3651 88 : pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
3652 :
3653 88 : if (iRing == 0)
3654 88 : pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
3655 : else
3656 0 : pasSecHdrs[iLastRing].numHoles = 0;
3657 :
3658 88 : poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
3659 88 : pasSecHdrs[iLastRing].nXMin,
3660 88 : pasSecHdrs[iLastRing].nYMin);
3661 88 : poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
3662 88 : pasSecHdrs[iLastRing].nXMax,
3663 88 : pasSecHdrs[iLastRing].nYMax);
3664 :
3665 88 : iLastRing++;
3666 : } /* for iRing*/
3667 :
3668 88 : return 0;
3669 : }
3670 :
3671 : /**********************************************************************
3672 : * TABRegion::GetRingRef()
3673 : *
3674 : * Returns a reference to the specified ring number making it look like
3675 : * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3676 : * of rings... hides the complexity of handling OGRMultiPolygons vs
3677 : * OGRPolygons, etc.
3678 : *
3679 : * Returns NULL if the geometry contained in the object is invalid or
3680 : * missing or if the specified ring index is invalid.
3681 : **********************************************************************/
3682 197 : OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
3683 : {
3684 197 : OGRLinearRing *poRing = nullptr;
3685 :
3686 197 : OGRGeometry *poGeom = GetGeometryRef();
3687 :
3688 209 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3689 12 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3690 : {
3691 : /*-------------------------------------------------------------
3692 : * Establish number of polygons based on geometry type
3693 : *------------------------------------------------------------*/
3694 197 : OGRMultiPolygon *poMultiPolygon = nullptr;
3695 197 : int iCurRing = 0;
3696 197 : int numOGRPolygons = 0;
3697 :
3698 197 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3699 : {
3700 12 : poMultiPolygon = poGeom->toMultiPolygon();
3701 12 : numOGRPolygons = poMultiPolygon->getNumGeometries();
3702 : }
3703 : else
3704 : {
3705 185 : numOGRPolygons = 1;
3706 : }
3707 :
3708 : /*-------------------------------------------------------------
3709 : * Loop through polygons until we find the requested ring.
3710 : *------------------------------------------------------------*/
3711 197 : iCurRing = 0;
3712 394 : for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
3713 : iPoly++)
3714 : {
3715 197 : OGRPolygon *poPolygon = nullptr;
3716 197 : if (poMultiPolygon)
3717 12 : poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3718 : else
3719 185 : poPolygon = poGeom->toPolygon();
3720 :
3721 197 : int numIntRings = poPolygon->getNumInteriorRings();
3722 :
3723 197 : if (iCurRing == nRequestedRingIndex)
3724 : {
3725 197 : poRing = poPolygon->getExteriorRing();
3726 : }
3727 0 : else if (nRequestedRingIndex > iCurRing &&
3728 0 : nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3729 : {
3730 0 : poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
3731 0 : (iCurRing + 1));
3732 : }
3733 197 : iCurRing += numIntRings + 1;
3734 : }
3735 : }
3736 :
3737 197 : return poRing;
3738 : }
3739 :
3740 : /**********************************************************************
3741 : * TABRegion::RingIsHole()
3742 : *
3743 : * Return false if the requested ring index is the first of a polygon
3744 : **********************************************************************/
3745 0 : GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
3746 : {
3747 0 : OGRGeometry *poGeom = GetGeometryRef();
3748 :
3749 0 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3750 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3751 : {
3752 : /*-------------------------------------------------------------
3753 : * Establish number of polygons based on geometry type
3754 : *------------------------------------------------------------*/
3755 0 : OGRMultiPolygon *poMultiPolygon = nullptr;
3756 0 : int iCurRing = 0;
3757 0 : int numOGRPolygons = 0;
3758 :
3759 0 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3760 : {
3761 0 : poMultiPolygon = poGeom->toMultiPolygon();
3762 0 : numOGRPolygons = poMultiPolygon->getNumGeometries();
3763 : }
3764 : else
3765 : {
3766 0 : numOGRPolygons = 1;
3767 : }
3768 :
3769 : /*-------------------------------------------------------------
3770 : * Loop through polygons until we find the requested ring.
3771 : *------------------------------------------------------------*/
3772 0 : iCurRing = 0;
3773 0 : for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
3774 : {
3775 0 : OGRPolygon *poPolygon = nullptr;
3776 0 : if (poMultiPolygon)
3777 0 : poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3778 : else
3779 0 : poPolygon = poGeom->toPolygon();
3780 :
3781 0 : int numIntRings = poPolygon->getNumInteriorRings();
3782 :
3783 0 : if (iCurRing == nRequestedRingIndex)
3784 : {
3785 0 : return FALSE;
3786 : }
3787 0 : else if (nRequestedRingIndex > iCurRing &&
3788 0 : nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3789 : {
3790 0 : return TRUE;
3791 : }
3792 0 : iCurRing += numIntRings + 1;
3793 : }
3794 : }
3795 :
3796 0 : return FALSE;
3797 : }
3798 :
3799 : /**********************************************************************
3800 : * TABRegion::GetStyleString() const
3801 : *
3802 : * Return style string for this feature.
3803 : *
3804 : * Style String is built only once during the first call to GetStyleString().
3805 : **********************************************************************/
3806 67 : const char *TABRegion::GetStyleString() const
3807 : {
3808 67 : if (m_pszStyleString == nullptr)
3809 : {
3810 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
3811 : // to use temporary buffers
3812 39 : char *pszPen = CPLStrdup(GetPenStyleString());
3813 39 : char *pszBrush = CPLStrdup(GetBrushStyleString());
3814 :
3815 39 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
3816 :
3817 39 : CPLFree(pszPen);
3818 39 : CPLFree(pszBrush);
3819 : }
3820 :
3821 67 : return m_pszStyleString;
3822 : }
3823 :
3824 : /**********************************************************************
3825 : * TABRegion::DumpMIF()
3826 : *
3827 : * Dump feature geometry in a format similar to .MIF REGIONs.
3828 : **********************************************************************/
3829 0 : void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
3830 : {
3831 0 : if (fpOut == nullptr)
3832 0 : fpOut = stdout;
3833 :
3834 : /*-----------------------------------------------------------------
3835 : * Fetch and validate geometry
3836 : *----------------------------------------------------------------*/
3837 0 : OGRGeometry *poGeom = GetGeometryRef();
3838 0 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3839 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3840 : {
3841 : /*-------------------------------------------------------------
3842 : * Generate output for region
3843 : *
3844 : * Note that we want to handle both OGRPolygons and OGRMultiPolygons
3845 : * that's why we use the GetNumRings()/GetRingRef() interface.
3846 : *------------------------------------------------------------*/
3847 0 : int numRingsTotal = GetNumRings();
3848 :
3849 0 : fprintf(fpOut, "REGION %d\n", numRingsTotal);
3850 :
3851 0 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3852 : {
3853 0 : OGRLinearRing *poRing = GetRingRef(iRing);
3854 :
3855 0 : if (poRing == nullptr)
3856 : {
3857 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3858 : "TABRegion: Object Geometry contains NULL rings!");
3859 0 : return;
3860 : }
3861 :
3862 0 : const int numPoints = poRing->getNumPoints();
3863 0 : fprintf(fpOut, " %d\n", numPoints);
3864 0 : for (int i = 0; i < numPoints; i++)
3865 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
3866 : poRing->getY(i));
3867 : }
3868 : }
3869 : else
3870 : {
3871 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3872 : "TABRegion: Missing or Invalid Geometry!");
3873 0 : return;
3874 : }
3875 :
3876 0 : if (m_bCenterIsSet)
3877 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
3878 :
3879 : // Finish with PEN/BRUSH/etc. clauses
3880 0 : DumpPenDef();
3881 0 : DumpBrushDef();
3882 :
3883 0 : fflush(fpOut);
3884 : }
3885 :
3886 : /**********************************************************************
3887 : * TABRegion::GetCenter()
3888 : *
3889 : * Returns the center/label point of the region.
3890 : * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
3891 : * before.
3892 : *
3893 : * Returns 0 on success, -1 on error.
3894 : **********************************************************************/
3895 88 : int TABRegion::GetCenter(double &dX, double &dY)
3896 : {
3897 88 : if (!m_bCenterIsSet)
3898 : {
3899 : /*-------------------------------------------------------------
3900 : * Calculate label point. If we have a multipolygon then we use
3901 : * the first OGRPolygon in the feature to calculate the point.
3902 : *------------------------------------------------------------*/
3903 88 : OGRGeometry *poGeom = GetGeometryRef();
3904 88 : if (poGeom == nullptr)
3905 0 : return -1;
3906 :
3907 88 : OGRPolygon *poPolygon = nullptr;
3908 :
3909 88 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3910 : {
3911 6 : OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
3912 6 : if (poMultiPolygon->getNumGeometries() > 0)
3913 6 : poPolygon = poMultiPolygon->getGeometryRef(0);
3914 : }
3915 82 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
3916 : {
3917 82 : poPolygon = poGeom->toPolygon();
3918 : }
3919 :
3920 88 : OGRPoint oLabelPoint;
3921 176 : if (poPolygon != nullptr &&
3922 88 : OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
3923 : {
3924 88 : m_dCenterX = oLabelPoint.getX();
3925 88 : m_dCenterY = oLabelPoint.getY();
3926 : }
3927 : else
3928 : {
3929 0 : OGREnvelope oEnv;
3930 0 : poGeom->getEnvelope(&oEnv);
3931 0 : m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
3932 0 : m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
3933 : }
3934 :
3935 88 : m_bCenterIsSet = TRUE;
3936 : }
3937 :
3938 88 : if (!m_bCenterIsSet)
3939 0 : return -1;
3940 :
3941 88 : dX = m_dCenterX;
3942 88 : dY = m_dCenterY;
3943 88 : return 0;
3944 : }
3945 :
3946 : /**********************************************************************
3947 : * TABRegion::SetCenter()
3948 : *
3949 : * Set the X,Y coordinates to use as center/label point for the region.
3950 : **********************************************************************/
3951 527 : void TABRegion::SetCenter(double dX, double dY)
3952 : {
3953 527 : m_dCenterX = dX;
3954 527 : m_dCenterY = dY;
3955 527 : m_bCenterIsSet = TRUE;
3956 527 : }
3957 :
3958 : /*=====================================================================
3959 : * class TABRectangle
3960 : *====================================================================*/
3961 :
3962 : /**********************************************************************
3963 : * TABRectangle::TABRectangle()
3964 : *
3965 : * Constructor.
3966 : **********************************************************************/
3967 949 : TABRectangle::TABRectangle(const OGRFeatureDefn *poDefnIn)
3968 : : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
3969 949 : m_dRoundYRadius(0.0)
3970 : {
3971 949 : }
3972 :
3973 : /**********************************************************************
3974 : * TABRectangle::~TABRectangle()
3975 : *
3976 : * Destructor.
3977 : **********************************************************************/
3978 1898 : TABRectangle::~TABRectangle()
3979 : {
3980 1898 : }
3981 :
3982 : /**********************************************************************
3983 : * TABRectangle::CloneTABFeature()
3984 : *
3985 : * Duplicate feature, including stuff specific to each TABFeature type.
3986 : *
3987 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
3988 : * then copies any members specific to its own type.
3989 : **********************************************************************/
3990 : TABFeature *
3991 0 : TABRectangle::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
3992 : {
3993 : /*-----------------------------------------------------------------
3994 : * Alloc new feature and copy the base stuff
3995 : *----------------------------------------------------------------*/
3996 : TABRectangle *poNew =
3997 0 : new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
3998 :
3999 0 : CopyTABFeatureBase(poNew);
4000 :
4001 : /*-----------------------------------------------------------------
4002 : * And members specific to this class
4003 : *----------------------------------------------------------------*/
4004 : // ITABFeaturePen
4005 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4006 :
4007 : // ITABFeatureBrush
4008 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4009 :
4010 0 : poNew->m_bRoundCorners = m_bRoundCorners;
4011 0 : poNew->m_dRoundXRadius = m_dRoundXRadius;
4012 0 : poNew->m_dRoundYRadius = m_dRoundYRadius;
4013 :
4014 0 : return poNew;
4015 : }
4016 :
4017 : /**********************************************************************
4018 : * TABRectangle::ValidateMapInfoType()
4019 : *
4020 : * Check the feature's geometry part and return the corresponding
4021 : * mapinfo object type code. The m_nMapInfoType member will also
4022 : * be updated for further calls to GetMapInfoType();
4023 : *
4024 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4025 : * is expected for this object class.
4026 : **********************************************************************/
4027 0 : TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4028 : {
4029 : /*-----------------------------------------------------------------
4030 : * Fetch and validate geometry
4031 : *----------------------------------------------------------------*/
4032 0 : OGRGeometry *poGeom = GetGeometryRef();
4033 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4034 : {
4035 0 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4036 0 : m_nMapInfoType = TAB_GEOM_ROUNDRECT;
4037 : else
4038 0 : m_nMapInfoType = TAB_GEOM_RECT;
4039 : }
4040 : else
4041 : {
4042 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4043 : "TABRectangle: Missing or Invalid Geometry!");
4044 0 : m_nMapInfoType = TAB_GEOM_NONE;
4045 : }
4046 :
4047 : /*-----------------------------------------------------------------
4048 : * Decide if coordinates should be compressed or not.
4049 : *----------------------------------------------------------------*/
4050 : // __TODO__ For now we always write uncompressed for this class...
4051 : // ValidateCoordType(poMapFile);
4052 0 : UpdateMBR(poMapFile);
4053 :
4054 0 : return m_nMapInfoType;
4055 : }
4056 :
4057 : /**********************************************************************
4058 : * TABRectangle::UpdateMBR()
4059 : *
4060 : * Update the feature MBR members using the geometry
4061 : *
4062 : * Returns 0 on success, or -1 if there is no geometry in object
4063 : **********************************************************************/
4064 0 : int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4065 : {
4066 0 : OGREnvelope sEnvelope;
4067 :
4068 : /*-----------------------------------------------------------------
4069 : * Fetch and validate geometry
4070 : *----------------------------------------------------------------*/
4071 0 : OGRGeometry *poGeom = GetGeometryRef();
4072 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4073 0 : poGeom->getEnvelope(&sEnvelope);
4074 : else
4075 : {
4076 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4077 : "TABRectangle: Missing or Invalid Geometry!");
4078 0 : return -1;
4079 : }
4080 :
4081 : /*-----------------------------------------------------------------
4082 : * Note that we will simply use the rectangle's MBR and don't really
4083 : * read the polygon geometry... this should be OK unless the
4084 : * polygon geometry was not really a rectangle.
4085 : *----------------------------------------------------------------*/
4086 0 : m_dXMin = sEnvelope.MinX;
4087 0 : m_dYMin = sEnvelope.MinY;
4088 0 : m_dXMax = sEnvelope.MaxX;
4089 0 : m_dYMax = sEnvelope.MaxY;
4090 :
4091 0 : if (poMapFile)
4092 : {
4093 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4094 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4095 : }
4096 :
4097 0 : return 0;
4098 : }
4099 :
4100 : /**********************************************************************
4101 : * TABRectangle::ReadGeometryFromMAPFile()
4102 : *
4103 : * Fill the geometry and representation (color, etc...) part of the
4104 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4105 : *
4106 : * It is assumed that poMAPFile currently points to the beginning of
4107 : * a map object.
4108 : *
4109 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4110 : * been called.
4111 : **********************************************************************/
4112 8 : int TABRectangle::ReadGeometryFromMAPFile(
4113 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4114 : GBool bCoordBlockDataOnly /*=FALSE*/,
4115 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4116 : {
4117 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4118 8 : if (bCoordBlockDataOnly)
4119 0 : return 0;
4120 :
4121 : /*-----------------------------------------------------------------
4122 : * Fetch and validate geometry type
4123 : *----------------------------------------------------------------*/
4124 8 : m_nMapInfoType = poObjHdr->m_nType;
4125 :
4126 8 : if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
4127 4 : m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
4128 0 : m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
4129 : {
4130 0 : CPLError(
4131 : CE_Failure, CPLE_AssertionFailed,
4132 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4133 0 : m_nMapInfoType, m_nMapInfoType);
4134 0 : return -1;
4135 : }
4136 :
4137 : /*-----------------------------------------------------------------
4138 : * Read object information
4139 : *----------------------------------------------------------------*/
4140 : TABMAPObjRectEllipse *poRectHdr =
4141 8 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4142 :
4143 : // Read the corners radius
4144 :
4145 8 : if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4146 4 : m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4147 : {
4148 : // Read the corner's diameters
4149 4 : poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
4150 4 : poRectHdr->m_nCornerHeight, m_dRoundXRadius,
4151 4 : m_dRoundYRadius);
4152 :
4153 : // Divide by 2 since we store the corner's radius
4154 4 : m_dRoundXRadius /= 2.0;
4155 4 : m_dRoundYRadius /= 2.0;
4156 :
4157 4 : m_bRoundCorners = TRUE;
4158 : }
4159 : else
4160 : {
4161 4 : m_bRoundCorners = FALSE;
4162 4 : m_dRoundXRadius = 0.0;
4163 4 : m_dRoundYRadius = 0.0;
4164 : }
4165 :
4166 : // A rectangle is defined by its MBR
4167 :
4168 8 : double dXMin = 0.0;
4169 8 : double dYMin = 0.0;
4170 8 : double dXMax = 0.0;
4171 8 : double dYMax = 0.0;
4172 8 : poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4173 : dYMin);
4174 8 : poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4175 : dYMax);
4176 :
4177 8 : m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index
4178 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4179 :
4180 8 : m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index
4181 8 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4182 :
4183 : /*-----------------------------------------------------------------
4184 : * Call SetMBR() and GetMBR() now to make sure that min values are
4185 : * really smaller than max values.
4186 : *----------------------------------------------------------------*/
4187 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
4188 8 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4189 :
4190 : /* Copy int MBR to feature class members */
4191 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4192 : poObjHdr->m_nMaxY);
4193 :
4194 : /*-----------------------------------------------------------------
4195 : * Create and fill geometry object
4196 : *----------------------------------------------------------------*/
4197 8 : OGRPolygon *poPolygon = new OGRPolygon;
4198 8 : OGRLinearRing *poRing = new OGRLinearRing();
4199 8 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4200 : {
4201 : /*-------------------------------------------------------------
4202 : * For rounded rectangles, we generate arcs with 45 line
4203 : * segments for each corner. We start with lower-left corner
4204 : * and proceed counterclockwise
4205 : * We also have to make sure that rounding radius is not too
4206 : * large for the MBR in the generated polygon... however, we
4207 : * always return the true X/Y radius (not adjusted) since this
4208 : * is the way MapInfo seems to do it when a radius bigger than
4209 : * the MBR is passed from TBA to MIF.
4210 : *------------------------------------------------------------*/
4211 : const double dXRadius =
4212 4 : std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
4213 : const double dYRadius =
4214 4 : std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
4215 4 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
4216 : dYRadius, M_PI, 3.0 * M_PI / 2.0);
4217 4 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
4218 : dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
4219 4 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
4220 : dYRadius, 0.0, M_PI / 2.0);
4221 4 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
4222 : dYRadius, M_PI / 2.0, M_PI);
4223 :
4224 4 : TABCloseRing(poRing);
4225 : }
4226 : else
4227 : {
4228 4 : poRing->addPoint(dXMin, dYMin);
4229 4 : poRing->addPoint(dXMax, dYMin);
4230 4 : poRing->addPoint(dXMax, dYMax);
4231 4 : poRing->addPoint(dXMin, dYMax);
4232 4 : poRing->addPoint(dXMin, dYMin);
4233 : }
4234 :
4235 8 : poPolygon->addRingDirectly(poRing);
4236 8 : SetGeometryDirectly(poPolygon);
4237 :
4238 8 : return 0;
4239 : }
4240 :
4241 : /**********************************************************************
4242 : * TABRectangle::WriteGeometryToMAPFile()
4243 : *
4244 : * Write the geometry and representation (color, etc...) part of the
4245 : * feature to the .MAP object pointed to by poMAPFile.
4246 : *
4247 : * It is assumed that poMAPFile currently points to a valid map object.
4248 : *
4249 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4250 : * been called.
4251 : **********************************************************************/
4252 0 : int TABRectangle::WriteGeometryToMAPFile(
4253 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4254 : GBool bCoordBlockDataOnly /*=FALSE*/,
4255 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4256 : {
4257 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4258 0 : if (bCoordBlockDataOnly)
4259 0 : return 0;
4260 :
4261 : /*-----------------------------------------------------------------
4262 : * We assume that ValidateMapInfoType() was called already and that
4263 : * the type in poObjHdr->m_nType is valid.
4264 : *----------------------------------------------------------------*/
4265 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4266 :
4267 : /*-----------------------------------------------------------------
4268 : * Fetch and validate geometry and update MBR
4269 : * Note that we will simply use the geometry's MBR and don't really
4270 : * read the polygon geometry... this should be OK unless the
4271 : * polygon geometry was not really a rectangle.
4272 : *----------------------------------------------------------------*/
4273 0 : if (UpdateMBR(poMapFile) != 0)
4274 0 : return -1; /* Error already reported */
4275 :
4276 : /*-----------------------------------------------------------------
4277 : * Copy object information
4278 : *----------------------------------------------------------------*/
4279 : TABMAPObjRectEllipse *poRectHdr =
4280 0 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4281 :
4282 0 : if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4283 0 : m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4284 : {
4285 0 : poMapFile->Coordsys2IntDist(
4286 0 : m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
4287 0 : poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
4288 : }
4289 : else
4290 : {
4291 0 : poRectHdr->m_nCornerWidth = 0;
4292 0 : poRectHdr->m_nCornerHeight = 0;
4293 : }
4294 :
4295 : // A rectangle is defined by its MBR (values were set in UpdateMBR())
4296 0 : poRectHdr->m_nMinX = m_nXMin;
4297 0 : poRectHdr->m_nMinY = m_nYMin;
4298 0 : poRectHdr->m_nMaxX = m_nXMax;
4299 0 : poRectHdr->m_nMaxY = m_nYMax;
4300 :
4301 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4302 0 : poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
4303 :
4304 0 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4305 0 : poRectHdr->m_nBrushId =
4306 0 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
4307 :
4308 0 : if (CPLGetLastErrorType() == CE_Failure)
4309 0 : return -1;
4310 :
4311 0 : return 0;
4312 : }
4313 :
4314 : /**********************************************************************
4315 : * TABRectangle::GetStyleString() const
4316 : *
4317 : * Return style string for this feature.
4318 : *
4319 : * Style String is built only once during the first call to GetStyleString().
4320 : **********************************************************************/
4321 18 : const char *TABRectangle::GetStyleString() const
4322 : {
4323 18 : if (m_pszStyleString == nullptr)
4324 : {
4325 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4326 : // to use temporary buffers
4327 10 : char *pszPen = CPLStrdup(GetPenStyleString());
4328 10 : char *pszBrush = CPLStrdup(GetBrushStyleString());
4329 :
4330 10 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4331 :
4332 10 : CPLFree(pszPen);
4333 10 : CPLFree(pszBrush);
4334 : }
4335 :
4336 18 : return m_pszStyleString;
4337 : }
4338 :
4339 : /**********************************************************************
4340 : * TABRectangle::DumpMIF()
4341 : *
4342 : * Dump feature geometry in a format similar to .MIF REGIONs.
4343 : **********************************************************************/
4344 0 : void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
4345 : {
4346 0 : if (fpOut == nullptr)
4347 0 : fpOut = stdout;
4348 :
4349 : /*-----------------------------------------------------------------
4350 : * Output RECT or ROUNDRECT parameters
4351 : *----------------------------------------------------------------*/
4352 0 : double dXMin = 0.0;
4353 0 : double dYMin = 0.0;
4354 0 : double dXMax = 0.0;
4355 0 : double dYMax = 0.0;
4356 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4357 :
4358 0 : if (m_bRoundCorners)
4359 0 : fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g %.15g %.15g)\n",
4360 : dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
4361 : else
4362 0 : fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4363 : dYMax);
4364 :
4365 : /*-----------------------------------------------------------------
4366 : * Fetch and validate geometry
4367 : *----------------------------------------------------------------*/
4368 0 : OGRGeometry *poGeom = GetGeometryRef();
4369 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4370 : {
4371 : /*-------------------------------------------------------------
4372 : * Generate rectangle output as a region
4373 : * We could also output as a RECT or ROUNDRECT in a real MIF generator
4374 : *------------------------------------------------------------*/
4375 0 : OGRPolygon *poPolygon = poGeom->toPolygon();
4376 0 : int numIntRings = poPolygon->getNumInteriorRings();
4377 0 : fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4378 : // In this loop, iRing=-1 for the outer ring.
4379 0 : for (int iRing = -1; iRing < numIntRings; iRing++)
4380 : {
4381 0 : OGRLinearRing *poRing = nullptr;
4382 :
4383 0 : if (iRing == -1)
4384 0 : poRing = poPolygon->getExteriorRing();
4385 : else
4386 0 : poRing = poPolygon->getInteriorRing(iRing);
4387 :
4388 0 : if (poRing == nullptr)
4389 : {
4390 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4391 : "TABRectangle: Object Geometry contains NULL rings!");
4392 0 : return;
4393 : }
4394 :
4395 0 : const int numPoints = poRing->getNumPoints();
4396 0 : fprintf(fpOut, " %d\n", numPoints);
4397 0 : for (int i = 0; i < numPoints; i++)
4398 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4399 : poRing->getY(i));
4400 : }
4401 : }
4402 : else
4403 : {
4404 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4405 : "TABRectangle: Missing or Invalid Geometry!");
4406 0 : return;
4407 : }
4408 :
4409 : // Finish with PEN/BRUSH/etc. clauses
4410 0 : DumpPenDef();
4411 0 : DumpBrushDef();
4412 :
4413 0 : fflush(fpOut);
4414 : }
4415 :
4416 : /*=====================================================================
4417 : * class TABEllipse
4418 : *====================================================================*/
4419 :
4420 : /**********************************************************************
4421 : * TABEllipse::TABEllipse()
4422 : *
4423 : * Constructor.
4424 : **********************************************************************/
4425 401 : TABEllipse::TABEllipse(const OGRFeatureDefn *poDefnIn)
4426 : : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
4427 401 : m_dYRadius(0.0)
4428 : {
4429 401 : }
4430 :
4431 : /**********************************************************************
4432 : * TABEllipse::~TABEllipse()
4433 : *
4434 : * Destructor.
4435 : **********************************************************************/
4436 802 : TABEllipse::~TABEllipse()
4437 : {
4438 802 : }
4439 :
4440 : /**********************************************************************
4441 : * TABEllipse::CloneTABFeature()
4442 : *
4443 : * Duplicate feature, including stuff specific to each TABFeature type.
4444 : *
4445 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
4446 : * then copies any members specific to its own type.
4447 : **********************************************************************/
4448 : TABFeature *
4449 0 : TABEllipse::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
4450 : {
4451 : /*-----------------------------------------------------------------
4452 : * Alloc new feature and copy the base stuff
4453 : *----------------------------------------------------------------*/
4454 0 : TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
4455 :
4456 0 : CopyTABFeatureBase(poNew);
4457 :
4458 : /*-----------------------------------------------------------------
4459 : * And members specific to this class
4460 : *----------------------------------------------------------------*/
4461 : // ITABFeaturePen
4462 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4463 :
4464 : // ITABFeatureBrush
4465 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4466 :
4467 0 : poNew->m_dCenterX = m_dCenterX;
4468 0 : poNew->m_dCenterY = m_dCenterY;
4469 0 : poNew->m_dXRadius = m_dXRadius;
4470 0 : poNew->m_dYRadius = m_dYRadius;
4471 :
4472 0 : return poNew;
4473 : }
4474 :
4475 : /**********************************************************************
4476 : * TABEllipse::ValidateMapInfoType()
4477 : *
4478 : * Check the feature's geometry part and return the corresponding
4479 : * mapinfo object type code. The m_nMapInfoType member will also
4480 : * be updated for further calls to GetMapInfoType();
4481 : *
4482 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4483 : * is expected for this object class.
4484 : **********************************************************************/
4485 0 : TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4486 : {
4487 : /*-----------------------------------------------------------------
4488 : * Fetch and validate geometry
4489 : *----------------------------------------------------------------*/
4490 0 : OGRGeometry *poGeom = GetGeometryRef();
4491 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4492 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4493 : {
4494 0 : m_nMapInfoType = TAB_GEOM_ELLIPSE;
4495 : }
4496 : else
4497 : {
4498 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4499 : "TABEllipse: Missing or Invalid Geometry!");
4500 0 : m_nMapInfoType = TAB_GEOM_NONE;
4501 : }
4502 :
4503 : /*-----------------------------------------------------------------
4504 : * Decide if coordinates should be compressed or not.
4505 : *----------------------------------------------------------------*/
4506 : // __TODO__ For now we always write uncompressed for this class...
4507 : // ValidateCoordType(poMapFile);
4508 0 : UpdateMBR(poMapFile);
4509 :
4510 0 : return m_nMapInfoType;
4511 : }
4512 :
4513 : /**********************************************************************
4514 : * TABEllipse::UpdateMBR()
4515 : *
4516 : * Update the feature MBR members using the geometry
4517 : *
4518 : * Returns 0 on success, or -1 if there is no geometry in object
4519 : **********************************************************************/
4520 0 : int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4521 : {
4522 0 : OGREnvelope sEnvelope;
4523 :
4524 : /*-----------------------------------------------------------------
4525 : * Fetch and validate geometry... Polygon and point are accepted.
4526 : * Note that we will simply use the ellipse's MBR and don't really
4527 : * read the polygon geometry... this should be OK unless the
4528 : * polygon geometry was not really an ellipse.
4529 : *----------------------------------------------------------------*/
4530 0 : OGRGeometry *poGeom = GetGeometryRef();
4531 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4532 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4533 0 : poGeom->getEnvelope(&sEnvelope);
4534 : else
4535 : {
4536 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4537 : "TABEllipse: Missing or Invalid Geometry!");
4538 0 : return -1;
4539 : }
4540 :
4541 : /*-----------------------------------------------------------------
4542 : * We use the center of the MBR as the ellipse center, and the
4543 : * X/Y radius to define the MBR size. If X/Y radius are null then
4544 : * we'll try to use the MBR to recompute them.
4545 : *----------------------------------------------------------------*/
4546 0 : const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
4547 0 : const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
4548 0 : if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
4549 : {
4550 0 : m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
4551 0 : m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
4552 : }
4553 :
4554 0 : m_dXMin = dXCenter - m_dXRadius;
4555 0 : m_dYMin = dYCenter - m_dYRadius;
4556 0 : m_dXMax = dXCenter + m_dXRadius;
4557 0 : m_dYMax = dYCenter + m_dYRadius;
4558 :
4559 0 : if (poMapFile)
4560 : {
4561 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4562 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4563 : }
4564 :
4565 0 : return 0;
4566 : }
4567 :
4568 : /**********************************************************************
4569 : * TABEllipse::ReadGeometryFromMAPFile()
4570 : *
4571 : * Fill the geometry and representation (color, etc...) part of the
4572 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4573 : *
4574 : * It is assumed that poMAPFile currently points to the beginning of
4575 : * a map object.
4576 : *
4577 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4578 : * been called.
4579 : **********************************************************************/
4580 4 : int TABEllipse::ReadGeometryFromMAPFile(
4581 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4582 : GBool bCoordBlockDataOnly /*=FALSE*/,
4583 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4584 : {
4585 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4586 4 : if (bCoordBlockDataOnly)
4587 0 : return 0;
4588 :
4589 : /*-----------------------------------------------------------------
4590 : * Fetch and validate geometry type
4591 : *----------------------------------------------------------------*/
4592 4 : m_nMapInfoType = poObjHdr->m_nType;
4593 :
4594 4 : if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
4595 0 : m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
4596 : {
4597 0 : CPLError(
4598 : CE_Failure, CPLE_AssertionFailed,
4599 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4600 0 : m_nMapInfoType, m_nMapInfoType);
4601 0 : return -1;
4602 : }
4603 :
4604 : /*-----------------------------------------------------------------
4605 : * Read object information
4606 : *----------------------------------------------------------------*/
4607 : TABMAPObjRectEllipse *poRectHdr =
4608 4 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4609 :
4610 : // An ellipse is defined by its MBR
4611 :
4612 4 : double dXMin = 0.0;
4613 4 : double dYMin = 0.0;
4614 4 : double dXMax = 0.0;
4615 4 : double dYMax = 0.0;
4616 4 : poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4617 : dYMin);
4618 4 : poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4619 : dYMax);
4620 :
4621 4 : m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index
4622 4 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4623 :
4624 4 : m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index
4625 4 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4626 :
4627 : /*-----------------------------------------------------------------
4628 : * Save info about the ellipse def. inside class members
4629 : *----------------------------------------------------------------*/
4630 4 : m_dCenterX = (dXMin + dXMax) / 2.0;
4631 4 : m_dCenterY = (dYMin + dYMax) / 2.0;
4632 4 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
4633 4 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
4634 :
4635 4 : SetMBR(dXMin, dYMin, dXMax, dYMax);
4636 :
4637 4 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4638 : poObjHdr->m_nMaxY);
4639 :
4640 : /*-----------------------------------------------------------------
4641 : * Create and fill geometry object
4642 : *----------------------------------------------------------------*/
4643 4 : OGRPolygon *poPolygon = new OGRPolygon;
4644 4 : OGRLinearRing *poRing = new OGRLinearRing();
4645 :
4646 : /*-----------------------------------------------------------------
4647 : * For the OGR geometry, we generate an ellipse with 2 degrees line
4648 : * segments.
4649 : *----------------------------------------------------------------*/
4650 4 : TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
4651 : 0.0, 2.0 * M_PI);
4652 4 : TABCloseRing(poRing);
4653 :
4654 4 : poPolygon->addRingDirectly(poRing);
4655 4 : SetGeometryDirectly(poPolygon);
4656 :
4657 4 : return 0;
4658 : }
4659 :
4660 : /**********************************************************************
4661 : * TABEllipse::WriteGeometryToMAPFile()
4662 : *
4663 : * Write the geometry and representation (color, etc...) part of the
4664 : * feature to the .MAP object pointed to by poMAPFile.
4665 : *
4666 : * It is assumed that poMAPFile currently points to a valid map object.
4667 : *
4668 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4669 : * been called.
4670 : **********************************************************************/
4671 0 : int TABEllipse::WriteGeometryToMAPFile(
4672 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4673 : GBool bCoordBlockDataOnly /*=FALSE*/,
4674 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4675 : {
4676 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4677 0 : if (bCoordBlockDataOnly)
4678 0 : return 0;
4679 :
4680 : /*-----------------------------------------------------------------
4681 : * We assume that ValidateMapInfoType() was called already and that
4682 : * the type in poObjHdr->m_nType is valid.
4683 : *----------------------------------------------------------------*/
4684 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4685 :
4686 : /*-----------------------------------------------------------------
4687 : * Fetch and validate geometry... Polygon and point are accepted.
4688 : * Note that we will simply use the ellipse's MBR and don't really
4689 : * read the polygon geometry... this should be OK unless the
4690 : * polygon geometry was not really an ellipse.
4691 : *
4692 : * We use the center of the MBR as the ellipse center, and the
4693 : * X/Y radius to define the MBR size. If X/Y radius are null then
4694 : * we'll try to use the MBR to recompute them.
4695 : *----------------------------------------------------------------*/
4696 0 : if (UpdateMBR(poMapFile) != 0)
4697 0 : return -1; /* Error already reported */
4698 :
4699 : /*-----------------------------------------------------------------
4700 : * Copy object information
4701 : *----------------------------------------------------------------*/
4702 : TABMAPObjRectEllipse *poRectHdr =
4703 0 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4704 :
4705 : // Reset RoundRect Corner members... just in case (unused for ellipse)
4706 0 : poRectHdr->m_nCornerWidth = 0;
4707 0 : poRectHdr->m_nCornerHeight = 0;
4708 :
4709 : // An ellipse is defined by its MBR (values were set in UpdateMBR())
4710 0 : poRectHdr->m_nMinX = m_nXMin;
4711 0 : poRectHdr->m_nMinY = m_nYMin;
4712 0 : poRectHdr->m_nMaxX = m_nXMax;
4713 0 : poRectHdr->m_nMaxY = m_nYMax;
4714 :
4715 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4716 0 : poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
4717 :
4718 0 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4719 0 : poRectHdr->m_nBrushId =
4720 0 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
4721 :
4722 0 : if (CPLGetLastErrorType() == CE_Failure)
4723 0 : return -1;
4724 :
4725 0 : return 0;
4726 : }
4727 :
4728 : /**********************************************************************
4729 : * TABEllipse::GetStyleString() const
4730 : *
4731 : * Return style string for this feature.
4732 : *
4733 : * Style String is built only once during the first call to GetStyleString().
4734 : **********************************************************************/
4735 9 : const char *TABEllipse::GetStyleString() const
4736 : {
4737 9 : if (m_pszStyleString == nullptr)
4738 : {
4739 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4740 : // to use temporary buffers
4741 5 : char *pszPen = CPLStrdup(GetPenStyleString());
4742 5 : char *pszBrush = CPLStrdup(GetBrushStyleString());
4743 :
4744 5 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4745 :
4746 5 : CPLFree(pszPen);
4747 5 : CPLFree(pszBrush);
4748 : }
4749 :
4750 9 : return m_pszStyleString;
4751 : }
4752 :
4753 : /**********************************************************************
4754 : * TABEllipse::DumpMIF()
4755 : *
4756 : * Dump feature geometry in a format similar to .MIF REGIONs.
4757 : **********************************************************************/
4758 0 : void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
4759 : {
4760 0 : if (fpOut == nullptr)
4761 0 : fpOut = stdout;
4762 :
4763 : /*-----------------------------------------------------------------
4764 : * Output ELLIPSE parameters
4765 : *----------------------------------------------------------------*/
4766 0 : double dXMin = 0.0;
4767 0 : double dYMin = 0.0;
4768 0 : double dXMax = 0.0;
4769 0 : double dYMax = 0.0;
4770 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4771 0 : fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4772 : dYMax);
4773 :
4774 : /*-----------------------------------------------------------------
4775 : * Fetch and validate geometry
4776 : *----------------------------------------------------------------*/
4777 0 : OGRGeometry *poGeom = GetGeometryRef();
4778 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4779 : {
4780 : /*-------------------------------------------------------------
4781 : * Generate ellipse output as a region
4782 : * We could also output as an ELLIPSE in a real MIF generator
4783 : *------------------------------------------------------------*/
4784 0 : OGRPolygon *poPolygon = poGeom->toPolygon();
4785 0 : int numIntRings = poPolygon->getNumInteriorRings();
4786 0 : fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4787 : // In this loop, iRing=-1 for the outer ring.
4788 0 : for (int iRing = -1; iRing < numIntRings; iRing++)
4789 : {
4790 0 : OGRLinearRing *poRing = nullptr;
4791 :
4792 0 : if (iRing == -1)
4793 0 : poRing = poPolygon->getExteriorRing();
4794 : else
4795 0 : poRing = poPolygon->getInteriorRing(iRing);
4796 :
4797 0 : if (poRing == nullptr)
4798 : {
4799 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4800 : "TABEllipse: Object Geometry contains NULL rings!");
4801 0 : return;
4802 : }
4803 :
4804 0 : int numPoints = poRing->getNumPoints();
4805 0 : fprintf(fpOut, " %d\n", numPoints);
4806 0 : for (int i = 0; i < numPoints; i++)
4807 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4808 : poRing->getY(i));
4809 : }
4810 : }
4811 : else
4812 : {
4813 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4814 : "TABEllipse: Missing or Invalid Geometry!");
4815 0 : return;
4816 : }
4817 :
4818 : // Finish with PEN/BRUSH/etc. clauses
4819 0 : DumpPenDef();
4820 0 : DumpBrushDef();
4821 :
4822 0 : fflush(fpOut);
4823 : }
4824 :
4825 : /*=====================================================================
4826 : * class TABArc
4827 : *====================================================================*/
4828 :
4829 : /**********************************************************************
4830 : * TABArc::TABArc()
4831 : *
4832 : * Constructor.
4833 : **********************************************************************/
4834 680 : TABArc::TABArc(const OGRFeatureDefn *poDefnIn)
4835 : : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
4836 680 : m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
4837 : {
4838 680 : }
4839 :
4840 : /**********************************************************************
4841 : * TABArc::~TABArc()
4842 : *
4843 : * Destructor.
4844 : **********************************************************************/
4845 1360 : TABArc::~TABArc()
4846 : {
4847 1360 : }
4848 :
4849 : /**********************************************************************
4850 : * TABArc::CloneTABFeature()
4851 : *
4852 : * Duplicate feature, including stuff specific to each TABFeature type.
4853 : *
4854 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
4855 : * then copies any members specific to its own type.
4856 : **********************************************************************/
4857 0 : TABFeature *TABArc::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
4858 : {
4859 : /*-----------------------------------------------------------------
4860 : * Alloc new feature and copy the base stuff
4861 : *----------------------------------------------------------------*/
4862 0 : TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
4863 :
4864 0 : CopyTABFeatureBase(poNew);
4865 :
4866 : /*-----------------------------------------------------------------
4867 : * And members specific to this class
4868 : *----------------------------------------------------------------*/
4869 : // ITABFeaturePen
4870 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4871 :
4872 0 : poNew->SetStartAngle(GetStartAngle());
4873 0 : poNew->SetEndAngle(GetEndAngle());
4874 :
4875 0 : poNew->m_dCenterX = m_dCenterX;
4876 0 : poNew->m_dCenterY = m_dCenterY;
4877 0 : poNew->m_dXRadius = m_dXRadius;
4878 0 : poNew->m_dYRadius = m_dYRadius;
4879 :
4880 0 : return poNew;
4881 : }
4882 :
4883 : /**********************************************************************
4884 : * TABArc::ValidateMapInfoType()
4885 : *
4886 : * Check the feature's geometry part and return the corresponding
4887 : * mapinfo object type code. The m_nMapInfoType member will also
4888 : * be updated for further calls to GetMapInfoType();
4889 : *
4890 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4891 : * is expected for this object class.
4892 : **********************************************************************/
4893 0 : TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4894 : {
4895 : /*-----------------------------------------------------------------
4896 : * Fetch and validate geometry
4897 : *----------------------------------------------------------------*/
4898 0 : OGRGeometry *poGeom = GetGeometryRef();
4899 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
4900 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4901 : {
4902 0 : m_nMapInfoType = TAB_GEOM_ARC;
4903 : }
4904 : else
4905 : {
4906 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4907 : "TABArc: Missing or Invalid Geometry!");
4908 0 : m_nMapInfoType = TAB_GEOM_NONE;
4909 : }
4910 :
4911 : /*-----------------------------------------------------------------
4912 : * Decide if coordinates should be compressed or not.
4913 : *----------------------------------------------------------------*/
4914 : // __TODO__ For now we always write uncompressed for this class...
4915 : // ValidateCoordType(poMapFile);
4916 0 : UpdateMBR(poMapFile);
4917 :
4918 0 : return m_nMapInfoType;
4919 : }
4920 :
4921 : /**********************************************************************
4922 : * TABArc::UpdateMBR()
4923 : *
4924 : * Update the feature MBR members using the geometry
4925 : *
4926 : * Returns 0 on success, or -1 if there is no geometry in object
4927 : **********************************************************************/
4928 0 : int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4929 : {
4930 0 : OGREnvelope sEnvelope;
4931 :
4932 0 : OGRGeometry *poGeom = GetGeometryRef();
4933 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
4934 : {
4935 : /*-------------------------------------------------------------
4936 : * POLYGON geometry:
4937 : * Note that we will simply use the ellipse's MBR and don't really
4938 : * read the polygon geometry... this should be OK unless the
4939 : * polygon geometry was not really an ellipse.
4940 : * In the case of a polygon geometry. the m_dCenterX/Y values MUST
4941 : * have been set by the caller.
4942 : *------------------------------------------------------------*/
4943 0 : poGeom->getEnvelope(&sEnvelope);
4944 : }
4945 0 : else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4946 : {
4947 : /*-------------------------------------------------------------
4948 : * In the case of a POINT GEOMETRY, we will make sure the
4949 : * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
4950 : *
4951 : * In this case we have to reconstruct the arc inside a temporary
4952 : * geometry object in order to find its real MBR.
4953 : *------------------------------------------------------------*/
4954 0 : OGRPoint *poPoint = poGeom->toPoint();
4955 0 : m_dCenterX = poPoint->getX();
4956 0 : m_dCenterY = poPoint->getY();
4957 :
4958 0 : OGRLineString oTmpLine;
4959 0 : int numPts = 0;
4960 0 : if (m_dEndAngle < m_dStartAngle)
4961 0 : numPts = static_cast<int>(
4962 0 : std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
4963 : else
4964 0 : numPts = static_cast<int>(
4965 0 : std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
4966 0 : numPts = std::max(2, numPts);
4967 :
4968 0 : TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
4969 0 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
4970 0 : m_dEndAngle * M_PI / 180.0);
4971 :
4972 0 : oTmpLine.getEnvelope(&sEnvelope);
4973 : }
4974 : else
4975 : {
4976 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4977 : "TABArc: Missing or Invalid Geometry!");
4978 0 : return -1;
4979 : }
4980 :
4981 : // Update the Arc's MBR
4982 0 : m_dXMin = sEnvelope.MinX;
4983 0 : m_dYMin = sEnvelope.MinY;
4984 0 : m_dXMax = sEnvelope.MaxX;
4985 0 : m_dYMax = sEnvelope.MaxY;
4986 :
4987 0 : if (poMapFile)
4988 : {
4989 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4990 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4991 : }
4992 :
4993 0 : return 0;
4994 : }
4995 :
4996 : /**********************************************************************
4997 : * TABArc::ReadGeometryFromMAPFile()
4998 : *
4999 : * Fill the geometry and representation (color, etc...) part of the
5000 : * feature from the contents of the .MAP object pointed to by poMAPFile.
5001 : *
5002 : * It is assumed that poMAPFile currently points to the beginning of
5003 : * a map object.
5004 : *
5005 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5006 : * been called.
5007 : **********************************************************************/
5008 8 : int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5009 : TABMAPObjHdr *poObjHdr,
5010 : GBool bCoordBlockDataOnly /*=FALSE*/,
5011 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5012 : {
5013 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5014 8 : if (bCoordBlockDataOnly)
5015 0 : return 0;
5016 :
5017 : /*-----------------------------------------------------------------
5018 : * Fetch and validate geometry type
5019 : *----------------------------------------------------------------*/
5020 8 : m_nMapInfoType = poObjHdr->m_nType;
5021 :
5022 8 : if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
5023 : {
5024 0 : CPLError(
5025 : CE_Failure, CPLE_AssertionFailed,
5026 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5027 0 : m_nMapInfoType, m_nMapInfoType);
5028 0 : return -1;
5029 : }
5030 :
5031 : /*-----------------------------------------------------------------
5032 : * Read object information
5033 : *----------------------------------------------------------------*/
5034 8 : TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5035 :
5036 : /*-------------------------------------------------------------
5037 : * Start/End angles
5038 : * Since the angles are specified for integer coordinates, and
5039 : * that these coordinates can have the X axis reversed, we have to
5040 : * adjust the angle values for the change in the X axis
5041 : * direction.
5042 : *
5043 : * This should be necessary only when X axis is flipped.
5044 : * __TODO__ Why is order of start/end values reversed as well???
5045 : *------------------------------------------------------------*/
5046 :
5047 : /*-------------------------------------------------------------
5048 : * OK, Arc angles again!!!!!!!!!!!!
5049 : * After some tests in 1999-11, it appeared that the angle values
5050 : * ALWAYS had to be flipped (read order= end angle followed by
5051 : * start angle), no matter which quadrant the file is in.
5052 : * This does not make any sense, so I suspect that there is something
5053 : * that we are missing here!
5054 : *
5055 : * 2000-01-14.... Again!!! Based on some sample data files:
5056 : * File Ver Quadr ReflXAxis Read_Order Adjust_Angle
5057 : * test_symb.tab 300 2 1 end,start X=yes Y=no
5058 : * alltypes.tab: 300 1 0 start,end X=no Y=no
5059 : * arcs.tab: 300 2 0 end,start X=yes Y=no
5060 : *
5061 : * Until we prove it wrong, the rule would be:
5062 : * -> Quadrant 1 and 3, angles order = start, end
5063 : * -> Quadrant 2 and 4, angles order = end, start
5064 : * + Always adjust angles for x and y axis based on quadrant.
5065 : *
5066 : * This was confirmed using some more files in which the quadrant was
5067 : * manually changed, but whether these are valid results is
5068 : * disputable.
5069 : *
5070 : * The ReflectXAxis flag seems to have no effect here...
5071 : *------------------------------------------------------------*/
5072 :
5073 : /*-------------------------------------------------------------
5074 : * In version 100 .tab files (version 400 .map), it is possible
5075 : * to have a quadrant value of 0 and it should be treated the
5076 : * same way as quadrant 3
5077 : *------------------------------------------------------------*/
5078 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
5079 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5080 0 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5081 : {
5082 : // Quadrants 1 and 3 ... read order = start, end
5083 8 : m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
5084 8 : m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
5085 : }
5086 : else
5087 : {
5088 : // Quadrants 2 and 4 ... read order = end, start
5089 0 : m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
5090 0 : m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
5091 : }
5092 :
5093 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
5094 16 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5095 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5096 : {
5097 : // X axis direction is flipped... adjust angle
5098 0 : m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
5099 0 : : (540.0 - m_dStartAngle);
5100 0 : m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
5101 0 : : (540.0 - m_dEndAngle);
5102 : }
5103 :
5104 8 : if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
5105 : {
5106 0 : CPLError(CE_Failure, CPLE_AppDefined,
5107 : "Wrong start and end angles: %f %f", m_dStartAngle,
5108 : m_dEndAngle);
5109 0 : return -1;
5110 : }
5111 :
5112 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5113 16 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
5114 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5115 : {
5116 : // Y axis direction is flipped... this reverses angle direction
5117 : // Unfortunately we never found any file that contains this case,
5118 : // but this should be the behavior to expect!!!
5119 : //
5120 : // 2000-01-14: some files in which quadrant was set to 3 and 4
5121 : // manually seemed to confirm that this is the right thing to do.
5122 0 : m_dStartAngle = 360.0 - m_dStartAngle;
5123 0 : m_dEndAngle = 360.0 - m_dEndAngle;
5124 : }
5125 :
5126 : // An arc is defined by its defining ellipse's MBR:
5127 :
5128 8 : double dXMin = 0.0;
5129 8 : double dYMin = 0.0;
5130 8 : double dXMax = 0.0;
5131 8 : double dYMax = 0.0;
5132 :
5133 8 : poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
5134 : poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
5135 8 : poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
5136 : poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
5137 :
5138 8 : m_dCenterX = (dXMin + dXMax) / 2.0;
5139 8 : m_dCenterY = (dYMin + dYMax) / 2.0;
5140 8 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
5141 8 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
5142 :
5143 : // Read the Arc's MBR and use that as this feature's MBR
5144 8 : poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
5145 8 : poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
5146 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
5147 :
5148 8 : m_nPenDefIndex = poArcHdr->m_nPenId; // Pen index
5149 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5150 :
5151 : /*-----------------------------------------------------------------
5152 : * Create and fill geometry object
5153 : * For the OGR geometry, we generate an arc with 2 degrees line
5154 : * segments.
5155 : *----------------------------------------------------------------*/
5156 8 : OGRLineString *poLine = new OGRLineString;
5157 :
5158 : const int numPts = std::max(
5159 16 : 2,
5160 8 : (m_dEndAngle < m_dStartAngle
5161 8 : ? static_cast<int>(
5162 0 : std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
5163 8 : : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
5164 8 : 1)));
5165 :
5166 8 : TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
5167 8 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
5168 8 : m_dEndAngle * M_PI / 180.0);
5169 :
5170 8 : SetGeometryDirectly(poLine);
5171 :
5172 8 : return 0;
5173 : }
5174 :
5175 : /**********************************************************************
5176 : * TABArc::WriteGeometryToMAPFile()
5177 : *
5178 : * Write the geometry and representation (color, etc...) part of the
5179 : * feature to the .MAP object pointed to by poMAPFile.
5180 : *
5181 : * It is assumed that poMAPFile currently points to a valid map object.
5182 : *
5183 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5184 : * been called.
5185 : **********************************************************************/
5186 0 : int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5187 : TABMAPObjHdr *poObjHdr,
5188 : GBool bCoordBlockDataOnly /*=FALSE*/,
5189 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5190 : {
5191 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5192 0 : if (bCoordBlockDataOnly)
5193 0 : return 0;
5194 :
5195 : /*-----------------------------------------------------------------
5196 : * We assume that ValidateMapInfoType() was called already and that
5197 : * the type in poObjHdr->m_nType is valid.
5198 : *----------------------------------------------------------------*/
5199 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5200 :
5201 : /*-----------------------------------------------------------------
5202 : * Fetch and validate geometry
5203 : * In the case of ARCs, this is all done inside UpdateMBR()
5204 : *----------------------------------------------------------------*/
5205 0 : if (UpdateMBR(poMapFile) != 0)
5206 0 : return -1; /* Error already reported */
5207 :
5208 : /*-----------------------------------------------------------------
5209 : * Copy object information
5210 : *----------------------------------------------------------------*/
5211 0 : TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5212 :
5213 : /*-------------------------------------------------------------
5214 : * Start/End angles
5215 : * Since we ALWAYS produce files in quadrant 1 then we can
5216 : * ignore the special angle conversion required by flipped axis.
5217 : *
5218 : * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
5219 : *------------------------------------------------------------*/
5220 0 : CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
5221 :
5222 0 : poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
5223 0 : poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
5224 :
5225 : // An arc is defined by its defining ellipse's MBR:
5226 0 : poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5227 0 : poArcHdr->m_nArcEllipseMinX,
5228 0 : poArcHdr->m_nArcEllipseMinY);
5229 0 : poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5230 0 : poArcHdr->m_nArcEllipseMaxX,
5231 0 : poArcHdr->m_nArcEllipseMaxY);
5232 :
5233 : // Pass the Arc's actual MBR (values were set in UpdateMBR())
5234 0 : poArcHdr->m_nMinX = m_nXMin;
5235 0 : poArcHdr->m_nMinY = m_nYMin;
5236 0 : poArcHdr->m_nMaxX = m_nXMax;
5237 0 : poArcHdr->m_nMaxY = m_nYMax;
5238 :
5239 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5240 0 : poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
5241 :
5242 0 : if (CPLGetLastErrorType() == CE_Failure)
5243 0 : return -1;
5244 :
5245 0 : return 0;
5246 : }
5247 :
5248 : /**********************************************************************
5249 : * TABArc::SetStart/EndAngle()
5250 : *
5251 : * Set the start/end angle values in degrees, making sure the values are
5252 : * always in the range [0..360]
5253 : **********************************************************************/
5254 0 : void TABArc::SetStartAngle(double dAngle)
5255 : {
5256 0 : dAngle = fmod(dAngle, 360.0);
5257 0 : if (dAngle < 0.0)
5258 0 : dAngle += 360.0;
5259 :
5260 0 : m_dStartAngle = dAngle;
5261 0 : }
5262 :
5263 0 : void TABArc::SetEndAngle(double dAngle)
5264 : {
5265 0 : dAngle = fmod(dAngle, 360.0);
5266 0 : if (dAngle < 0.0)
5267 0 : dAngle += 360.0;
5268 :
5269 0 : m_dEndAngle = dAngle;
5270 0 : }
5271 :
5272 : /**********************************************************************
5273 : * TABArc::GetStyleString() const
5274 : *
5275 : * Return style string for this feature.
5276 : *
5277 : * Style String is built only once during the first call to GetStyleString().
5278 : **********************************************************************/
5279 14 : const char *TABArc::GetStyleString() const
5280 : {
5281 14 : if (m_pszStyleString == nullptr)
5282 : {
5283 10 : m_pszStyleString = CPLStrdup(GetPenStyleString());
5284 : }
5285 :
5286 14 : return m_pszStyleString;
5287 : }
5288 :
5289 : /**********************************************************************
5290 : * TABArc::DumpMIF()
5291 : *
5292 : * Dump feature geometry in a format similar to .MIF REGIONs.
5293 : **********************************************************************/
5294 0 : void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
5295 : {
5296 0 : if (fpOut == nullptr)
5297 0 : fpOut = stdout;
5298 :
5299 : /*-----------------------------------------------------------------
5300 : * Output ARC parameters
5301 : *----------------------------------------------------------------*/
5302 0 : fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g %d %d)\n",
5303 0 : m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5304 0 : m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5305 0 : static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
5306 :
5307 : /*-----------------------------------------------------------------
5308 : * Fetch and validate geometry
5309 : *----------------------------------------------------------------*/
5310 0 : OGRGeometry *poGeom = GetGeometryRef();
5311 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
5312 : {
5313 : /*-------------------------------------------------------------
5314 : * Generate arc output as a simple polyline
5315 : * We could also output as an ELLIPSE in a real MIF generator
5316 : *------------------------------------------------------------*/
5317 0 : OGRLineString *poLine = poGeom->toLineString();
5318 0 : const int numPoints = poLine->getNumPoints();
5319 0 : fprintf(fpOut, "PLINE %d\n", numPoints);
5320 0 : for (int i = 0; i < numPoints; i++)
5321 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
5322 : }
5323 : else
5324 : {
5325 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5326 : "TABArc: Missing or Invalid Geometry!");
5327 0 : return;
5328 : }
5329 :
5330 : // Finish with PEN/BRUSH/etc. clauses
5331 0 : DumpPenDef();
5332 :
5333 0 : fflush(fpOut);
5334 : }
5335 :
5336 : /*=====================================================================
5337 : * class TABText
5338 : *====================================================================*/
5339 :
5340 : /**********************************************************************
5341 : * TABText::TABText()
5342 : *
5343 : * Constructor.
5344 : **********************************************************************/
5345 309 : TABText::TABText(const OGRFeatureDefn *poDefnIn)
5346 : : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
5347 : m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
5348 : m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
5349 : m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
5350 309 : m_nFontStyle(0)
5351 : {
5352 309 : }
5353 :
5354 : /**********************************************************************
5355 : * TABText::~TABText()
5356 : *
5357 : * Destructor.
5358 : **********************************************************************/
5359 618 : TABText::~TABText()
5360 : {
5361 309 : CPLFree(m_pszString);
5362 618 : }
5363 :
5364 : /**********************************************************************
5365 : * TABText::CloneTABFeature()
5366 : *
5367 : * Duplicate feature, including stuff specific to each TABFeature type.
5368 : *
5369 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
5370 : * then copies any members specific to its own type.
5371 : **********************************************************************/
5372 0 : TABFeature *TABText::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
5373 : {
5374 : /*-----------------------------------------------------------------
5375 : * Alloc new feature and copy the base stuff
5376 : *----------------------------------------------------------------*/
5377 0 : TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
5378 :
5379 0 : CopyTABFeatureBase(poNew);
5380 :
5381 : /*-----------------------------------------------------------------
5382 : * And members specific to this class
5383 : *----------------------------------------------------------------*/
5384 : // ITABFeaturePen
5385 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
5386 :
5387 : // ITABFeatureFont
5388 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
5389 :
5390 0 : poNew->SetTextString(GetTextString());
5391 0 : poNew->SetTextAngle(GetTextAngle());
5392 0 : poNew->SetTextBoxHeight(GetTextBoxHeight());
5393 0 : poNew->SetTextBoxWidth(GetTextBoxWidth());
5394 0 : poNew->SetFontStyleTABValue(GetFontStyleTABValue());
5395 0 : poNew->SetFontBGColor(GetFontBGColor());
5396 0 : poNew->SetFontFGColor(GetFontFGColor());
5397 0 : poNew->SetFontOColor(GetFontOColor());
5398 0 : poNew->SetFontSColor(GetFontSColor());
5399 :
5400 0 : poNew->SetTextJustification(GetTextJustification());
5401 0 : poNew->SetTextSpacing(GetTextSpacing());
5402 : // Note: Text arrow/line coordinates are not transported... but
5403 : // we ignore them most of the time anyways.
5404 0 : poNew->SetTextLineType(TABTLNoLine);
5405 :
5406 0 : return poNew;
5407 : }
5408 :
5409 : /**********************************************************************
5410 : * TABText::ValidateMapInfoType()
5411 : *
5412 : * Check the feature's geometry part and return the corresponding
5413 : * mapinfo object type code. The m_nMapInfoType member will also
5414 : * be updated for further calls to GetMapInfoType();
5415 : *
5416 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
5417 : * is expected for this object class.
5418 : **********************************************************************/
5419 4 : TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
5420 : {
5421 : /*-----------------------------------------------------------------
5422 : * Fetch and validate geometry
5423 : *----------------------------------------------------------------*/
5424 4 : OGRGeometry *poGeom = GetGeometryRef();
5425 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5426 : {
5427 4 : m_nMapInfoType = TAB_GEOM_TEXT;
5428 : }
5429 : else
5430 : {
5431 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5432 : "TABText: Missing or Invalid Geometry!");
5433 0 : m_nMapInfoType = TAB_GEOM_NONE;
5434 : }
5435 :
5436 : /*-----------------------------------------------------------------
5437 : * Decide if coordinates should be compressed or not.
5438 : *----------------------------------------------------------------*/
5439 : // __TODO__ For now we always write uncompressed for this class...
5440 : // ValidateCoordType(poMapFile);
5441 4 : UpdateMBR(poMapFile);
5442 :
5443 4 : return m_nMapInfoType;
5444 : }
5445 :
5446 : /**********************************************************************
5447 : * TABText::ReadGeometryFromMAPFile()
5448 : *
5449 : * Fill the geometry and representation (color, etc...) part of the
5450 : * feature from the contents of the .MAP object pointed to by poMAPFile.
5451 : *
5452 : * It is assumed that poMAPFile currently points to the beginning of
5453 : * a map object.
5454 : *
5455 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5456 : * been called.
5457 : **********************************************************************/
5458 8 : int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5459 : TABMAPObjHdr *poObjHdr,
5460 : GBool bCoordBlockDataOnly /*=FALSE*/,
5461 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5462 : {
5463 : /*-----------------------------------------------------------------
5464 : * Fetch and validate geometry type
5465 : *----------------------------------------------------------------*/
5466 8 : m_nMapInfoType = poObjHdr->m_nType;
5467 :
5468 8 : if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
5469 : {
5470 0 : CPLError(
5471 : CE_Failure, CPLE_AssertionFailed,
5472 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5473 0 : m_nMapInfoType, m_nMapInfoType);
5474 0 : return -1;
5475 : }
5476 :
5477 : /*=============================================================
5478 : * TEXT
5479 : *============================================================*/
5480 :
5481 : /*-----------------------------------------------------------------
5482 : * Read object information
5483 : *----------------------------------------------------------------*/
5484 8 : TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5485 :
5486 8 : const GInt32 nCoordBlockPtr =
5487 : poTextHdr->m_nCoordBlockPtr; // String position
5488 8 : const int nStringLen = poTextHdr->m_nCoordDataSize; // String length
5489 8 : m_nTextAlignment = poTextHdr->m_nTextAlignment; // just./spacing/arrow
5490 :
5491 : /*-------------------------------------------------------------
5492 : * Text Angle, in tenths of degree.
5493 : * Contrary to arc start/end angles, no conversion based on
5494 : * origin quadrant is required here.
5495 : *------------------------------------------------------------*/
5496 8 : m_dAngle = poTextHdr->m_nAngle / 10.0;
5497 :
5498 8 : m_nFontStyle = poTextHdr->m_nFontStyle; // Font style
5499 :
5500 8 : m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
5501 8 : poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
5502 8 : m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
5503 8 : poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
5504 8 : m_rgbOutline = m_rgbBackground;
5505 : // In MapInfo, the shadow color is always gray (128,128,128)
5506 8 : m_rgbShadow = 0x808080;
5507 :
5508 : // arrow endpoint
5509 8 : poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
5510 8 : m_dfLineEndX, m_dfLineEndY);
5511 8 : m_bLineEndSet = TRUE;
5512 :
5513 : // Text Height
5514 8 : double dJunk = 0.0;
5515 8 : poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
5516 :
5517 8 : if (!bCoordBlockDataOnly)
5518 : {
5519 8 : m_nFontDefIndex = poTextHdr->m_nFontId; // Font name index
5520 8 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
5521 : }
5522 :
5523 : // MBR after rotation
5524 8 : double dXMin = 0.0;
5525 8 : double dYMin = 0.0;
5526 8 : double dXMax = 0.0;
5527 8 : double dYMax = 0.0;
5528 8 : poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
5529 : dYMin);
5530 8 : poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
5531 : dYMax);
5532 :
5533 8 : if (!bCoordBlockDataOnly)
5534 : {
5535 8 : m_nPenDefIndex = poTextHdr->m_nPenId; // Pen index for line
5536 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5537 : }
5538 :
5539 : /*-------------------------------------------------------------
5540 : * Read text string from the coord. block
5541 : * Note that the string may contain binary '\n' and '\\' chars
5542 : * that we keep to an unescaped form internally. This is to
5543 : * be like OGR drivers. See bug 1107 for details.
5544 : *------------------------------------------------------------*/
5545 : char *pszTmpString =
5546 8 : static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
5547 :
5548 8 : if (nStringLen > 0)
5549 : {
5550 8 : TABMAPCoordBlock *poCoordBlock = nullptr;
5551 :
5552 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5553 0 : poCoordBlock = *ppoCoordBlock;
5554 : else
5555 8 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
5556 16 : if (poCoordBlock == nullptr ||
5557 8 : poCoordBlock->ReadBytes(
5558 : nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
5559 : {
5560 0 : CPLError(CE_Failure, CPLE_FileIO,
5561 : "Failed reading text string at offset %d", nCoordBlockPtr);
5562 0 : CPLFree(pszTmpString);
5563 0 : return -1;
5564 : }
5565 :
5566 : /* Return a ref to coord block so that caller can continue reading
5567 : * after the end of this object (used by index splitting)
5568 : */
5569 8 : if (ppoCoordBlock)
5570 0 : *ppoCoordBlock = poCoordBlock;
5571 : }
5572 :
5573 8 : pszTmpString[nStringLen] = '\0';
5574 :
5575 8 : if (!poMapFile->GetEncoding().empty())
5576 : {
5577 : char *pszUtf8String =
5578 1 : CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
5579 1 : CPLFree(pszTmpString);
5580 1 : pszTmpString = pszUtf8String;
5581 : }
5582 :
5583 8 : CPLFree(m_pszString);
5584 8 : m_pszString = pszTmpString; // This string was Escaped before 20050714
5585 :
5586 : /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
5587 : */
5588 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
5589 8 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5590 :
5591 : /* Copy int MBR to feature class members */
5592 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
5593 : poObjHdr->m_nMaxY);
5594 :
5595 : /*-----------------------------------------------------------------
5596 : * Create an OGRPoint Geometry...
5597 : * The point X,Y values will be the coords of the lower-left corner before
5598 : * rotation is applied. (Note that the rotation in MapInfo is done around
5599 : * the upper-left corner)
5600 : * We need to calculate the true lower left corner of the text based
5601 : * on the MBR after rotation, the text height and the rotation angle.
5602 : *----------------------------------------------------------------*/
5603 8 : double dSin = sin(m_dAngle * M_PI / 180.0);
5604 8 : double dCos = cos(m_dAngle * M_PI / 180.0);
5605 8 : double dX = 0.0;
5606 8 : double dY = 0.0;
5607 8 : if (dSin > 0.0 && dCos > 0.0)
5608 : {
5609 7 : dX = dXMin + m_dHeight * dSin;
5610 7 : dY = dYMin;
5611 : }
5612 1 : else if (dSin > 0.0 && dCos < 0.0)
5613 : {
5614 0 : dX = dXMax;
5615 0 : dY = dYMin - m_dHeight * dCos;
5616 : }
5617 1 : else if (dSin < 0.0 && dCos < 0.0)
5618 : {
5619 0 : dX = dXMax + m_dHeight * dSin;
5620 0 : dY = dYMax;
5621 : }
5622 : else // dSin < 0 && dCos > 0
5623 : {
5624 1 : dX = dXMin;
5625 1 : dY = dYMax - m_dHeight * dCos;
5626 : }
5627 :
5628 8 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
5629 :
5630 8 : SetGeometryDirectly(poGeometry);
5631 :
5632 : /*-----------------------------------------------------------------
5633 : * Compute Text Width: the width of the Text MBR before rotation
5634 : * in ground units... unfortunately this value is not stored in the
5635 : * file, so we have to compute it with the MBR after rotation and
5636 : * the height of the MBR before rotation:
5637 : * With W = Width of MBR before rotation
5638 : * H = Height of MBR before rotation
5639 : * dX = Width of MBR after rotation
5640 : * dY = Height of MBR after rotation
5641 : * teta = rotation angle
5642 : *
5643 : * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
5644 : * W = H * (dX - H * sin(teta)) / (H * cos(teta))
5645 : *
5646 : * and for other teta values, use:
5647 : * W = H * (dY - H * cos(teta)) / (H * sin(teta))
5648 : *----------------------------------------------------------------*/
5649 8 : dSin = std::abs(dSin);
5650 8 : dCos = std::abs(dCos);
5651 8 : if (m_dHeight == 0.0)
5652 0 : m_dWidth = 0.0;
5653 8 : else if (dCos > dSin)
5654 8 : m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
5655 8 : (m_dHeight * dCos);
5656 : else
5657 0 : m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
5658 0 : (m_dHeight * dSin);
5659 8 : m_dWidth = std::abs(m_dWidth);
5660 :
5661 8 : return 0;
5662 : }
5663 :
5664 : /**********************************************************************
5665 : * TABText::WriteGeometryToMAPFile()
5666 : *
5667 : * Write the geometry and representation (color, etc...) part of the
5668 : * feature to the .MAP object pointed to by poMAPFile.
5669 : *
5670 : * It is assumed that poMAPFile currently points to a valid map object.
5671 : *
5672 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5673 : * been called.
5674 : **********************************************************************/
5675 4 : int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5676 : TABMAPObjHdr *poObjHdr,
5677 : GBool bCoordBlockDataOnly /*=FALSE*/,
5678 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5679 : {
5680 : GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
5681 :
5682 : /*-----------------------------------------------------------------
5683 : * We assume that ValidateMapInfoType() was called already and that
5684 : * the type in poObjHdr->m_nType is valid.
5685 : *----------------------------------------------------------------*/
5686 4 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5687 :
5688 : /*-----------------------------------------------------------------
5689 : * Fetch and validate geometry
5690 : *----------------------------------------------------------------*/
5691 4 : OGRGeometry *poGeom = GetGeometryRef();
5692 4 : OGRPoint *poPoint = nullptr;
5693 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5694 4 : poPoint = poGeom->toPoint();
5695 : else
5696 : {
5697 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5698 : "TABText: Missing or Invalid Geometry!");
5699 0 : return -1;
5700 : }
5701 :
5702 4 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
5703 :
5704 : /*-----------------------------------------------------------------
5705 : * Write string to a coord block first...
5706 : * Note that the string may contain unescaped '\n' and '\\'
5707 : * that we have to keep like that for the MAP file.
5708 : * See MapTools bug 1107 for more details.
5709 : *----------------------------------------------------------------*/
5710 4 : TABMAPCoordBlock *poCoordBlock = nullptr;
5711 4 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5712 0 : poCoordBlock = *ppoCoordBlock;
5713 : else
5714 4 : poCoordBlock = poMapFile->GetCurCoordBlock();
5715 4 : poCoordBlock->StartNewFeature();
5716 4 : GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
5717 :
5718 : // This string was escaped before 20050714
5719 8 : CPLString oTmpString(m_pszString ? m_pszString : "");
5720 4 : if (!poMapFile->GetEncoding().empty())
5721 : {
5722 0 : oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
5723 : }
5724 :
5725 4 : int nStringLen = static_cast<int>(oTmpString.length());
5726 :
5727 4 : if (nStringLen > 0)
5728 : {
5729 3 : poCoordBlock->WriteBytes(
5730 3 : nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
5731 : }
5732 : else
5733 : {
5734 1 : nCoordBlockPtr = 0;
5735 : }
5736 :
5737 : /*-----------------------------------------------------------------
5738 : * Copy object information
5739 : *----------------------------------------------------------------*/
5740 4 : TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5741 :
5742 4 : poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr; // String position
5743 4 : poTextHdr->m_nCoordDataSize = nStringLen; // String length
5744 4 : poTextHdr->m_nTextAlignment = m_nTextAlignment; // just./spacing/arrow
5745 :
5746 : /*-----------------------------------------------------------------
5747 : * Text Angle, (written in tenths of degrees)
5748 : * Contrary to arc start/end angles, no conversion based on
5749 : * origin quadrant is required here.
5750 : *----------------------------------------------------------------*/
5751 4 : poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
5752 :
5753 4 : poTextHdr->m_nFontStyle = m_nFontStyle; // Font style/effect
5754 :
5755 4 : poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
5756 4 : poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
5757 4 : poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
5758 :
5759 4 : poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
5760 4 : poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
5761 4 : poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
5762 :
5763 : /*-----------------------------------------------------------------
5764 : * The OGRPoint's X,Y values were the coords of the lower-left corner
5765 : * before rotation was applied. (Note that the rotation in MapInfo is
5766 : * done around the upper-left corner)
5767 : * The Feature's MBR is the MBR of the text after rotation... that's
5768 : * what MapInfo uses to define the text location.
5769 : *----------------------------------------------------------------*/
5770 4 : double dXMin = 0.0;
5771 4 : double dYMin = 0.0;
5772 4 : double dXMax = 0.0;
5773 4 : double dYMax = 0.0;
5774 : // Make sure Feature MBR is in sync with other params
5775 :
5776 4 : UpdateMBR();
5777 4 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5778 :
5779 4 : poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
5780 4 : poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
5781 :
5782 : // Label line end point
5783 4 : double dX = 0.0;
5784 4 : double dY = 0.0;
5785 4 : GetTextLineEndPoint(dX, dY); // Make sure a default line end point is set
5786 4 : poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
5787 4 : poTextHdr->m_nLineEndY);
5788 :
5789 : // Text Height
5790 4 : poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
5791 4 : poTextHdr->m_nHeight = nY;
5792 :
5793 4 : if (!bCoordBlockDataOnly)
5794 : {
5795 : // Font name
5796 4 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
5797 4 : poTextHdr->m_nFontId =
5798 4 : static_cast<GByte>(m_nFontDefIndex); // Font name index
5799 : }
5800 :
5801 : // MBR after rotation
5802 4 : poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
5803 :
5804 4 : if (!bCoordBlockDataOnly)
5805 : {
5806 4 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5807 4 : poTextHdr->m_nPenId =
5808 4 : static_cast<GByte>(m_nPenDefIndex); // Pen index for line/arrow
5809 : }
5810 :
5811 4 : if (CPLGetLastErrorType() == CE_Failure)
5812 0 : return -1;
5813 :
5814 : /* Return a ref to coord block so that caller can continue writing
5815 : * after the end of this object (used by index splitting)
5816 : */
5817 4 : if (ppoCoordBlock)
5818 0 : *ppoCoordBlock = poCoordBlock;
5819 :
5820 4 : return 0;
5821 : }
5822 :
5823 : /**********************************************************************
5824 : * TABText::GetTextString()
5825 : *
5826 : * Return ref to text string value.
5827 : *
5828 : * Returned string is a reference to the internal string buffer and should
5829 : * not be modified or freed by the caller.
5830 : **********************************************************************/
5831 300 : const char *TABText::GetTextString() const
5832 : {
5833 300 : if (m_pszString == nullptr)
5834 0 : return "";
5835 :
5836 300 : return m_pszString;
5837 : }
5838 :
5839 : /**********************************************************************
5840 : * TABText::SetTextString()
5841 : *
5842 : * Set new text string value.
5843 : *
5844 : * Note: The text string may contain "\n" chars or "\\" chars
5845 : * and we expect to receive them in a 2 chars escaped form as
5846 : * described in the MIF format specs.
5847 : **********************************************************************/
5848 3 : void TABText::SetTextString(const char *pszNewStr)
5849 : {
5850 3 : CPLFree(m_pszString);
5851 3 : m_pszString = CPLStrdup(pszNewStr);
5852 3 : }
5853 :
5854 : /**********************************************************************
5855 : * TABText::GetTextAngle()
5856 : *
5857 : * Return text angle in degrees.
5858 : **********************************************************************/
5859 10 : double TABText::GetTextAngle() const
5860 : {
5861 10 : return m_dAngle;
5862 : }
5863 :
5864 211 : void TABText::SetTextAngle(double dAngle)
5865 : {
5866 : // Make sure angle is in the range [0..360]
5867 211 : dAngle = fmod(dAngle, 360.0);
5868 211 : if (dAngle < 0.0)
5869 0 : dAngle += 360.0;
5870 211 : m_dAngle = dAngle;
5871 211 : UpdateMBR();
5872 211 : }
5873 :
5874 : /**********************************************************************
5875 : * TABText::GetTextBoxHeight()
5876 : *
5877 : * Return text height in Y axis coord. units of the text box before rotation.
5878 : **********************************************************************/
5879 10 : double TABText::GetTextBoxHeight() const
5880 : {
5881 10 : return m_dHeight;
5882 : }
5883 :
5884 3 : void TABText::SetTextBoxHeight(double dHeight)
5885 : {
5886 3 : m_dHeight = dHeight;
5887 3 : UpdateMBR();
5888 3 : }
5889 :
5890 : /**********************************************************************
5891 : * TABText::GetTextBoxWidth()
5892 : *
5893 : * Return text width in X axis coord. units. of the text box before rotation.
5894 : *
5895 : * If value has not been set, then we force a default value that assumes
5896 : * that one char's box width is 60% of its height... and we ignore
5897 : * the multiline case. This should not matter when the user PROPERLY sets
5898 : * the value.
5899 : **********************************************************************/
5900 12 : double TABText::GetTextBoxWidth() const
5901 : {
5902 12 : if (m_dWidth == 0.0 && m_pszString)
5903 : {
5904 3 : m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
5905 : }
5906 12 : return m_dWidth;
5907 : }
5908 :
5909 0 : void TABText::SetTextBoxWidth(double dWidth)
5910 : {
5911 0 : m_dWidth = dWidth;
5912 0 : UpdateMBR();
5913 0 : }
5914 :
5915 : /**********************************************************************
5916 : * TABText::GetTextLineEndPoint()
5917 : *
5918 : * Return X,Y coordinates of the text label line end point.
5919 : * Default is the center of the text MBR.
5920 : **********************************************************************/
5921 4 : void TABText::GetTextLineEndPoint(double &dX, double &dY)
5922 : {
5923 4 : if (!m_bLineEndSet)
5924 : {
5925 : // Set default location at center of text MBR
5926 4 : double dXMin = 0.0;
5927 4 : double dYMin = 0.0;
5928 4 : double dXMax = 0.0;
5929 4 : double dYMax = 0.0;
5930 4 : UpdateMBR();
5931 4 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5932 4 : m_dfLineEndX = (dXMin + dXMax) / 2.0;
5933 4 : m_dfLineEndY = (dYMin + dYMax) / 2.0;
5934 4 : m_bLineEndSet = TRUE;
5935 : }
5936 :
5937 : // Return values
5938 4 : dX = m_dfLineEndX;
5939 4 : dY = m_dfLineEndY;
5940 4 : }
5941 :
5942 178 : void TABText::SetTextLineEndPoint(double dX, double dY)
5943 : {
5944 178 : m_dfLineEndX = dX;
5945 178 : m_dfLineEndY = dY;
5946 178 : m_bLineEndSet = TRUE;
5947 178 : }
5948 :
5949 : /**********************************************************************
5950 : * TABText::UpdateMBR()
5951 : *
5952 : * Update the feature MBR using the text origin (OGRPoint geometry), the
5953 : * rotation angle, and the Width/height before rotation.
5954 : *
5955 : * This function cannot perform properly unless all the above have been set.
5956 : *
5957 : * Returns 0 on success, or -1 if there is no geometry in object
5958 : **********************************************************************/
5959 226 : int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
5960 : {
5961 226 : OGRGeometry *poGeom = GetGeometryRef();
5962 226 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5963 : {
5964 12 : OGRPoint *poPoint = poGeom->toPoint();
5965 :
5966 12 : const double dX0 = poPoint->getX();
5967 12 : const double dY0 = poPoint->getY();
5968 :
5969 12 : const double dSin = sin(m_dAngle * M_PI / 180.0);
5970 12 : const double dCos = cos(m_dAngle * M_PI / 180.0);
5971 :
5972 12 : GetTextBoxWidth(); // Force default width value if necessary.
5973 :
5974 12 : const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
5975 12 : const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
5976 :
5977 12 : SetMBR(dX0, dY0, dX0, dY0);
5978 60 : for (int i = 0; i < 4; i++)
5979 : {
5980 : // Rotate one of the box corners
5981 48 : const double dX1 =
5982 48 : dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
5983 48 : const double dY1 =
5984 48 : dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
5985 :
5986 : // And update feature MBR with rotated coordinate
5987 48 : if (dX1 < m_dXMin)
5988 9 : m_dXMin = dX1;
5989 48 : if (dX1 > m_dXMax)
5990 9 : m_dXMax = dX1;
5991 48 : if (dY1 < m_dYMin)
5992 0 : m_dYMin = dY1;
5993 48 : if (dY1 > m_dYMax)
5994 18 : m_dYMax = dY1;
5995 : }
5996 :
5997 12 : if (poMapFile)
5998 : {
5999 4 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
6000 4 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
6001 : }
6002 :
6003 12 : return 0;
6004 : }
6005 :
6006 214 : return -1;
6007 : }
6008 :
6009 : /**********************************************************************
6010 : * TABText::GetFontBGColor()
6011 : *
6012 : * Return background color.
6013 : **********************************************************************/
6014 8 : GInt32 TABText::GetFontBGColor() const
6015 : {
6016 8 : return m_rgbBackground;
6017 : }
6018 :
6019 258 : void TABText::SetFontBGColor(GInt32 rgbColor)
6020 : {
6021 258 : m_rgbBackground = rgbColor;
6022 258 : }
6023 :
6024 : /**********************************************************************
6025 : * TABText::GetFontOColor()
6026 : *
6027 : * Return outline color.
6028 : **********************************************************************/
6029 1 : GInt32 TABText::GetFontOColor() const
6030 : {
6031 1 : return m_rgbOutline;
6032 : }
6033 :
6034 1 : void TABText::SetFontOColor(GInt32 rgbColor)
6035 : {
6036 1 : m_rgbOutline = rgbColor;
6037 1 : }
6038 :
6039 : /**********************************************************************
6040 : * TABText::GetFontSColor()
6041 : *
6042 : * Return shadow color.
6043 : **********************************************************************/
6044 0 : GInt32 TABText::GetFontSColor() const
6045 : {
6046 0 : return m_rgbShadow;
6047 : }
6048 :
6049 0 : void TABText::SetFontSColor(GInt32 rgbColor)
6050 : {
6051 0 : m_rgbShadow = rgbColor;
6052 0 : }
6053 :
6054 : /**********************************************************************
6055 : * TABText::GetFontFGColor()
6056 : *
6057 : * Return foreground color.
6058 : **********************************************************************/
6059 10 : GInt32 TABText::GetFontFGColor() const
6060 : {
6061 10 : return m_rgbForeground;
6062 : }
6063 :
6064 265 : void TABText::SetFontFGColor(GInt32 rgbColor)
6065 : {
6066 265 : m_rgbForeground = rgbColor;
6067 265 : }
6068 :
6069 : /**********************************************************************
6070 : * TABText::GetTextJustification()
6071 : *
6072 : * Return text justification. Default is TABTJLeft
6073 : **********************************************************************/
6074 10 : TABTextJust TABText::GetTextJustification() const
6075 : {
6076 10 : TABTextJust eJust = TABTJLeft;
6077 :
6078 10 : if (m_nTextAlignment & 0x0200)
6079 9 : eJust = TABTJCenter;
6080 1 : else if (m_nTextAlignment & 0x0400)
6081 0 : eJust = TABTJRight;
6082 :
6083 10 : return eJust;
6084 : }
6085 :
6086 222 : void TABText::SetTextJustification(TABTextJust eJustification)
6087 : {
6088 : // Flush current value... default is TABTJLeft
6089 222 : m_nTextAlignment &= ~0x0600;
6090 : // ... and set new one.
6091 222 : if (eJustification == TABTJCenter)
6092 222 : m_nTextAlignment |= 0x0200;
6093 0 : else if (eJustification == TABTJRight)
6094 0 : m_nTextAlignment |= 0x0400;
6095 222 : }
6096 :
6097 : /**********************************************************************
6098 : * TABText::GetTextSpacing()
6099 : *
6100 : * Return text vertical spacing factor. Default is TABTSSingle
6101 : **********************************************************************/
6102 0 : TABTextSpacing TABText::GetTextSpacing() const
6103 : {
6104 0 : TABTextSpacing eSpacing = TABTSSingle;
6105 :
6106 0 : if (m_nTextAlignment & 0x0800)
6107 0 : eSpacing = TABTS1_5;
6108 0 : else if (m_nTextAlignment & 0x1000)
6109 0 : eSpacing = TABTSDouble;
6110 :
6111 0 : return eSpacing;
6112 : }
6113 :
6114 237 : void TABText::SetTextSpacing(TABTextSpacing eSpacing)
6115 : {
6116 : // Flush current value... default is TABTSSingle
6117 237 : m_nTextAlignment &= ~0x1800;
6118 : // ... and set new one.
6119 237 : if (eSpacing == TABTS1_5)
6120 0 : m_nTextAlignment |= 0x0800;
6121 237 : else if (eSpacing == TABTSDouble)
6122 237 : m_nTextAlignment |= 0x1000;
6123 237 : }
6124 :
6125 : /**********************************************************************
6126 : * TABText::GetTextLineType()
6127 : *
6128 : * Return text line (arrow) type. Default is TABTLNoLine
6129 : **********************************************************************/
6130 0 : TABTextLineType TABText::GetTextLineType() const
6131 : {
6132 0 : TABTextLineType eLine = TABTLNoLine;
6133 :
6134 0 : if (m_nTextAlignment & 0x2000)
6135 0 : eLine = TABTLSimple;
6136 0 : else if (m_nTextAlignment & 0x4000)
6137 0 : eLine = TABTLArrow;
6138 :
6139 0 : return eLine;
6140 : }
6141 :
6142 178 : void TABText::SetTextLineType(TABTextLineType eLineType)
6143 : {
6144 : // Flush current value... default is TABTLNoLine
6145 178 : m_nTextAlignment &= ~0x6000;
6146 : // ... and set new one.
6147 178 : if (eLineType == TABTLSimple)
6148 178 : m_nTextAlignment |= 0x2000;
6149 0 : else if (eLineType == TABTLArrow)
6150 0 : m_nTextAlignment |= 0x4000;
6151 178 : }
6152 :
6153 : /**********************************************************************
6154 : * TABText::QueryFontStyle()
6155 : *
6156 : * Return TRUE if the specified font style attribute is turned ON,
6157 : * or FALSE otherwise. See enum TABFontStyle for the list of styles
6158 : * that can be queried on.
6159 : **********************************************************************/
6160 385 : GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
6161 : {
6162 385 : return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
6163 : }
6164 :
6165 264 : void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
6166 : {
6167 264 : if (bStyleOn)
6168 264 : m_nFontStyle |= static_cast<int>(eStyleToToggle);
6169 : else
6170 0 : m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
6171 264 : }
6172 :
6173 : /**********************************************************************
6174 : * TABText::GetFontStyleMIFValue()
6175 : *
6176 : * Return the Font Style value for this object using the style values
6177 : * that are used in a MIF FONT() clause. See MIF specs (appendix A).
6178 : *
6179 : * The reason why we have to differentiate between the TAB and the MIF font
6180 : * style values is that in TAB, TABFSBox is included in the style value
6181 : * as code 0x100, but in MIF it is not included, instead it is implied by
6182 : * the presence of the BG color in the FONT() clause (the BG color is
6183 : * present only when TABFSBox or TABFSHalo is set).
6184 : * This also has the effect of shifting all the other style values > 0x100
6185 : * by 1 byte.
6186 : **********************************************************************/
6187 0 : int TABText::GetFontStyleMIFValue() const
6188 : {
6189 : // The conversion is simply to remove bit 0x100 from the value and shift
6190 : // down all values past this bit.
6191 0 : return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
6192 : }
6193 :
6194 261 : void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
6195 : {
6196 261 : m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
6197 : // When BG color is set, then either BOX or HALO should be set.
6198 261 : if (bBGColorSet && !QueryFontStyle(TABFSHalo))
6199 254 : ToggleFontStyle(TABFSBox, TRUE);
6200 261 : }
6201 :
6202 10 : int TABText::IsFontBGColorUsed() const
6203 : {
6204 : // Font BG color is used only when BOX is set.
6205 10 : return QueryFontStyle(TABFSBox);
6206 : }
6207 :
6208 10 : int TABText::IsFontOColorUsed() const
6209 : {
6210 : // Font outline color is used only when HALO is set.
6211 10 : return QueryFontStyle(TABFSHalo);
6212 : }
6213 :
6214 10 : int TABText::IsFontSColorUsed() const
6215 : {
6216 : // Font shadow color is used only when Shadow is set.
6217 10 : return QueryFontStyle(TABFSShadow);
6218 : }
6219 :
6220 10 : int TABText::IsFontBold() const
6221 : {
6222 : // Font bold is used only when Bold is set.
6223 10 : return QueryFontStyle(TABFSBold);
6224 : }
6225 :
6226 10 : int TABText::IsFontItalic() const
6227 : {
6228 : // Font italic is used only when Italic is set.
6229 10 : return QueryFontStyle(TABFSItalic);
6230 : }
6231 :
6232 10 : int TABText::IsFontUnderline() const
6233 : {
6234 : // Font underline is used only when Underline is set.
6235 10 : return QueryFontStyle(TABFSUnderline);
6236 : }
6237 :
6238 : /**********************************************************************
6239 : * TABText::GetLabelStyleString()
6240 : *
6241 : * This is not the correct location, it should be in ITABFeatureFont,
6242 : * but it is really more easy to put it here. This fct return a complete
6243 : * string for the representation with the string to display
6244 : **********************************************************************/
6245 10 : const char *TABText::GetLabelStyleString() const
6246 : {
6247 10 : const char *pszStyle = nullptr;
6248 10 : int nStringLen = static_cast<int>(strlen(GetTextString()));
6249 : // ALL Caps, Extpanded need to modify the string value
6250 : char *pszTextString =
6251 10 : static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
6252 : /* char szPattern[20]; */
6253 10 : int nJustification = 1;
6254 :
6255 10 : strcpy(pszTextString, GetTextString());
6256 : /* szPattern[0] = '\0'; */
6257 :
6258 10 : switch (GetTextJustification())
6259 : {
6260 9 : case TABTJCenter:
6261 9 : nJustification = 2;
6262 9 : break;
6263 0 : case TABTJRight:
6264 0 : nJustification = 3;
6265 0 : break;
6266 1 : case TABTJLeft:
6267 : default:
6268 1 : nJustification = 1;
6269 1 : break;
6270 : }
6271 :
6272 : // Compute real font size, taking number of lines ("\\n", "\n") and line
6273 : // spacing into account.
6274 10 : int numLines = 1;
6275 61 : for (int i = 0; pszTextString[i];
6276 51 : numLines +=
6277 102 : ((pszTextString[i] == '\n' ||
6278 51 : (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
6279 0 : pszTextString[i + 1] != '\0'),
6280 : ++i)
6281 : ;
6282 :
6283 10 : double dHeight = GetTextBoxHeight() / numLines;
6284 :
6285 : // In all cases, take out 20% of font height to account for line spacing
6286 10 : if (numLines > 1)
6287 : {
6288 0 : switch (GetTextSpacing())
6289 : {
6290 0 : case TABTS1_5:
6291 0 : dHeight *= (0.80 * 0.69);
6292 0 : break;
6293 0 : case TABTSDouble:
6294 0 : dHeight *= (0.66 * 0.69);
6295 0 : break;
6296 0 : default:
6297 0 : dHeight *= 0.69;
6298 : }
6299 : }
6300 : else
6301 : {
6302 10 : dHeight *= 0.69;
6303 : }
6304 :
6305 10 : if (QueryFontStyle(TABFSAllCaps))
6306 0 : for (int i = 0; pszTextString[i]; ++i)
6307 0 : if (isalpha(static_cast<unsigned char>(pszTextString[i])))
6308 0 : pszTextString[i] = static_cast<char>(
6309 0 : CPLToupper(static_cast<unsigned char>(pszTextString[i])));
6310 :
6311 : /* Escape the double quote chars and expand the text */
6312 10 : char *pszTmpTextString = nullptr;
6313 :
6314 10 : if (QueryFontStyle(TABFSExpanded))
6315 : pszTmpTextString = static_cast<char *>(
6316 0 : CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
6317 : else
6318 : pszTmpTextString = static_cast<char *>(
6319 10 : CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
6320 :
6321 10 : int j = 0;
6322 61 : for (int i = 0; i < nStringLen; ++i, ++j)
6323 : {
6324 51 : if (pszTextString[i] == '"')
6325 : {
6326 0 : pszTmpTextString[j] = '\\';
6327 0 : pszTmpTextString[j + 1] = pszTextString[i];
6328 0 : ++j;
6329 : }
6330 : else
6331 51 : pszTmpTextString[j] = pszTextString[i];
6332 :
6333 51 : if (QueryFontStyle(TABFSExpanded))
6334 : {
6335 0 : pszTmpTextString[j + 1] = ' ';
6336 0 : ++j;
6337 : }
6338 : }
6339 :
6340 10 : pszTmpTextString[j] = '\0';
6341 10 : CPLFree(pszTextString);
6342 : pszTextString = static_cast<char *>(
6343 10 : CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
6344 10 : strcpy(pszTextString, pszTmpTextString);
6345 10 : CPLFree(pszTmpTextString);
6346 :
6347 : const char *pszBGColor =
6348 10 : IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
6349 : const char *pszOColor =
6350 10 : IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
6351 : const char *pszSColor =
6352 10 : IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
6353 10 : const char *pszBold = IsFontBold() ? ",bo:1" : "";
6354 10 : const char *pszItalic = IsFontItalic() ? ",it:1" : "";
6355 10 : const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
6356 :
6357 10 : pszStyle = CPLSPrintf(
6358 : "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
6359 : pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
6360 : pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
6361 : GetFontNameRef());
6362 :
6363 10 : CPLFree(pszTextString);
6364 10 : return pszStyle;
6365 : }
6366 :
6367 : /**********************************************************************
6368 : * TABText::GetStyleString() const
6369 : *
6370 : * Return style string for this feature.
6371 : *
6372 : * Style String is built only once during the first call to GetStyleString().
6373 : **********************************************************************/
6374 10 : const char *TABText::GetStyleString() const
6375 : {
6376 10 : if (m_pszStyleString == nullptr)
6377 : {
6378 10 : m_pszStyleString = CPLStrdup(GetLabelStyleString());
6379 : }
6380 :
6381 10 : return m_pszStyleString;
6382 : }
6383 :
6384 4 : void TABText::SetLabelFromStyleString(const char *pszStyleString)
6385 : {
6386 : // Use the Style Manager to retrieve all the information we need.
6387 4 : auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
6388 0 : std::unique_ptr<OGRStyleTool> poStylePart;
6389 :
6390 : // Init the StyleMgr with the StyleString.
6391 4 : poStyleMgr->InitStyleString(pszStyleString);
6392 :
6393 : // Retrieve the Symbol info.
6394 4 : const int numParts = poStyleMgr->GetPartCount();
6395 4 : for (int i = 0; i < numParts; i++)
6396 : {
6397 4 : poStylePart.reset(poStyleMgr->GetPart(i));
6398 4 : if (poStylePart == nullptr)
6399 : {
6400 0 : continue;
6401 : }
6402 :
6403 4 : if (poStylePart->GetType() == OGRSTCLabel)
6404 : {
6405 4 : break;
6406 : }
6407 : else
6408 : {
6409 0 : poStylePart.reset();
6410 : }
6411 : }
6412 :
6413 : // If the no Symbol found, do nothing.
6414 4 : if (poStylePart == nullptr)
6415 : {
6416 0 : return;
6417 : }
6418 :
6419 4 : auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
6420 :
6421 4 : GBool bIsNull = 0;
6422 4 : const char *pszText = poLabelStyle->TextString(bIsNull);
6423 4 : if (!bIsNull && pszText)
6424 : {
6425 3 : SetTextString(pszText);
6426 :
6427 3 : poLabelStyle->SetUnit(OGRSTUMM);
6428 3 : double dfSize = poLabelStyle->Size(bIsNull);
6429 3 : if (!bIsNull)
6430 : {
6431 3 : dfSize /= 1000;
6432 :
6433 : // Compute text box height, taking number of lines ("\\n", "\n") and
6434 : // line spacing into account.
6435 3 : int numLines = 1;
6436 18 : for (int i = 0; pszText[i];
6437 45 : numLines += ((pszText[i] == '\n' ||
6438 15 : (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
6439 0 : pszText[i + 1] != '\0'),
6440 : ++i)
6441 : ;
6442 :
6443 : // Cf GetLabelStyleString() for 0.69. We should likely also take
6444 : // into account line spacing if we knew how to compute it.
6445 3 : SetTextBoxHeight(dfSize / 0.69 * numLines);
6446 : }
6447 : }
6448 :
6449 4 : if (poLabelStyle->Bold(bIsNull))
6450 3 : ToggleFontStyle(TABFSBold, true);
6451 :
6452 4 : if (poLabelStyle->Italic(bIsNull))
6453 1 : ToggleFontStyle(TABFSItalic, true);
6454 :
6455 4 : if (poLabelStyle->Underline(bIsNull))
6456 1 : ToggleFontStyle(TABFSUnderline, true);
6457 :
6458 4 : const char *pszFontName = poLabelStyle->FontName(bIsNull);
6459 4 : if (!bIsNull && pszFontName)
6460 4 : SetFontName(pszFontName);
6461 :
6462 : // Set the ForeColor
6463 4 : const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
6464 4 : if (bIsNull)
6465 0 : pszForeColor = nullptr;
6466 4 : if (pszForeColor)
6467 : {
6468 4 : if (pszForeColor[0] == '#')
6469 4 : pszForeColor++;
6470 8 : CPLString osForeColor(pszForeColor);
6471 4 : if (strlen(pszForeColor) > 6)
6472 1 : osForeColor.resize(6);
6473 4 : const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
6474 4 : SetFontFGColor(static_cast<GInt32>(nColor));
6475 : }
6476 :
6477 : // Set the BackgroundColor
6478 4 : const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
6479 4 : if (bIsNull)
6480 0 : pszBackColor = nullptr;
6481 4 : if (pszBackColor)
6482 : {
6483 4 : if (pszBackColor[0] == '#')
6484 4 : pszBackColor++;
6485 8 : CPLString osBackColor(pszBackColor);
6486 4 : if (strlen(pszBackColor) > 6)
6487 1 : osBackColor.resize(6);
6488 4 : const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
6489 4 : ToggleFontStyle(TABFSBox, true);
6490 4 : SetFontBGColor(static_cast<GInt32>(nColor));
6491 : }
6492 :
6493 : // Set the OutlineColor
6494 4 : const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
6495 4 : if (bIsNull)
6496 3 : pszOutlineColor = nullptr;
6497 4 : if (pszOutlineColor)
6498 : {
6499 1 : if (pszOutlineColor[0] == '#')
6500 1 : pszOutlineColor++;
6501 2 : CPLString osOutlineColor(pszOutlineColor);
6502 1 : if (strlen(pszOutlineColor) > 6)
6503 0 : osOutlineColor.resize(6);
6504 : const int nColor =
6505 1 : static_cast<int>(strtol(osOutlineColor, nullptr, 16));
6506 1 : ToggleFontStyle(TABFSHalo, true);
6507 1 : SetFontOColor(static_cast<GInt32>(nColor));
6508 : }
6509 :
6510 : #if 0
6511 : // Commented out since it is hardcoded to 0x808080.
6512 : // Set the ShadowColor
6513 : const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
6514 : if(bIsNull) pszShadowColor = nullptr;
6515 : if(pszShadowColor)
6516 : {
6517 : if(pszShadowColor[0] == '#')
6518 : pszShadowColor++;
6519 : CPLString osShadowColor(pszShadowColor);
6520 : if( strlen(pszShadowColor) > 6 )
6521 : osShadowColor.resize(6);
6522 : const int nColor =
6523 : static_cast<int>(strtol(osShadowColor, nullptr, 16));
6524 : ToggleFontStyle(TABFSShadow, true);
6525 : SetFontSColor(static_cast<GInt32>(nColor));
6526 : }
6527 : #endif
6528 :
6529 4 : const double dfAngle = poLabelStyle->Angle(bIsNull);
6530 4 : if (!bIsNull)
6531 3 : SetTextAngle(dfAngle);
6532 :
6533 4 : const int nAnchor = poLabelStyle->Anchor(bIsNull);
6534 4 : if (!bIsNull)
6535 : {
6536 3 : switch ((nAnchor - 1) % 3)
6537 : {
6538 0 : case 0:
6539 0 : SetTextJustification(TABTJLeft);
6540 0 : break;
6541 3 : case 1:
6542 3 : SetTextJustification(TABTJCenter);
6543 3 : break;
6544 0 : default /* 2 */:
6545 0 : SetTextJustification(TABTJRight);
6546 0 : break;
6547 : }
6548 : }
6549 : }
6550 :
6551 : /**********************************************************************
6552 : * TABText::DumpMIF()
6553 : *
6554 : * Dump feature geometry in a format similar to .MIF REGIONs.
6555 : **********************************************************************/
6556 0 : void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
6557 : {
6558 0 : if (fpOut == nullptr)
6559 0 : fpOut = stdout;
6560 :
6561 : /*-----------------------------------------------------------------
6562 : * Fetch and validate geometry
6563 : *----------------------------------------------------------------*/
6564 0 : OGRGeometry *poGeom = GetGeometryRef();
6565 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6566 : {
6567 : /*-------------------------------------------------------------
6568 : * Generate output for text object
6569 : *------------------------------------------------------------*/
6570 0 : OGRPoint *poPoint = poGeom->toPoint();
6571 :
6572 0 : fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
6573 0 : m_pszString ? m_pszString : "", poPoint->getX(),
6574 : poPoint->getY());
6575 :
6576 0 : fprintf(fpOut, " m_pszString = '%s'\n", m_pszString);
6577 0 : fprintf(fpOut, " m_dAngle = %.15g\n", m_dAngle);
6578 0 : fprintf(fpOut, " m_dHeight = %.15g\n", m_dHeight);
6579 0 : fprintf(fpOut, " m_rgbForeground = 0x%6.6x (%d)\n", m_rgbForeground,
6580 : m_rgbForeground);
6581 0 : fprintf(fpOut, " m_rgbBackground = 0x%6.6x (%d)\n", m_rgbBackground,
6582 : m_rgbBackground);
6583 0 : fprintf(fpOut, " m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
6584 0 : fprintf(fpOut, " m_nFontStyle = 0x%4.4x\n", m_nFontStyle);
6585 : }
6586 : else
6587 : {
6588 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6589 : "TABText: Missing or Invalid Geometry!");
6590 0 : return;
6591 : }
6592 :
6593 : // Finish with PEN/BRUSH/etc. clauses
6594 0 : DumpPenDef();
6595 0 : DumpFontDef();
6596 :
6597 0 : fflush(fpOut);
6598 : }
6599 :
6600 : /*=====================================================================
6601 : * class TABMultiPoint
6602 : *====================================================================*/
6603 :
6604 : /**********************************************************************
6605 : * TABMultiPoint::TABMultiPoint()
6606 : *
6607 : * Constructor.
6608 : **********************************************************************/
6609 191 : TABMultiPoint::TABMultiPoint(const OGRFeatureDefn *poDefnIn)
6610 : : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
6611 191 : m_dCenterY(0.0)
6612 : {
6613 191 : }
6614 :
6615 : /**********************************************************************
6616 : * TABMultiPoint::~TABMultiPoint()
6617 : *
6618 : * Destructor.
6619 : **********************************************************************/
6620 382 : TABMultiPoint::~TABMultiPoint()
6621 : {
6622 382 : }
6623 :
6624 : /**********************************************************************
6625 : * TABMultiPoint::CloneTABFeature()
6626 : *
6627 : * Duplicate feature, including stuff specific to each TABFeature type.
6628 : *
6629 : * This method calls the generic TABFeature::CloneTABFeature() and
6630 : * then copies any members specific to its own type.
6631 : **********************************************************************/
6632 : TABFeature *
6633 0 : TABMultiPoint::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
6634 : {
6635 : /*-----------------------------------------------------------------
6636 : * Alloc new feature and copy the base stuff
6637 : *----------------------------------------------------------------*/
6638 : TABMultiPoint *poNew =
6639 0 : new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
6640 :
6641 0 : CopyTABFeatureBase(poNew);
6642 :
6643 : /*-----------------------------------------------------------------
6644 : * And members specific to this class
6645 : *----------------------------------------------------------------*/
6646 : // ITABFeatureSymbol
6647 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
6648 :
6649 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
6650 0 : poNew->m_dCenterX = m_dCenterX;
6651 0 : poNew->m_dCenterY = m_dCenterY;
6652 :
6653 0 : return poNew;
6654 : }
6655 :
6656 : /**********************************************************************
6657 : * TABMultiPoint::ValidateMapInfoType()
6658 : *
6659 : * Check the feature's geometry part and return the corresponding
6660 : * mapinfo object type code. The m_nMapInfoType member will also
6661 : * be updated for further calls to GetMapInfoType();
6662 : *
6663 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
6664 : * is expected for this object class.
6665 : **********************************************************************/
6666 0 : TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
6667 : {
6668 : /*-----------------------------------------------------------------
6669 : * Fetch and validate geometry
6670 : *----------------------------------------------------------------*/
6671 0 : OGRGeometry *poGeom = GetGeometryRef();
6672 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6673 : {
6674 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6675 :
6676 0 : if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
6677 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
6678 : else
6679 0 : m_nMapInfoType = TAB_GEOM_MULTIPOINT;
6680 : }
6681 : else
6682 : {
6683 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6684 : "TABMultiPoint: Missing or Invalid Geometry!");
6685 0 : m_nMapInfoType = TAB_GEOM_NONE;
6686 : }
6687 :
6688 : /*-----------------------------------------------------------------
6689 : * Decide if coordinates should be compressed or not.
6690 : *----------------------------------------------------------------*/
6691 0 : ValidateCoordType(poMapFile);
6692 :
6693 0 : return m_nMapInfoType;
6694 : }
6695 :
6696 : /**********************************************************************
6697 : * TABMultiPoint::ReadGeometryFromMAPFile()
6698 : *
6699 : * Fill the geometry and representation (color, etc...) part of the
6700 : * feature from the contents of the .MAP object pointed to by poMAPFile.
6701 : *
6702 : * It is assumed that poMAPFile currently points to the beginning of
6703 : * a map object.
6704 : *
6705 : * Returns 0 on success, -1 on error, in which case CPLError() will have
6706 : * been called.
6707 : **********************************************************************/
6708 8 : int TABMultiPoint::ReadGeometryFromMAPFile(
6709 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6710 : GBool bCoordBlockDataOnly /*=FALSE*/,
6711 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6712 : {
6713 8 : double dXMin = 0.0;
6714 8 : double dYMin = 0.0;
6715 8 : double dXMax = 0.0;
6716 8 : double dYMax = 0.0;
6717 8 : OGRGeometry *poGeometry = nullptr;
6718 8 : GBool bComprCoord = poObjHdr->IsCompressedType();
6719 8 : TABMAPCoordBlock *poCoordBlock = nullptr;
6720 :
6721 : /*-----------------------------------------------------------------
6722 : * Fetch and validate geometry type
6723 : *----------------------------------------------------------------*/
6724 8 : m_nMapInfoType = poObjHdr->m_nType;
6725 :
6726 : /*-----------------------------------------------------------------
6727 : * Read object information
6728 : *----------------------------------------------------------------*/
6729 8 : if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
6730 8 : m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
6731 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
6732 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
6733 : {
6734 : /*-------------------------------------------------------------
6735 : * Copy data from poObjHdr
6736 : *------------------------------------------------------------*/
6737 : TABMAPObjMultiPoint *poMPointHdr =
6738 8 : cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6739 :
6740 8 : const GUInt32 nMinimumBytesForPoints =
6741 8 : (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
6742 8 : if (nMinimumBytesForPoints > 1024 * 1024 &&
6743 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
6744 : {
6745 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
6746 0 : return -1;
6747 : }
6748 :
6749 : // MBR
6750 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
6751 : dXMin, dYMin);
6752 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
6753 : dXMax, dYMax);
6754 :
6755 8 : if (!bCoordBlockDataOnly)
6756 : {
6757 8 : m_nSymbolDefIndex = poMPointHdr->m_nSymbolId; // Symbol index
6758 8 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
6759 : }
6760 :
6761 8 : double dX = 0.0;
6762 8 : double dY = 0.0;
6763 : // Centroid/label point
6764 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
6765 : dX, dY);
6766 8 : SetCenter(dX, dY);
6767 :
6768 : // Compressed coordinate origin (useful only in compressed case!)
6769 8 : m_nComprOrgX = poMPointHdr->m_nComprOrgX;
6770 8 : m_nComprOrgY = poMPointHdr->m_nComprOrgY;
6771 :
6772 : /*-------------------------------------------------------------
6773 : * Read Point Coordinates
6774 : *------------------------------------------------------------*/
6775 8 : OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
6776 8 : poGeometry = poMultiPoint;
6777 :
6778 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6779 4 : poCoordBlock = *ppoCoordBlock;
6780 : else
6781 : poCoordBlock =
6782 4 : poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
6783 8 : if (poCoordBlock == nullptr)
6784 : {
6785 0 : delete poGeometry;
6786 0 : return -1;
6787 : }
6788 8 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6789 :
6790 24 : for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6791 : {
6792 16 : GInt32 nX = 0;
6793 16 : GInt32 nY = 0;
6794 16 : if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
6795 : {
6796 0 : CPLError(CE_Failure, CPLE_FileIO,
6797 : "Failed reading coordinate data at offset %d",
6798 : poMPointHdr->m_nCoordBlockPtr);
6799 0 : delete poGeometry;
6800 0 : return -1;
6801 : }
6802 :
6803 16 : poMapFile->Int2Coordsys(nX, nY, dX, dY);
6804 16 : OGRPoint *poPoint = new OGRPoint(dX, dY);
6805 :
6806 16 : if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
6807 : {
6808 0 : CPLAssert(false); // Just in case lower-level lib is modified
6809 : }
6810 8 : }
6811 : }
6812 : else
6813 : {
6814 0 : CPLError(
6815 : CE_Failure, CPLE_AssertionFailed,
6816 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
6817 0 : m_nMapInfoType, m_nMapInfoType);
6818 0 : return -1;
6819 : }
6820 :
6821 8 : SetGeometryDirectly(poGeometry);
6822 :
6823 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
6824 :
6825 : /* Copy int MBR to feature class members */
6826 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
6827 : poObjHdr->m_nMaxY);
6828 :
6829 : /* Return a ref to coord block so that caller can continue reading
6830 : * after the end of this object (used by TABCollection and index splitting)
6831 : */
6832 8 : if (ppoCoordBlock)
6833 4 : *ppoCoordBlock = poCoordBlock;
6834 :
6835 8 : return 0;
6836 : }
6837 :
6838 : /**********************************************************************
6839 : * TABMultiPoint::WriteGeometryToMAPFile()
6840 : *
6841 : * Write the geometry and representation (color, etc...) part of the
6842 : * feature to the .MAP object pointed to by poMAPFile.
6843 : *
6844 : * It is assumed that poMAPFile currently points to a valid map object.
6845 : *
6846 : * Returns 0 on success, -1 on error, in which case CPLError() will have
6847 : * been called.
6848 : **********************************************************************/
6849 0 : int TABMultiPoint::WriteGeometryToMAPFile(
6850 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6851 : GBool bCoordBlockDataOnly /*=FALSE*/,
6852 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6853 : {
6854 : GInt32 nX, nY;
6855 :
6856 : /*-----------------------------------------------------------------
6857 : * We assume that ValidateMapInfoType() was called already and that
6858 : * the type in poObjHdr->m_nType is valid.
6859 : *----------------------------------------------------------------*/
6860 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
6861 :
6862 : TABMAPObjMultiPoint *poMPointHdr =
6863 0 : cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6864 :
6865 : /*-----------------------------------------------------------------
6866 : * Fetch and validate geometry
6867 : *----------------------------------------------------------------*/
6868 0 : OGRGeometry *poGeom = GetGeometryRef();
6869 0 : OGRMultiPoint *poMPoint = nullptr;
6870 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6871 0 : poMPoint = poGeom->toMultiPoint();
6872 : else
6873 : {
6874 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6875 : "TABMultiPoint: Missing or Invalid Geometry!");
6876 0 : return -1;
6877 : }
6878 :
6879 0 : poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
6880 :
6881 : /*-----------------------------------------------------------------
6882 : * Write data to coordinate block
6883 : *----------------------------------------------------------------*/
6884 0 : const GBool bCompressed = poObjHdr->IsCompressedType();
6885 :
6886 0 : TABMAPCoordBlock *poCoordBlock = nullptr;
6887 0 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6888 0 : poCoordBlock = *ppoCoordBlock;
6889 : else
6890 0 : poCoordBlock = poMapFile->GetCurCoordBlock();
6891 0 : poCoordBlock->StartNewFeature();
6892 0 : poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
6893 0 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6894 :
6895 0 : for (int iPoint = 0, nStatus = 0;
6896 0 : nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6897 : {
6898 0 : poGeom = poMPoint->getGeometryRef(iPoint);
6899 :
6900 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6901 : {
6902 0 : OGRPoint *poPoint = poGeom->toPoint();
6903 :
6904 0 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
6905 0 : if (iPoint == 0)
6906 : {
6907 : // Default to the first point, we may use explicit value below
6908 0 : poMPointHdr->m_nLabelX = nX;
6909 0 : poMPointHdr->m_nLabelY = nY;
6910 : }
6911 :
6912 0 : if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
6913 : 0)
6914 : {
6915 : // Failed ... error message has already been produced
6916 0 : return nStatus;
6917 : }
6918 : }
6919 : else
6920 : {
6921 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6922 : "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
6923 0 : return -1;
6924 : }
6925 : }
6926 :
6927 : /*-----------------------------------------------------------------
6928 : * Copy object information
6929 : *----------------------------------------------------------------*/
6930 :
6931 : // Compressed coordinate origin (useful only in compressed case!)
6932 0 : poMPointHdr->m_nComprOrgX = m_nComprOrgX;
6933 0 : poMPointHdr->m_nComprOrgY = m_nComprOrgY;
6934 :
6935 0 : poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
6936 0 : poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
6937 :
6938 : // Center/label point (default value already set above)
6939 0 : double dX = 0.0;
6940 0 : double dY = 0.0;
6941 0 : if (GetCenter(dX, dY) != -1)
6942 : {
6943 0 : poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
6944 0 : poMPointHdr->m_nLabelY);
6945 : }
6946 :
6947 0 : if (!bCoordBlockDataOnly)
6948 : {
6949 0 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
6950 0 : poMPointHdr->m_nSymbolId =
6951 0 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
6952 : }
6953 :
6954 0 : if (CPLGetLastErrorType() == CE_Failure)
6955 0 : return -1;
6956 :
6957 : /* Return a ref to coord block so that caller can continue writing
6958 : * after the end of this object (used by index splitting)
6959 : */
6960 0 : if (ppoCoordBlock)
6961 0 : *ppoCoordBlock = poCoordBlock;
6962 :
6963 0 : return 0;
6964 : }
6965 :
6966 : /**********************************************************************
6967 : * TABMultiPoint::GetXY()
6968 : *
6969 : * Return this point's X,Y coordinates.
6970 : **********************************************************************/
6971 0 : int TABMultiPoint::GetXY(int i, double &dX, double &dY)
6972 : {
6973 : /*-----------------------------------------------------------------
6974 : * Fetch and validate geometry
6975 : *----------------------------------------------------------------*/
6976 0 : OGRGeometry *poGeom = GetGeometryRef();
6977 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6978 : {
6979 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6980 :
6981 0 : if (i >= 0 && i < poMPoint->getNumGeometries() &&
6982 0 : (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
6983 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6984 : {
6985 0 : OGRPoint *poPoint = poGeom->toPoint();
6986 :
6987 0 : dX = poPoint->getX();
6988 0 : dY = poPoint->getY();
6989 : }
6990 : }
6991 : else
6992 : {
6993 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6994 : "TABMultiPoint: Missing or Invalid Geometry!");
6995 0 : dX = 0.0;
6996 0 : dY = 0.0;
6997 0 : return -1;
6998 : }
6999 :
7000 0 : return 0;
7001 : }
7002 :
7003 : /**********************************************************************
7004 : * TABMultiPoint::GetNumPoints()
7005 : *
7006 : * Return the number of points in this multipoint object
7007 : **********************************************************************/
7008 0 : int TABMultiPoint::GetNumPoints()
7009 : {
7010 : /*-----------------------------------------------------------------
7011 : * Fetch and validate geometry
7012 : *----------------------------------------------------------------*/
7013 0 : OGRGeometry *poGeom = GetGeometryRef();
7014 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7015 : {
7016 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
7017 :
7018 0 : return poMPoint->getNumGeometries();
7019 : }
7020 : else
7021 : {
7022 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7023 : "TABMultiPoint: Missing or Invalid Geometry!");
7024 0 : return 0;
7025 : }
7026 : }
7027 :
7028 : /**********************************************************************
7029 : * TABMultiPoint::GetStyleString() const
7030 : *
7031 : * Return style string for this feature.
7032 : *
7033 : * Style String is built only once during the first call to GetStyleString().
7034 : **********************************************************************/
7035 3 : const char *TABMultiPoint::GetStyleString() const
7036 : {
7037 3 : if (m_pszStyleString == nullptr)
7038 : {
7039 3 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
7040 : }
7041 :
7042 3 : return m_pszStyleString;
7043 : }
7044 :
7045 : /**********************************************************************
7046 : * TABMultiPoint::GetCenter()
7047 : *
7048 : * Returns the center point (or label point?) of the object. Compute one
7049 : * if it was not explicitly set:
7050 : *
7051 : * The default seems to be to use the first point in the collection as
7052 : * the center.. so we'll use that.
7053 : *
7054 : * Returns 0 on success, -1 on error.
7055 : **********************************************************************/
7056 0 : int TABMultiPoint::GetCenter(double &dX, double &dY)
7057 : {
7058 0 : if (!m_bCenterIsSet && GetNumPoints() > 0)
7059 : {
7060 : // The default seems to be to use the first point in the collection
7061 : // as the center... so we'll use that.
7062 0 : if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
7063 0 : m_bCenterIsSet = TRUE;
7064 : }
7065 :
7066 0 : if (!m_bCenterIsSet)
7067 0 : return -1;
7068 :
7069 0 : dX = m_dCenterX;
7070 0 : dY = m_dCenterY;
7071 0 : return 0;
7072 : }
7073 :
7074 : /**********************************************************************
7075 : * TABMultiPoint::SetCenter()
7076 : *
7077 : * Set the X,Y coordinates to use as center point (or label point?)
7078 : **********************************************************************/
7079 173 : void TABMultiPoint::SetCenter(double dX, double dY)
7080 : {
7081 173 : m_dCenterX = dX;
7082 173 : m_dCenterY = dY;
7083 173 : m_bCenterIsSet = TRUE;
7084 173 : }
7085 :
7086 : /**********************************************************************
7087 : * TABMultiPoint::DumpMIF()
7088 : *
7089 : * Dump feature geometry in a format similar to .MIF POINTs.
7090 : **********************************************************************/
7091 0 : void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
7092 : {
7093 0 : if (fpOut == nullptr)
7094 0 : fpOut = stdout;
7095 :
7096 : /*-----------------------------------------------------------------
7097 : * Fetch and validate geometry
7098 : *----------------------------------------------------------------*/
7099 0 : OGRGeometry *poGeom = GetGeometryRef();
7100 0 : OGRMultiPoint *poMPoint = nullptr;
7101 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7102 0 : poMPoint = poGeom->toMultiPoint();
7103 : else
7104 : {
7105 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7106 : "TABMultiPoint: Missing or Invalid Geometry!");
7107 0 : return;
7108 : }
7109 :
7110 : /*-----------------------------------------------------------------
7111 : * Generate output
7112 : *----------------------------------------------------------------*/
7113 0 : fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
7114 :
7115 0 : for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
7116 : {
7117 0 : poGeom = poMPoint->getGeometryRef(iPoint);
7118 :
7119 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
7120 : {
7121 0 : OGRPoint *poPoint = poGeom->toPoint();
7122 0 : fprintf(fpOut, " %.15g %.15g\n", poPoint->getX(), poPoint->getY());
7123 : }
7124 : else
7125 : {
7126 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7127 : "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
7128 0 : return;
7129 : }
7130 : }
7131 :
7132 0 : DumpSymbolDef(fpOut);
7133 :
7134 0 : if (m_bCenterIsSet)
7135 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
7136 :
7137 0 : fflush(fpOut);
7138 : }
7139 :
7140 : /*=====================================================================
7141 : * class TABCollection
7142 : *====================================================================*/
7143 :
7144 : /**********************************************************************
7145 : * TABCollection::TABCollection()
7146 : *
7147 : * Constructor.
7148 : **********************************************************************/
7149 103 : TABCollection::TABCollection(const OGRFeatureDefn *poDefnIn)
7150 : : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
7151 103 : m_poMpoint(nullptr)
7152 : {
7153 103 : }
7154 :
7155 : /**********************************************************************
7156 : * TABCollection::~TABCollection()
7157 : *
7158 : * Destructor.
7159 : **********************************************************************/
7160 206 : TABCollection::~TABCollection()
7161 : {
7162 103 : EmptyCollection();
7163 206 : }
7164 :
7165 : /**********************************************************************
7166 : * TABCollection::EmptyCollection()
7167 : *
7168 : * Delete/free all collection components.
7169 : **********************************************************************/
7170 204 : void TABCollection::EmptyCollection()
7171 : {
7172 :
7173 204 : if (m_poRegion)
7174 : {
7175 54 : delete m_poRegion;
7176 54 : m_poRegion = nullptr;
7177 : }
7178 :
7179 204 : if (m_poPline)
7180 : {
7181 37 : delete m_poPline;
7182 37 : m_poPline = nullptr;
7183 : }
7184 :
7185 204 : if (m_poMpoint)
7186 : {
7187 6 : delete m_poMpoint;
7188 6 : m_poMpoint = nullptr;
7189 : }
7190 :
7191 : // Empty OGR Geometry Collection as well
7192 204 : SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
7193 204 : }
7194 :
7195 : /**********************************************************************
7196 : * TABCollection::CloneTABFeature()
7197 : *
7198 : * Duplicate feature, including stuff specific to each TABFeature type.
7199 : *
7200 : * This method calls the generic TABFeature::CloneTABFeature() and
7201 : * then copies any members specific to its own type.
7202 : **********************************************************************/
7203 : TABFeature *
7204 0 : TABCollection::CloneTABFeature(const OGRFeatureDefn *poNewDefn /*=NULL*/)
7205 : {
7206 : /*-----------------------------------------------------------------
7207 : * Alloc new feature and copy the base stuff
7208 : *----------------------------------------------------------------*/
7209 : TABCollection *poNew =
7210 0 : new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
7211 :
7212 0 : CopyTABFeatureBase(poNew);
7213 :
7214 : /*-----------------------------------------------------------------
7215 : * And members specific to this class
7216 : *----------------------------------------------------------------*/
7217 :
7218 0 : if (m_poRegion)
7219 0 : poNew->SetRegionDirectly(
7220 0 : cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
7221 :
7222 0 : if (m_poPline)
7223 0 : poNew->SetPolylineDirectly(
7224 0 : cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
7225 :
7226 0 : if (m_poMpoint)
7227 0 : poNew->SetMultiPointDirectly(
7228 0 : cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
7229 :
7230 0 : return poNew;
7231 : }
7232 :
7233 : /**********************************************************************
7234 : * TABCollection::ValidateMapInfoType()
7235 : *
7236 : * Check the feature's geometry part and return the corresponding
7237 : * mapinfo object type code. The m_nMapInfoType member will also
7238 : * be updated for further calls to GetMapInfoType();
7239 : *
7240 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
7241 : * is expected for this object class.
7242 : **********************************************************************/
7243 0 : TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
7244 : {
7245 0 : int nRegionType = TAB_GEOM_NONE;
7246 0 : int nPLineType = TAB_GEOM_NONE;
7247 0 : int nMPointType = TAB_GEOM_NONE;
7248 0 : int nVersion = 650;
7249 :
7250 : /*-----------------------------------------------------------------
7251 : * Fetch and validate geometry
7252 : *----------------------------------------------------------------*/
7253 0 : OGRGeometry *poGeom = GetGeometryRef();
7254 0 : if (poGeom &&
7255 0 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
7256 : {
7257 0 : m_nMapInfoType = TAB_GEOM_COLLECTION;
7258 : }
7259 : else
7260 : {
7261 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7262 : "TABCollection: Missing or Invalid Geometry!");
7263 0 : m_nMapInfoType = TAB_GEOM_NONE;
7264 : }
7265 :
7266 : /*-----------------------------------------------------------------
7267 : * Decide if coordinates should be compressed or not.
7268 : *----------------------------------------------------------------*/
7269 0 : GBool bComprCoord = ValidateCoordType(poMapFile);
7270 :
7271 : /*-----------------------------------------------------------------
7272 : * Since all members of the collection share the same compressed coord
7273 : * origin, we should force the compressed origin in all components
7274 : * to be the same.
7275 : * This also implies that ValidateMapInfoType() should *NOT* be called
7276 : * again until the collection components are written by WriteGeom...()
7277 : *----------------------------------------------------------------*/
7278 :
7279 : // First pass to figure collection type...
7280 0 : if (m_poRegion)
7281 : {
7282 0 : m_poRegion->ValidateCoordType(poMapFile);
7283 0 : nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
7284 0 : if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
7285 0 : nVersion = TAB_GEOM_GET_VERSION(nRegionType);
7286 : }
7287 :
7288 0 : if (m_poPline)
7289 : {
7290 0 : m_poPline->ValidateCoordType(poMapFile);
7291 0 : nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
7292 0 : if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
7293 0 : nVersion = TAB_GEOM_GET_VERSION(nPLineType);
7294 : }
7295 :
7296 0 : if (m_poMpoint)
7297 : {
7298 0 : m_poMpoint->ValidateCoordType(poMapFile);
7299 0 : nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
7300 0 : if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
7301 0 : nVersion = TAB_GEOM_GET_VERSION(nMPointType);
7302 : }
7303 :
7304 : // Need to upgrade native type of collection?
7305 0 : if (nVersion == 800)
7306 : {
7307 0 : m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
7308 : }
7309 :
7310 : // Make another pass updating native type and coordinates type and origin
7311 : // of each component
7312 0 : if (m_poRegion && nRegionType != TAB_GEOM_NONE)
7313 : {
7314 0 : GInt32 nXMin = 0;
7315 0 : GInt32 nYMin = 0;
7316 0 : GInt32 nXMax = 0;
7317 0 : GInt32 nYMax = 0;
7318 0 : m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7319 0 : m_poRegion->ForceCoordTypeAndOrigin(
7320 : (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
7321 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7322 : nYMax);
7323 : }
7324 :
7325 0 : if (m_poPline && nPLineType != TAB_GEOM_NONE)
7326 : {
7327 : GInt32 nXMin, nYMin, nXMax, nYMax;
7328 0 : m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7329 0 : m_poPline->ForceCoordTypeAndOrigin(
7330 : (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
7331 : : TAB_GEOM_V450_MULTIPLINE),
7332 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7333 : nYMax);
7334 : }
7335 :
7336 0 : if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
7337 : {
7338 : GInt32 nXMin, nYMin, nXMax, nYMax;
7339 0 : m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7340 0 : m_poMpoint->ForceCoordTypeAndOrigin(
7341 : (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
7342 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7343 : nYMax);
7344 : }
7345 :
7346 0 : return m_nMapInfoType;
7347 : }
7348 :
7349 : /**********************************************************************
7350 : * TABCollection::ReadLabelAndMBR()
7351 : *
7352 : * Reads the label and MBR elements of the header of a collection component
7353 : *
7354 : * Returns 0 on success, -1 on failure.
7355 : **********************************************************************/
7356 12 : int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7357 : GBool bComprCoord, GInt32 nComprOrgX,
7358 : GInt32 nComprOrgY, GInt32 &pnMinX,
7359 : GInt32 &pnMinY, GInt32 &pnMaxX,
7360 : GInt32 &pnMaxY, GInt32 &pnLabelX,
7361 : GInt32 &pnLabelY)
7362 : {
7363 : //
7364 : // The sections in the collection's coord blocks start with center/label
7365 : // point + MBR that are normally found in the object data blocks
7366 : // of regular region/pline/mulitpoint objects.
7367 : //
7368 :
7369 12 : if (bComprCoord)
7370 : {
7371 : // Region center/label point, relative to compr. coord. origin
7372 : // No it is not relative to the Object block center
7373 12 : pnLabelX = poCoordBlock->ReadInt16();
7374 12 : pnLabelY = poCoordBlock->ReadInt16();
7375 :
7376 12 : TABSaturatedAdd(pnLabelX, nComprOrgX);
7377 12 : TABSaturatedAdd(pnLabelY, nComprOrgY);
7378 :
7379 12 : pnMinX = poCoordBlock->ReadInt16(); // Read MBR
7380 12 : pnMinY = poCoordBlock->ReadInt16();
7381 12 : pnMaxX = poCoordBlock->ReadInt16();
7382 12 : pnMaxY = poCoordBlock->ReadInt16();
7383 12 : TABSaturatedAdd(pnMinX, nComprOrgX);
7384 12 : TABSaturatedAdd(pnMinY, nComprOrgY);
7385 12 : TABSaturatedAdd(pnMaxX, nComprOrgX);
7386 12 : TABSaturatedAdd(pnMaxY, nComprOrgY);
7387 : }
7388 : else
7389 : {
7390 : // Region center/label point, relative to compr. coord. origin
7391 : // No it is not relative to the Object block center
7392 0 : pnLabelX = poCoordBlock->ReadInt32();
7393 0 : pnLabelY = poCoordBlock->ReadInt32();
7394 :
7395 0 : pnMinX = poCoordBlock->ReadInt32(); // Read MBR
7396 0 : pnMinY = poCoordBlock->ReadInt32();
7397 0 : pnMaxX = poCoordBlock->ReadInt32();
7398 0 : pnMaxY = poCoordBlock->ReadInt32();
7399 : }
7400 :
7401 12 : return 0;
7402 : }
7403 :
7404 : /**********************************************************************
7405 : * TABCollection::WriteLabelAndMBR()
7406 : *
7407 : * Writes the label and MBR elements of the header of a collection component
7408 : *
7409 : * Returns 0 on success, -1 on failure.
7410 : **********************************************************************/
7411 0 : int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7412 : GBool bComprCoord, GInt32 nMinX,
7413 : GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
7414 : GInt32 nLabelX, GInt32 nLabelY)
7415 : {
7416 : //
7417 : // The sections in the collection's coord blocks start with center/label
7418 : // point + MBR that are normally found in the object data blocks
7419 : // of regular region/pline/mulitpoint objects.
7420 : //
7421 :
7422 0 : int nStatus = 0;
7423 0 : if ((nStatus =
7424 0 : poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
7425 0 : (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
7426 0 : 0 ||
7427 0 : (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
7428 : {
7429 : // Failed ... error message has already been produced
7430 0 : return nStatus;
7431 : }
7432 :
7433 0 : return 0;
7434 : }
7435 :
7436 : /**********************************************************************
7437 : * TABCollection::ReadGeometryFromMAPFile()
7438 : *
7439 : * Fill the geometry and representation (color, etc...) part of the
7440 : * feature from the contents of the .MAP object pointed to by poMAPFile.
7441 : *
7442 : * It is assumed that poMAPFile currently points to the beginning of
7443 : * a map object.
7444 : *
7445 : * Returns 0 on success, -1 on error, in which case CPLError() will have
7446 : * been called.
7447 : **********************************************************************/
7448 4 : int TABCollection::ReadGeometryFromMAPFile(
7449 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7450 : GBool bCoordBlockDataOnly /*=FALSE*/,
7451 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7452 : {
7453 4 : const GBool bComprCoord = poObjHdr->IsCompressedType();
7454 :
7455 : /*-----------------------------------------------------------------
7456 : * Fetch and validate geometry type
7457 : *----------------------------------------------------------------*/
7458 4 : m_nMapInfoType = poObjHdr->m_nType;
7459 :
7460 4 : if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
7461 4 : m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
7462 0 : m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
7463 0 : m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
7464 : {
7465 0 : CPLError(
7466 : CE_Failure, CPLE_AssertionFailed,
7467 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
7468 0 : m_nMapInfoType, m_nMapInfoType);
7469 0 : return -1;
7470 : }
7471 :
7472 4 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7473 :
7474 : // Make sure collection is empty
7475 4 : EmptyCollection();
7476 :
7477 : /*-------------------------------------------------------------
7478 : * Copy data from poObjHdr
7479 : *------------------------------------------------------------*/
7480 : TABMAPObjCollection *poCollHdr =
7481 4 : cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7482 :
7483 : // MBR
7484 4 : double dXMin = 0.0;
7485 4 : double dYMin = 0.0;
7486 4 : double dXMax = 0.0;
7487 4 : double dYMax = 0.0;
7488 4 : poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
7489 : dYMin);
7490 4 : poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
7491 : dYMax);
7492 :
7493 4 : SetMBR(dXMin, dYMin, dXMax, dYMax);
7494 :
7495 4 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
7496 : poObjHdr->m_nMaxY);
7497 :
7498 4 : int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
7499 4 : TABMAPCoordBlock *poCoordBlock = nullptr;
7500 4 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7501 0 : poCoordBlock = *ppoCoordBlock;
7502 : else
7503 4 : poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
7504 :
7505 : // Compressed coordinate origin (useful only in compressed case!)
7506 4 : m_nComprOrgX = poCollHdr->m_nComprOrgX;
7507 4 : m_nComprOrgY = poCollHdr->m_nComprOrgY;
7508 :
7509 : /*-----------------------------------------------------------------
7510 : * Region Component
7511 : *----------------------------------------------------------------*/
7512 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
7513 : {
7514 : //
7515 : // Build fake coord section header to pass to TABRegion::ReadGeom...()
7516 : //
7517 4 : TABMAPObjPLine oRegionHdr;
7518 :
7519 4 : oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7520 4 : oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7521 :
7522 : //
7523 : // The region section in the coord block starts with center/label
7524 : // point + MBR that are normally found in the object data blocks
7525 : // of regular region objects.
7526 : //
7527 :
7528 : // In V800 the mini-header starts with a copy of num_parts
7529 4 : if (nVersion >= 800)
7530 : {
7531 : // int numParts = poCoordBlock->ReadInt32();
7532 0 : CPLAssert(poCoordBlock->ReadInt32() ==
7533 : poCollHdr->m_nNumRegSections);
7534 : }
7535 :
7536 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
7537 : oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
7538 : oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
7539 : oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
7540 : oRegionHdr.m_nLabelY);
7541 :
7542 : // Set CoordBlockPtr so that TABRegion continues reading here
7543 4 : oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7544 :
7545 4 : if (bComprCoord)
7546 4 : oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
7547 : else
7548 0 : oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
7549 4 : if (nVersion == 800)
7550 0 : oRegionHdr.m_nType = static_cast<TABGeomType>(
7551 0 : oRegionHdr.m_nType +
7552 : (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
7553 :
7554 4 : oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
7555 4 : oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
7556 4 : oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
7557 4 : oRegionHdr.m_bSmooth = 0; // TODO
7558 :
7559 : //
7560 : // Use a TABRegion to read/store the Region coord data
7561 : //
7562 4 : m_poRegion = new TABRegion(GetDefnRef());
7563 4 : if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
7564 : bCoordBlockDataOnly,
7565 4 : &poCoordBlock) != 0)
7566 0 : return -1;
7567 :
7568 : // Set new coord block ptr for next object
7569 : /*if (poCoordBlock)
7570 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7571 : }
7572 :
7573 : /*-----------------------------------------------------------------
7574 : * PLine Component
7575 : *----------------------------------------------------------------*/
7576 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
7577 : {
7578 : //
7579 : // Build fake coord section header to pass to TABPolyline::ReadGeom..()
7580 : //
7581 4 : TABMAPObjPLine oPLineHdr;
7582 :
7583 4 : oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7584 4 : oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7585 :
7586 : //
7587 : // The pline section in the coord block starts with center/label
7588 : // point + MBR that are normally found in the object data blocks
7589 : // of regular pline objects.
7590 : //
7591 :
7592 : // In V800 the mini-header starts with a copy of num_parts
7593 4 : if (nVersion >= 800)
7594 : {
7595 : // int numParts = poCoordBlock->ReadInt32();
7596 0 : CPLAssert(poCoordBlock->ReadInt32() ==
7597 : poCollHdr->m_nNumPLineSections);
7598 : }
7599 :
7600 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
7601 : oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
7602 : oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
7603 : oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
7604 :
7605 : // Set CoordBlockPtr so that TABRegion continues reading here
7606 4 : oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7607 :
7608 4 : if (bComprCoord)
7609 4 : oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
7610 : else
7611 0 : oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
7612 4 : if (nVersion == 800)
7613 0 : oPLineHdr.m_nType = static_cast<TABGeomType>(
7614 0 : oPLineHdr.m_nType +
7615 : (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
7616 :
7617 4 : oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
7618 4 : oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
7619 4 : oPLineHdr.m_bSmooth = 0; // TODO
7620 :
7621 : //
7622 : // Use a TABPolyline to read/store the Polyline coord data
7623 : //
7624 4 : m_poPline = new TABPolyline(GetDefnRef());
7625 4 : if (m_poPline->ReadGeometryFromMAPFile(
7626 4 : poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7627 0 : return -1;
7628 :
7629 : // Set new coord block ptr for next object
7630 : /*if (poCoordBlock)
7631 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7632 : }
7633 :
7634 : /*-----------------------------------------------------------------
7635 : * MultiPoint Component
7636 : *----------------------------------------------------------------*/
7637 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
7638 : {
7639 : //
7640 : // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
7641 : //
7642 4 : TABMAPObjMultiPoint oMPointHdr;
7643 :
7644 4 : oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7645 4 : oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7646 :
7647 : //
7648 : // The pline section in the coord block starts with center/label
7649 : // point + MBR that are normally found in the object data blocks
7650 : // of regular pline objects.
7651 : //
7652 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
7653 : oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
7654 : oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
7655 : oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
7656 : oMPointHdr.m_nLabelY);
7657 :
7658 : // Set CoordBlockPtr so that TABRegion continues reading here
7659 4 : oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7660 :
7661 4 : if (bComprCoord)
7662 4 : oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
7663 : else
7664 0 : oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
7665 4 : if (nVersion == 800)
7666 0 : oMPointHdr.m_nType = static_cast<TABGeomType>(
7667 0 : oMPointHdr.m_nType +
7668 : (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
7669 :
7670 4 : oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
7671 4 : oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
7672 :
7673 : //
7674 : // Use a TABMultiPoint to read/store the coord data
7675 : //
7676 4 : m_poMpoint = new TABMultiPoint(GetDefnRef());
7677 4 : if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
7678 : bCoordBlockDataOnly,
7679 4 : &poCoordBlock) != 0)
7680 0 : return -1;
7681 :
7682 : // Set new coord block ptr for next object (not really useful here)
7683 : /*if (poCoordBlock)
7684 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7685 : }
7686 :
7687 : /*-----------------------------------------------------------------
7688 : * Set the main OGRFeature Geometry
7689 : * (this is actually duplicating geometries from each member)
7690 : *----------------------------------------------------------------*/
7691 4 : if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
7692 0 : return -1;
7693 :
7694 : /* Return a ref to coord block so that caller can continue reading
7695 : * after the end of this object (used by index splitting)
7696 : */
7697 4 : if (ppoCoordBlock)
7698 0 : *ppoCoordBlock = poCoordBlock;
7699 :
7700 4 : return 0;
7701 : }
7702 :
7703 : /**********************************************************************
7704 : * TABCollection::WriteGeometryToMAPFile()
7705 : *
7706 : * Write the geometry and representation (color, etc...) part of the
7707 : * feature to the .MAP object pointed to by poMAPFile.
7708 : *
7709 : * It is assumed that poMAPFile currently points to a valid map object.
7710 : *
7711 : * Returns 0 on success, -1 on error, in which case CPLError() will have
7712 : * been called.
7713 : **********************************************************************/
7714 0 : int TABCollection::WriteGeometryToMAPFile(
7715 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7716 : GBool bCoordBlockDataOnly /*=FALSE*/,
7717 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7718 : {
7719 : /*-----------------------------------------------------------------
7720 : * Note that the current implementation does not allow setting the
7721 : * Geometry via OGRFeature::SetGeometry(). The geometries must be set
7722 : * via the SetRegion/Pline/MpointDirectly() methods which will take
7723 : * care of keeping the OGRFeature's geometry in sync.
7724 : *
7725 : * TODO: If we ever want to support sync'ing changes from the OGRFeature's
7726 : * geometry to the m_poRegion/Pline/Mpoint then a call should be added
7727 : * here, or perhaps in ValidateMapInfoType(), or even better in
7728 : * custom TABCollection::SetGeometry*()... but then this last option
7729 : * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
7730 : *----------------------------------------------------------------*/
7731 :
7732 : /*-----------------------------------------------------------------
7733 : * We assume that ValidateMapInfoType() was called already and that
7734 : * the type in poObjHdr->m_nType is valid.
7735 : *----------------------------------------------------------------*/
7736 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
7737 :
7738 : TABMAPObjCollection *poCollHdr =
7739 0 : cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7740 :
7741 : /*-----------------------------------------------------------------
7742 : * Write data to coordinate block for each component...
7743 : *
7744 : * Note that at this point, the caller (TABFile) has called
7745 : * TABCollection::ValidateMapInfoType() which in turn has called
7746 : * each component's respective ValidateMapInfoType() and
7747 : * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
7748 : * their respective WriteGeometryToMapFile() called.
7749 : *----------------------------------------------------------------*/
7750 0 : const GBool bCompressed = poObjHdr->IsCompressedType();
7751 : // TODO: ??? Do we need to track overall collection coord data size???
7752 0 : int nTotalFeatureDataSize = 0;
7753 :
7754 0 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7755 :
7756 0 : TABMAPCoordBlock *poCoordBlock = nullptr;
7757 0 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7758 0 : poCoordBlock = *ppoCoordBlock;
7759 : else
7760 0 : poCoordBlock = poMapFile->GetCurCoordBlock();
7761 0 : poCoordBlock->StartNewFeature();
7762 0 : poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7763 0 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
7764 :
7765 : /*-----------------------------------------------------------------
7766 : * Region component
7767 : *----------------------------------------------------------------*/
7768 0 : if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
7769 : {
7770 0 : CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
7771 : m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
7772 : m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
7773 : m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
7774 :
7775 0 : TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
7776 0 : TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
7777 :
7778 : // Update count of objects by type in header
7779 0 : if (!bCoordBlockDataOnly)
7780 0 : poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
7781 :
7782 : // Write a placeholder for centroid/label point and MBR mini-header
7783 : // and we'll come back later to write the real values.
7784 : //
7785 : // Note that the call to WriteGeometryToMAPFile() below will call
7786 : // StartNewFeature() as well, so we need to track the current
7787 : // value before calling it
7788 :
7789 0 : poCoordBlock->StartNewFeature();
7790 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7791 :
7792 : // In V800 the mini-header starts with a copy of num_parts
7793 0 : if (nVersion >= 800)
7794 : {
7795 0 : poCoordBlock->WriteInt32(0);
7796 : }
7797 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7798 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7799 :
7800 0 : if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
7801 : bCoordBlockDataOnly,
7802 0 : &poCoordBlock) != 0)
7803 : {
7804 0 : CPLError(CE_Failure, CPLE_FileIO,
7805 : "Failed writing Region part in collection.");
7806 0 : delete poRegionHdr;
7807 0 : return -1;
7808 : }
7809 :
7810 0 : nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
7811 :
7812 : // Come back to write the real values in the mini-header
7813 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7814 0 : poCoordBlock->StartNewFeature();
7815 :
7816 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7817 : {
7818 0 : delete poRegionHdr;
7819 0 : return -1;
7820 : }
7821 :
7822 : // In V800 the mini-header starts with a copy of num_parts
7823 0 : if (nVersion >= 800)
7824 : {
7825 0 : poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
7826 : }
7827 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
7828 : poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
7829 : poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
7830 : poRegionHdr->m_nLabelY);
7831 :
7832 : // And finally move the pointer back to the end of this component
7833 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7834 : {
7835 0 : delete poRegionHdr;
7836 0 : return -1;
7837 : }
7838 :
7839 : // Copy other header members to the main collection header
7840 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7841 : // mini-header???
7842 0 : poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
7843 0 : poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
7844 :
7845 0 : if (!bCoordBlockDataOnly)
7846 : {
7847 0 : poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
7848 0 : poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
7849 : // TODO: Smooth flag = poRegionHdr->m_bSmooth;
7850 : }
7851 :
7852 0 : delete poRegionHdr;
7853 : }
7854 : else
7855 : {
7856 : // No Region component. Set corresponding header fields to 0
7857 :
7858 0 : poCollHdr->m_nRegionDataSize = 0;
7859 0 : poCollHdr->m_nNumRegSections = 0;
7860 0 : poCollHdr->m_nRegionPenId = 0;
7861 0 : poCollHdr->m_nRegionBrushId = 0;
7862 : }
7863 :
7864 : /*-----------------------------------------------------------------
7865 : * PLine component
7866 : *----------------------------------------------------------------*/
7867 0 : if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
7868 : {
7869 0 : CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
7870 : m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
7871 : m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
7872 : m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
7873 :
7874 0 : TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
7875 0 : TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
7876 :
7877 : // Update count of objects by type in header
7878 0 : if (!bCoordBlockDataOnly)
7879 0 : poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
7880 :
7881 : // Write a placeholder for centroid/label point and MBR mini-header
7882 : // and we'll come back later to write the real values.
7883 : //
7884 : // Note that the call to WriteGeometryToMAPFile() below will call
7885 : // StartNewFeature() as well, so we need to track the current
7886 : // value before calling it
7887 :
7888 0 : poCoordBlock->StartNewFeature();
7889 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7890 :
7891 : // In V800 the mini-header starts with a copy of num_parts
7892 0 : if (nVersion >= 800)
7893 : {
7894 0 : poCoordBlock->WriteInt32(0);
7895 : }
7896 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7897 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7898 :
7899 0 : if (m_poPline->WriteGeometryToMAPFile(
7900 0 : poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7901 : {
7902 0 : CPLError(CE_Failure, CPLE_FileIO,
7903 : "Failed writing Region part in collection.");
7904 0 : delete poPlineHdr;
7905 0 : return -1;
7906 : }
7907 :
7908 0 : nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
7909 :
7910 : // Come back to write the real values in the mini-header
7911 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7912 0 : poCoordBlock->StartNewFeature();
7913 :
7914 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7915 : {
7916 0 : delete poPlineHdr;
7917 0 : return -1;
7918 : }
7919 :
7920 : // In V800 the mini-header starts with a copy of num_parts
7921 0 : if (nVersion >= 800)
7922 : {
7923 0 : poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
7924 : }
7925 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
7926 : poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
7927 : poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
7928 : poPlineHdr->m_nLabelY);
7929 :
7930 : // And finally move the pointer back to the end of this component
7931 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7932 : {
7933 0 : delete poPlineHdr;
7934 0 : return -1;
7935 : }
7936 :
7937 : // Copy other header members to the main collection header
7938 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7939 : // mini-header???
7940 0 : poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
7941 0 : poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
7942 0 : if (!bCoordBlockDataOnly)
7943 : {
7944 0 : poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
7945 : // TODO: Smooth flag = poPlineHdr->m_bSmooth;
7946 : }
7947 :
7948 0 : delete poPlineHdr;
7949 : }
7950 : else
7951 : {
7952 : // No Polyline component. Set corresponding header fields to 0
7953 :
7954 0 : poCollHdr->m_nPolylineDataSize = 0;
7955 0 : poCollHdr->m_nNumPLineSections = 0;
7956 0 : poCollHdr->m_nPolylinePenId = 0;
7957 : }
7958 :
7959 : /*-----------------------------------------------------------------
7960 : * MultiPoint component
7961 : *----------------------------------------------------------------*/
7962 0 : if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
7963 : {
7964 0 : CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
7965 : m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
7966 : m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
7967 : m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
7968 :
7969 : TABMAPObjMultiPoint *poMpointHdr =
7970 0 : cpl::down_cast<TABMAPObjMultiPoint *>(
7971 0 : TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
7972 :
7973 : // Update count of objects by type in header
7974 0 : if (!bCoordBlockDataOnly)
7975 0 : poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
7976 :
7977 : // Write a placeholder for centroid/label point and MBR mini-header
7978 : // and we'll come back later to write the real values.
7979 : //
7980 : // Note that the call to WriteGeometryToMAPFile() below will call
7981 : // StartNewFeature() as well, so we need to track the current
7982 : // value before calling it
7983 :
7984 0 : poCoordBlock->StartNewFeature();
7985 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7986 :
7987 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7988 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7989 :
7990 0 : if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
7991 : bCoordBlockDataOnly,
7992 0 : &poCoordBlock) != 0)
7993 : {
7994 0 : CPLError(CE_Failure, CPLE_FileIO,
7995 : "Failed writing Region part in collection.");
7996 0 : delete poMpointHdr;
7997 0 : return -1;
7998 : }
7999 :
8000 0 : nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
8001 :
8002 : // Come back to write the real values in the mini-header
8003 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
8004 0 : poCoordBlock->StartNewFeature();
8005 :
8006 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
8007 : {
8008 0 : delete poMpointHdr;
8009 0 : return -1;
8010 : }
8011 :
8012 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
8013 : poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
8014 : poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
8015 : poMpointHdr->m_nLabelY);
8016 :
8017 : // And finally move the pointer back to the end of this component
8018 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
8019 : {
8020 0 : delete poMpointHdr;
8021 0 : return -1;
8022 : }
8023 :
8024 : // Copy other header members to the main collection header
8025 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
8026 : // mini-header???
8027 0 : poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
8028 0 : poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
8029 0 : if (!bCoordBlockDataOnly)
8030 : {
8031 0 : poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
8032 : }
8033 :
8034 0 : delete poMpointHdr;
8035 : }
8036 : else
8037 : {
8038 : // No Multipoint component. Set corresponding header fields to 0
8039 :
8040 0 : poCollHdr->m_nMPointDataSize = 0;
8041 0 : poCollHdr->m_nNumMultiPoints = 0;
8042 0 : poCollHdr->m_nMultiPointSymbolId = 0;
8043 : }
8044 :
8045 : /*-----------------------------------------------------------------
8046 : * Copy object information
8047 : *----------------------------------------------------------------*/
8048 :
8049 : // Compressed coordinate origin (useful only in compressed case!)
8050 0 : poCollHdr->m_nComprOrgX = m_nComprOrgX;
8051 0 : poCollHdr->m_nComprOrgY = m_nComprOrgY;
8052 :
8053 0 : poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
8054 :
8055 0 : poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
8056 :
8057 0 : if (CPLGetLastErrorType() == CE_Failure)
8058 0 : return -1;
8059 :
8060 : /* Return a ref to coord block so that caller can continue writing
8061 : * after the end of this object (used by index splitting)
8062 : */
8063 0 : if (ppoCoordBlock)
8064 0 : *ppoCoordBlock = poCoordBlock;
8065 :
8066 0 : return 0;
8067 : }
8068 :
8069 : /**********************************************************************
8070 : * TABCollection::SyncOGRGeometryCollection()
8071 : *
8072 : * Copy the region/pline/multipoint's geometries to the OGRFeature's
8073 : * geometry.
8074 : **********************************************************************/
8075 208 : int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
8076 : GBool bSyncPline,
8077 : GBool bSyncMpoint)
8078 : {
8079 208 : OGRGeometry *poThisGeom = GetGeometryRef();
8080 208 : OGRGeometryCollection *poGeomColl = nullptr;
8081 :
8082 : // poGeometry is defined in the OGRFeature class
8083 208 : if (poThisGeom == nullptr)
8084 : {
8085 103 : poGeomColl = new OGRGeometryCollection();
8086 : }
8087 105 : else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
8088 : {
8089 105 : poGeomColl = poThisGeom->toGeometryCollection();
8090 : }
8091 : else
8092 : {
8093 0 : CPLError(
8094 : CE_Failure, CPLE_AssertionFailed,
8095 : "TABCollection: Invalid Geometry. Type must be OGRCollection.");
8096 0 : return -1;
8097 : }
8098 :
8099 : /*-----------------------------------------------------------------
8100 : * Start by removing geometries that need to be replaced
8101 : * In theory there should be a single geometry of each type, but
8102 : * just in case, we'll loop over the whole collection and delete all
8103 : * instances of each type if there are some.
8104 : *----------------------------------------------------------------*/
8105 208 : int numGeometries = poGeomColl->getNumGeometries();
8106 220 : for (int i = 0; i < numGeometries; i++)
8107 : {
8108 12 : OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
8109 12 : if (!poGeom)
8110 0 : continue;
8111 :
8112 24 : if ((bSyncRegion &&
8113 12 : (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
8114 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
8115 6 : (bSyncPline &&
8116 6 : (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
8117 30 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
8118 6 : (bSyncMpoint &&
8119 6 : (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
8120 : {
8121 : // Remove this geometry
8122 12 : poGeomColl->removeGeometry(i);
8123 :
8124 : // Unless this was the last geometry, we need to restart
8125 : // scanning the collection since we modified it
8126 12 : if (i != numGeometries - 1)
8127 : {
8128 6 : i = 0;
8129 6 : numGeometries = poGeomColl->getNumGeometries();
8130 : }
8131 : }
8132 : }
8133 :
8134 : /*-----------------------------------------------------------------
8135 : * Copy TAB Feature geometries to OGRGeometryCollection
8136 : *----------------------------------------------------------------*/
8137 208 : if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
8138 4 : poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
8139 :
8140 208 : if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
8141 4 : poGeomColl->addGeometry(m_poPline->GetGeometryRef());
8142 :
8143 208 : if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
8144 4 : poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
8145 :
8146 208 : if (poThisGeom == nullptr)
8147 103 : SetGeometryDirectly(poGeomColl);
8148 :
8149 208 : return 0;
8150 : }
8151 :
8152 : /**********************************************************************
8153 : * TABCollection::SetRegionDirectly()
8154 : *
8155 : * Set the region component of the collection, deleting the current
8156 : * region component if there is one. The object is then owned by the
8157 : * TABCollection object. Passing NULL just deletes it.
8158 : *
8159 : * Note that an intentional side-effect is that calling this method
8160 : * with the same poRegion pointer that is already owned by this object
8161 : * will force resync'ing the OGR Geometry member.
8162 : **********************************************************************/
8163 0 : int TABCollection::SetRegionDirectly(TABRegion *poRegion)
8164 : {
8165 0 : if (m_poRegion && m_poRegion != poRegion)
8166 0 : delete m_poRegion;
8167 0 : m_poRegion = poRegion;
8168 :
8169 : // Update OGRGeometryCollection component as well
8170 0 : return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
8171 : }
8172 :
8173 : /**********************************************************************
8174 : * TABCollection::SetPolylineDirectly()
8175 : *
8176 : * Set the polyline component of the collection, deleting the current
8177 : * polyline component if there is one. The object is then owned by the
8178 : * TABCollection object. Passing NULL just deletes it.
8179 : *
8180 : * Note that an intentional side-effect is that calling this method
8181 : * with the same poPline pointer that is already owned by this object
8182 : * will force resync'ing the OGR Geometry member.
8183 : **********************************************************************/
8184 0 : int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
8185 : {
8186 0 : if (m_poPline && m_poPline != poPline)
8187 0 : delete m_poPline;
8188 0 : m_poPline = poPline;
8189 :
8190 : // Update OGRGeometryCollection component as well
8191 0 : return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
8192 : }
8193 :
8194 : /**********************************************************************
8195 : * TABCollection::SetMultiPointDirectly()
8196 : *
8197 : * Set the multipoint component of the collection, deleting the current
8198 : * multipoint component if there is one. The object is then owned by the
8199 : * TABCollection object. Passing NULL just deletes it.
8200 : *
8201 : * Note that an intentional side-effect is that calling this method
8202 : * with the same poMpoint pointer that is already owned by this object
8203 : * will force resync'ing the OGR Geometry member.
8204 : **********************************************************************/
8205 0 : int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
8206 : {
8207 0 : if (m_poMpoint && m_poMpoint != poMpoint)
8208 0 : delete m_poMpoint;
8209 0 : m_poMpoint = poMpoint;
8210 :
8211 : // Update OGRGeometryCollection component as well
8212 0 : return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
8213 : }
8214 :
8215 : /**********************************************************************
8216 : * TABCollection::GetStyleString() const
8217 : *
8218 : * Return style string for this feature.
8219 : *
8220 : * Style String is built only once during the first call to GetStyleString().
8221 : **********************************************************************/
8222 3 : const char *TABCollection::GetStyleString() const
8223 : {
8224 3 : if (m_pszStyleString == nullptr)
8225 : {
8226 3 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
8227 : }
8228 :
8229 3 : return m_pszStyleString;
8230 : }
8231 :
8232 : /**********************************************************************
8233 : * TABCollection::DumpMIF()
8234 : *
8235 : * Dump feature geometry
8236 : **********************************************************************/
8237 0 : void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
8238 : {
8239 0 : if (fpOut == nullptr)
8240 0 : fpOut = stdout;
8241 :
8242 : /*-----------------------------------------------------------------
8243 : * Generate output
8244 : *----------------------------------------------------------------*/
8245 0 : int numParts = 0;
8246 0 : if (m_poRegion)
8247 0 : numParts++;
8248 0 : if (m_poPline)
8249 0 : numParts++;
8250 0 : if (m_poMpoint)
8251 0 : numParts++;
8252 :
8253 0 : fprintf(fpOut, "COLLECTION %d\n", numParts);
8254 :
8255 0 : if (m_poRegion)
8256 0 : m_poRegion->DumpMIF(fpOut);
8257 :
8258 0 : if (m_poPline)
8259 0 : m_poPline->DumpMIF(fpOut);
8260 :
8261 0 : if (m_poMpoint)
8262 0 : m_poMpoint->DumpMIF(fpOut);
8263 :
8264 0 : DumpSymbolDef(fpOut);
8265 :
8266 0 : fflush(fpOut);
8267 0 : }
8268 :
8269 : /*=====================================================================
8270 : * class TABDebugFeature
8271 : *====================================================================*/
8272 :
8273 : /**********************************************************************
8274 : * TABDebugFeature::TABDebugFeature()
8275 : *
8276 : * Constructor.
8277 : **********************************************************************/
8278 0 : TABDebugFeature::TABDebugFeature(const OGRFeatureDefn *poDefnIn)
8279 0 : : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
8280 : {
8281 0 : memset(m_abyBuf, 0, sizeof(m_abyBuf));
8282 0 : }
8283 :
8284 : /**********************************************************************
8285 : * TABDebugFeature::~TABDebugFeature()
8286 : *
8287 : * Destructor.
8288 : **********************************************************************/
8289 0 : TABDebugFeature::~TABDebugFeature()
8290 : {
8291 0 : }
8292 :
8293 : /**********************************************************************
8294 : * TABDebugFeature::ReadGeometryFromMAPFile()
8295 : *
8296 : * Fill the geometry and representation (color, etc...) part of the
8297 : * feature from the contents of the .MAP object pointed to by poMAPFile.
8298 : *
8299 : * It is assumed that poMAPFile currently points to the beginning of
8300 : * a map object.
8301 : *
8302 : * Returns 0 on success, -1 on error, in which case CPLError() will have
8303 : * been called.
8304 : **********************************************************************/
8305 0 : int TABDebugFeature::ReadGeometryFromMAPFile(
8306 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
8307 : GBool /*bCoordBlockDataOnly=FALSE*/,
8308 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8309 : {
8310 : /*-----------------------------------------------------------------
8311 : * Fetch geometry type
8312 : *----------------------------------------------------------------*/
8313 0 : m_nMapInfoType = poObjHdr->m_nType;
8314 :
8315 0 : TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
8316 0 : TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
8317 :
8318 : /*-----------------------------------------------------------------
8319 : * If object type has coords in a type 3 block, then its position
8320 : * follows
8321 : *----------------------------------------------------------------*/
8322 0 : if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
8323 : {
8324 0 : m_nCoordDataPtr = poObjBlock->ReadInt32();
8325 0 : m_nCoordDataSize = poObjBlock->ReadInt32();
8326 : }
8327 : else
8328 : {
8329 0 : m_nCoordDataPtr = -1;
8330 0 : m_nCoordDataSize = 0;
8331 : }
8332 :
8333 0 : m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
8334 0 : if (m_nSize > 0)
8335 : {
8336 0 : poObjBlock->GotoByteRel(-5); // Go back to beginning of header
8337 0 : poObjBlock->ReadBytes(
8338 0 : std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
8339 : }
8340 :
8341 0 : return 0;
8342 : }
8343 :
8344 : /**********************************************************************
8345 : * TABDebugFeature::WriteGeometryToMAPFile()
8346 : *
8347 : * Write the geometry and representation (color, etc...) part of the
8348 : * feature to the .MAP object pointed to by poMAPFile.
8349 : *
8350 : * It is assumed that poMAPFile currently points to a valid map object.
8351 : *
8352 : * Returns 0 on success, -1 on error, in which case CPLError() will have
8353 : * been called.
8354 : **********************************************************************/
8355 0 : int TABDebugFeature::WriteGeometryToMAPFile(
8356 : TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
8357 : GBool /*bCoordBlockDataOnly=FALSE*/,
8358 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8359 : {
8360 : // Nothing to do here!
8361 :
8362 0 : CPLError(CE_Failure, CPLE_NotSupported,
8363 : "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
8364 :
8365 0 : return -1;
8366 : }
8367 :
8368 : /**********************************************************************
8369 : * TABDebugFeature::DumpMIF()
8370 : *
8371 : * Dump feature contents... available only in DEBUG mode.
8372 : **********************************************************************/
8373 0 : void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
8374 : {
8375 0 : if (fpOut == nullptr)
8376 0 : fpOut = stdout;
8377 :
8378 0 : fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
8379 0 : GetMapInfoType());
8380 0 : fprintf(fpOut, " Object size: %d bytes\n", m_nSize);
8381 0 : fprintf(fpOut, " m_nCoordDataPtr = %d\n", m_nCoordDataPtr);
8382 0 : fprintf(fpOut, " m_nCoordDataSize = %d\n", m_nCoordDataSize);
8383 0 : fprintf(fpOut, " ");
8384 :
8385 0 : for (int i = 0; i < m_nSize; i++)
8386 0 : fprintf(fpOut, " %2.2x", m_abyBuf[i]);
8387 :
8388 0 : fprintf(fpOut, " \n");
8389 :
8390 0 : fflush(fpOut);
8391 0 : }
8392 :
8393 : /*=====================================================================
8394 : * class ITABFeaturePen
8395 : *====================================================================*/
8396 :
8397 : /**********************************************************************
8398 : * ITABFeaturePen::ITABFeaturePen()
8399 : **********************************************************************/
8400 :
8401 : // MI default is PEN(1, 2, 0)
8402 : static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
8403 :
8404 7777 : ITABFeaturePen::ITABFeaturePen()
8405 7777 : : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
8406 : {
8407 7777 : }
8408 :
8409 : /**********************************************************************
8410 : * ITABFeaturePen::~ITABFeaturePen()
8411 : **********************************************************************/
8412 :
8413 : ITABFeaturePen::~ITABFeaturePen() = default;
8414 :
8415 : /**********************************************************************
8416 : * ITABFeaturePen::GetPenWidthPixel()
8417 : * ITABFeaturePen::SetPenWidthPixel()
8418 : * ITABFeaturePen::GetPenWidthPoint()
8419 : * ITABFeaturePen::SetPenWidthPoint()
8420 : *
8421 : * Pen width can be expressed in pixels (value from 1 to 7 pixels) or
8422 : * in points (value from 0.1 to 203.7 points). The default pen width
8423 : * in MapInfo is 1 pixel. Pen width in points exist only in file version 450.
8424 : *
8425 : * The following methods hide the way the pen width is stored in the files.
8426 : *
8427 : * In order to establish if a given pen def had its width specified in
8428 : * pixels or in points, one should first call GetPenWidthPoint(), and if
8429 : * it returns 0 then the Pixel width should be used instead:
8430 : * if (GetPenWidthPoint() == 0)
8431 : * ... use pen width in points ...
8432 : * else
8433 : * ... use Pixel width from GetPenWidthPixel()
8434 : *
8435 : * Note that the reverse is not true: the default pixel width is always 1,
8436 : * even when the pen width was actually set in points.
8437 : **********************************************************************/
8438 :
8439 99 : GByte ITABFeaturePen::GetPenWidthPixel() const
8440 : {
8441 99 : return m_sPenDef.nPixelWidth;
8442 : }
8443 :
8444 29 : void ITABFeaturePen::SetPenWidthPixel(GByte val)
8445 : {
8446 29 : const GByte nPixelWidthMin = 1;
8447 29 : const GByte nPixelWidthMax = 7;
8448 29 : m_sPenDef.nPixelWidth =
8449 29 : std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
8450 29 : m_sPenDef.nPointWidth = 0;
8451 29 : }
8452 :
8453 0 : double ITABFeaturePen::GetPenWidthPoint() const
8454 : {
8455 : // We store point width internally as tenths of points
8456 0 : return m_sPenDef.nPointWidth / 10.0;
8457 : }
8458 :
8459 0 : void ITABFeaturePen::SetPenWidthPoint(double val)
8460 : {
8461 0 : m_sPenDef.nPointWidth =
8462 0 : std::min(std::max(static_cast<int>(val * 10), 1), 2037);
8463 0 : m_sPenDef.nPixelWidth = 1;
8464 0 : }
8465 :
8466 : /**********************************************************************
8467 : * ITABFeaturePen::GetPenWidthMIF()
8468 : * ITABFeaturePen::SetPenWidthMIF()
8469 : *
8470 : * The MIF representation for pen width is either a value from 1 to 7
8471 : * for a pen width in pixels, or a value from 11 to 2047 for a pen
8472 : * width in points = 10 + (point_width*10)
8473 : **********************************************************************/
8474 21 : int ITABFeaturePen::GetPenWidthMIF() const
8475 : {
8476 21 : return (m_sPenDef.nPointWidth > 0 ? (m_sPenDef.nPointWidth + 10)
8477 21 : : m_sPenDef.nPixelWidth);
8478 : }
8479 :
8480 2434 : void ITABFeaturePen::SetPenWidthMIF(int val)
8481 : {
8482 2434 : if (val > 10)
8483 : {
8484 0 : m_sPenDef.nPointWidth = std::min((val - 10), 2037);
8485 0 : m_sPenDef.nPixelWidth = 0;
8486 : }
8487 : else
8488 : {
8489 2434 : m_sPenDef.nPixelWidth =
8490 2434 : static_cast<GByte>(std::min(std::max(val, 1), 7));
8491 2434 : m_sPenDef.nPointWidth = 0;
8492 : }
8493 2434 : }
8494 :
8495 : /**********************************************************************
8496 : * ITABFeaturePen::GetPenStyleString()
8497 : *
8498 : * Return a PEN() string. All representations info for the pen are here.
8499 : **********************************************************************/
8500 99 : const char *ITABFeaturePen::GetPenStyleString() const
8501 : {
8502 99 : const char *pszStyle = nullptr;
8503 99 : int nOGRStyle = 0;
8504 : char szPattern[20];
8505 :
8506 99 : szPattern[0] = '\0';
8507 :
8508 : // For now, I only add the 25 first styles
8509 99 : switch (GetPenPattern())
8510 : {
8511 0 : case 1:
8512 0 : nOGRStyle = 1;
8513 0 : break;
8514 99 : case 2:
8515 99 : nOGRStyle = 0;
8516 99 : break;
8517 0 : case 3:
8518 0 : nOGRStyle = 3;
8519 0 : strcpy(szPattern, "1 1");
8520 0 : break;
8521 0 : case 4:
8522 0 : nOGRStyle = 3;
8523 0 : strcpy(szPattern, "2 1");
8524 0 : break;
8525 0 : case 5:
8526 0 : nOGRStyle = 3;
8527 0 : strcpy(szPattern, "3 1");
8528 0 : break;
8529 0 : case 6:
8530 0 : nOGRStyle = 3;
8531 0 : strcpy(szPattern, "6 1");
8532 0 : break;
8533 0 : case 7:
8534 0 : nOGRStyle = 4;
8535 0 : strcpy(szPattern, "12 2");
8536 0 : break;
8537 0 : case 8:
8538 0 : nOGRStyle = 4;
8539 0 : strcpy(szPattern, "24 4");
8540 0 : break;
8541 0 : case 9:
8542 0 : nOGRStyle = 3;
8543 0 : strcpy(szPattern, "4 3");
8544 0 : break;
8545 0 : case 10:
8546 0 : nOGRStyle = 5;
8547 0 : strcpy(szPattern, "1 4");
8548 0 : break;
8549 0 : case 11:
8550 0 : nOGRStyle = 3;
8551 0 : strcpy(szPattern, "4 6");
8552 0 : break;
8553 0 : case 12:
8554 0 : nOGRStyle = 3;
8555 0 : strcpy(szPattern, "6 4");
8556 0 : break;
8557 0 : case 13:
8558 0 : nOGRStyle = 4;
8559 0 : strcpy(szPattern, "12 12");
8560 0 : break;
8561 0 : case 14:
8562 0 : nOGRStyle = 6;
8563 0 : strcpy(szPattern, "8 2 1 2");
8564 0 : break;
8565 0 : case 15:
8566 0 : nOGRStyle = 6;
8567 0 : strcpy(szPattern, "12 1 1 1");
8568 0 : break;
8569 0 : case 16:
8570 0 : nOGRStyle = 6;
8571 0 : strcpy(szPattern, "12 1 3 1");
8572 0 : break;
8573 0 : case 17:
8574 0 : nOGRStyle = 6;
8575 0 : strcpy(szPattern, "24 6 4 6");
8576 0 : break;
8577 0 : case 18:
8578 0 : nOGRStyle = 7;
8579 0 : strcpy(szPattern, "24 3 3 3 3 3");
8580 0 : break;
8581 0 : case 19:
8582 0 : nOGRStyle = 7;
8583 0 : strcpy(szPattern, "24 3 3 3 3 3 3 3");
8584 0 : break;
8585 0 : case 20:
8586 0 : nOGRStyle = 7;
8587 0 : strcpy(szPattern, "6 3 1 3 1 3");
8588 0 : break;
8589 0 : case 21:
8590 0 : nOGRStyle = 7;
8591 0 : strcpy(szPattern, "12 2 1 2 1 2");
8592 0 : break;
8593 0 : case 22:
8594 0 : nOGRStyle = 7;
8595 0 : strcpy(szPattern, "12 2 1 2 1 2 1 2");
8596 0 : break;
8597 0 : case 23:
8598 0 : nOGRStyle = 6;
8599 0 : strcpy(szPattern, "4 1 1 1");
8600 0 : break;
8601 0 : case 24:
8602 0 : nOGRStyle = 7;
8603 0 : strcpy(szPattern, "4 1 1 1 1");
8604 0 : break;
8605 0 : case 25:
8606 0 : nOGRStyle = 6;
8607 0 : strcpy(szPattern, "4 1 1 1 2 1 1 1");
8608 0 : break;
8609 :
8610 0 : default:
8611 0 : nOGRStyle = 0;
8612 0 : break;
8613 : }
8614 :
8615 : // note - MapInfo renders all lines using a round pen cap and round pen join
8616 : // which are not the default values for OGR pen cap/join styles. So we need
8617 : // to explicitly include the cap/j parameters in these strings
8618 99 : if (strlen(szPattern) != 0)
8619 : {
8620 0 : if (m_sPenDef.nPointWidth > 0)
8621 0 : pszStyle = CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d,"
8622 : "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8623 0 : static_cast<int>(GetPenWidthPoint()),
8624 0 : m_sPenDef.rgbColor, GetPenPattern(),
8625 : nOGRStyle, szPattern);
8626 : else
8627 0 : pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d,"
8628 : "ogr-pen-%d\",p:\"%spx\",cap:r,j:r)",
8629 0 : GetPenWidthPixel(), m_sPenDef.rgbColor,
8630 0 : GetPenPattern(), nOGRStyle, szPattern);
8631 : }
8632 : else
8633 : {
8634 99 : if (m_sPenDef.nPointWidth > 0)
8635 : pszStyle =
8636 0 : CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\""
8637 : "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8638 0 : static_cast<int>(GetPenWidthPoint()),
8639 0 : m_sPenDef.rgbColor, GetPenPattern(), nOGRStyle);
8640 : else
8641 99 : pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
8642 : "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8643 99 : GetPenWidthPixel(), m_sPenDef.rgbColor,
8644 99 : GetPenPattern(), nOGRStyle);
8645 : }
8646 :
8647 99 : return pszStyle;
8648 : }
8649 :
8650 : /**********************************************************************
8651 : * ITABFeaturePen::SetPenFromStyleString()
8652 : *
8653 : * Init the Pen properties from a style string.
8654 : **********************************************************************/
8655 32 : void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
8656 : {
8657 32 : GBool bIsNull = 0;
8658 :
8659 : // Use the Style Manager to retrieve all the information we need.
8660 32 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8661 32 : OGRStyleTool *poStylePart = nullptr;
8662 :
8663 : // Init the StyleMgr with the StyleString.
8664 32 : poStyleMgr->InitStyleString(pszStyleString);
8665 :
8666 : // Retrieve the Pen info.
8667 32 : const int numParts = poStyleMgr->GetPartCount();
8668 51 : for (int i = 0; i < numParts; i++)
8669 : {
8670 49 : poStylePart = poStyleMgr->GetPart(i);
8671 49 : if (poStylePart == nullptr)
8672 0 : continue;
8673 :
8674 49 : if (poStylePart->GetType() == OGRSTCPen)
8675 : {
8676 30 : break;
8677 : }
8678 : else
8679 : {
8680 19 : delete poStylePart;
8681 19 : poStylePart = nullptr;
8682 : }
8683 : }
8684 :
8685 : // If the no Pen found, do nothing.
8686 32 : if (poStylePart == nullptr)
8687 : {
8688 2 : delete poStyleMgr;
8689 2 : return;
8690 : }
8691 :
8692 30 : OGRStylePen *poPenStyle = cpl::down_cast<OGRStylePen *>(poStylePart);
8693 :
8694 : // With Pen, we always want to output points or pixels (which are the same,
8695 : // so just use points).
8696 : //
8697 : // It's very important to set the output unit of the feature.
8698 : // The default value is meter. If we don't do it all numerical values
8699 : // will be assumed to be converted from the input unit to meter when we
8700 : // will get them via GetParam...() functions.
8701 : // See OGRStyleTool::Parse() for more details.
8702 30 : poPenStyle->SetUnit(OGRSTUPoints, 1);
8703 :
8704 : // Get the Pen Id or pattern
8705 30 : const char *pszPenName = poPenStyle->Id(bIsNull);
8706 30 : if (bIsNull)
8707 1 : pszPenName = nullptr;
8708 :
8709 : // Set the width
8710 30 : if (poPenStyle->Width(bIsNull) != 0.0)
8711 : {
8712 29 : const double nPenWidth = poPenStyle->Width(bIsNull);
8713 : // Width < 10 is a pixel
8714 29 : if (nPenWidth > 10)
8715 0 : SetPenWidthPoint(nPenWidth);
8716 : else
8717 29 : SetPenWidthPixel(static_cast<GByte>(nPenWidth));
8718 : }
8719 :
8720 : // Set the color
8721 30 : const char *pszPenColor = poPenStyle->Color(bIsNull);
8722 30 : if (pszPenColor != nullptr)
8723 : {
8724 30 : if (pszPenColor[0] == '#')
8725 30 : pszPenColor++;
8726 : // The Pen color is an Hexa string that need to be convert in a int
8727 : const GInt32 nPenColor =
8728 30 : static_cast<int>(strtol(pszPenColor, nullptr, 16));
8729 30 : SetPenColor(nPenColor);
8730 : }
8731 :
8732 30 : const char *pszPenPattern = nullptr;
8733 :
8734 : // Set the Id of the Pen, use Pattern if necessary.
8735 30 : if (pszPenName &&
8736 29 : (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
8737 : {
8738 29 : const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
8739 29 : if (pszPenId != nullptr)
8740 : {
8741 29 : const int nPenId = atoi(pszPenId + 12);
8742 29 : SetPenPattern(static_cast<GByte>(nPenId));
8743 : }
8744 : else
8745 : {
8746 0 : pszPenId = strstr(pszPenName, "ogr-pen-");
8747 0 : if (pszPenId != nullptr)
8748 : {
8749 0 : int nPenId = atoi(pszPenId + 8);
8750 0 : if (nPenId == 0)
8751 0 : nPenId = 2;
8752 0 : SetPenPattern(static_cast<GByte>(nPenId));
8753 : }
8754 29 : }
8755 : }
8756 : else
8757 : {
8758 : // If no Pen Id, use the Pen Pattern to retrieve the Id.
8759 1 : pszPenPattern = poPenStyle->Pattern(bIsNull);
8760 1 : if (bIsNull)
8761 1 : pszPenPattern = nullptr;
8762 : else
8763 : {
8764 0 : if (strcmp(pszPenPattern, "1 1") == 0)
8765 0 : SetPenPattern(3);
8766 0 : else if (strcmp(pszPenPattern, "2 1") == 0)
8767 0 : SetPenPattern(4);
8768 0 : else if (strcmp(pszPenPattern, "3 1") == 0)
8769 0 : SetPenPattern(5);
8770 0 : else if (strcmp(pszPenPattern, "6 1") == 0)
8771 0 : SetPenPattern(6);
8772 0 : else if (strcmp(pszPenPattern, "12 2") == 0)
8773 0 : SetPenPattern(7);
8774 0 : else if (strcmp(pszPenPattern, "24 4") == 0)
8775 0 : SetPenPattern(8);
8776 0 : else if (strcmp(pszPenPattern, "4 3") == 0)
8777 0 : SetPenPattern(9);
8778 0 : else if (strcmp(pszPenPattern, "1 4") == 0)
8779 0 : SetPenPattern(10);
8780 0 : else if (strcmp(pszPenPattern, "4 6") == 0)
8781 0 : SetPenPattern(11);
8782 0 : else if (strcmp(pszPenPattern, "6 4") == 0)
8783 0 : SetPenPattern(12);
8784 0 : else if (strcmp(pszPenPattern, "12 12") == 0)
8785 0 : SetPenPattern(13);
8786 0 : else if (strcmp(pszPenPattern, "8 2 1 2") == 0)
8787 0 : SetPenPattern(14);
8788 0 : else if (strcmp(pszPenPattern, "12 1 1 1") == 0)
8789 0 : SetPenPattern(15);
8790 0 : else if (strcmp(pszPenPattern, "12 1 3 1") == 0)
8791 0 : SetPenPattern(16);
8792 0 : else if (strcmp(pszPenPattern, "24 6 4 6") == 0)
8793 0 : SetPenPattern(17);
8794 0 : else if (strcmp(pszPenPattern, "24 3 3 3 3 3") == 0)
8795 0 : SetPenPattern(18);
8796 0 : else if (strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0)
8797 0 : SetPenPattern(19);
8798 0 : else if (strcmp(pszPenPattern, "6 3 1 3 1 3") == 0)
8799 0 : SetPenPattern(20);
8800 0 : else if (strcmp(pszPenPattern, "12 2 1 2 1 2") == 0)
8801 0 : SetPenPattern(21);
8802 0 : else if (strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0)
8803 0 : SetPenPattern(22);
8804 0 : else if (strcmp(pszPenPattern, "4 1 1 1") == 0)
8805 0 : SetPenPattern(23);
8806 0 : else if (strcmp(pszPenPattern, "4 1 1 1 1") == 0)
8807 0 : SetPenPattern(24);
8808 0 : else if (strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0)
8809 0 : SetPenPattern(25);
8810 : }
8811 : }
8812 :
8813 30 : delete poStyleMgr;
8814 30 : delete poStylePart;
8815 :
8816 30 : return;
8817 : }
8818 :
8819 : /**********************************************************************
8820 : * ITABFeaturePen::DumpPenDef()
8821 : *
8822 : * Dump pen definition information.
8823 : **********************************************************************/
8824 0 : void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/)
8825 : {
8826 0 : if (fpOut == nullptr)
8827 0 : fpOut = stdout;
8828 :
8829 0 : fprintf(fpOut, " m_nPenDefIndex = %d\n", m_nPenDefIndex);
8830 0 : fprintf(fpOut, " m_sPenDef.nRefCount = %d\n", m_sPenDef.nRefCount);
8831 0 : fprintf(fpOut, " m_sPenDef.nPixelWidth = %u\n", m_sPenDef.nPixelWidth);
8832 0 : fprintf(fpOut, " m_sPenDef.nLinePattern = %u\n", m_sPenDef.nLinePattern);
8833 0 : fprintf(fpOut, " m_sPenDef.nPointWidth = %d\n", m_sPenDef.nPointWidth);
8834 0 : fprintf(fpOut, " m_sPenDef.rgbColor = 0x%6.6x (%d)\n",
8835 : m_sPenDef.rgbColor, m_sPenDef.rgbColor);
8836 :
8837 0 : fflush(fpOut);
8838 0 : }
8839 :
8840 : /*=====================================================================
8841 : * class ITABFeatureBrush
8842 : *====================================================================*/
8843 :
8844 : /**********************************************************************
8845 : * ITABFeatureBrush::ITABFeatureBrush()
8846 : **********************************************************************/
8847 :
8848 : // MI default is BRUSH(2, 16777215, 16777215)
8849 : static const TABBrushDef MITABcsDefaultBrush = MITAB_BRUSH_DEFAULT;
8850 :
8851 2540 : ITABFeatureBrush::ITABFeatureBrush()
8852 2540 : : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
8853 : {
8854 2540 : }
8855 :
8856 : /**********************************************************************
8857 : * ITABFeatureBrush::~ITABFeatureBrush()
8858 : **********************************************************************/
8859 :
8860 : ITABFeatureBrush::~ITABFeatureBrush() = default;
8861 :
8862 : /**********************************************************************
8863 : * ITABFeatureBrush::GetBrushStyleString()
8864 : *
8865 : * Return a Brush() string. All representations info for the Brush are here.
8866 : **********************************************************************/
8867 54 : const char *ITABFeatureBrush::GetBrushStyleString() const
8868 : {
8869 54 : const char *pszStyle = nullptr;
8870 54 : int nOGRStyle = 0;
8871 : /* char szPattern[20]; */
8872 : //* szPattern[0] = '\0'; */
8873 :
8874 54 : if (m_sBrushDef.nFillPattern == 1)
8875 53 : nOGRStyle = 1;
8876 1 : else if (m_sBrushDef.nFillPattern == 3)
8877 0 : nOGRStyle = 2;
8878 1 : else if (m_sBrushDef.nFillPattern == 4)
8879 0 : nOGRStyle = 3;
8880 1 : else if (m_sBrushDef.nFillPattern == 5)
8881 0 : nOGRStyle = 5;
8882 1 : else if (m_sBrushDef.nFillPattern == 6)
8883 0 : nOGRStyle = 4;
8884 1 : else if (m_sBrushDef.nFillPattern == 7)
8885 0 : nOGRStyle = 6;
8886 1 : else if (m_sBrushDef.nFillPattern == 8)
8887 0 : nOGRStyle = 7;
8888 :
8889 54 : if (GetBrushTransparent())
8890 : {
8891 : /* Omit BG Color for transparent brushes */
8892 2 : pszStyle = CPLSPrintf(
8893 : "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8894 2 : m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
8895 : }
8896 : else
8897 : {
8898 52 : pszStyle = CPLSPrintf(
8899 : "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8900 52 : m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
8901 52 : m_sBrushDef.nFillPattern, nOGRStyle);
8902 : }
8903 :
8904 54 : return pszStyle;
8905 : }
8906 :
8907 : /**********************************************************************
8908 : * ITABFeatureBrush::SetBrushFromStyleString()
8909 : *
8910 : * Set all Brush elements from a StyleString.
8911 : * Use StyleMgr to do so.
8912 : **********************************************************************/
8913 19 : void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
8914 : {
8915 19 : GBool bIsNull = 0;
8916 :
8917 : // Use the Style Manager to retrieve all the information we need.
8918 19 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8919 19 : OGRStyleTool *poStylePart = nullptr;
8920 :
8921 : // Init the StyleMgr with the StyleString.
8922 19 : poStyleMgr->InitStyleString(pszStyleString);
8923 :
8924 : // Retrieve the Brush info.
8925 19 : const int numParts = poStyleMgr->GetPartCount();
8926 19 : for (int i = 0; i < numParts; i++)
8927 : {
8928 19 : poStylePart = poStyleMgr->GetPart(i);
8929 19 : if (poStylePart == nullptr)
8930 0 : continue;
8931 :
8932 19 : if (poStylePart->GetType() == OGRSTCBrush)
8933 : {
8934 19 : break;
8935 : }
8936 : else
8937 : {
8938 0 : delete poStylePart;
8939 0 : poStylePart = nullptr;
8940 : }
8941 : }
8942 :
8943 : // If the no Brush found, do nothing.
8944 19 : if (poStylePart == nullptr)
8945 : {
8946 0 : delete poStyleMgr;
8947 0 : return;
8948 : }
8949 :
8950 19 : OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
8951 :
8952 : // Set the Brush Id (FillPattern)
8953 19 : const char *pszBrushId = poBrushStyle->Id(bIsNull);
8954 19 : if (bIsNull)
8955 2 : pszBrushId = nullptr;
8956 19 : bool bHasBrushId = false;
8957 :
8958 19 : if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
8959 0 : strstr(pszBrushId, "ogr-brush-")))
8960 : {
8961 17 : if (strstr(pszBrushId, "mapinfo-brush-"))
8962 : {
8963 17 : const int nBrushId = atoi(pszBrushId + 14);
8964 17 : SetBrushPattern(static_cast<GByte>(nBrushId));
8965 17 : bHasBrushId = true;
8966 : }
8967 0 : else if (strstr(pszBrushId, "ogr-brush-"))
8968 : {
8969 0 : int nBrushId = atoi(pszBrushId + 10);
8970 0 : if (nBrushId > 1)
8971 0 : nBrushId++;
8972 0 : SetBrushPattern(static_cast<GByte>(nBrushId));
8973 0 : bHasBrushId = true;
8974 : }
8975 : }
8976 :
8977 : // Set the BackColor, if not set, then it is transparent
8978 19 : const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
8979 19 : if (bIsNull)
8980 1 : pszBrushColor = nullptr;
8981 :
8982 19 : if (pszBrushColor)
8983 : {
8984 18 : if (pszBrushColor[0] == '#')
8985 18 : pszBrushColor++;
8986 18 : if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
8987 1 : pszBrushColor[7] == '0')
8988 : {
8989 1 : SetBrushTransparent(1);
8990 : }
8991 : else
8992 : {
8993 34 : CPLString osBrushColor(pszBrushColor);
8994 17 : if (strlen(pszBrushColor) > 6)
8995 0 : osBrushColor.resize(6);
8996 : const int nBrushColor =
8997 17 : static_cast<int>(strtol(osBrushColor, nullptr, 16));
8998 17 : SetBrushBGColor(static_cast<GInt32>(nBrushColor));
8999 : }
9000 : }
9001 : else
9002 : {
9003 1 : SetBrushTransparent(1);
9004 : }
9005 :
9006 : // Set the ForeColor
9007 19 : pszBrushColor = poBrushStyle->ForeColor(bIsNull);
9008 19 : if (bIsNull)
9009 0 : pszBrushColor = nullptr;
9010 :
9011 19 : if (pszBrushColor)
9012 : {
9013 19 : if (pszBrushColor[0] == '#')
9014 19 : pszBrushColor++;
9015 19 : if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
9016 1 : pszBrushColor[7] == '0')
9017 : {
9018 1 : if (!bHasBrushId)
9019 1 : SetBrushPattern(static_cast<GByte>(1)); // No-fill
9020 : }
9021 : else
9022 : {
9023 18 : if (!bHasBrushId)
9024 1 : SetBrushPattern(static_cast<GByte>(2)); // Solid-fill
9025 : }
9026 :
9027 38 : CPLString osBrushColor(pszBrushColor);
9028 19 : if (strlen(pszBrushColor) > 6)
9029 1 : osBrushColor.resize(6);
9030 : const int nBrushColor =
9031 19 : static_cast<int>(strtol(osBrushColor, nullptr, 16));
9032 19 : SetBrushFGColor(static_cast<GInt32>(nBrushColor));
9033 : }
9034 :
9035 19 : delete poStyleMgr;
9036 19 : delete poStylePart;
9037 :
9038 19 : return;
9039 : }
9040 :
9041 : /**********************************************************************
9042 : * ITABFeatureBrush::DumpBrushDef()
9043 : *
9044 : * Dump Brush definition information.
9045 : **********************************************************************/
9046 0 : void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
9047 : {
9048 0 : if (fpOut == nullptr)
9049 0 : fpOut = stdout;
9050 :
9051 0 : fprintf(fpOut, " m_nBrushDefIndex = %d\n", m_nBrushDefIndex);
9052 0 : fprintf(fpOut, " m_sBrushDef.nRefCount = %d\n", m_sBrushDef.nRefCount);
9053 0 : fprintf(fpOut, " m_sBrushDef.nFillPattern = %d\n",
9054 0 : static_cast<int>(m_sBrushDef.nFillPattern));
9055 0 : fprintf(fpOut, " m_sBrushDef.bTransparentFill = %d\n",
9056 0 : static_cast<int>(m_sBrushDef.bTransparentFill));
9057 0 : fprintf(fpOut, " m_sBrushDef.rgbFGColor = 0x%6.6x (%d)\n",
9058 : m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
9059 0 : fprintf(fpOut, " m_sBrushDef.rgbBGColor = 0x%6.6x (%d)\n",
9060 : m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
9061 :
9062 0 : fflush(fpOut);
9063 0 : }
9064 :
9065 : /*=====================================================================
9066 : * class ITABFeatureFont
9067 : *====================================================================*/
9068 :
9069 : /**********************************************************************
9070 : * ITABFeatureFont::ITABFeatureFont()
9071 : **********************************************************************/
9072 :
9073 : // MI default is Font("Arial", 0, 0, 0)
9074 : static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
9075 :
9076 1638 : ITABFeatureFont::ITABFeatureFont()
9077 1638 : : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
9078 : {
9079 1638 : }
9080 :
9081 : /**********************************************************************
9082 : * ITABFeatureFont::~ITABFeatureFont()
9083 : **********************************************************************/
9084 :
9085 : ITABFeatureFont::~ITABFeatureFont() = default;
9086 :
9087 : /**********************************************************************
9088 : * ITABFeatureFont::SetFontName()
9089 : **********************************************************************/
9090 1582 : void ITABFeatureFont::SetFontName(const char *pszName)
9091 : {
9092 1582 : strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
9093 1582 : m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
9094 1582 : }
9095 :
9096 : /**********************************************************************
9097 : * ITABFeatureFont::DumpFontDef()
9098 : *
9099 : * Dump Font definition information.
9100 : **********************************************************************/
9101 0 : void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
9102 : {
9103 0 : if (fpOut == nullptr)
9104 0 : fpOut = stdout;
9105 :
9106 0 : fprintf(fpOut, " m_nFontDefIndex = %d\n", m_nFontDefIndex);
9107 0 : fprintf(fpOut, " m_sFontDef.nRefCount = %d\n", m_sFontDef.nRefCount);
9108 0 : fprintf(fpOut, " m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
9109 :
9110 0 : fflush(fpOut);
9111 0 : }
9112 :
9113 : /*=====================================================================
9114 : * class ITABFeatureSymbol
9115 : *====================================================================*/
9116 :
9117 : /**********************************************************************
9118 : * ITABFeatureSymbol::ITABFeatureSymbol()
9119 : **********************************************************************/
9120 :
9121 : // MI default is Symbol(35, 0, 12)
9122 : static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
9123 :
9124 566233 : ITABFeatureSymbol::ITABFeatureSymbol()
9125 566233 : : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
9126 : {
9127 566233 : }
9128 :
9129 : /**********************************************************************
9130 : * ITABFeatureSymbol::GetSymbolStyleString()
9131 : *
9132 : * Return a Symbol() string. All representations info for the Symbol are here.
9133 : **********************************************************************/
9134 33 : const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
9135 : {
9136 33 : const char *pszStyle = nullptr;
9137 33 : int nOGRStyle = 0;
9138 : /* char szPattern[20]; */
9139 33 : int nAngle = 0;
9140 : /* szPattern[0] = '\0'; */
9141 :
9142 33 : switch (m_sSymbolDef.nSymbolNo)
9143 : {
9144 0 : case 31:
9145 : // this is actually a "null" symbol in MapInfo!
9146 0 : nOGRStyle = 0;
9147 0 : break;
9148 0 : case 32: // filled square
9149 0 : nOGRStyle = 5;
9150 0 : break;
9151 0 : case 33: // filled diamond
9152 0 : nAngle = 45;
9153 0 : nOGRStyle = 5;
9154 0 : break;
9155 0 : case 34: // filled circle
9156 0 : nOGRStyle = 3;
9157 0 : break;
9158 33 : case 35: // filled star
9159 33 : nOGRStyle = 9;
9160 33 : break;
9161 0 : case 36: // filled upward pointing triangle
9162 0 : nOGRStyle = 7;
9163 0 : break;
9164 0 : case 37: // filled downward pointing triangle
9165 0 : nAngle = 180;
9166 0 : nOGRStyle = 7;
9167 0 : break;
9168 0 : case 38: // hollow square
9169 0 : nOGRStyle = 4;
9170 0 : break;
9171 0 : case 39: // hollow diamond
9172 0 : nAngle = 45;
9173 0 : nOGRStyle = 4;
9174 0 : break;
9175 0 : case 40: // hollow circle
9176 0 : nOGRStyle = 2;
9177 0 : break;
9178 0 : case 41: // hollow star
9179 0 : nOGRStyle = 8;
9180 0 : break;
9181 0 : case 42: // hollow upward pointing triangle
9182 0 : nOGRStyle = 6;
9183 0 : break;
9184 0 : case 43: // hollow downward pointing triangle
9185 0 : nAngle = 180;
9186 0 : nOGRStyle = 6;
9187 0 : break;
9188 0 : case 44: // filled square (with shadow)
9189 0 : nOGRStyle = 5;
9190 0 : break;
9191 0 : case 45: // filled upward triangle (with shadow)
9192 0 : nOGRStyle = 7;
9193 0 : break;
9194 0 : case 46: // filled circle (with shadow)
9195 0 : nOGRStyle = 3;
9196 0 : break;
9197 0 : case 49: // crossed lines
9198 0 : nOGRStyle = 0;
9199 0 : break;
9200 0 : case 50: // X crossed lines
9201 0 : nOGRStyle = 1;
9202 0 : break;
9203 : }
9204 :
9205 33 : nAngle += static_cast<int>(dfAngle);
9206 :
9207 66 : pszStyle = CPLSPrintf(
9208 : "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
9209 33 : m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
9210 : nOGRStyle);
9211 :
9212 33 : return pszStyle;
9213 : }
9214 :
9215 : /**********************************************************************
9216 : * ITABFeatureSymbol::SetSymbolFromStyleString()
9217 : *
9218 : * Set all Symbol var from a OGRStyleSymbol.
9219 : **********************************************************************/
9220 109 : void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
9221 : {
9222 109 : GBool bIsNull = 0;
9223 :
9224 : // Set the Symbol Id (SymbolNo)
9225 109 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9226 109 : if (bIsNull)
9227 0 : pszSymbolId = nullptr;
9228 :
9229 109 : if (pszSymbolId)
9230 : {
9231 109 : if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
9232 : {
9233 105 : const int nSymbolId = atoi(pszSymbolId + 12);
9234 105 : SetSymbolNo(static_cast<GByte>(nSymbolId));
9235 : }
9236 4 : else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
9237 : {
9238 0 : const int nSymbolId = atoi(pszSymbolId + 8);
9239 :
9240 : // The OGR symbol is not the MapInfo one
9241 : // Here's some mapping
9242 0 : switch (nSymbolId)
9243 : {
9244 0 : case 0:
9245 0 : SetSymbolNo(49);
9246 0 : break;
9247 0 : case 1:
9248 0 : SetSymbolNo(50);
9249 0 : break;
9250 0 : case 2:
9251 0 : SetSymbolNo(40);
9252 0 : break;
9253 0 : case 3:
9254 0 : SetSymbolNo(34);
9255 0 : break;
9256 0 : case 4:
9257 0 : SetSymbolNo(38);
9258 0 : break;
9259 0 : case 5:
9260 0 : SetSymbolNo(32);
9261 0 : break;
9262 0 : case 6:
9263 0 : SetSymbolNo(42);
9264 0 : break;
9265 0 : case 7:
9266 0 : SetSymbolNo(36);
9267 0 : break;
9268 0 : case 8:
9269 0 : SetSymbolNo(41);
9270 0 : break;
9271 0 : case 9:
9272 0 : SetSymbolNo(35);
9273 0 : break;
9274 0 : case 10: // vertical bar -- no mapinfo equivalent, so use
9275 : // crosshairs as closest match
9276 0 : SetSymbolNo(49);
9277 0 : break;
9278 : }
9279 : }
9280 : }
9281 :
9282 : // Set SymbolSize
9283 109 : const double dSymbolSize = poSymbolStyle->Size(bIsNull);
9284 109 : if (dSymbolSize != 0.0)
9285 : {
9286 109 : SetSymbolSize(static_cast<GInt16>(dSymbolSize));
9287 : }
9288 :
9289 : // Set Symbol Color
9290 109 : const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
9291 109 : if (pszSymbolColor)
9292 : {
9293 109 : if (pszSymbolColor[0] == '#')
9294 109 : pszSymbolColor++;
9295 : int nSymbolColor =
9296 109 : static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
9297 109 : SetSymbolColor(static_cast<GInt32>(nSymbolColor));
9298 : }
9299 109 : }
9300 :
9301 : /**********************************************************************
9302 : * ITABFeatureSymbol::SetSymbolFromStyleString()
9303 : *
9304 : * Set all Symbol var from a StyleString. Use StyleMgr to do so.
9305 : **********************************************************************/
9306 109 : void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
9307 : {
9308 : // Use the Style Manager to retrieve all the information we need.
9309 109 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9310 109 : OGRStyleTool *poStylePart = nullptr;
9311 :
9312 : // Init the StyleMgr with the StyleString.
9313 109 : poStyleMgr->InitStyleString(pszStyleString);
9314 :
9315 : // Retrieve the Symbol info.
9316 109 : const int numParts = poStyleMgr->GetPartCount();
9317 109 : for (int i = 0; i < numParts; i++)
9318 : {
9319 109 : poStylePart = poStyleMgr->GetPart(i);
9320 109 : if (poStylePart == nullptr)
9321 0 : continue;
9322 :
9323 109 : if (poStylePart->GetType() == OGRSTCSymbol)
9324 : {
9325 109 : break;
9326 : }
9327 : else
9328 : {
9329 0 : delete poStylePart;
9330 0 : poStylePart = nullptr;
9331 : }
9332 : }
9333 :
9334 : // If the no Symbol found, do nothing.
9335 109 : if (poStylePart == nullptr)
9336 : {
9337 0 : delete poStyleMgr;
9338 0 : return;
9339 : }
9340 :
9341 : OGRStyleSymbol *poSymbolStyle =
9342 109 : cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9343 :
9344 : // With Symbol, we always want to output points
9345 : //
9346 : // It's very important to set the output unit of the feature.
9347 : // The default value is meter. If we don't do it all numerical values
9348 : // will be assumed to be converted from the input unit to meter when we
9349 : // will get them via GetParam...() functions.
9350 : // See OGRStyleTool::Parse() for more details.
9351 109 : poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
9352 :
9353 109 : SetSymbolFromStyle(poSymbolStyle);
9354 :
9355 109 : delete poStyleMgr;
9356 109 : delete poStylePart;
9357 :
9358 109 : return;
9359 : }
9360 :
9361 : /**********************************************************************
9362 : * ITABFeatureSymbol::GetSymbolFeatureClass()
9363 : *
9364 : * Return the feature class needed to represent the style string.
9365 : **********************************************************************/
9366 : TABFeatureClass
9367 109 : ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
9368 : {
9369 : // Use the Style Manager to retrieve all the information we need.
9370 109 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9371 109 : OGRStyleTool *poStylePart = nullptr;
9372 :
9373 : // Init the StyleMgr with the StyleString.
9374 109 : poStyleMgr->InitStyleString(pszStyleString);
9375 :
9376 : // Retrieve the Symbol info.
9377 109 : const int numParts = poStyleMgr->GetPartCount();
9378 109 : for (int i = 0; i < numParts; i++)
9379 : {
9380 109 : poStylePart = poStyleMgr->GetPart(i);
9381 109 : if (poStylePart == nullptr)
9382 : {
9383 0 : continue;
9384 : }
9385 :
9386 109 : if (poStylePart->GetType() == OGRSTCSymbol)
9387 : {
9388 109 : break;
9389 : }
9390 : else
9391 : {
9392 0 : delete poStylePart;
9393 0 : poStylePart = nullptr;
9394 : }
9395 : }
9396 :
9397 109 : TABFeatureClass result = TABFCPoint;
9398 :
9399 : // If the no Symbol found, do nothing.
9400 109 : if (poStylePart == nullptr)
9401 : {
9402 0 : delete poStyleMgr;
9403 0 : return result;
9404 : }
9405 :
9406 : OGRStyleSymbol *poSymbolStyle =
9407 109 : cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9408 :
9409 109 : GBool bIsNull = 0;
9410 :
9411 : // Set the Symbol Id (SymbolNo)
9412 109 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9413 109 : if (bIsNull)
9414 0 : pszSymbolId = nullptr;
9415 :
9416 109 : if (pszSymbolId)
9417 : {
9418 109 : if (STARTS_WITH(pszSymbolId, "font-sym-"))
9419 : {
9420 2 : result = TABFCFontPoint;
9421 : }
9422 107 : else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
9423 : {
9424 2 : result = TABFCCustomPoint;
9425 : }
9426 : }
9427 :
9428 109 : delete poStyleMgr;
9429 109 : delete poStylePart;
9430 :
9431 109 : return result;
9432 : }
9433 :
9434 : /**********************************************************************
9435 : * ITABFeatureSymbol::DumpSymbolDef()
9436 : *
9437 : * Dump Symbol definition information.
9438 : **********************************************************************/
9439 0 : void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
9440 : {
9441 0 : if (fpOut == nullptr)
9442 0 : fpOut = stdout;
9443 :
9444 0 : fprintf(fpOut, " m_nSymbolDefIndex = %d\n", m_nSymbolDefIndex);
9445 0 : fprintf(fpOut, " m_sSymbolDef.nRefCount = %d\n", m_sSymbolDef.nRefCount);
9446 0 : fprintf(fpOut, " m_sSymbolDef.nSymbolNo = %d\n", m_sSymbolDef.nSymbolNo);
9447 0 : fprintf(fpOut, " m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
9448 0 : fprintf(fpOut, " m_sSymbolDef._unknown_ = %d\n",
9449 0 : static_cast<int>(m_sSymbolDef._nUnknownValue_));
9450 0 : fprintf(fpOut, " m_sSymbolDef.rgbColor = 0x%6.6x (%d)\n",
9451 : m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
9452 :
9453 0 : fflush(fpOut);
9454 0 : }
|