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 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : **********************************************************************/
31 :
32 : #include "cpl_port.h"
33 : #include "mitab.h"
34 : #include "mitab_geometry.h"
35 : #include "mitab_utils.h"
36 :
37 : #include <cctype>
38 : #include <cmath>
39 : #include <cstdio>
40 : #include <cstdlib>
41 : #include <cstring>
42 : #include <algorithm>
43 : #include <utility>
44 :
45 : #include "cpl_conv.h"
46 : #include "cpl_error.h"
47 : #include "cpl_string.h"
48 : #include "cpl_vsi.h"
49 : #include "mitab.h"
50 : #include "mitab_geometry.h"
51 : #include "mitab_priv.h"
52 : #include "mitab_utils.h"
53 : #include "ogr_core.h"
54 : #include "ogr_feature.h"
55 : #include "ogr_featurestyle.h"
56 : #include "ogr_geometry.h"
57 :
58 : /*=====================================================================
59 : * class TABFeature
60 : *====================================================================*/
61 :
62 : /**********************************************************************
63 : * TABFeature::TABFeature()
64 : *
65 : * Constructor.
66 : **********************************************************************/
67 539662 : TABFeature::TABFeature(OGRFeatureDefn *poDefnIn)
68 : : OGRFeature(poDefnIn), m_nMapInfoType(TAB_GEOM_NONE), m_dXMin(0),
69 : m_dYMin(0), m_dXMax(0), m_dYMax(0), m_bDeletedFlag(FALSE), m_nXMin(0),
70 539662 : m_nYMin(0), m_nXMax(0), m_nYMax(0), m_nComprOrgX(0), m_nComprOrgY(0)
71 : {
72 539662 : }
73 :
74 : /**********************************************************************
75 : * TABFeature::~TABFeature()
76 : *
77 : * Destructor.
78 : **********************************************************************/
79 539910 : TABFeature::~TABFeature()
80 : {
81 539910 : }
82 :
83 : /**********************************************************************
84 : * TABFeature::CreateFromMapInfoType()
85 : *
86 : * Factory that creates a TABFeature of the right class for the specified
87 : * MapInfo Type
88 : *
89 : **********************************************************************/
90 516804 : TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType,
91 : OGRFeatureDefn *poDefn)
92 : {
93 516804 : TABFeature *poFeature = nullptr;
94 :
95 : /*-----------------------------------------------------------------
96 : * Create new feature object of the right type
97 : *----------------------------------------------------------------*/
98 516804 : switch (nMapInfoType)
99 : {
100 147 : case TAB_GEOM_NONE:
101 147 : poFeature = new TABFeature(poDefn);
102 147 : break;
103 514727 : case TAB_GEOM_SYMBOL_C:
104 : case TAB_GEOM_SYMBOL:
105 514727 : poFeature = new TABPoint(poDefn);
106 514727 : break;
107 6 : case TAB_GEOM_FONTSYMBOL_C:
108 : case TAB_GEOM_FONTSYMBOL:
109 6 : poFeature = new TABFontPoint(poDefn);
110 6 : break;
111 6 : case TAB_GEOM_CUSTOMSYMBOL_C:
112 : case TAB_GEOM_CUSTOMSYMBOL:
113 6 : poFeature = new TABCustomPoint(poDefn);
114 6 : break;
115 1383 : case TAB_GEOM_LINE_C:
116 : case TAB_GEOM_LINE:
117 : case TAB_GEOM_PLINE_C:
118 : case TAB_GEOM_PLINE:
119 : case TAB_GEOM_MULTIPLINE_C:
120 : case TAB_GEOM_MULTIPLINE:
121 : case TAB_GEOM_V450_MULTIPLINE_C:
122 : case TAB_GEOM_V450_MULTIPLINE:
123 : case TAB_GEOM_V800_MULTIPLINE_C:
124 : case TAB_GEOM_V800_MULTIPLINE:
125 1383 : poFeature = new TABPolyline(poDefn);
126 1383 : break;
127 8 : case TAB_GEOM_ARC_C:
128 : case TAB_GEOM_ARC:
129 8 : poFeature = new TABArc(poDefn);
130 8 : break;
131 :
132 499 : case TAB_GEOM_REGION_C:
133 : case TAB_GEOM_REGION:
134 : case TAB_GEOM_V450_REGION_C:
135 : case TAB_GEOM_V450_REGION:
136 : case TAB_GEOM_V800_REGION_C:
137 : case TAB_GEOM_V800_REGION:
138 499 : poFeature = new TABRegion(poDefn);
139 499 : break;
140 8 : case TAB_GEOM_RECT_C:
141 : case TAB_GEOM_RECT:
142 : case TAB_GEOM_ROUNDRECT_C:
143 : case TAB_GEOM_ROUNDRECT:
144 8 : poFeature = new TABRectangle(poDefn);
145 8 : break;
146 4 : case TAB_GEOM_ELLIPSE_C:
147 : case TAB_GEOM_ELLIPSE:
148 4 : poFeature = new TABEllipse(poDefn);
149 4 : break;
150 8 : case TAB_GEOM_TEXT_C:
151 : case TAB_GEOM_TEXT:
152 8 : poFeature = new TABText(poDefn);
153 8 : break;
154 4 : case TAB_GEOM_MULTIPOINT_C:
155 : case TAB_GEOM_MULTIPOINT:
156 : case TAB_GEOM_V800_MULTIPOINT_C:
157 : case TAB_GEOM_V800_MULTIPOINT:
158 4 : poFeature = new TABMultiPoint(poDefn);
159 4 : break;
160 4 : case TAB_GEOM_COLLECTION_C:
161 : case TAB_GEOM_COLLECTION:
162 : case TAB_GEOM_V800_COLLECTION_C:
163 : case TAB_GEOM_V800_COLLECTION:
164 4 : poFeature = new TABCollection(poDefn);
165 4 : break;
166 0 : default:
167 : /*-------------------------------------------------------------
168 : * Unsupported feature type... we still return a valid feature
169 : * with NONE geometry after producing a Warning.
170 : * Callers can trap that case by checking CPLGetLastErrorNo()
171 : * against TAB_WarningFeatureTypeNotSupported
172 : *------------------------------------------------------------*/
173 : // poFeature = new TABDebugFeature(poDefn);
174 0 : poFeature = new TABFeature(poDefn);
175 :
176 0 : CPLError(
177 : CE_Warning,
178 : static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
179 : "Unsupported object type %d (0x%2.2x). Feature will be "
180 : "returned with NONE geometry.",
181 : nMapInfoType, nMapInfoType);
182 : }
183 :
184 516804 : return poFeature;
185 : }
186 :
187 : /**********************************************************************
188 : * TABFeature::CopyTABFeatureBase()
189 : *
190 : * Used by CloneTABFeature() to copy the basic (fields, geometry, etc.)
191 : * TABFeature members.
192 : *
193 : * The newly created feature is owned by the caller, and will have its own
194 : * reference to the OGRFeatureDefn.
195 : *
196 : * It is possible to create the clone with a different OGRFeatureDefn,
197 : * in this case, the fields won't be copied of course.
198 : *
199 : **********************************************************************/
200 2 : void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature)
201 : {
202 : /*-----------------------------------------------------------------
203 : * Copy fields only if OGRFeatureDefn is the same
204 : *----------------------------------------------------------------*/
205 2 : OGRFeatureDefn *poThisDefnRef = GetDefnRef();
206 :
207 2 : if (poThisDefnRef == poDestFeature->GetDefnRef())
208 : {
209 0 : for (int i = 0; i < poThisDefnRef->GetFieldCount(); i++)
210 : {
211 0 : poDestFeature->SetField(i, GetRawFieldRef(i));
212 : }
213 : }
214 :
215 : /*-----------------------------------------------------------------
216 : * Copy the geometry
217 : *----------------------------------------------------------------*/
218 2 : poDestFeature->SetGeometry(GetGeometryRef());
219 :
220 2 : double dXMin = 0.0;
221 2 : double dYMin = 0.0;
222 2 : double dXMax = 0.0;
223 2 : double dYMax = 0.0;
224 2 : GetMBR(dXMin, dYMin, dXMax, dYMax);
225 2 : poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax);
226 :
227 2 : GInt32 nXMin = 0;
228 2 : GInt32 nYMin = 0;
229 2 : GInt32 nXMax = 0;
230 2 : GInt32 nYMax = 0;
231 2 : GetIntMBR(nXMin, nYMin, nXMax, nYMax);
232 2 : poDestFeature->SetIntMBR(nXMin, nYMin, nXMax, nYMax);
233 :
234 : // m_nMapInfoType is not carried but it is not required anyways.
235 : // it will default to TAB_GEOM_NONE
236 2 : }
237 :
238 : /**********************************************************************
239 : * TABFeature::CloneTABFeature()
240 : *
241 : * Duplicate feature, including stuff specific to each TABFeature type.
242 : *
243 : * The newly created feature is owned by the caller, and will have its own
244 : * reference to the OGRFeatureDefn.
245 : *
246 : * It is possible to create the clone with a different OGRFeatureDefn,
247 : * in this case, the fields won't be copied of course.
248 : *
249 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
250 : * then copies any members specific to its own type.
251 : **********************************************************************/
252 0 : TABFeature *TABFeature::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
253 : {
254 : /*-----------------------------------------------------------------
255 : * Alloc new feature and copy the base stuff
256 : *----------------------------------------------------------------*/
257 0 : TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef());
258 :
259 0 : CopyTABFeatureBase(poNew);
260 :
261 : /*-----------------------------------------------------------------
262 : * And members specific to this class
263 : *----------------------------------------------------------------*/
264 : // Nothing to do for this class
265 :
266 0 : return poNew;
267 : }
268 :
269 : /**********************************************************************
270 : * TABFeature::SetMBR()
271 : *
272 : * Set the values for the MBR corners for this feature.
273 : **********************************************************************/
274 523942 : void TABFeature::SetMBR(double dXMin, double dYMin, double dXMax, double dYMax)
275 : {
276 523942 : m_dXMin = std::min(dXMin, dXMax);
277 523942 : m_dYMin = std::min(dYMin, dYMax);
278 523942 : m_dXMax = std::max(dXMin, dXMax);
279 523942 : m_dYMax = std::max(dYMin, dYMax);
280 523942 : }
281 :
282 : /**********************************************************************
283 : * TABFeature::GetMBR()
284 : *
285 : * Return the values for the MBR corners for this feature.
286 : **********************************************************************/
287 1227 : void TABFeature::GetMBR(double &dXMin, double &dYMin, double &dXMax,
288 : double &dYMax)
289 : {
290 1227 : dXMin = m_dXMin;
291 1227 : dYMin = m_dYMin;
292 1227 : dXMax = m_dXMax;
293 1227 : dYMax = m_dYMax;
294 1227 : }
295 :
296 : /**********************************************************************
297 : * TABFeature::SetIntMBR()
298 : *
299 : * Set the integer coordinates values of the MBR of this feature.
300 : **********************************************************************/
301 516663 : void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
302 : GInt32 nYMax)
303 : {
304 516663 : m_nXMin = nXMin;
305 516663 : m_nYMin = nYMin;
306 516663 : m_nXMax = nXMax;
307 516663 : m_nYMax = nYMax;
308 516663 : }
309 :
310 : /**********************************************************************
311 : * TABFeature::GetIntMBR()
312 : *
313 : * Return the integer coordinates values of the MBR of this feature.
314 : **********************************************************************/
315 15019 : void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
316 : GInt32 &nYMax)
317 : {
318 15019 : nXMin = m_nXMin;
319 15019 : nYMin = m_nYMin;
320 15019 : nXMax = m_nXMax;
321 15019 : nYMax = m_nYMax;
322 15019 : }
323 :
324 : /**********************************************************************
325 : * TABFeature::ReadRecordFromDATFile()
326 : *
327 : * Fill the fields part of the feature from the contents of the
328 : * table record pointed to by poDATFile.
329 : *
330 : * It is assumed that poDATFile currently points to the beginning of
331 : * the table record and that this feature's OGRFeatureDefn has been
332 : * properly initialized for this table.
333 : **********************************************************************/
334 516678 : int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
335 : {
336 516678 : CPLAssert(poDATFile);
337 :
338 516678 : const int numFields = poDATFile->GetNumFields();
339 :
340 1035080 : for (int iField = 0; iField < numFields; iField++)
341 : {
342 518400 : switch (poDATFile->GetFieldType(iField))
343 : {
344 1291 : case TABFChar:
345 : {
346 1291 : int iWidth(poDATFile->GetFieldWidth(iField));
347 2582 : CPLString osValue(poDATFile->ReadCharField(iWidth));
348 :
349 1291 : if (!poDATFile->GetEncoding().empty())
350 : {
351 107 : osValue.Recode(poDATFile->GetEncoding(), CPL_ENC_UTF8);
352 : }
353 1291 : SetField(iField, osValue);
354 1291 : break;
355 : }
356 23 : case TABFDecimal:
357 : {
358 23 : const double dValue = poDATFile->ReadDecimalField(
359 : poDATFile->GetFieldWidth(iField));
360 23 : SetField(iField, dValue);
361 23 : break;
362 : }
363 516457 : case TABFInteger:
364 : {
365 516457 : const int nValue = poDATFile->ReadIntegerField(
366 : poDATFile->GetFieldWidth(iField));
367 516457 : SetField(iField, nValue);
368 516457 : break;
369 : }
370 1 : case TABFSmallInt:
371 : {
372 1 : const int nValue = poDATFile->ReadSmallIntField(
373 1 : poDATFile->GetFieldWidth(iField));
374 1 : SetField(iField, nValue);
375 1 : break;
376 : }
377 1 : case TABFLargeInt:
378 : {
379 1 : const GInt64 nValue = poDATFile->ReadLargeIntField(
380 : poDATFile->GetFieldWidth(iField));
381 1 : SetField(iField, nValue);
382 1 : break;
383 : }
384 582 : case TABFFloat:
385 : {
386 : const double dValue =
387 582 : poDATFile->ReadFloatField(poDATFile->GetFieldWidth(iField));
388 582 : SetField(iField, dValue);
389 582 : break;
390 : }
391 1 : case TABFLogical:
392 : {
393 1 : const char *pszValue = poDATFile->ReadLogicalField(
394 : poDATFile->GetFieldWidth(iField));
395 1 : SetField(iField, pszValue);
396 1 : break;
397 : }
398 24 : case TABFDate:
399 : {
400 : #ifdef MITAB_USE_OFTDATETIME
401 24 : int nYear = 0;
402 24 : int nMonth = 0;
403 24 : int nDay = 0;
404 24 : const int status = poDATFile->ReadDateField(
405 : poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay);
406 24 : if (status == 0)
407 : {
408 7 : SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0);
409 : }
410 : #else
411 : const char *pszValue =
412 : poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField));
413 : SetField(iField, pszValue);
414 : #endif
415 24 : break;
416 : }
417 2 : case TABFTime:
418 : {
419 : #ifdef MITAB_USE_OFTDATETIME
420 2 : int nHour = 0;
421 2 : int nMin = 0;
422 2 : int nMS = 0;
423 2 : int nSec = 0;
424 : const int status =
425 2 : poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField),
426 : &nHour, &nMin, &nSec, &nMS);
427 2 : if (status == 0)
428 : {
429 1 : int nYear = 0;
430 1 : int nMonth = 0;
431 1 : int nDay = 0;
432 1 : SetField(iField, nYear, nMonth, nDay, nHour, nMin,
433 1 : nSec + nMS / 1000.0f, 0);
434 : }
435 : #else
436 : const char *pszValue =
437 : poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField));
438 : SetField(iField, pszValue);
439 : #endif
440 2 : break;
441 : }
442 18 : case TABFDateTime:
443 : {
444 : #ifdef MITAB_USE_OFTDATETIME
445 18 : int nYear = 0;
446 18 : int nMonth = 0;
447 18 : int nDay = 0;
448 18 : int nHour = 0;
449 18 : int nMin = 0;
450 18 : int nMS = 0;
451 18 : int nSec = 0;
452 18 : const int status = poDATFile->ReadDateTimeField(
453 : poDATFile->GetFieldWidth(iField), &nYear, &nMonth, &nDay,
454 : &nHour, &nMin, &nSec, &nMS);
455 18 : if (status == 0)
456 : {
457 1 : SetField(iField, nYear, nMonth, nDay, nHour, nMin,
458 1 : nSec + nMS / 1000.0f, 0);
459 : }
460 : #else
461 : const char *pszValue = poDATFile->ReadDateTimeField(
462 : poDATFile->GetFieldWidth(iField));
463 : SetField(iField, pszValue);
464 : #endif
465 18 : break;
466 : }
467 0 : default:
468 : // Other type??? Impossible!
469 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
470 : "Unsupported field type!");
471 : }
472 : }
473 :
474 516678 : return 0;
475 : }
476 :
477 : /**********************************************************************
478 : * TABFeature::WriteRecordToDATFile()
479 : *
480 : * Write the attribute part of the feature to the .DAT file.
481 : *
482 : * It is assumed that poDATFile currently points to the beginning of
483 : * the table record and that this feature's OGRFeatureDefn has been
484 : * properly initialized for this table.
485 : *
486 : * Returns 0 on success, -1 on error.
487 : **********************************************************************/
488 15078 : int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
489 : TABINDFile *poINDFile, int *panIndexNo)
490 : {
491 : #ifdef MITAB_USE_OFTDATETIME
492 15078 : int nYear = 0;
493 15078 : int nMon = 0;
494 15078 : int nDay = 0;
495 15078 : int nHour = 0;
496 15078 : int nMin = 0;
497 15078 : int nTZFlag = 0;
498 15078 : float fSec = 0.0f;
499 : #endif
500 :
501 15078 : CPLAssert(poDATFile);
502 :
503 15078 : const int numFields = poDATFile->GetNumFields();
504 :
505 15078 : poDATFile->MarkRecordAsExisting();
506 :
507 15078 : int nStatus = 0;
508 30827 : for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
509 : {
510 : // Hack for "extra" introduced field.
511 15749 : if (iField >= GetDefnRef()->GetFieldCount())
512 : {
513 1 : CPLAssert(poDATFile->GetFieldType(iField) == TABFInteger &&
514 : iField == 0);
515 1 : nStatus = poDATFile->WriteIntegerField(static_cast<int>(GetFID()),
516 : poINDFile, 0);
517 1 : continue;
518 : }
519 15748 : CPLAssert(panIndexNo != nullptr);
520 :
521 15748 : switch (poDATFile->GetFieldType(iField))
522 : {
523 374 : case TABFChar:
524 : {
525 374 : CPLString osValue(GetFieldAsString(iField));
526 374 : if (!poDATFile->GetEncoding().empty())
527 : {
528 30 : osValue.Recode(CPL_ENC_UTF8, poDATFile->GetEncoding());
529 : }
530 374 : nStatus = poDATFile->WriteCharField(
531 : osValue, poDATFile->GetFieldWidth(iField), poINDFile,
532 374 : panIndexNo[iField]);
533 : }
534 374 : break;
535 4 : case TABFDecimal:
536 4 : nStatus = poDATFile->WriteDecimalField(
537 : GetFieldAsDouble(iField), poDATFile->GetFieldWidth(iField),
538 : poDATFile->GetFieldPrecision(iField), poINDFile,
539 4 : panIndexNo[iField]);
540 4 : break;
541 14984 : case TABFInteger:
542 14984 : nStatus = poDATFile->WriteIntegerField(
543 14984 : GetFieldAsInteger(iField), poINDFile, panIndexNo[iField]);
544 14984 : break;
545 0 : case TABFSmallInt:
546 0 : nStatus = poDATFile->WriteSmallIntField(
547 0 : static_cast<GInt16>(GetFieldAsInteger(iField)), poINDFile,
548 0 : panIndexNo[iField]);
549 0 : break;
550 1 : case TABFLargeInt:
551 3 : nStatus = poDATFile->WriteLargeIntField(
552 1 : static_cast<GInt64>(GetFieldAsInteger64(iField)), poINDFile,
553 1 : panIndexNo[iField]);
554 1 : break;
555 234 : case TABFFloat:
556 234 : nStatus = poDATFile->WriteFloatField(
557 234 : GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
558 234 : break;
559 0 : case TABFLogical:
560 0 : nStatus = poDATFile->WriteLogicalField(
561 0 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
562 0 : break;
563 75 : case TABFDate:
564 : #ifdef MITAB_USE_OFTDATETIME
565 75 : if (IsFieldSetAndNotNull(iField))
566 : {
567 58 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
568 : &nMin, &fSec, &nTZFlag);
569 : }
570 : else
571 : {
572 17 : nYear = 0;
573 17 : nMon = 0;
574 17 : nDay = 0;
575 : }
576 :
577 150 : nStatus = poDATFile->WriteDateField(
578 75 : nYear, nMon, nDay, poINDFile, panIndexNo[iField]);
579 : #else
580 : nStatus = poDATFile->WriteDateField(
581 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
582 : #endif
583 75 : break;
584 2 : case TABFTime:
585 : #ifdef MITAB_USE_OFTDATETIME
586 2 : if (IsFieldSetAndNotNull(iField))
587 : {
588 1 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
589 : &nMin, &fSec, &nTZFlag);
590 : }
591 : else
592 : {
593 : // Put negative values, so that WriteTimeField() forges
594 : // a negative value, and ultimately write -1 in the binary
595 : // field
596 1 : nHour = -1;
597 1 : nMin = -1;
598 1 : fSec = -1;
599 : }
600 2 : nStatus = poDATFile->WriteTimeField(
601 : nHour, nMin, static_cast<int>(fSec), OGR_GET_MS(fSec),
602 2 : poINDFile, panIndexNo[iField]);
603 :
604 : #else
605 : nStatus = poDATFile->WriteTimeField(
606 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
607 : #endif
608 2 : break;
609 74 : case TABFDateTime:
610 : #ifdef MITAB_USE_OFTDATETIME
611 74 : if (IsFieldSetAndNotNull(iField))
612 : {
613 57 : GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, &nHour,
614 : &nMin, &fSec, &nTZFlag);
615 : }
616 : else
617 : {
618 17 : nYear = 0;
619 17 : nMon = 0;
620 17 : nDay = 0;
621 17 : nHour = 0;
622 17 : nMin = 0;
623 17 : fSec = 0;
624 : }
625 :
626 74 : nStatus = poDATFile->WriteDateTimeField(
627 : nYear, nMon, nDay, nHour, nMin, static_cast<int>(fSec),
628 74 : OGR_GET_MS(fSec), poINDFile, panIndexNo[iField]);
629 : #else
630 : nStatus = poDATFile->WriteDateTimeField(
631 : GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
632 : #endif
633 74 : break;
634 0 : default:
635 : // Other type??? Impossible!
636 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
637 : "Unsupported field type!");
638 : }
639 : }
640 :
641 15078 : if (nStatus != 0)
642 1 : return nStatus;
643 :
644 15077 : if (poDATFile->CommitRecordToFile() != 0)
645 0 : return -1;
646 :
647 15077 : return 0;
648 : }
649 :
650 : /**********************************************************************
651 : * TABFeature::ReadGeometryFromMAPFile()
652 : *
653 : * In derived classes, this method should be reimplemented to
654 : * fill the geometry and representation (color, etc...) part of the
655 : * feature from the contents of the .MAP object pointed to by poMAPFile.
656 : *
657 : * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
658 : * currently points to the beginning of a map object.
659 : *
660 : * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
661 : * the CoordBlock data during splitting of object blocks. In this case we
662 : * need to process only the information related to the CoordBlock. One
663 : * important thing to avoid is reading/writing pen/brush/symbol definitions
664 : * as that would screw up their ref counters.
665 : *
666 : * ppoCoordBlock is used by TABCollection and by index splitting code
667 : * to provide a CoordBlock to use instead of the one from the poMAPFile and
668 : * return the current pointer at the end of the call.
669 : *
670 : * The current implementation does nothing since instances of TABFeature
671 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
672 : *
673 : * Returns 0 on success, -1 on error, in which case CPLError() will have
674 : * been called.
675 : **********************************************************************/
676 147 : int TABFeature::ReadGeometryFromMAPFile(
677 : TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
678 : GBool /*bCoordBlockDataOnly=FALSE*/,
679 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
680 : {
681 : // Nothing to do. Instances of TABFeature objects contain no geometry.
682 147 : return 0;
683 : }
684 :
685 : /**********************************************************************
686 : * TABFeature::UpdateMBR()
687 : *
688 : * Fetch envelope of poGeom and update MBR.
689 : * Integer coord MBR is updated only if poMapFile is not NULL.
690 : *
691 : * Returns 0 on success, or -1 if there is no geometry in object
692 : **********************************************************************/
693 15013 : int TABFeature::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
694 : {
695 15013 : OGRGeometry *poGeom = GetGeometryRef();
696 :
697 15013 : if (poGeom)
698 : {
699 15013 : OGREnvelope oEnv;
700 15013 : poGeom->getEnvelope(&oEnv);
701 :
702 15013 : m_dXMin = oEnv.MinX;
703 15013 : m_dYMin = oEnv.MinY;
704 15013 : m_dXMax = oEnv.MaxX;
705 15013 : m_dYMax = oEnv.MaxY;
706 :
707 15013 : if (poMapFile)
708 : {
709 15013 : poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin);
710 15013 : poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax);
711 : // Coordsy2Int can transform a min value to a max one and vice
712 : // versa.
713 15013 : if (m_nXMin > m_nXMax)
714 : {
715 0 : std::swap(m_nXMin, m_nXMax);
716 : }
717 15013 : if (m_nYMin > m_nYMax)
718 : {
719 0 : std::swap(m_nYMin, m_nYMax);
720 : }
721 : }
722 :
723 15013 : return 0;
724 : }
725 :
726 0 : return -1;
727 : }
728 :
729 : /**********************************************************************
730 : * TABFeature::ValidateCoordType()
731 : *
732 : * Checks the feature envelope to establish if the feature should be
733 : * written using Compressed coordinates or not and adjust m_nMapInfoType
734 : * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin,
735 : * m_nXMax, m_nYMax
736 : *
737 : * This function should be used only by the ValidateMapInfoType()
738 : * implementations.
739 : *
740 : * Returns TRUE if coord. should be compressed, FALSE otherwise
741 : **********************************************************************/
742 303 : GBool TABFeature::ValidateCoordType(TABMAPFile *poMapFile)
743 : {
744 303 : GBool bCompr = FALSE;
745 :
746 : /*-------------------------------------------------------------
747 : * Decide if coordinates should be compressed or not.
748 : *------------------------------------------------------------*/
749 303 : if (UpdateMBR(poMapFile) == 0)
750 : {
751 : /* Test for max range < 65535 here instead of < 65536 to avoid
752 : * compressed coordinate overflows in some boundary situations
753 : */
754 303 : if ((static_cast<GIntBig>(m_nXMax) - m_nXMin) < 65535 &&
755 294 : (static_cast<GIntBig>(m_nYMax) - m_nYMin) < 65535)
756 : {
757 294 : bCompr = TRUE;
758 : }
759 303 : m_nComprOrgX =
760 303 : static_cast<int>((static_cast<GIntBig>(m_nXMin) + m_nXMax) / 2);
761 303 : m_nComprOrgY =
762 303 : static_cast<int>((static_cast<GIntBig>(m_nYMin) + m_nYMax) / 2);
763 : }
764 :
765 : /*-------------------------------------------------------------
766 : * Adjust native type
767 : *------------------------------------------------------------*/
768 303 : if (bCompr && ((m_nMapInfoType % 3) == 2))
769 294 : m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
770 : 1); // compr = 1, 4, 7, ...
771 9 : else if (!bCompr && ((m_nMapInfoType % 3) == 1))
772 0 : m_nMapInfoType = static_cast<TABGeomType>(
773 0 : m_nMapInfoType + 1); // non-compr = 2, 5, 8, ...
774 :
775 303 : return bCompr;
776 : }
777 :
778 : /**********************************************************************
779 : * TABFeature::ForceCoordTypeAndOrigin()
780 : *
781 : * This function is used by TABCollection::ValidateMapInfoType() to force
782 : * the coord type and compressed origin of all members of a collection
783 : * to be the same. (A replacement for ValidateCoordType() for this
784 : * specific case)
785 : **********************************************************************/
786 0 : void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr,
787 : GInt32 nComprOrgX, GInt32 nComprOrgY,
788 : GInt32 nXMin, GInt32 nYMin,
789 : GInt32 nXMax, GInt32 nYMax)
790 : {
791 : /*-------------------------------------------------------------
792 : * Set Compressed Origin and adjust native type
793 : *------------------------------------------------------------*/
794 0 : m_nComprOrgX = nComprOrgX;
795 0 : m_nComprOrgY = nComprOrgY;
796 :
797 0 : m_nMapInfoType = nMapInfoType;
798 :
799 0 : if (bCompr && ((m_nMapInfoType % 3) == 2))
800 0 : m_nMapInfoType = static_cast<TABGeomType>(m_nMapInfoType -
801 : 1); // compr = 1, 4, 7, ...
802 0 : else if (!bCompr && ((m_nMapInfoType % 3) == 1))
803 0 : m_nMapInfoType = static_cast<TABGeomType>(
804 0 : m_nMapInfoType + 1); // non-compr = 2, 5, 8, ...
805 :
806 0 : m_nXMin = nXMin;
807 0 : m_nYMin = nYMin;
808 0 : m_nXMax = nXMax;
809 0 : m_nYMax = nYMax;
810 0 : }
811 :
812 : /**********************************************************************
813 : * TABFeature::WriteGeometryToMAPFile()
814 : *
815 : *
816 : * In derived classes, this method should be reimplemented to
817 : * write the geometry and representation (color, etc...) part of the
818 : * feature to the .MAP object pointed to by poMAPFile.
819 : *
820 : * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
821 : * currently points to a valid map object.
822 : *
823 : * bCoordBlockDataOnly=TRUE is used when this method is called to copy only
824 : * the CoordBlock data during splitting of object blocks. In this case we
825 : * need to process only the information related to the CoordBlock. One
826 : * important thing to avoid is reading/writing pen/brush/symbol definitions
827 : * as that would screw up their ref counters.
828 : *
829 : * ppoCoordBlock is used by TABCollection and by index splitting code
830 : * to provide a CoordBlock to use instead of the one from the poMAPFile and
831 : * return the current pointer at the end of the call.
832 : *
833 : * The current implementation does nothing since instances of TABFeature
834 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
835 : *
836 : * Returns 0 on success, -1 on error, in which case CPLError() will have
837 : * been called.
838 : **********************************************************************/
839 57 : int TABFeature::WriteGeometryToMAPFile(
840 : TABMAPFile * /* poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
841 : GBool /*bCoordBlockDataOnly=FALSE*/,
842 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
843 : {
844 : /*-----------------------------------------------------------------
845 : * Nothing to do... instances of TABFeature objects contain no geometry.
846 : *----------------------------------------------------------------*/
847 :
848 57 : return 0;
849 : }
850 :
851 : /**********************************************************************
852 : * TABFeature::DumpMID()
853 : *
854 : * Dump feature attributes in a format similar to .MID data records.
855 : **********************************************************************/
856 0 : void TABFeature::DumpMID(FILE *fpOut /*=NULL*/)
857 : {
858 0 : OGRFeatureDefn *l_poDefn = GetDefnRef();
859 :
860 0 : if (fpOut == nullptr)
861 0 : fpOut = stdout;
862 :
863 0 : for (int iField = 0; iField < GetFieldCount(); iField++)
864 : {
865 0 : OGRFieldDefn *poFDefn = l_poDefn->GetFieldDefn(iField);
866 :
867 0 : fprintf(fpOut, " %s (%s) = %s\n", poFDefn->GetNameRef(),
868 : OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()),
869 : GetFieldAsString(iField));
870 : }
871 :
872 0 : fflush(fpOut);
873 0 : }
874 :
875 : /**********************************************************************
876 : * TABFeature::DumpMIF()
877 : *
878 : * Dump feature geometry in a format similar to .MIF files.
879 : **********************************************************************/
880 0 : void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/)
881 : {
882 0 : if (fpOut == nullptr)
883 0 : fpOut = stdout;
884 :
885 : /*-----------------------------------------------------------------
886 : * Generate output... not much to do, feature contains no geometry.
887 : *----------------------------------------------------------------*/
888 0 : fprintf(fpOut, "NONE\n");
889 :
890 0 : fflush(fpOut);
891 0 : }
892 :
893 : /*=====================================================================
894 : * class TABPoint
895 : *====================================================================*/
896 :
897 : /**********************************************************************
898 : * TABPoint::TABPoint()
899 : *
900 : * Constructor.
901 : **********************************************************************/
902 531570 : TABPoint::TABPoint(OGRFeatureDefn *poDefnIn) : TABFeature(poDefnIn)
903 : {
904 531570 : }
905 :
906 : /**********************************************************************
907 : * TABPoint::~TABPoint()
908 : *
909 : * Destructor.
910 : **********************************************************************/
911 1061811 : TABPoint::~TABPoint()
912 : {
913 1061811 : }
914 :
915 : /**********************************************************************
916 : * TABPoint::CloneTABFeature()
917 : *
918 : * Duplicate feature, including stuff specific to each TABFeature type.
919 : *
920 : * This method calls the generic TABFeature::CloneTABFeature() and
921 : * then copies any members specific to its own type.
922 : **********************************************************************/
923 2 : TABFeature *TABPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
924 : {
925 : /*-----------------------------------------------------------------
926 : * Alloc new feature and copy the base stuff
927 : *----------------------------------------------------------------*/
928 2 : TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef());
929 :
930 2 : CopyTABFeatureBase(poNew);
931 :
932 : /*-----------------------------------------------------------------
933 : * And members specific to this class
934 : *----------------------------------------------------------------*/
935 : // ITABFeatureSymbol
936 2 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
937 :
938 2 : return poNew;
939 : }
940 :
941 : /**********************************************************************
942 : * TABPoint::ValidateMapInfoType()
943 : *
944 : * Check the feature's geometry part and return the corresponding
945 : * mapinfo object type code. The m_nMapInfoType member will also
946 : * be updated for further calls to GetMapInfoType();
947 : *
948 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
949 : * is expected for this object class.
950 : **********************************************************************/
951 14687 : TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
952 : {
953 : /*-----------------------------------------------------------------
954 : * Fetch and validate geometry
955 : * __TODO__ For now we always write in uncompressed format (until we
956 : * find that this is not correct... note that at this point the
957 : * decision to use compressed/uncompressed will likely be based on
958 : * the distance between the point and the object block center in
959 : * integer coordinates being > 32767 or not... remains to be verified)
960 : *----------------------------------------------------------------*/
961 14687 : OGRGeometry *poGeom = GetGeometryRef();
962 14687 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
963 : {
964 14687 : switch (GetFeatureClass())
965 : {
966 2 : case TABFCFontPoint:
967 2 : m_nMapInfoType = TAB_GEOM_FONTSYMBOL;
968 2 : break;
969 2 : case TABFCCustomPoint:
970 2 : m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL;
971 2 : break;
972 14683 : case TABFCPoint:
973 : default:
974 14683 : m_nMapInfoType = TAB_GEOM_SYMBOL;
975 14683 : break;
976 : }
977 : }
978 : else
979 : {
980 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
981 : "TABPoint: Missing or Invalid Geometry!");
982 0 : m_nMapInfoType = TAB_GEOM_NONE;
983 : }
984 :
985 14687 : UpdateMBR(poMapFile);
986 :
987 14687 : return m_nMapInfoType;
988 : }
989 :
990 : /**********************************************************************
991 : * TABPoint::ReadGeometryFromMAPFile()
992 : *
993 : * Fill the geometry and representation (color, etc...) part of the
994 : * feature from the contents of the .MAP object pointed to by poMAPFile.
995 : *
996 : * It is assumed that poMAPFile currently points to the beginning of
997 : * a map object.
998 : *
999 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1000 : * been called.
1001 : **********************************************************************/
1002 514727 : int TABPoint::ReadGeometryFromMAPFile(
1003 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1004 : GBool bCoordBlockDataOnly /*=FALSE*/,
1005 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1006 : {
1007 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1008 514727 : if (bCoordBlockDataOnly)
1009 0 : return 0;
1010 :
1011 : /*-----------------------------------------------------------------
1012 : * Fetch and validate geometry type
1013 : *----------------------------------------------------------------*/
1014 514727 : m_nMapInfoType = poObjHdr->m_nType;
1015 :
1016 514727 : if (m_nMapInfoType != TAB_GEOM_SYMBOL &&
1017 13206 : m_nMapInfoType != TAB_GEOM_SYMBOL_C)
1018 : {
1019 0 : CPLError(
1020 : CE_Failure, CPLE_AssertionFailed,
1021 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1022 0 : m_nMapInfoType, m_nMapInfoType);
1023 0 : return -1;
1024 : }
1025 :
1026 : /*-----------------------------------------------------------------
1027 : * Read object information
1028 : *----------------------------------------------------------------*/
1029 514727 : TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1030 :
1031 514727 : m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index
1032 :
1033 514727 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1034 :
1035 : /*-----------------------------------------------------------------
1036 : * Create and fill geometry object
1037 : *----------------------------------------------------------------*/
1038 514727 : double dX = 0.0;
1039 514727 : double dY = 0.0;
1040 :
1041 514727 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1042 514727 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1043 :
1044 514727 : SetGeometryDirectly(poGeometry);
1045 :
1046 514727 : SetMBR(dX, dY, dX, dY);
1047 514727 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1048 : poObjHdr->m_nMaxY);
1049 :
1050 514727 : return 0;
1051 : }
1052 :
1053 : /**********************************************************************
1054 : * TABPoint::WriteGeometryToMAPFile()
1055 : *
1056 : * Write the geometry and representation (color, etc...) part of the
1057 : * feature to the .MAP object pointed to by poMAPFile.
1058 : *
1059 : * It is assumed that poMAPFile currently points to a valid map object.
1060 : *
1061 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1062 : * been called.
1063 : **********************************************************************/
1064 14683 : int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
1065 : TABMAPObjHdr *poObjHdr,
1066 : GBool bCoordBlockDataOnly /*=FALSE*/,
1067 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1068 : {
1069 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1070 14683 : if (bCoordBlockDataOnly)
1071 0 : return 0;
1072 :
1073 : /*-----------------------------------------------------------------
1074 : * We assume that ValidateMapInfoType() was called already and that
1075 : * the type in poObjHdr->m_nType is valid.
1076 : *----------------------------------------------------------------*/
1077 14683 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1078 :
1079 : /*-----------------------------------------------------------------
1080 : * Fetch and validate geometry
1081 : *----------------------------------------------------------------*/
1082 14683 : OGRGeometry *poGeom = GetGeometryRef();
1083 14683 : OGRPoint *poPoint = nullptr;
1084 14683 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1085 14683 : poPoint = poGeom->toPoint();
1086 : else
1087 : {
1088 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1089 : "TABPoint: Missing or Invalid Geometry!");
1090 0 : return -1;
1091 : }
1092 :
1093 14683 : GInt32 nX = 0;
1094 14683 : GInt32 nY = 0;
1095 14683 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1096 :
1097 : /*-----------------------------------------------------------------
1098 : * Copy object information
1099 : *----------------------------------------------------------------*/
1100 14683 : TABMAPObjPoint *poPointHdr = cpl::down_cast<TABMAPObjPoint *>(poObjHdr);
1101 :
1102 14683 : poPointHdr->m_nX = nX;
1103 14683 : poPointHdr->m_nY = nY;
1104 14683 : poPointHdr->SetMBR(nX, nY, nX, nY);
1105 :
1106 14683 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1107 14683 : poPointHdr->m_nSymbolId =
1108 14683 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
1109 :
1110 14683 : if (CPLGetLastErrorType() == CE_Failure)
1111 0 : return -1;
1112 :
1113 14683 : return 0;
1114 : }
1115 :
1116 : /**********************************************************************
1117 : * TABPoint::GetX()
1118 : *
1119 : * Return this point's X coordinate.
1120 : **********************************************************************/
1121 0 : double TABPoint::GetX()
1122 : {
1123 :
1124 : /*-----------------------------------------------------------------
1125 : * Fetch and validate geometry
1126 : *----------------------------------------------------------------*/
1127 0 : OGRGeometry *poGeom = GetGeometryRef();
1128 0 : OGRPoint *poPoint = nullptr;
1129 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1130 0 : poPoint = poGeom->toPoint();
1131 : else
1132 : {
1133 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1134 : "TABPoint: Missing or Invalid Geometry!");
1135 0 : return 0.0;
1136 : }
1137 :
1138 0 : return poPoint->getX();
1139 : }
1140 :
1141 : /**********************************************************************
1142 : * TABPoint::GetY()
1143 : *
1144 : * Return this point's Y coordinate.
1145 : **********************************************************************/
1146 0 : double TABPoint::GetY()
1147 : {
1148 : /*-----------------------------------------------------------------
1149 : * Fetch and validate geometry
1150 : *----------------------------------------------------------------*/
1151 0 : OGRGeometry *poGeom = GetGeometryRef();
1152 0 : OGRPoint *poPoint = nullptr;
1153 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1154 0 : poPoint = poGeom->toPoint();
1155 : else
1156 : {
1157 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1158 : "TABPoint: Missing or Invalid Geometry!");
1159 0 : return 0.0;
1160 : }
1161 :
1162 0 : return poPoint->getY();
1163 : }
1164 :
1165 : /**********************************************************************
1166 : * TABPoint::GetStyleString() const
1167 : *
1168 : * Return style string for this feature.
1169 : *
1170 : * Style String is built only once during the first call to GetStyleString().
1171 : **********************************************************************/
1172 239 : const char *TABPoint::GetStyleString() const
1173 : {
1174 239 : if (m_pszStyleString == nullptr)
1175 : {
1176 27 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1177 : }
1178 :
1179 239 : return m_pszStyleString;
1180 : }
1181 :
1182 : /**********************************************************************
1183 : * TABPoint::DumpMIF()
1184 : *
1185 : * Dump feature geometry in a format similar to .MIF POINTs.
1186 : **********************************************************************/
1187 0 : void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/)
1188 : {
1189 0 : if (fpOut == nullptr)
1190 0 : fpOut = stdout;
1191 :
1192 : /*-----------------------------------------------------------------
1193 : * Fetch and validate geometry
1194 : *----------------------------------------------------------------*/
1195 0 : OGRGeometry *poGeom = GetGeometryRef();
1196 0 : OGRPoint *poPoint = nullptr;
1197 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1198 0 : poPoint = poGeom->toPoint();
1199 : else
1200 : {
1201 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1202 : "TABPoint: Missing or Invalid Geometry!");
1203 0 : return;
1204 : }
1205 :
1206 : /*-----------------------------------------------------------------
1207 : * Generate output
1208 : *----------------------------------------------------------------*/
1209 0 : fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY());
1210 :
1211 0 : DumpSymbolDef(fpOut);
1212 :
1213 : /*-----------------------------------------------------------------
1214 : * Handle stuff specific to derived classes
1215 : *----------------------------------------------------------------*/
1216 : // cppcheck-suppress knownConditionTrueFalse
1217 0 : if (GetFeatureClass() == TABFCFontPoint)
1218 : {
1219 0 : TABFontPoint *poFeature = cpl::down_cast<TABFontPoint *>(this);
1220 0 : fprintf(fpOut, " m_nFontStyle = 0x%2.2x (%d)\n",
1221 : poFeature->GetFontStyleTABValue(),
1222 : poFeature->GetFontStyleTABValue());
1223 :
1224 0 : poFeature->DumpFontDef(fpOut);
1225 : }
1226 : // cppcheck-suppress knownConditionTrueFalse
1227 0 : if (GetFeatureClass() == TABFCCustomPoint)
1228 : {
1229 0 : TABCustomPoint *poFeature = cpl::down_cast<TABCustomPoint *>(this);
1230 :
1231 0 : fprintf(fpOut, " m_nUnknown_ = 0x%2.2x (%d)\n",
1232 0 : poFeature->m_nUnknown_, poFeature->m_nUnknown_);
1233 0 : fprintf(fpOut, " m_nCustomStyle = 0x%2.2x (%d)\n",
1234 0 : poFeature->GetCustomSymbolStyle(),
1235 0 : poFeature->GetCustomSymbolStyle());
1236 :
1237 0 : poFeature->DumpFontDef(fpOut);
1238 : }
1239 :
1240 0 : fflush(fpOut);
1241 : }
1242 :
1243 : /*=====================================================================
1244 : * class TABFontPoint
1245 : *====================================================================*/
1246 :
1247 : /**********************************************************************
1248 : * TABFontPoint::TABFontPoint()
1249 : *
1250 : * Constructor.
1251 : **********************************************************************/
1252 643 : TABFontPoint::TABFontPoint(OGRFeatureDefn *poDefnIn)
1253 643 : : TABPoint(poDefnIn), m_dAngle(0.0), m_nFontStyle(0)
1254 : {
1255 643 : }
1256 :
1257 : /**********************************************************************
1258 : * TABFontPoint::~TABFontPoint()
1259 : *
1260 : * Destructor.
1261 : **********************************************************************/
1262 1286 : TABFontPoint::~TABFontPoint()
1263 : {
1264 1286 : }
1265 :
1266 : /**********************************************************************
1267 : * TABFontPoint::CloneTABFeature()
1268 : *
1269 : * Duplicate feature, including stuff specific to each TABFeature type.
1270 : *
1271 : * This method calls the generic TABFeature::CloneTABFeature() and
1272 : * then copies any members specific to its own type.
1273 : **********************************************************************/
1274 0 : TABFeature *TABFontPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1275 : {
1276 : /*-----------------------------------------------------------------
1277 : * Alloc new feature and copy the base stuff
1278 : *----------------------------------------------------------------*/
1279 : TABFontPoint *poNew =
1280 0 : new TABFontPoint(poNewDefn ? poNewDefn : GetDefnRef());
1281 :
1282 0 : CopyTABFeatureBase(poNew);
1283 :
1284 : /*-----------------------------------------------------------------
1285 : * And members specific to this class
1286 : *----------------------------------------------------------------*/
1287 : // ITABFeatureSymbol
1288 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1289 :
1290 : // ITABFeatureFont
1291 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
1292 :
1293 0 : poNew->SetSymbolAngle(GetSymbolAngle());
1294 0 : poNew->SetFontStyleTABValue(GetFontStyleTABValue());
1295 :
1296 0 : return poNew;
1297 : }
1298 :
1299 : /**********************************************************************
1300 : * TABFontPoint::ReadGeometryFromMAPFile()
1301 : *
1302 : * Fill the geometry and representation (color, etc...) part of the
1303 : * feature from the contents of the .MAP object pointed to by poMAPFile.
1304 : *
1305 : * It is assumed that poMAPFile currently points to the beginning of
1306 : * a map object.
1307 : *
1308 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1309 : * been called.
1310 : **********************************************************************/
1311 6 : int TABFontPoint::ReadGeometryFromMAPFile(
1312 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1313 : GBool bCoordBlockDataOnly /*=FALSE*/,
1314 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1315 : {
1316 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1317 6 : if (bCoordBlockDataOnly)
1318 0 : return 0;
1319 :
1320 : /*-----------------------------------------------------------------
1321 : * Fetch and validate geometry type
1322 : *----------------------------------------------------------------*/
1323 6 : m_nMapInfoType = poObjHdr->m_nType;
1324 :
1325 6 : if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL &&
1326 0 : m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C)
1327 : {
1328 0 : CPLError(
1329 : CE_Failure, CPLE_AssertionFailed,
1330 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1331 0 : m_nMapInfoType, m_nMapInfoType);
1332 0 : return -1;
1333 : }
1334 :
1335 : /*-----------------------------------------------------------------
1336 : * Read object information
1337 : * NOTE: This symbol type does not contain a reference to a
1338 : * SymbolDef block in the file, but we still use the m_sSymbolDef
1339 : * structure to store the information inside the class so that the
1340 : * ITABFeatureSymbol methods work properly for the class user.
1341 : *----------------------------------------------------------------*/
1342 : TABMAPObjFontPoint *poPointHdr =
1343 6 : cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1344 :
1345 6 : m_nSymbolDefIndex = -1;
1346 6 : m_sSymbolDef.nRefCount = 0;
1347 :
1348 6 : m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId; // shape
1349 6 : m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize; // point size
1350 :
1351 6 : m_nFontStyle = poPointHdr->m_nFontStyle; // font style
1352 :
1353 6 : m_sSymbolDef.rgbColor = poPointHdr->m_nR * 256 * 256 +
1354 6 : poPointHdr->m_nG * 256 + poPointHdr->m_nB;
1355 :
1356 : /*-------------------------------------------------------------
1357 : * Symbol Angle, in tenths of degree.
1358 : * Contrary to arc start/end angles, no conversion based on
1359 : * origin quadrant is required here.
1360 : *------------------------------------------------------------*/
1361 6 : m_dAngle = poPointHdr->m_nAngle / 10.0;
1362 :
1363 6 : m_nFontDefIndex = poPointHdr->m_nFontId; // Font name index
1364 :
1365 6 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1366 :
1367 : /*-----------------------------------------------------------------
1368 : * Create and fill geometry object
1369 : *----------------------------------------------------------------*/
1370 6 : double dX = 0.0;
1371 6 : double dY = 0.0;
1372 6 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1373 6 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1374 :
1375 6 : SetGeometryDirectly(poGeometry);
1376 :
1377 6 : SetMBR(dX, dY, dX, dY);
1378 6 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1379 : poObjHdr->m_nMaxY);
1380 :
1381 6 : return 0;
1382 : }
1383 :
1384 : /**********************************************************************
1385 : * TABFontPoint::WriteGeometryToMAPFile()
1386 : *
1387 : * Write the geometry and representation (color, etc...) part of the
1388 : * feature to the .MAP object pointed to by poMAPFile.
1389 : *
1390 : * It is assumed that poMAPFile currently points to a valid map object.
1391 : *
1392 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1393 : * been called.
1394 : **********************************************************************/
1395 2 : int TABFontPoint::WriteGeometryToMAPFile(
1396 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1397 : GBool bCoordBlockDataOnly /*=FALSE*/,
1398 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1399 : {
1400 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1401 2 : if (bCoordBlockDataOnly)
1402 0 : return 0;
1403 :
1404 : /*-----------------------------------------------------------------
1405 : * We assume that ValidateMapInfoType() was called already and that
1406 : * the type in poObjHdr->m_nType is valid.
1407 : *----------------------------------------------------------------*/
1408 2 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1409 :
1410 : /*-----------------------------------------------------------------
1411 : * Fetch and validate geometry
1412 : *----------------------------------------------------------------*/
1413 2 : OGRGeometry *poGeom = GetGeometryRef();
1414 2 : OGRPoint *poPoint = nullptr;
1415 2 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1416 2 : poPoint = poGeom->toPoint();
1417 : else
1418 : {
1419 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1420 : "TABFontPoint: Missing or Invalid Geometry!");
1421 0 : return -1;
1422 : }
1423 :
1424 2 : GInt32 nX = 0;
1425 2 : GInt32 nY = 0;
1426 2 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1427 :
1428 : /*-----------------------------------------------------------------
1429 : * Copy object information
1430 : * NOTE: This symbol type does not contain a reference to a
1431 : * SymbolDef block in the file, but we still use the m_sSymbolDef
1432 : * structure to store the information inside the class so that the
1433 : * ITABFeatureSymbol methods work properly for the class user.
1434 : *----------------------------------------------------------------*/
1435 : TABMAPObjFontPoint *poPointHdr =
1436 2 : cpl::down_cast<TABMAPObjFontPoint *>(poObjHdr);
1437 :
1438 2 : poPointHdr->m_nX = nX;
1439 2 : poPointHdr->m_nY = nY;
1440 2 : poPointHdr->SetMBR(nX, nY, nX, nY);
1441 :
1442 2 : poPointHdr->m_nSymbolId =
1443 2 : static_cast<GByte>(m_sSymbolDef.nSymbolNo); // shape
1444 2 : poPointHdr->m_nPointSize =
1445 2 : static_cast<GByte>(m_sSymbolDef.nPointSize); // point size
1446 2 : poPointHdr->m_nFontStyle = m_nFontStyle; // font style
1447 :
1448 2 : poPointHdr->m_nR = static_cast<GByte>(COLOR_R(m_sSymbolDef.rgbColor));
1449 2 : poPointHdr->m_nG = static_cast<GByte>(COLOR_G(m_sSymbolDef.rgbColor));
1450 2 : poPointHdr->m_nB = static_cast<GByte>(COLOR_B(m_sSymbolDef.rgbColor));
1451 :
1452 : /*-------------------------------------------------------------
1453 : * Symbol Angle, in tenths of degree.
1454 : * Contrary to arc start/end angles, no conversion based on
1455 : * origin quadrant is required here.
1456 : *------------------------------------------------------------*/
1457 2 : poPointHdr->m_nAngle = static_cast<GInt16>(ROUND_INT(m_dAngle * 10.0));
1458 :
1459 : // Write Font Def
1460 2 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1461 2 : poPointHdr->m_nFontId =
1462 2 : static_cast<GByte>(m_nFontDefIndex); // Font name index
1463 :
1464 2 : if (CPLGetLastErrorType() == CE_Failure)
1465 0 : return -1;
1466 :
1467 2 : return 0;
1468 : }
1469 :
1470 : /**********************************************************************
1471 : * TABFontPoint::QueryFontStyle()
1472 : *
1473 : * Return TRUE if the specified font style attribute is turned ON,
1474 : * or FALSE otherwise. See enum TABFontStyle for the list of styles
1475 : * that can be queried on.
1476 : **********************************************************************/
1477 0 : GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery)
1478 : {
1479 0 : return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
1480 : }
1481 :
1482 0 : void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
1483 : {
1484 0 : if (bStyleOn)
1485 0 : m_nFontStyle |= static_cast<int>(eStyleToToggle);
1486 : else
1487 0 : m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
1488 0 : }
1489 :
1490 : /**********************************************************************
1491 : * TABFontPoint::GetFontStyleMIFValue()
1492 : *
1493 : * Return the Font Style value for this object using the style values
1494 : * that are used in a MIF FONT() clause. See MIF specs (appendix A).
1495 : *
1496 : * The reason why we have to differentiate between the TAB and the MIF font
1497 : * style values is that in TAB, TABFSBox is included in the style value
1498 : * as code 0x100, but in MIF it is not included, instead it is implied by
1499 : * the presence of the BG color in the FONT() clause (the BG color is
1500 : * present only when TABFSBox or TABFSHalo is set).
1501 : * This also has the effect of shifting all the other style values > 0x100
1502 : * by 1 byte.
1503 : *
1504 : * NOTE: Even if there is no BG color for font symbols, we inherit this
1505 : * problem because Font Point styles use the same codes as Text Font styles.
1506 : **********************************************************************/
1507 0 : int TABFontPoint::GetFontStyleMIFValue()
1508 : {
1509 : // The conversion is simply to remove bit 0x100 from the value and shift
1510 : // down all values past this bit.
1511 0 : return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
1512 : }
1513 :
1514 635 : void TABFontPoint::SetFontStyleMIFValue(int nStyle)
1515 : {
1516 635 : m_nFontStyle = static_cast<GByte>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
1517 635 : }
1518 :
1519 : /**********************************************************************
1520 : * TABFontPoint::SetSymbolAngle()
1521 : *
1522 : * Set the symbol angle value in degrees, making sure the value is
1523 : * always in the range [0..360]
1524 : **********************************************************************/
1525 635 : void TABFontPoint::SetSymbolAngle(double dAngle)
1526 : {
1527 635 : dAngle = fmod(dAngle, 360.0);
1528 635 : if (dAngle < 0.0)
1529 0 : dAngle += 360.0;
1530 :
1531 635 : m_dAngle = dAngle;
1532 635 : }
1533 :
1534 : /**********************************************************************
1535 : * TABFontPoint::GetSymbolStyleString()
1536 : *
1537 : * Return a Symbol() string. All representations info for the Symbol are here.
1538 : **********************************************************************/
1539 7 : const char *TABFontPoint::GetSymbolStyleString(double dfAngle) const
1540 : {
1541 : /* Get the SymbolStyleString, and add the outline Color
1542 : (halo/border in MapInfo Symbol terminology) */
1543 7 : const char *outlineColor = nullptr;
1544 7 : if (m_nFontStyle & 16)
1545 0 : outlineColor = ",o:#000000";
1546 7 : else if (m_nFontStyle & 512)
1547 0 : outlineColor = ",o:#ffffff";
1548 : else
1549 7 : outlineColor = "";
1550 :
1551 7 : int nAngle = static_cast<int>(dfAngle);
1552 : const char *pszStyle;
1553 :
1554 7 : pszStyle = CPLSPrintf(
1555 : "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"font-sym-%d,ogr-sym-9\"%s,f:\"%s\")",
1556 7 : nAngle, m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize,
1557 7 : m_sSymbolDef.nSymbolNo, outlineColor, GetFontNameRef());
1558 7 : return pszStyle;
1559 : }
1560 :
1561 : /**********************************************************************
1562 : * TABFontPoint::GetStyleString() const
1563 : *
1564 : * Return style string for this feature.
1565 : *
1566 : * Style String is built only once during the first call to GetStyleString().
1567 : **********************************************************************/
1568 9 : const char *TABFontPoint::GetStyleString() const
1569 : {
1570 9 : if (m_pszStyleString == nullptr)
1571 : {
1572 7 : m_pszStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle()));
1573 : }
1574 :
1575 9 : return m_pszStyleString;
1576 : }
1577 :
1578 : /**********************************************************************
1579 : * TABFontPoint::SetSymbolFromStyle()
1580 : *
1581 : * Set all Symbol var from a OGRStyleSymbol.
1582 : **********************************************************************/
1583 2 : void TABFontPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1584 : {
1585 2 : ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1586 :
1587 2 : GBool bIsNull = 0;
1588 :
1589 : // Try to set font glyph number
1590 2 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1591 2 : if ((!bIsNull) && pszSymbolId && STARTS_WITH(pszSymbolId, "font-sym-"))
1592 : {
1593 2 : const int nSymbolId = atoi(pszSymbolId + 9);
1594 2 : SetSymbolNo(static_cast<GInt16>(nSymbolId));
1595 : }
1596 :
1597 2 : const char *pszFontName = poSymbolStyle->FontName(bIsNull);
1598 2 : if ((!bIsNull) && pszFontName)
1599 : {
1600 2 : SetFontName(pszFontName);
1601 : }
1602 2 : }
1603 :
1604 : /*=====================================================================
1605 : * class TABCustomPoint
1606 : *====================================================================*/
1607 :
1608 : /**********************************************************************
1609 : * TABCustomPoint::TABCustomPoint()
1610 : *
1611 : * Constructor.
1612 : **********************************************************************/
1613 686 : TABCustomPoint::TABCustomPoint(OGRFeatureDefn *poDefnIn)
1614 686 : : TABPoint(poDefnIn), m_nCustomStyle(0), m_nUnknown_(0)
1615 : {
1616 686 : }
1617 :
1618 : /**********************************************************************
1619 : * TABCustomPoint::~TABCustomPoint()
1620 : *
1621 : * Destructor.
1622 : **********************************************************************/
1623 1372 : TABCustomPoint::~TABCustomPoint()
1624 : {
1625 1372 : }
1626 :
1627 : /**********************************************************************
1628 : * TABCustomPoint::CloneTABFeature()
1629 : *
1630 : * Duplicate feature, including stuff specific to each TABFeature type.
1631 : *
1632 : * This method calls the generic TABFeature::CloneTABFeature() and
1633 : * then copies any members specific to its own type.
1634 : **********************************************************************/
1635 0 : TABFeature *TABCustomPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1636 : {
1637 : /*-----------------------------------------------------------------
1638 : * Alloc new feature and copy the base stuff
1639 : *----------------------------------------------------------------*/
1640 : TABCustomPoint *poNew =
1641 0 : new TABCustomPoint(poNewDefn ? poNewDefn : GetDefnRef());
1642 :
1643 0 : CopyTABFeatureBase(poNew);
1644 :
1645 : /*-----------------------------------------------------------------
1646 : * And members specific to this class
1647 : *----------------------------------------------------------------*/
1648 : // ITABFeatureSymbol
1649 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
1650 :
1651 : // ITABFeatureFont
1652 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
1653 :
1654 0 : poNew->SetCustomSymbolStyle(GetCustomSymbolStyle());
1655 :
1656 0 : return poNew;
1657 : }
1658 :
1659 : /**********************************************************************
1660 : * TABCustomPoint::ReadGeometryFromMAPFile()
1661 : *
1662 : * Fill the geometry and representation (color, etc...) part of the
1663 : * feature from the contents of the .MAP object pointed to by poMAPFile.
1664 : *
1665 : * It is assumed that poMAPFile currently points to the beginning of
1666 : * a map object.
1667 : *
1668 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1669 : * been called.
1670 : **********************************************************************/
1671 6 : int TABCustomPoint::ReadGeometryFromMAPFile(
1672 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1673 : GBool bCoordBlockDataOnly /*=FALSE*/,
1674 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1675 : {
1676 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1677 6 : if (bCoordBlockDataOnly)
1678 0 : return 0;
1679 :
1680 : /*-----------------------------------------------------------------
1681 : * Fetch and validate geometry type
1682 : *----------------------------------------------------------------*/
1683 6 : m_nMapInfoType = poObjHdr->m_nType;
1684 :
1685 6 : if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL &&
1686 0 : m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C)
1687 : {
1688 0 : CPLError(
1689 : CE_Failure, CPLE_AssertionFailed,
1690 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
1691 0 : m_nMapInfoType, m_nMapInfoType);
1692 0 : return -1;
1693 : }
1694 :
1695 : /*-----------------------------------------------------------------
1696 : * Read object information
1697 : *----------------------------------------------------------------*/
1698 : TABMAPObjCustomPoint *poPointHdr =
1699 6 : cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1700 :
1701 6 : m_nUnknown_ = poPointHdr->m_nUnknown_; // ???
1702 6 : m_nCustomStyle = poPointHdr->m_nCustomStyle; // 0x01=Show BG,
1703 : // 0x02=Apply Color
1704 :
1705 6 : m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index
1706 6 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
1707 :
1708 6 : m_nFontDefIndex = poPointHdr->m_nFontId; // Font index
1709 6 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
1710 :
1711 : /*-----------------------------------------------------------------
1712 : * Create and fill geometry object
1713 : *----------------------------------------------------------------*/
1714 6 : double dX = 0.0;
1715 6 : double dY = 0.0;
1716 6 : poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY);
1717 6 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1718 :
1719 6 : SetGeometryDirectly(poGeometry);
1720 :
1721 6 : SetMBR(dX, dY, dX, dY);
1722 6 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
1723 : poObjHdr->m_nMaxY);
1724 :
1725 6 : return 0;
1726 : }
1727 :
1728 : /**********************************************************************
1729 : * TABCustomPoint::WriteGeometryToMAPFile()
1730 : *
1731 : * Write the geometry and representation (color, etc...) part of the
1732 : * feature to the .MAP object pointed to by poMAPFile.
1733 : *
1734 : * It is assumed that poMAPFile currently points to a valid map object.
1735 : *
1736 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1737 : * been called.
1738 : **********************************************************************/
1739 2 : int TABCustomPoint::WriteGeometryToMAPFile(
1740 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
1741 : GBool bCoordBlockDataOnly /*=FALSE*/,
1742 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
1743 : {
1744 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
1745 2 : if (bCoordBlockDataOnly)
1746 0 : return 0;
1747 :
1748 : /*-----------------------------------------------------------------
1749 : * We assume that ValidateMapInfoType() was called already and that
1750 : * the type in poObjHdr->m_nType is valid.
1751 : *----------------------------------------------------------------*/
1752 2 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
1753 :
1754 : /*-----------------------------------------------------------------
1755 : * Fetch and validate geometry
1756 : *----------------------------------------------------------------*/
1757 2 : OGRGeometry *poGeom = GetGeometryRef();
1758 2 : OGRPoint *poPoint = nullptr;
1759 2 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1760 2 : poPoint = poGeom->toPoint();
1761 : else
1762 : {
1763 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1764 : "TABCustomPoint: Missing or Invalid Geometry!");
1765 0 : return -1;
1766 : }
1767 :
1768 2 : GInt32 nX = 0;
1769 2 : GInt32 nY = 0;
1770 2 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
1771 :
1772 : /*-----------------------------------------------------------------
1773 : * Copy object information
1774 : *----------------------------------------------------------------*/
1775 : TABMAPObjCustomPoint *poPointHdr =
1776 2 : cpl::down_cast<TABMAPObjCustomPoint *>(poObjHdr);
1777 :
1778 2 : poPointHdr->m_nX = nX;
1779 2 : poPointHdr->m_nY = nY;
1780 2 : poPointHdr->SetMBR(nX, nY, nX, nY);
1781 2 : poPointHdr->m_nUnknown_ = m_nUnknown_;
1782 2 : poPointHdr->m_nCustomStyle = m_nCustomStyle; // 0x01=Show BG,
1783 : // 0x02=Apply Color
1784 :
1785 2 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
1786 2 : poPointHdr->m_nSymbolId =
1787 2 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
1788 :
1789 2 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
1790 2 : poPointHdr->m_nFontId = static_cast<GByte>(m_nFontDefIndex); // Font index
1791 :
1792 2 : if (CPLGetLastErrorType() == CE_Failure)
1793 0 : return -1;
1794 :
1795 2 : return 0;
1796 : }
1797 :
1798 : /**********************************************************************
1799 : * TABCustomPoint::GetSymbolStyleString()
1800 : *
1801 : * Return a Symbol() string. All representations info for the Symbol are here.
1802 : **********************************************************************/
1803 7 : const char *TABCustomPoint::GetSymbolStyleString(double dfAngle) const
1804 : {
1805 : /* Get the SymbolStyleString, and add the color if m_nCustomStyle contains
1806 : * "apply color". */
1807 7 : const char *color = nullptr;
1808 7 : if (m_nCustomStyle & 0x02)
1809 7 : color = CPLSPrintf(",c:#%6.6x", m_sSymbolDef.rgbColor);
1810 : else
1811 0 : color = "";
1812 :
1813 7 : int nAngle = static_cast<int>(dfAngle);
1814 : const char *pszStyle;
1815 7 : const char *pszExt = CPLGetExtension(GetSymbolNameRef());
1816 7 : char szLowerExt[8] = "";
1817 7 : const char *pszPtr = pszExt;
1818 : int i;
1819 :
1820 7 : for (i = 0; i < 7 && *pszPtr != '\0' && *pszPtr != ' '; i++, pszPtr++)
1821 : {
1822 0 : szLowerExt[i] =
1823 0 : static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszPtr)));
1824 : }
1825 7 : szLowerExt[i] = '\0';
1826 :
1827 7 : pszStyle = CPLSPrintf(
1828 : "SYMBOL(a:%d%s,s:%dpt,id:\"mapinfo-custom-sym-%d-%s,%s-%s,ogr-sym-9\")",
1829 7 : nAngle, color, m_sSymbolDef.nPointSize, m_nCustomStyle,
1830 : GetSymbolNameRef(), szLowerExt, GetSymbolNameRef());
1831 7 : return pszStyle;
1832 : }
1833 :
1834 : /**********************************************************************
1835 : * TABCustomPoint::SetSymbolFromStyle()
1836 : *
1837 : * Set all Symbol var from a OGRStyleSymbol.
1838 : **********************************************************************/
1839 2 : void TABCustomPoint::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
1840 : {
1841 2 : ITABFeatureSymbol::SetSymbolFromStyle(poSymbolStyle);
1842 :
1843 2 : GBool bIsNull = 0;
1844 :
1845 : // Try to set font glyph number
1846 2 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
1847 2 : if ((!bIsNull) && pszSymbolId &&
1848 2 : STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
1849 : {
1850 2 : const int nSymbolStyle = atoi(pszSymbolId + 19);
1851 2 : SetCustomSymbolStyle(static_cast<GByte>(nSymbolStyle));
1852 :
1853 2 : const char *pszPtr = pszSymbolId + 19;
1854 4 : while (*pszPtr != '-')
1855 : {
1856 2 : pszPtr++;
1857 : }
1858 2 : pszPtr++;
1859 :
1860 2 : char szSymbolName[256] = "";
1861 : int i;
1862 2 : for (i = 0;
1863 8 : i < 255 && *pszPtr != '\0' && *pszPtr != ',' && *pszPtr != '"';
1864 : i++, pszPtr++)
1865 : {
1866 6 : szSymbolName[i] = *pszPtr;
1867 : }
1868 2 : szSymbolName[i] = '\0';
1869 2 : SetSymbolName(szSymbolName);
1870 : }
1871 2 : }
1872 :
1873 : /**********************************************************************
1874 : * TABCustomPoint::GetStyleString() const
1875 : *
1876 : * Return style string for this feature.
1877 : *
1878 : * Style String is built only once during the first call to GetStyleString().
1879 : **********************************************************************/
1880 9 : const char *TABCustomPoint::GetStyleString() const
1881 : {
1882 9 : if (m_pszStyleString == nullptr)
1883 : {
1884 7 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
1885 : }
1886 :
1887 9 : return m_pszStyleString;
1888 : }
1889 :
1890 : /*=====================================================================
1891 : * class TABPolyline
1892 : *====================================================================*/
1893 :
1894 : /**********************************************************************
1895 : * TABPolyline::TABPolyline()
1896 : *
1897 : * Constructor.
1898 : **********************************************************************/
1899 4057 : TABPolyline::TABPolyline(OGRFeatureDefn *poDefnIn)
1900 : : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
1901 4057 : m_dCenterY(0.0), m_bWriteTwoPointLineAsPolyline(FALSE), m_bSmooth(FALSE)
1902 : {
1903 4057 : }
1904 :
1905 : /**********************************************************************
1906 : * TABPolyline::~TABPolyline()
1907 : *
1908 : * Destructor.
1909 : **********************************************************************/
1910 8114 : TABPolyline::~TABPolyline()
1911 : {
1912 8114 : }
1913 :
1914 : /**********************************************************************
1915 : * TABPolyline::CloneTABFeature()
1916 : *
1917 : * Duplicate feature, including stuff specific to each TABFeature type.
1918 : *
1919 : * This method calls the generic TABFeature::CloneTABFeature() and
1920 : * then copies any members specific to its own type.
1921 : **********************************************************************/
1922 0 : TABFeature *TABPolyline::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
1923 : {
1924 : /*-----------------------------------------------------------------
1925 : * Alloc new feature and copy the base stuff
1926 : *----------------------------------------------------------------*/
1927 0 : TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef());
1928 :
1929 0 : CopyTABFeatureBase(poNew);
1930 :
1931 : /*-----------------------------------------------------------------
1932 : * And members specific to this class
1933 : *----------------------------------------------------------------*/
1934 : // ITABFeaturePen
1935 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
1936 :
1937 0 : poNew->m_bSmooth = m_bSmooth;
1938 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
1939 0 : poNew->m_dCenterX = m_dCenterX;
1940 0 : poNew->m_dCenterY = m_dCenterY;
1941 :
1942 0 : return poNew;
1943 : }
1944 :
1945 : /**********************************************************************
1946 : * TABPolyline::GetNumParts()
1947 : *
1948 : * Return the total number of parts in this object.
1949 : *
1950 : * Returns 0 if the geometry contained in the object is invalid or missing.
1951 : **********************************************************************/
1952 0 : int TABPolyline::GetNumParts()
1953 : {
1954 0 : int numParts = 0;
1955 :
1956 0 : OGRGeometry *poGeom = GetGeometryRef();
1957 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1958 : {
1959 : /*-------------------------------------------------------------
1960 : * Simple polyline
1961 : *------------------------------------------------------------*/
1962 0 : numParts = 1;
1963 : }
1964 0 : else if (poGeom &&
1965 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1966 : {
1967 : /*-------------------------------------------------------------
1968 : * Multiple polyline
1969 : *------------------------------------------------------------*/
1970 0 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
1971 0 : numParts = poMultiLine->getNumGeometries();
1972 : }
1973 :
1974 0 : return numParts;
1975 : }
1976 :
1977 : /**********************************************************************
1978 : * TABPolyline::GetPartRef()
1979 : *
1980 : * Returns a reference to the specified OGRLineString number, hiding the
1981 : * complexity of dealing with OGRMultiLineString vs OGRLineString cases.
1982 : *
1983 : * Returns NULL if the geometry contained in the object is invalid or
1984 : * missing or if the specified part index is invalid.
1985 : **********************************************************************/
1986 0 : OGRLineString *TABPolyline::GetPartRef(int nPartIndex)
1987 : {
1988 0 : OGRGeometry *poGeom = GetGeometryRef();
1989 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
1990 : nPartIndex == 0)
1991 : {
1992 : /*-------------------------------------------------------------
1993 : * Simple polyline
1994 : *------------------------------------------------------------*/
1995 0 : return poGeom->toLineString();
1996 : }
1997 0 : else if (poGeom &&
1998 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1999 : {
2000 : /*-------------------------------------------------------------
2001 : * Multiple polyline
2002 : *------------------------------------------------------------*/
2003 0 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2004 0 : if (nPartIndex >= 0 && nPartIndex < poMultiLine->getNumGeometries())
2005 : {
2006 0 : return poMultiLine->getGeometryRef(nPartIndex);
2007 : }
2008 : else
2009 0 : return nullptr;
2010 : }
2011 :
2012 0 : return nullptr;
2013 : }
2014 :
2015 : /**********************************************************************
2016 : * TABPolyline::ValidateMapInfoType()
2017 : *
2018 : * Check the feature's geometry part and return the corresponding
2019 : * mapinfo object type code. The m_nMapInfoType member will also
2020 : * be updated for further calls to GetMapInfoType();
2021 : *
2022 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
2023 : * is expected for this object class.
2024 : **********************************************************************/
2025 238 : TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
2026 : {
2027 : /*-----------------------------------------------------------------
2028 : * Fetch and validate geometry
2029 : *----------------------------------------------------------------*/
2030 238 : OGRGeometry *poGeom = GetGeometryRef();
2031 238 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2032 : {
2033 : /*-------------------------------------------------------------
2034 : * Simple polyline
2035 : *------------------------------------------------------------*/
2036 230 : OGRLineString *poLine = poGeom->toLineString();
2037 230 : if (TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()))
2038 : {
2039 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2040 : }
2041 230 : else if (poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES)
2042 : {
2043 0 : m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2044 : }
2045 230 : else if (poLine->getNumPoints() > 2)
2046 : {
2047 207 : m_nMapInfoType = TAB_GEOM_PLINE;
2048 : }
2049 46 : else if ((poLine->getNumPoints() == 2) &&
2050 23 : (m_bWriteTwoPointLineAsPolyline == TRUE))
2051 : {
2052 0 : m_nMapInfoType = TAB_GEOM_PLINE;
2053 : }
2054 46 : else if ((poLine->getNumPoints() == 2) &&
2055 23 : (m_bWriteTwoPointLineAsPolyline == FALSE))
2056 : {
2057 23 : m_nMapInfoType = TAB_GEOM_LINE;
2058 : }
2059 : else
2060 : {
2061 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2062 : "TABPolyline: Geometry must contain at least 2 points.");
2063 0 : m_nMapInfoType = TAB_GEOM_NONE;
2064 : }
2065 : }
2066 16 : else if (poGeom &&
2067 8 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2068 : {
2069 : /*-------------------------------------------------------------
2070 : * Multiple polyline... validate all components
2071 : *------------------------------------------------------------*/
2072 8 : GInt32 numPointsTotal = 0;
2073 8 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2074 8 : int numLines = poMultiLine->getNumGeometries();
2075 :
2076 8 : m_nMapInfoType = TAB_GEOM_MULTIPLINE;
2077 :
2078 18 : for (int iLine = 0; iLine < numLines; iLine++)
2079 : {
2080 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2081 20 : if (poGeom == nullptr ||
2082 10 : wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
2083 : {
2084 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2085 : "TABPolyline: Object contains an invalid Geometry!");
2086 0 : m_nMapInfoType = TAB_GEOM_NONE;
2087 0 : numPointsTotal = 0;
2088 0 : break;
2089 : }
2090 10 : OGRLineString *poLine = poGeom->toLineString();
2091 10 : numPointsTotal += poLine->getNumPoints();
2092 : }
2093 :
2094 8 : if (TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal))
2095 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE;
2096 8 : else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
2097 0 : m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE;
2098 : }
2099 : else
2100 : {
2101 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2102 : "TABPolyline: Missing or Invalid Geometry!");
2103 0 : m_nMapInfoType = TAB_GEOM_NONE;
2104 : }
2105 :
2106 : /*-----------------------------------------------------------------
2107 : * Decide if coordinates should be compressed or not.
2108 : *
2109 : * __TODO__ We never write type LINE (2 points line) as compressed
2110 : * for the moment. If we ever do it, then the decision to write
2111 : * a 2 point line in compressed coordinates or not should take into
2112 : * account the location of the object block MBR, so this would be
2113 : * better handled directly by TABMAPObjLine::WriteObject() since the
2114 : * object block center is not known until it is written to disk.
2115 : *----------------------------------------------------------------*/
2116 238 : if (m_nMapInfoType != TAB_GEOM_LINE)
2117 : {
2118 215 : ValidateCoordType(poMapFile);
2119 : }
2120 : else
2121 : {
2122 23 : UpdateMBR(poMapFile);
2123 : }
2124 :
2125 238 : return m_nMapInfoType;
2126 : }
2127 :
2128 : /**********************************************************************
2129 : * TABPolyline::ReadGeometryFromMAPFile()
2130 : *
2131 : * Fill the geometry and representation (color, etc...) part of the
2132 : * feature from the contents of the .MAP object pointed to by poMAPFile.
2133 : *
2134 : * It is assumed that poMAPFile currently points to the beginning of
2135 : * a map object.
2136 : *
2137 : * Returns 0 on success, -1 on error, in which case CPLError() will have
2138 : * been called.
2139 : **********************************************************************/
2140 1387 : int TABPolyline::ReadGeometryFromMAPFile(
2141 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2142 : GBool bCoordBlockDataOnly /*=FALSE*/,
2143 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2144 : {
2145 1387 : GInt32 nX = 0;
2146 1387 : GInt32 nY = 0;
2147 1387 : double dX = 0.0;
2148 1387 : double dY = 0.0;
2149 1387 : double dXMin = 0.0;
2150 1387 : double dYMin = 0.0;
2151 1387 : double dXMax = 0.0;
2152 1387 : double dYMax = 0.0;
2153 1387 : OGRGeometry *poGeometry = nullptr;
2154 1387 : GBool bComprCoord = poObjHdr->IsCompressedType();
2155 1387 : TABMAPCoordBlock *poCoordBlock = nullptr;
2156 :
2157 : /*-----------------------------------------------------------------
2158 : * Fetch and validate geometry type
2159 : *----------------------------------------------------------------*/
2160 1387 : m_nMapInfoType = poObjHdr->m_nType;
2161 :
2162 1387 : if (m_nMapInfoType == TAB_GEOM_LINE || m_nMapInfoType == TAB_GEOM_LINE_C)
2163 : {
2164 : /*=============================================================
2165 : * LINE (2 vertices)
2166 : *============================================================*/
2167 21 : TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2168 :
2169 21 : m_bSmooth = FALSE;
2170 :
2171 21 : auto poLine = new OGRLineString();
2172 21 : poGeometry = poLine;
2173 21 : poLine->setNumPoints(2);
2174 :
2175 21 : poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, dXMin,
2176 : dYMin);
2177 21 : poLine->setPoint(0, dXMin, dYMin);
2178 :
2179 21 : poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, dXMax,
2180 : dYMax);
2181 21 : poLine->setPoint(1, dXMax, dYMax);
2182 :
2183 21 : if (!bCoordBlockDataOnly)
2184 : {
2185 21 : m_nPenDefIndex = poLineHdr->m_nPenId; // Pen index
2186 21 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2187 21 : }
2188 : }
2189 1366 : else if (m_nMapInfoType == TAB_GEOM_PLINE ||
2190 1361 : m_nMapInfoType == TAB_GEOM_PLINE_C)
2191 : {
2192 : /*=============================================================
2193 : * PLINE ( > 2 vertices)
2194 : *============================================================*/
2195 :
2196 : /*-------------------------------------------------------------
2197 : * Copy data from poObjHdr
2198 : *------------------------------------------------------------*/
2199 1356 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2200 :
2201 1356 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2202 1356 : const GUInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize;
2203 1356 : if (nCoordDataSize > 1024 * 1024 &&
2204 0 : nCoordDataSize > poMapFile->GetFileSize())
2205 : {
2206 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too big nCoordDataSize = %u",
2207 : nCoordDataSize);
2208 0 : return -1;
2209 : }
2210 : // numLineSections = poPLineHdr->m_numLineSections; // Always 1
2211 1356 : m_bSmooth = poPLineHdr->m_bSmooth;
2212 :
2213 : // Centroid/label point
2214 1356 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2215 : dX, dY);
2216 1356 : SetCenter(dX, dY);
2217 :
2218 : // Compressed coordinate origin (useful only in compressed case!)
2219 1356 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2220 1356 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2221 :
2222 : // MBR
2223 1356 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2224 : dYMin);
2225 1356 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2226 : dYMax);
2227 :
2228 1356 : if (!bCoordBlockDataOnly)
2229 : {
2230 1230 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
2231 1230 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2232 : }
2233 :
2234 : /*-------------------------------------------------------------
2235 : * Create Geometry and read coordinates
2236 : *------------------------------------------------------------*/
2237 1356 : const int numPoints = nCoordDataSize / (bComprCoord ? 4 : 8);
2238 :
2239 1356 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2240 126 : poCoordBlock = *ppoCoordBlock;
2241 : else
2242 1230 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2243 1356 : if (poCoordBlock == nullptr)
2244 : {
2245 0 : CPLError(CE_Failure, CPLE_FileIO,
2246 : "Can't access coordinate block at offset %d",
2247 : nCoordBlockPtr);
2248 0 : return -1;
2249 : }
2250 :
2251 1356 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2252 :
2253 1356 : auto poLine = new OGRLineString();
2254 1356 : poGeometry = poLine;
2255 1356 : poLine->setNumPoints(numPoints);
2256 :
2257 1356 : int nStatus = 0;
2258 11126 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2259 : {
2260 9770 : nStatus = poCoordBlock->ReadIntCoord(bComprCoord, nX, nY);
2261 9770 : if (nStatus != 0)
2262 0 : break;
2263 9770 : poMapFile->Int2Coordsys(nX, nY, dX, dY);
2264 9770 : poLine->setPoint(i, dX, dY);
2265 : }
2266 :
2267 1356 : if (nStatus != 0)
2268 : {
2269 : // Failed ... error message has already been produced
2270 0 : delete poGeometry;
2271 0 : return nStatus;
2272 1356 : }
2273 : }
2274 10 : else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2275 10 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2276 4 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2277 4 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2278 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2279 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C)
2280 : {
2281 : /*=============================================================
2282 : * PLINE MULTIPLE
2283 : *============================================================*/
2284 10 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2285 :
2286 : /*-------------------------------------------------------------
2287 : * Copy data from poObjHdr
2288 : *------------------------------------------------------------*/
2289 10 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2290 :
2291 10 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
2292 : /* GInt32 nCoordDataSize = poPLineHdr->m_nCoordDataSize; */
2293 10 : GInt32 numLineSections = poPLineHdr->m_numLineSections;
2294 10 : m_bSmooth = poPLineHdr->m_bSmooth;
2295 :
2296 : // Centroid/label point
2297 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
2298 : dX, dY);
2299 10 : SetCenter(dX, dY);
2300 :
2301 : // Compressed coordinate origin (useful only in compressed case!)
2302 10 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
2303 10 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
2304 :
2305 : // MBR
2306 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
2307 : dYMin);
2308 10 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
2309 : dYMax);
2310 :
2311 10 : if (!bCoordBlockDataOnly)
2312 : {
2313 10 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
2314 10 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
2315 : }
2316 :
2317 10 : const int nMinSizeOfSection = 24;
2318 10 : if (numLineSections > INT_MAX / nMinSizeOfSection)
2319 : {
2320 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2321 0 : return -1;
2322 : }
2323 10 : const GUInt32 nMinimumBytesForSections =
2324 10 : nMinSizeOfSection * numLineSections;
2325 10 : if (nMinimumBytesForSections > 1024 * 1024 &&
2326 0 : nMinimumBytesForSections > poMapFile->GetFileSize())
2327 : {
2328 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
2329 0 : return -1;
2330 : }
2331 :
2332 : /*-------------------------------------------------------------
2333 : * Read data from the coord. block
2334 : *------------------------------------------------------------*/
2335 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2336 10 : VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
2337 10 : if (pasSecHdrs == nullptr)
2338 0 : return -1;
2339 :
2340 10 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2341 4 : poCoordBlock = *ppoCoordBlock;
2342 : else
2343 6 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
2344 :
2345 10 : GInt32 numPointsTotal = 0;
2346 20 : if (poCoordBlock == nullptr ||
2347 10 : poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
2348 : numLineSections, pasSecHdrs,
2349 : numPointsTotal) != 0)
2350 : {
2351 0 : CPLError(CE_Failure, CPLE_FileIO,
2352 : "Failed reading coordinate data at offset %d",
2353 : nCoordBlockPtr);
2354 0 : CPLFree(pasSecHdrs);
2355 0 : return -1;
2356 : }
2357 :
2358 10 : const GUInt32 nMinimumBytesForPoints =
2359 10 : (bComprCoord ? 4 : 8) * numPointsTotal;
2360 10 : if (nMinimumBytesForPoints > 1024 * 1024 &&
2361 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
2362 : {
2363 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
2364 0 : CPLFree(pasSecHdrs);
2365 0 : return -1;
2366 : }
2367 :
2368 10 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2369 :
2370 : GInt32 *panXY = static_cast<GInt32 *>(
2371 10 : VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
2372 10 : if (panXY == nullptr)
2373 : {
2374 0 : CPLFree(pasSecHdrs);
2375 0 : return -1;
2376 : }
2377 :
2378 10 : if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
2379 : 0)
2380 : {
2381 0 : CPLError(CE_Failure, CPLE_FileIO,
2382 : "Failed reading coordinate data at offset %d",
2383 : nCoordBlockPtr);
2384 0 : CPLFree(pasSecHdrs);
2385 0 : CPLFree(panXY);
2386 0 : return -1;
2387 : }
2388 :
2389 : /*-------------------------------------------------------------
2390 : * Create a Geometry collection with one line geometry for
2391 : * each coordinates section
2392 : * If object contains only one section, then return a simple LineString
2393 : *------------------------------------------------------------*/
2394 10 : OGRMultiLineString *poMultiLine = nullptr;
2395 10 : if (numLineSections > 1)
2396 : {
2397 6 : poMultiLine = new OGRMultiLineString();
2398 6 : poGeometry = poMultiLine;
2399 : }
2400 :
2401 26 : for (int iSection = 0; iSection < numLineSections; iSection++)
2402 : {
2403 16 : const int numSectionVertices = pasSecHdrs[iSection].numVertices;
2404 16 : GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
2405 :
2406 16 : auto poLine = new OGRLineString();
2407 16 : poLine->setNumPoints(numSectionVertices);
2408 :
2409 48 : for (int i = 0; i < numSectionVertices; i++)
2410 : {
2411 32 : poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
2412 32 : poLine->setPoint(i, dX, dY);
2413 32 : pnXYPtr += 2;
2414 : }
2415 :
2416 16 : if (poGeometry == nullptr)
2417 4 : poGeometry = poLine;
2418 12 : else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
2419 : {
2420 0 : CPLAssert(false); // Just in case lower-level lib is modified
2421 : }
2422 : }
2423 :
2424 10 : CPLFree(pasSecHdrs);
2425 10 : CPLFree(panXY);
2426 : }
2427 : else
2428 : {
2429 0 : CPLError(
2430 : CE_Failure, CPLE_AssertionFailed,
2431 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
2432 0 : m_nMapInfoType, m_nMapInfoType);
2433 0 : return -1;
2434 : }
2435 :
2436 1387 : SetGeometryDirectly(poGeometry);
2437 :
2438 1387 : SetMBR(dXMin, dYMin, dXMax, dYMax);
2439 1387 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
2440 : poObjHdr->m_nMaxY);
2441 :
2442 : /* Return a ref to coord block so that caller can continue reading
2443 : * after the end of this object (used by TABCollection and index splitting)
2444 : */
2445 1387 : if (ppoCoordBlock)
2446 130 : *ppoCoordBlock = poCoordBlock;
2447 :
2448 1387 : return 0;
2449 : }
2450 :
2451 : /**********************************************************************
2452 : * TABPolyline::WriteGeometryToMAPFile()
2453 : *
2454 : * Write the geometry and representation (color, etc...) part of the
2455 : * feature to the .MAP object pointed to by poMAPFile.
2456 : *
2457 : * It is assumed that poMAPFile currently points to a valid map object.
2458 : *
2459 : * Returns 0 on success, -1 on error, in which case CPLError() will have
2460 : * been called.
2461 : **********************************************************************/
2462 364 : int TABPolyline::WriteGeometryToMAPFile(
2463 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
2464 : GBool bCoordBlockDataOnly /*=FALSE*/,
2465 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
2466 : {
2467 364 : GInt32 nX = 0;
2468 364 : GInt32 nY = 0;
2469 364 : OGRLineString *poLine = nullptr;
2470 364 : TABMAPCoordBlock *poCoordBlock = nullptr;
2471 :
2472 : /*-----------------------------------------------------------------
2473 : * We assume that ValidateMapInfoType() was called already and that
2474 : * the type in poObjHdr->m_nType is valid.
2475 : *----------------------------------------------------------------*/
2476 364 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
2477 364 : CPLErrorReset();
2478 :
2479 : /*-----------------------------------------------------------------
2480 : * Fetch and validate geometry
2481 : *----------------------------------------------------------------*/
2482 364 : OGRGeometry *poGeom = GetGeometryRef();
2483 :
2484 1069 : if ((m_nMapInfoType == TAB_GEOM_LINE ||
2485 364 : m_nMapInfoType == TAB_GEOM_LINE_C) &&
2486 751 : poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
2487 23 : (poLine = poGeom->toLineString())->getNumPoints() == 2)
2488 : {
2489 : /*=============================================================
2490 : * LINE (2 vertices)
2491 : *============================================================*/
2492 23 : TABMAPObjLine *poLineHdr = cpl::down_cast<TABMAPObjLine *>(poObjHdr);
2493 :
2494 23 : poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0),
2495 23 : poLineHdr->m_nX1, poLineHdr->m_nY1);
2496 23 : poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1),
2497 23 : poLineHdr->m_nX2, poLineHdr->m_nY2);
2498 23 : poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, poLineHdr->m_nX2,
2499 : poLineHdr->m_nY2);
2500 :
2501 23 : if (!bCoordBlockDataOnly)
2502 : {
2503 23 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2504 23 : poLineHdr->m_nPenId =
2505 23 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2506 : }
2507 : }
2508 1020 : else if ((m_nMapInfoType == TAB_GEOM_PLINE ||
2509 341 : m_nMapInfoType == TAB_GEOM_PLINE_C) &&
2510 682 : poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2511 : {
2512 : /*=============================================================
2513 : * PLINE ( > 2 vertices and less than 32767 vertices)
2514 : *============================================================*/
2515 333 : GBool bCompressed = poObjHdr->IsCompressedType();
2516 :
2517 : /*-------------------------------------------------------------
2518 : * Process geometry first...
2519 : *------------------------------------------------------------*/
2520 333 : poLine = poGeom->toLineString();
2521 333 : const int numPoints = poLine->getNumPoints();
2522 333 : CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES);
2523 :
2524 333 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2525 126 : poCoordBlock = *ppoCoordBlock;
2526 : else
2527 207 : poCoordBlock = poMapFile->GetCurCoordBlock();
2528 333 : poCoordBlock->StartNewFeature();
2529 333 : const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2530 333 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2531 :
2532 333 : int nStatus = 0;
2533 5039 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2534 : {
2535 4706 : poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY);
2536 4706 : if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
2537 : 0)
2538 : {
2539 : // Failed ... error message has already been produced
2540 0 : return nStatus;
2541 : }
2542 : }
2543 :
2544 333 : const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2545 :
2546 : /*-------------------------------------------------------------
2547 : * Copy info to poObjHdr
2548 : *------------------------------------------------------------*/
2549 333 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2550 :
2551 333 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2552 333 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2553 333 : poPLineHdr->m_numLineSections = 1;
2554 :
2555 333 : poPLineHdr->m_bSmooth = m_bSmooth;
2556 :
2557 : // MBR
2558 333 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2559 :
2560 : // Polyline center/label point
2561 333 : double dX = 0.0;
2562 333 : double dY = 0.0;
2563 333 : if (GetCenter(dX, dY) != -1)
2564 : {
2565 333 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2566 333 : poPLineHdr->m_nLabelY);
2567 : }
2568 : else
2569 : {
2570 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
2571 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
2572 : }
2573 :
2574 : // Compressed coordinate origin (useful only in compressed case!)
2575 333 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2576 333 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2577 :
2578 333 : if (!bCoordBlockDataOnly)
2579 : {
2580 207 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2581 207 : poPLineHdr->m_nPenId =
2582 207 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2583 : }
2584 : }
2585 24 : else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2586 8 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2587 0 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2588 0 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2589 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2590 8 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) &&
2591 16 : poGeom &&
2592 8 : (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
2593 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
2594 : {
2595 : /*=============================================================
2596 : * PLINE MULTIPLE (or single PLINE with more than 32767 vertices)
2597 : *============================================================*/
2598 :
2599 8 : CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE ||
2600 : m_nMapInfoType == TAB_GEOM_MULTIPLINE_C ||
2601 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE ||
2602 : m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C ||
2603 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE ||
2604 : m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C);
2605 :
2606 8 : int nStatus = 0;
2607 8 : OGREnvelope sEnvelope;
2608 8 : GBool bCompressed = poObjHdr->IsCompressedType();
2609 :
2610 : /*-------------------------------------------------------------
2611 : * Process geometry first...
2612 : *------------------------------------------------------------*/
2613 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
2614 0 : poCoordBlock = *ppoCoordBlock;
2615 : else
2616 8 : poCoordBlock = poMapFile->GetCurCoordBlock();
2617 8 : poCoordBlock->StartNewFeature();
2618 8 : const GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
2619 8 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
2620 :
2621 8 : OGRMultiLineString *poMultiLine = nullptr;
2622 8 : GInt32 numLines = 1;
2623 8 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2624 : {
2625 8 : poMultiLine = poGeom->toMultiLineString();
2626 8 : numLines = poMultiLine->getNumGeometries();
2627 : }
2628 : // else
2629 : // {
2630 : // poMultiLine = NULL;
2631 : // numLines = 1;
2632 : // }
2633 :
2634 : /*-------------------------------------------------------------
2635 : * Build and write array of coord sections headers
2636 : *------------------------------------------------------------*/
2637 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
2638 8 : VSI_CALLOC_VERBOSE(numLines, sizeof(TABMAPCoordSecHdr)));
2639 8 : if (pasSecHdrs == nullptr)
2640 : {
2641 0 : return -1;
2642 : }
2643 :
2644 : /*-------------------------------------------------------------
2645 : * In calculation of nDataOffset, we have to take into account that
2646 : * V450 header section uses int32 instead of int16 for numVertices
2647 : * and we add another 2 bytes to align with a 4 bytes boundary.
2648 : *------------------------------------------------------------*/
2649 8 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
2650 :
2651 8 : const int nTotalHdrSizeUncompressed =
2652 8 : (nVersion >= 450 ? 28 : 24) * numLines;
2653 :
2654 8 : GInt32 numPointsTotal = 0;
2655 18 : for (int iLine = 0; iLine < numLines; iLine++)
2656 : {
2657 10 : if (poMultiLine)
2658 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2659 :
2660 20 : if (poGeom &&
2661 10 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2662 : {
2663 10 : poLine = poGeom->toLineString();
2664 10 : const GInt32 numPoints = poLine->getNumPoints();
2665 10 : poLine->getEnvelope(&sEnvelope);
2666 :
2667 10 : pasSecHdrs[iLine].numVertices = poLine->getNumPoints();
2668 10 : pasSecHdrs[iLine].numHoles = 0; // It is a line!
2669 :
2670 10 : poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
2671 10 : pasSecHdrs[iLine].nXMin,
2672 10 : pasSecHdrs[iLine].nYMin);
2673 10 : poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
2674 10 : pasSecHdrs[iLine].nXMax,
2675 10 : pasSecHdrs[iLine].nYMax);
2676 10 : pasSecHdrs[iLine].nDataOffset =
2677 10 : nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
2678 10 : pasSecHdrs[iLine].nVertexOffset = numPointsTotal;
2679 :
2680 10 : numPointsTotal += numPoints;
2681 : }
2682 : else
2683 : {
2684 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2685 : "TABPolyline: Object contains an invalid Geometry!");
2686 0 : nStatus = -1;
2687 : }
2688 : }
2689 :
2690 8 : if (nStatus == 0)
2691 8 : nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines,
2692 : pasSecHdrs, bCompressed);
2693 :
2694 8 : CPLFree(pasSecHdrs);
2695 8 : pasSecHdrs = nullptr;
2696 :
2697 8 : if (nStatus != 0)
2698 0 : return nStatus; // Error has already been reported.
2699 :
2700 : /*-------------------------------------------------------------
2701 : * Then write the coordinates themselves...
2702 : *------------------------------------------------------------*/
2703 18 : for (int iLine = 0; nStatus == 0 && iLine < numLines; iLine++)
2704 : {
2705 10 : if (poMultiLine)
2706 10 : poGeom = poMultiLine->getGeometryRef(iLine);
2707 :
2708 20 : if (poGeom &&
2709 10 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2710 : {
2711 10 : poLine = poGeom->toLineString();
2712 10 : GInt32 numPoints = poLine->getNumPoints();
2713 :
2714 30 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
2715 : {
2716 20 : poMapFile->Coordsys2Int(poLine->getX(i), poLine->getY(i),
2717 : nX, nY);
2718 20 : if ((nStatus = poCoordBlock->WriteIntCoord(
2719 20 : nX, nY, bCompressed)) != 0)
2720 : {
2721 : // Failed ... error message has already been produced
2722 0 : return nStatus;
2723 : }
2724 : }
2725 : }
2726 : else
2727 : {
2728 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2729 : "TABPolyline: Object contains an invalid Geometry!");
2730 0 : return -1;
2731 : }
2732 : }
2733 :
2734 8 : const GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
2735 :
2736 : /*-------------------------------------------------------------
2737 : * ... and finally copy info to poObjHdr
2738 : *------------------------------------------------------------*/
2739 8 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
2740 :
2741 8 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
2742 8 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
2743 8 : poPLineHdr->m_numLineSections = numLines;
2744 :
2745 8 : poPLineHdr->m_bSmooth = m_bSmooth;
2746 :
2747 : // MBR
2748 8 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
2749 :
2750 : // Polyline center/label point
2751 8 : double dX = 0.0;
2752 8 : double dY = 0.0;
2753 8 : if (GetCenter(dX, dY) != -1)
2754 : {
2755 8 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
2756 8 : poPLineHdr->m_nLabelY);
2757 : }
2758 : else
2759 : {
2760 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
2761 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
2762 : }
2763 :
2764 : // Compressed coordinate origin (useful only in compressed case!)
2765 8 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
2766 8 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
2767 :
2768 8 : if (!bCoordBlockDataOnly)
2769 : {
2770 8 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
2771 8 : poPLineHdr->m_nPenId =
2772 8 : static_cast<GByte>(m_nPenDefIndex); // Pen index
2773 : }
2774 : }
2775 : else
2776 : {
2777 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2778 : "TABPolyline: Object contains an invalid Geometry!");
2779 0 : return -1;
2780 : }
2781 :
2782 364 : if (CPLGetLastErrorType() == CE_Failure)
2783 0 : return -1;
2784 :
2785 : /* Return a ref to coord block so that caller can continue writing
2786 : * after the end of this object (used by index splitting)
2787 : */
2788 364 : if (ppoCoordBlock)
2789 126 : *ppoCoordBlock = poCoordBlock;
2790 :
2791 364 : return 0;
2792 : }
2793 :
2794 : /**********************************************************************
2795 : * TABPolyline::GetStyleString() const
2796 : *
2797 : * Return style string for this feature.
2798 : *
2799 : * Style String is built only once during the first call to GetStyleString().
2800 : **********************************************************************/
2801 44 : const char *TABPolyline::GetStyleString() const
2802 : {
2803 44 : if (m_pszStyleString == nullptr)
2804 : {
2805 35 : m_pszStyleString = CPLStrdup(GetPenStyleString());
2806 : }
2807 :
2808 44 : return m_pszStyleString;
2809 : }
2810 :
2811 : /**********************************************************************
2812 : * TABPolyline::DumpMIF()
2813 : *
2814 : * Dump feature geometry in a format similar to .MIF PLINEs.
2815 : **********************************************************************/
2816 0 : void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/)
2817 : {
2818 0 : OGRMultiLineString *poMultiLine = nullptr;
2819 0 : OGRLineString *poLine = nullptr;
2820 : int i, numPoints;
2821 :
2822 0 : if (fpOut == nullptr)
2823 0 : fpOut = stdout;
2824 :
2825 : /*-----------------------------------------------------------------
2826 : * Fetch and validate geometry
2827 : *----------------------------------------------------------------*/
2828 0 : OGRGeometry *poGeom = GetGeometryRef();
2829 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2830 : {
2831 : /*-------------------------------------------------------------
2832 : * Generate output for simple polyline
2833 : *------------------------------------------------------------*/
2834 0 : poLine = poGeom->toLineString();
2835 0 : numPoints = poLine->getNumPoints();
2836 0 : fprintf(fpOut, "PLINE %d\n", numPoints);
2837 0 : for (i = 0; i < numPoints; i++)
2838 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
2839 : }
2840 0 : else if (poGeom &&
2841 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2842 : {
2843 : /*-------------------------------------------------------------
2844 : * Generate output for multiple polyline
2845 : *------------------------------------------------------------*/
2846 : int iLine, numLines;
2847 0 : poMultiLine = poGeom->toMultiLineString();
2848 0 : numLines = poMultiLine->getNumGeometries();
2849 0 : fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines);
2850 0 : for (iLine = 0; iLine < numLines; iLine++)
2851 : {
2852 0 : poGeom = poMultiLine->getGeometryRef(iLine);
2853 0 : if (poGeom &&
2854 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2855 : {
2856 0 : poLine = poGeom->toLineString();
2857 0 : numPoints = poLine->getNumPoints();
2858 0 : fprintf(fpOut, " %d\n", numPoints);
2859 0 : for (i = 0; i < numPoints; i++)
2860 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i),
2861 : poLine->getY(i));
2862 : }
2863 : else
2864 : {
2865 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2866 : "TABPolyline: Object contains an invalid Geometry!");
2867 0 : return;
2868 : }
2869 : }
2870 : }
2871 : else
2872 : {
2873 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2874 : "TABPolyline: Missing or Invalid Geometry!");
2875 0 : return;
2876 : }
2877 :
2878 0 : if (m_bCenterIsSet)
2879 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
2880 :
2881 : // Finish with PEN/BRUSH/etc. clauses
2882 0 : DumpPenDef();
2883 :
2884 0 : fflush(fpOut);
2885 : }
2886 :
2887 : /**********************************************************************
2888 : * TABPolyline::GetCenter()
2889 : *
2890 : * Returns the center point of the line. Compute one if it was not
2891 : * explicitly set:
2892 : *
2893 : * In MapInfo, for a simple or multiple polyline (pline), the center point
2894 : * in the object definition is supposed to be either the center point of
2895 : * the pline or the first section of a multiple pline (if an odd number of
2896 : * points in the pline or first section), or the midway point between the
2897 : * two central points (if an even number of points involved).
2898 : *
2899 : * Returns 0 on success, -1 on error.
2900 : **********************************************************************/
2901 341 : int TABPolyline::GetCenter(double &dX, double &dY)
2902 : {
2903 341 : if (!m_bCenterIsSet)
2904 : {
2905 215 : OGRLineString *poLine = nullptr;
2906 :
2907 215 : OGRGeometry *poGeom = GetGeometryRef();
2908 215 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
2909 : {
2910 207 : poLine = poGeom->toLineString();
2911 : }
2912 16 : else if (poGeom &&
2913 8 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
2914 : {
2915 8 : OGRMultiLineString *poMultiLine = poGeom->toMultiLineString();
2916 8 : if (poMultiLine->getNumGeometries() > 0)
2917 8 : poLine = poMultiLine->getGeometryRef(0);
2918 : }
2919 :
2920 215 : if (poLine && poLine->getNumPoints() > 0)
2921 : {
2922 215 : int i = poLine->getNumPoints() / 2;
2923 215 : if (poLine->getNumPoints() % 2 == 0)
2924 : {
2925 : // Return the midway between the 2 center points
2926 15 : m_dCenterX = (poLine->getX(i - 1) + poLine->getX(i)) / 2.0;
2927 15 : m_dCenterY = (poLine->getY(i - 1) + poLine->getY(i)) / 2.0;
2928 : }
2929 : else
2930 : {
2931 : // Return the center point
2932 200 : m_dCenterX = poLine->getX(i);
2933 200 : m_dCenterY = poLine->getY(i);
2934 : }
2935 215 : m_bCenterIsSet = TRUE;
2936 : }
2937 : }
2938 :
2939 341 : if (!m_bCenterIsSet)
2940 0 : return -1;
2941 :
2942 341 : dX = m_dCenterX;
2943 341 : dY = m_dCenterY;
2944 341 : return 0;
2945 : }
2946 :
2947 : /**********************************************************************
2948 : * TABPolyline::SetCenter()
2949 : *
2950 : * Set the X,Y coordinates to use as center point for the line.
2951 : **********************************************************************/
2952 1366 : void TABPolyline::SetCenter(double dX, double dY)
2953 : {
2954 1366 : m_dCenterX = dX;
2955 1366 : m_dCenterY = dY;
2956 1366 : m_bCenterIsSet = TRUE;
2957 1366 : }
2958 :
2959 : /**********************************************************************
2960 : * TABPolyline::TwoPointLineAsPolyline()
2961 : *
2962 : * Returns the value of m_bWriteTwoPointLineAsPolyline
2963 : **********************************************************************/
2964 0 : GBool TABPolyline::TwoPointLineAsPolyline()
2965 : {
2966 0 : return m_bWriteTwoPointLineAsPolyline;
2967 : }
2968 :
2969 : /**********************************************************************
2970 : * TABPolyline::TwoPointLineAsPolyline()
2971 : *
2972 : * Sets the value of m_bWriteTwoPointLineAsPolyline
2973 : **********************************************************************/
2974 0 : void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline)
2975 : {
2976 0 : m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline;
2977 0 : }
2978 :
2979 : /*=====================================================================
2980 : * class TABRegion
2981 : *====================================================================*/
2982 :
2983 : /**********************************************************************
2984 : * TABRegion::TABRegion()
2985 : *
2986 : * Constructor.
2987 : **********************************************************************/
2988 1154 : TABRegion::TABRegion(OGRFeatureDefn *poDefnIn)
2989 : : TABFeature(poDefnIn), m_bSmooth(FALSE), m_bCenterIsSet(FALSE),
2990 1154 : m_dCenterX(0.0), m_dCenterY(0.0)
2991 : {
2992 1154 : }
2993 :
2994 : /**********************************************************************
2995 : * TABRegion::~TABRegion()
2996 : *
2997 : * Destructor.
2998 : **********************************************************************/
2999 2308 : TABRegion::~TABRegion()
3000 : {
3001 2308 : }
3002 :
3003 : /**********************************************************************
3004 : * TABRegion::CloneTABFeature()
3005 : *
3006 : * Duplicate feature, including stuff specific to each TABFeature type.
3007 : *
3008 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
3009 : * then copies any members specific to its own type.
3010 : **********************************************************************/
3011 0 : TABFeature *TABRegion::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
3012 : {
3013 : /*-----------------------------------------------------------------
3014 : * Alloc new feature and copy the base stuff
3015 : *----------------------------------------------------------------*/
3016 0 : TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef());
3017 :
3018 0 : CopyTABFeatureBase(poNew);
3019 :
3020 : /*-----------------------------------------------------------------
3021 : * And members specific to this class
3022 : *----------------------------------------------------------------*/
3023 : // ITABFeaturePen
3024 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
3025 :
3026 : // ITABFeatureBrush
3027 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
3028 :
3029 0 : poNew->m_bSmooth = m_bSmooth;
3030 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
3031 0 : poNew->m_dCenterX = m_dCenterX;
3032 0 : poNew->m_dCenterY = m_dCenterY;
3033 :
3034 0 : return poNew;
3035 : }
3036 :
3037 : /**********************************************************************
3038 : * TABRegion::ValidateMapInfoType()
3039 : *
3040 : * Check the feature's geometry part and return the corresponding
3041 : * mapinfo object type code. The m_nMapInfoType member will also
3042 : * be updated for further calls to GetMapInfoType();
3043 : *
3044 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
3045 : * is expected for this object class.
3046 : **********************************************************************/
3047 88 : TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
3048 : {
3049 : /*-----------------------------------------------------------------
3050 : * Fetch and validate geometry
3051 : *----------------------------------------------------------------*/
3052 88 : OGRGeometry *poGeom = GetGeometryRef();
3053 94 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3054 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3055 : {
3056 88 : GInt32 numPointsTotal = 0;
3057 88 : GInt32 numRings = GetNumRings();
3058 176 : for (int i = 0; i < numRings; i++)
3059 : {
3060 88 : OGRLinearRing *poRing = GetRingRef(i);
3061 88 : if (poRing)
3062 88 : numPointsTotal += poRing->getNumPoints();
3063 : }
3064 88 : if (TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal))
3065 0 : m_nMapInfoType = TAB_GEOM_V800_REGION;
3066 88 : else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES)
3067 0 : m_nMapInfoType = TAB_GEOM_V450_REGION;
3068 : else
3069 88 : m_nMapInfoType = TAB_GEOM_REGION;
3070 : }
3071 : else
3072 : {
3073 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3074 : "TABRegion: Missing or Invalid Geometry!");
3075 0 : m_nMapInfoType = TAB_GEOM_NONE;
3076 : }
3077 :
3078 : /*-----------------------------------------------------------------
3079 : * Decide if coordinates should be compressed or not.
3080 : *----------------------------------------------------------------*/
3081 88 : ValidateCoordType(poMapFile);
3082 :
3083 88 : return m_nMapInfoType;
3084 : }
3085 :
3086 : /**********************************************************************
3087 : * TABRegion::ReadGeometryFromMAPFile()
3088 : *
3089 : * Fill the geometry and representation (color, etc...) part of the
3090 : * feature from the contents of the .MAP object pointed to by poMAPFile.
3091 : *
3092 : * It is assumed that poMAPFile currently points to the beginning of
3093 : * a map object.
3094 : *
3095 : * Returns 0 on success, -1 on error, in which case CPLError() will have
3096 : * been called.
3097 : **********************************************************************/
3098 503 : int TABRegion::ReadGeometryFromMAPFile(
3099 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3100 : GBool bCoordBlockDataOnly /*=FALSE*/,
3101 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3102 : {
3103 503 : double dXMin = 0.0;
3104 503 : double dYMin = 0.0;
3105 503 : double dXMax = 0.0;
3106 503 : double dYMax = 0.0;
3107 503 : OGRGeometry *poGeometry = nullptr;
3108 503 : TABMAPCoordBlock *poCoordBlock = nullptr;
3109 :
3110 : /*-----------------------------------------------------------------
3111 : * Fetch and validate geometry type
3112 : *----------------------------------------------------------------*/
3113 503 : m_nMapInfoType = poObjHdr->m_nType;
3114 :
3115 503 : if (m_nMapInfoType == TAB_GEOM_REGION ||
3116 448 : m_nMapInfoType == TAB_GEOM_REGION_C ||
3117 4 : m_nMapInfoType == TAB_GEOM_V450_REGION ||
3118 4 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3119 0 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3120 0 : m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3121 : {
3122 : /*=============================================================
3123 : * REGION (Similar to PLINE MULTIPLE)
3124 : *============================================================*/
3125 : GInt32 /* nCoordDataSize, */ numPointsTotal;
3126 503 : OGRMultiPolygon *poMultiPolygon = nullptr;
3127 503 : OGRPolygon *poPolygon = nullptr;
3128 503 : GBool bComprCoord = poObjHdr->IsCompressedType();
3129 503 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3130 :
3131 : /*-------------------------------------------------------------
3132 : * Copy data from poObjHdr
3133 : *------------------------------------------------------------*/
3134 503 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3135 :
3136 503 : GInt32 nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr;
3137 : /* nCoordDataSize = poPLineHdr->m_nCoordDataSize; */
3138 503 : GInt32 numLineSections = poPLineHdr->m_numLineSections;
3139 503 : m_bSmooth = poPLineHdr->m_bSmooth;
3140 :
3141 : // Centroid/label point
3142 503 : double dX = 0.0;
3143 503 : double dY = 0.0;
3144 503 : poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY,
3145 : dX, dY);
3146 503 : SetCenter(dX, dY);
3147 :
3148 : // Compressed coordinate origin (useful only in compressed case!)
3149 503 : m_nComprOrgX = poPLineHdr->m_nComprOrgX;
3150 503 : m_nComprOrgY = poPLineHdr->m_nComprOrgY;
3151 :
3152 : // MBR
3153 503 : poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, dXMin,
3154 : dYMin);
3155 503 : poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, dXMax,
3156 : dYMax);
3157 :
3158 503 : if (!bCoordBlockDataOnly)
3159 : {
3160 503 : m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index
3161 503 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
3162 503 : m_nBrushDefIndex = poPLineHdr->m_nBrushId; // Brush index
3163 503 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
3164 : }
3165 :
3166 : /*-------------------------------------------------------------
3167 : * Read data from the coord. block
3168 : *------------------------------------------------------------*/
3169 :
3170 503 : const int nMinSizeOfSection = 24;
3171 503 : if (numLineSections > INT_MAX / nMinSizeOfSection)
3172 : {
3173 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3174 0 : return -1;
3175 : }
3176 503 : const GUInt32 nMinimumBytesForSections =
3177 503 : nMinSizeOfSection * numLineSections;
3178 503 : if (nMinimumBytesForSections > 1024 * 1024 &&
3179 0 : nMinimumBytesForSections > poMapFile->GetFileSize())
3180 : {
3181 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numLineSections");
3182 0 : return -1;
3183 : }
3184 :
3185 : TABMAPCoordSecHdr *pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3186 503 : VSI_MALLOC2_VERBOSE(numLineSections, sizeof(TABMAPCoordSecHdr)));
3187 503 : if (pasSecHdrs == nullptr)
3188 0 : return -1;
3189 :
3190 503 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3191 4 : poCoordBlock = *ppoCoordBlock;
3192 : else
3193 499 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
3194 :
3195 503 : if (poCoordBlock)
3196 503 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3197 :
3198 1006 : if (poCoordBlock == nullptr ||
3199 503 : poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion,
3200 : numLineSections, pasSecHdrs,
3201 : numPointsTotal) != 0)
3202 : {
3203 0 : CPLError(CE_Failure, CPLE_FileIO,
3204 : "Failed reading coordinate data at offset %d",
3205 : nCoordBlockPtr);
3206 0 : CPLFree(pasSecHdrs);
3207 0 : return -1;
3208 : }
3209 :
3210 503 : const GUInt32 nMinimumBytesForPoints =
3211 503 : (bComprCoord ? 4 : 8) * numPointsTotal;
3212 503 : if (nMinimumBytesForPoints > 1024 * 1024 &&
3213 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
3214 : {
3215 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many numPointsTotal");
3216 0 : CPLFree(pasSecHdrs);
3217 0 : return -1;
3218 : }
3219 :
3220 : GInt32 *panXY = static_cast<GInt32 *>(
3221 503 : VSI_MALLOC2_VERBOSE(numPointsTotal, 2 * sizeof(GInt32)));
3222 503 : if (panXY == nullptr)
3223 : {
3224 0 : CPLFree(pasSecHdrs);
3225 0 : return -1;
3226 : }
3227 :
3228 503 : if (poCoordBlock->ReadIntCoords(bComprCoord, numPointsTotal, panXY) !=
3229 : 0)
3230 : {
3231 0 : CPLError(CE_Failure, CPLE_FileIO,
3232 : "Failed reading coordinate data at offset %d",
3233 : nCoordBlockPtr);
3234 0 : CPLFree(pasSecHdrs);
3235 0 : CPLFree(panXY);
3236 0 : return -1;
3237 : }
3238 :
3239 : /*-------------------------------------------------------------
3240 : * Decide if we should return an OGRPolygon or an OGRMultiPolygon
3241 : * depending on the number of outer rings found in CoordSecHdr blocks.
3242 : * The CoodSecHdr block for each outer ring in the region has a flag
3243 : * indicating the number of inner rings that follow.
3244 : * In older versions of the format, the count of inner rings was
3245 : * always zero, so in this case we would always return MultiPolygons.
3246 : *
3247 : * Note: The current implementation assumes that there cannot be
3248 : * holes inside holes (i.e. multiple levels of inner rings)... if
3249 : * that case was encountered then we would return an OGRMultiPolygon
3250 : * in which the topological relationship between the rings would
3251 : * be lost.
3252 : *------------------------------------------------------------*/
3253 503 : int numOuterRings = 0;
3254 1006 : for (int iSection = 0; iSection < numLineSections; iSection++)
3255 : {
3256 : // Count this as an outer ring.
3257 503 : numOuterRings++;
3258 : // Skip inner rings... so loop continues on an outer ring.
3259 503 : iSection += pasSecHdrs[iSection].numHoles;
3260 : }
3261 :
3262 503 : if (numOuterRings > 1)
3263 : {
3264 0 : poMultiPolygon = new OGRMultiPolygon;
3265 0 : poGeometry = poMultiPolygon;
3266 : }
3267 : else
3268 : {
3269 503 : poGeometry = nullptr; // Will be set later
3270 : }
3271 :
3272 : /*-------------------------------------------------------------
3273 : * OK, build the OGRGeometry object.
3274 : *------------------------------------------------------------*/
3275 503 : int numHolesToRead = 0;
3276 503 : poPolygon = nullptr;
3277 1006 : for (int iSection = 0; iSection < numLineSections; iSection++)
3278 : {
3279 :
3280 503 : if (poPolygon == nullptr)
3281 503 : poPolygon = new OGRPolygon();
3282 :
3283 503 : if (numHolesToRead < 1)
3284 503 : numHolesToRead = pasSecHdrs[iSection].numHoles;
3285 : else
3286 0 : numHolesToRead--;
3287 :
3288 503 : int numSectionVertices = pasSecHdrs[iSection].numVertices;
3289 503 : GInt32 *pnXYPtr = panXY + (pasSecHdrs[iSection].nVertexOffset * 2);
3290 :
3291 503 : OGRLinearRing *poRing = new OGRLinearRing();
3292 503 : poRing->setNumPoints(numSectionVertices);
3293 :
3294 13753 : for (int i = 0; i < numSectionVertices; i++)
3295 : {
3296 13250 : poMapFile->Int2Coordsys(*pnXYPtr, *(pnXYPtr + 1), dX, dY);
3297 13250 : poRing->setPoint(i, dX, dY);
3298 13250 : pnXYPtr += 2;
3299 : }
3300 :
3301 503 : poPolygon->addRingDirectly(poRing);
3302 503 : poRing = nullptr;
3303 :
3304 503 : if (numHolesToRead < 1)
3305 : {
3306 503 : if (numOuterRings > 1)
3307 : {
3308 0 : poMultiPolygon->addGeometryDirectly(poPolygon);
3309 : }
3310 : else
3311 : {
3312 503 : poGeometry = poPolygon;
3313 503 : CPLAssert(iSection == numLineSections - 1);
3314 : }
3315 :
3316 503 : poPolygon = nullptr; // We'll alloc a new polygon next loop.
3317 : }
3318 : }
3319 503 : delete poPolygon; // should only trigger on corrupted files
3320 :
3321 503 : CPLFree(pasSecHdrs);
3322 503 : CPLFree(panXY);
3323 : }
3324 : else
3325 : {
3326 0 : CPLError(
3327 : CE_Failure, CPLE_AssertionFailed,
3328 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
3329 0 : m_nMapInfoType, m_nMapInfoType);
3330 0 : return -1;
3331 : }
3332 :
3333 503 : SetGeometryDirectly(poGeometry);
3334 :
3335 503 : SetMBR(dXMin, dYMin, dXMax, dYMax);
3336 503 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
3337 : poObjHdr->m_nMaxY);
3338 :
3339 : /* Return a ref to coord block so that caller can continue reading
3340 : * after the end of this object (used by TABCollection and index splitting)
3341 : */
3342 503 : if (ppoCoordBlock)
3343 4 : *ppoCoordBlock = poCoordBlock;
3344 :
3345 503 : return 0;
3346 : }
3347 :
3348 : /**********************************************************************
3349 : * TABRegion::WriteGeometryToMAPFile()
3350 : *
3351 : * Write the geometry and representation (color, etc...) part of the
3352 : * feature to the .MAP object pointed to by poMAPFile.
3353 : *
3354 : * It is assumed that poMAPFile currently points to a valid map object.
3355 : *
3356 : * Returns 0 on success, -1 on error, in which case CPLError() will have
3357 : * been called.
3358 : **********************************************************************/
3359 88 : int TABRegion::WriteGeometryToMAPFile(
3360 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
3361 : GBool bCoordBlockDataOnly /*=FALSE*/,
3362 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
3363 : {
3364 : /*-----------------------------------------------------------------
3365 : * We assume that ValidateMapInfoType() was called already and that
3366 : * the type in poObjHdr->m_nType is valid.
3367 : *----------------------------------------------------------------*/
3368 88 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
3369 :
3370 : /*-----------------------------------------------------------------
3371 : * Fetch and validate geometry
3372 : *----------------------------------------------------------------*/
3373 88 : OGRGeometry *poGeom = GetGeometryRef();
3374 88 : TABMAPCoordBlock *poCoordBlock = nullptr;
3375 :
3376 258 : if ((m_nMapInfoType == TAB_GEOM_REGION ||
3377 82 : m_nMapInfoType == TAB_GEOM_REGION_C ||
3378 0 : m_nMapInfoType == TAB_GEOM_V450_REGION ||
3379 0 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3380 0 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3381 88 : m_nMapInfoType == TAB_GEOM_V800_REGION_C) &&
3382 176 : poGeom &&
3383 88 : (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3384 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3385 : {
3386 : /*=============================================================
3387 : * REGIONs are similar to PLINE MULTIPLE
3388 : *
3389 : * We accept both OGRPolygons (with one or multiple rings) and
3390 : * OGRMultiPolygons as input.
3391 : *============================================================*/
3392 88 : GBool bCompressed = poObjHdr->IsCompressedType();
3393 :
3394 : /*-------------------------------------------------------------
3395 : * Process geometry first...
3396 : *------------------------------------------------------------*/
3397 88 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
3398 0 : poCoordBlock = *ppoCoordBlock;
3399 : else
3400 88 : poCoordBlock = poMapFile->GetCurCoordBlock();
3401 88 : poCoordBlock->StartNewFeature();
3402 88 : GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
3403 88 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
3404 :
3405 : #ifdef TABDUMP
3406 : printf(/*ok*/
3407 : "TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n",
3408 : m_nComprOrgX, m_nComprOrgY);
3409 : #endif
3410 : /*-------------------------------------------------------------
3411 : * Fetch total number of rings and build array of coord
3412 : * sections headers.
3413 : *------------------------------------------------------------*/
3414 88 : TABMAPCoordSecHdr *pasSecHdrs = nullptr;
3415 88 : int numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile);
3416 88 : int nStatus = numRingsTotal == 0 ? -1 : 0;
3417 :
3418 : /*-------------------------------------------------------------
3419 : * Write the Coord. Section Header
3420 : *------------------------------------------------------------*/
3421 88 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
3422 :
3423 88 : if (nStatus == 0)
3424 88 : nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal,
3425 : pasSecHdrs, bCompressed);
3426 :
3427 88 : CPLFree(pasSecHdrs);
3428 88 : pasSecHdrs = nullptr;
3429 :
3430 88 : if (nStatus != 0)
3431 0 : return nStatus; // Error has already been reported.
3432 :
3433 : /*-------------------------------------------------------------
3434 : * Go through all the rings in our OGRMultiPolygon or OGRPolygon
3435 : * to write the coordinates themselves...
3436 : *------------------------------------------------------------*/
3437 :
3438 88 : GInt32 nX = 0, nY = 0;
3439 176 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3440 : {
3441 88 : OGRLinearRing *poRing = GetRingRef(iRing);
3442 88 : if (poRing == nullptr)
3443 : {
3444 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3445 : "TABRegion: Object Geometry contains NULL rings!");
3446 0 : return -1;
3447 : }
3448 :
3449 88 : int numPoints = poRing->getNumPoints();
3450 :
3451 2515 : for (int i = 0; nStatus == 0 && i < numPoints; i++)
3452 : {
3453 2427 : poMapFile->Coordsys2Int(poRing->getX(i), poRing->getY(i), nX,
3454 : nY);
3455 2427 : if ((nStatus =
3456 2427 : poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) != 0)
3457 : {
3458 : // Failed ... error message has already been produced
3459 0 : return nStatus;
3460 : }
3461 : }
3462 : } /* for iRing*/
3463 :
3464 88 : GUInt32 nCoordDataSize = poCoordBlock->GetFeatureDataSize();
3465 :
3466 : /*-------------------------------------------------------------
3467 : * ... and finally copy info to poObjHdr
3468 : *------------------------------------------------------------*/
3469 88 : TABMAPObjPLine *poPLineHdr = cpl::down_cast<TABMAPObjPLine *>(poObjHdr);
3470 :
3471 88 : poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr;
3472 88 : poPLineHdr->m_nCoordDataSize = nCoordDataSize;
3473 88 : poPLineHdr->m_numLineSections = numRingsTotal;
3474 :
3475 88 : poPLineHdr->m_bSmooth = m_bSmooth;
3476 :
3477 : // MBR
3478 88 : poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
3479 :
3480 : // Region center/label point
3481 88 : double dX = 0.0;
3482 88 : double dY = 0.0;
3483 88 : if (GetCenter(dX, dY) != -1)
3484 : {
3485 88 : poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX,
3486 88 : poPLineHdr->m_nLabelY);
3487 : }
3488 : else
3489 : {
3490 0 : poPLineHdr->m_nLabelX = m_nComprOrgX;
3491 0 : poPLineHdr->m_nLabelY = m_nComprOrgY;
3492 : }
3493 :
3494 : // Compressed coordinate origin (useful only in compressed case!)
3495 88 : poPLineHdr->m_nComprOrgX = m_nComprOrgX;
3496 88 : poPLineHdr->m_nComprOrgY = m_nComprOrgY;
3497 :
3498 88 : if (!bCoordBlockDataOnly)
3499 : {
3500 88 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
3501 88 : poPLineHdr->m_nPenId =
3502 88 : static_cast<GByte>(m_nPenDefIndex); // Pen index
3503 :
3504 88 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
3505 88 : poPLineHdr->m_nBrushId =
3506 88 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
3507 : }
3508 : }
3509 : else
3510 : {
3511 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3512 : "TABRegion: Object contains an invalid Geometry!");
3513 0 : return -1;
3514 : }
3515 :
3516 88 : if (CPLGetLastErrorType() == CE_Failure)
3517 0 : return -1;
3518 :
3519 : /* Return a ref to coord block so that caller can continue writing
3520 : * after the end of this object (used by index splitting)
3521 : */
3522 88 : if (ppoCoordBlock)
3523 0 : *ppoCoordBlock = poCoordBlock;
3524 :
3525 88 : return 0;
3526 : }
3527 :
3528 : /**********************************************************************
3529 : * TABRegion::GetNumRings()
3530 : *
3531 : * Return the total number of rings in this object making it look like
3532 : * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3533 : * of rings... hides the complexity of handling OGRMultiPolygons vs
3534 : * OGRPolygons, etc.
3535 : *
3536 : * Returns 0 if the geometry contained in the object is invalid or missing.
3537 : **********************************************************************/
3538 109 : int TABRegion::GetNumRings()
3539 : {
3540 109 : return ComputeNumRings(nullptr, nullptr);
3541 : }
3542 :
3543 197 : int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs,
3544 : TABMAPFile *poMapFile)
3545 : {
3546 197 : int numRingsTotal = 0;
3547 197 : int iLastSect = 0;
3548 :
3549 197 : if (ppasSecHdrs)
3550 88 : *ppasSecHdrs = nullptr;
3551 :
3552 197 : OGRGeometry *poGeom = GetGeometryRef();
3553 :
3554 209 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3555 12 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3556 : {
3557 : /*-------------------------------------------------------------
3558 : * Calculate total number of rings...
3559 : *------------------------------------------------------------*/
3560 197 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3561 : {
3562 24 : for (auto &&poPolygon : *(poGeom->toMultiPolygon()))
3563 : {
3564 12 : numRingsTotal += poPolygon->getNumInteriorRings() + 1;
3565 :
3566 12 : if (ppasSecHdrs && poMapFile)
3567 : {
3568 6 : if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3569 6 : iLastSect) != 0)
3570 0 : return 0; // An error happened, return count=0
3571 : }
3572 : } // for
3573 : }
3574 : else
3575 : {
3576 185 : OGRPolygon *poPolygon = poGeom->toPolygon();
3577 185 : numRingsTotal = poPolygon->getNumInteriorRings() + 1;
3578 :
3579 185 : if (ppasSecHdrs && poMapFile)
3580 : {
3581 82 : if (AppendSecHdrs(poPolygon, *ppasSecHdrs, poMapFile,
3582 82 : iLastSect) != 0)
3583 0 : return 0; // An error happened, return count=0
3584 : }
3585 : }
3586 : }
3587 :
3588 : /*-----------------------------------------------------------------
3589 : * If we're generating section header blocks, then init the
3590 : * coordinate offset values.
3591 : *
3592 : * In calculation of nDataOffset, we have to take into account that
3593 : * V450 header section uses int32 instead of int16 for numVertices
3594 : * and we add another 2 bytes to align with a 4 bytes boundary.
3595 : *------------------------------------------------------------*/
3596 197 : const int nTotalHdrSizeUncompressed =
3597 394 : (m_nMapInfoType == TAB_GEOM_V450_REGION ||
3598 197 : m_nMapInfoType == TAB_GEOM_V450_REGION_C ||
3599 197 : m_nMapInfoType == TAB_GEOM_V800_REGION ||
3600 197 : m_nMapInfoType == TAB_GEOM_V800_REGION_C)
3601 394 : ? 28 * numRingsTotal
3602 : : 24 * numRingsTotal;
3603 :
3604 197 : if (ppasSecHdrs)
3605 : {
3606 88 : int numPointsTotal = 0;
3607 88 : CPLAssert(iLastSect == numRingsTotal);
3608 176 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3609 : {
3610 88 : (*ppasSecHdrs)[iRing].nDataOffset =
3611 88 : nTotalHdrSizeUncompressed + numPointsTotal * 4 * 2;
3612 88 : (*ppasSecHdrs)[iRing].nVertexOffset = numPointsTotal;
3613 :
3614 88 : numPointsTotal += (*ppasSecHdrs)[iRing].numVertices;
3615 : }
3616 : }
3617 :
3618 197 : return numRingsTotal;
3619 : }
3620 :
3621 : /**********************************************************************
3622 : * TABRegion::AppendSecHdrs()
3623 : *
3624 : * (Private method)
3625 : *
3626 : * Add a TABMAPCoordSecHdr for each ring in the specified polygon.
3627 : **********************************************************************/
3628 88 : int TABRegion::AppendSecHdrs(OGRPolygon *poPolygon,
3629 : TABMAPCoordSecHdr *&pasSecHdrs,
3630 : TABMAPFile *poMapFile, int &iLastRing)
3631 : {
3632 : /*-------------------------------------------------------------
3633 : * Add a pasSecHdrs[] entry for each ring in this polygon.
3634 : * Note that the structs won't be fully initialized.
3635 : *------------------------------------------------------------*/
3636 88 : int numRingsInPolygon = poPolygon->getNumInteriorRings() + 1;
3637 :
3638 88 : pasSecHdrs = static_cast<TABMAPCoordSecHdr *>(
3639 88 : CPLRealloc(pasSecHdrs, (iLastRing + numRingsInPolygon) *
3640 : sizeof(TABMAPCoordSecHdr)));
3641 :
3642 176 : for (int iRing = 0; iRing < numRingsInPolygon; iRing++)
3643 : {
3644 88 : OGRLinearRing *poRing = nullptr;
3645 88 : OGREnvelope sEnvelope;
3646 :
3647 88 : if (iRing == 0)
3648 88 : poRing = poPolygon->getExteriorRing();
3649 : else
3650 0 : poRing = poPolygon->getInteriorRing(iRing - 1);
3651 :
3652 88 : if (poRing == nullptr)
3653 : {
3654 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3655 : "Assertion Failed: Encountered NULL ring in OGRPolygon");
3656 0 : return -1;
3657 : }
3658 :
3659 88 : poRing->getEnvelope(&sEnvelope);
3660 :
3661 88 : pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints();
3662 :
3663 88 : if (iRing == 0)
3664 88 : pasSecHdrs[iLastRing].numHoles = numRingsInPolygon - 1;
3665 : else
3666 0 : pasSecHdrs[iLastRing].numHoles = 0;
3667 :
3668 88 : poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY,
3669 88 : pasSecHdrs[iLastRing].nXMin,
3670 88 : pasSecHdrs[iLastRing].nYMin);
3671 88 : poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY,
3672 88 : pasSecHdrs[iLastRing].nXMax,
3673 88 : pasSecHdrs[iLastRing].nYMax);
3674 :
3675 88 : iLastRing++;
3676 : } /* for iRing*/
3677 :
3678 88 : return 0;
3679 : }
3680 :
3681 : /**********************************************************************
3682 : * TABRegion::GetRingRef()
3683 : *
3684 : * Returns a reference to the specified ring number making it look like
3685 : * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection
3686 : * of rings... hides the complexity of handling OGRMultiPolygons vs
3687 : * OGRPolygons, etc.
3688 : *
3689 : * Returns NULL if the geometry contained in the object is invalid or
3690 : * missing or if the specified ring index is invalid.
3691 : **********************************************************************/
3692 197 : OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex)
3693 : {
3694 197 : OGRLinearRing *poRing = nullptr;
3695 :
3696 197 : OGRGeometry *poGeom = GetGeometryRef();
3697 :
3698 209 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3699 12 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3700 : {
3701 : /*-------------------------------------------------------------
3702 : * Establish number of polygons based on geometry type
3703 : *------------------------------------------------------------*/
3704 197 : OGRMultiPolygon *poMultiPolygon = nullptr;
3705 197 : int iCurRing = 0;
3706 197 : int numOGRPolygons = 0;
3707 :
3708 197 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3709 : {
3710 12 : poMultiPolygon = poGeom->toMultiPolygon();
3711 12 : numOGRPolygons = poMultiPolygon->getNumGeometries();
3712 : }
3713 : else
3714 : {
3715 185 : numOGRPolygons = 1;
3716 : }
3717 :
3718 : /*-------------------------------------------------------------
3719 : * Loop through polygons until we find the requested ring.
3720 : *------------------------------------------------------------*/
3721 197 : iCurRing = 0;
3722 394 : for (int iPoly = 0; poRing == nullptr && iPoly < numOGRPolygons;
3723 : iPoly++)
3724 : {
3725 197 : OGRPolygon *poPolygon = nullptr;
3726 197 : if (poMultiPolygon)
3727 12 : poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3728 : else
3729 185 : poPolygon = poGeom->toPolygon();
3730 :
3731 197 : int numIntRings = poPolygon->getNumInteriorRings();
3732 :
3733 197 : if (iCurRing == nRequestedRingIndex)
3734 : {
3735 197 : poRing = poPolygon->getExteriorRing();
3736 : }
3737 0 : else if (nRequestedRingIndex > iCurRing &&
3738 0 : nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3739 : {
3740 0 : poRing = poPolygon->getInteriorRing(nRequestedRingIndex -
3741 0 : (iCurRing + 1));
3742 : }
3743 197 : iCurRing += numIntRings + 1;
3744 : }
3745 : }
3746 :
3747 197 : return poRing;
3748 : }
3749 :
3750 : /**********************************************************************
3751 : * TABRegion::RingIsHole()
3752 : *
3753 : * Return false if the requested ring index is the first of a polygon
3754 : **********************************************************************/
3755 0 : GBool TABRegion::IsInteriorRing(int nRequestedRingIndex)
3756 : {
3757 0 : OGRGeometry *poGeom = GetGeometryRef();
3758 :
3759 0 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3760 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3761 : {
3762 : /*-------------------------------------------------------------
3763 : * Establish number of polygons based on geometry type
3764 : *------------------------------------------------------------*/
3765 0 : OGRMultiPolygon *poMultiPolygon = nullptr;
3766 0 : int iCurRing = 0;
3767 0 : int numOGRPolygons = 0;
3768 :
3769 0 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3770 : {
3771 0 : poMultiPolygon = poGeom->toMultiPolygon();
3772 0 : numOGRPolygons = poMultiPolygon->getNumGeometries();
3773 : }
3774 : else
3775 : {
3776 0 : numOGRPolygons = 1;
3777 : }
3778 :
3779 : /*-------------------------------------------------------------
3780 : * Loop through polygons until we find the requested ring.
3781 : *------------------------------------------------------------*/
3782 0 : iCurRing = 0;
3783 0 : for (int iPoly = 0; iPoly < numOGRPolygons; iPoly++)
3784 : {
3785 0 : OGRPolygon *poPolygon = nullptr;
3786 0 : if (poMultiPolygon)
3787 0 : poPolygon = poMultiPolygon->getGeometryRef(iPoly);
3788 : else
3789 0 : poPolygon = poGeom->toPolygon();
3790 :
3791 0 : int numIntRings = poPolygon->getNumInteriorRings();
3792 :
3793 0 : if (iCurRing == nRequestedRingIndex)
3794 : {
3795 0 : return FALSE;
3796 : }
3797 0 : else if (nRequestedRingIndex > iCurRing &&
3798 0 : nRequestedRingIndex - (iCurRing + 1) < numIntRings)
3799 : {
3800 0 : return TRUE;
3801 : }
3802 0 : iCurRing += numIntRings + 1;
3803 : }
3804 : }
3805 :
3806 0 : return FALSE;
3807 : }
3808 :
3809 : /**********************************************************************
3810 : * TABRegion::GetStyleString() const
3811 : *
3812 : * Return style string for this feature.
3813 : *
3814 : * Style String is built only once during the first call to GetStyleString().
3815 : **********************************************************************/
3816 57 : const char *TABRegion::GetStyleString() const
3817 : {
3818 57 : if (m_pszStyleString == nullptr)
3819 : {
3820 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
3821 : // to use temporary buffers
3822 35 : char *pszPen = CPLStrdup(GetPenStyleString());
3823 35 : char *pszBrush = CPLStrdup(GetBrushStyleString());
3824 :
3825 35 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
3826 :
3827 35 : CPLFree(pszPen);
3828 35 : CPLFree(pszBrush);
3829 : }
3830 :
3831 57 : return m_pszStyleString;
3832 : }
3833 :
3834 : /**********************************************************************
3835 : * TABRegion::DumpMIF()
3836 : *
3837 : * Dump feature geometry in a format similar to .MIF REGIONs.
3838 : **********************************************************************/
3839 0 : void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/)
3840 : {
3841 0 : if (fpOut == nullptr)
3842 0 : fpOut = stdout;
3843 :
3844 : /*-----------------------------------------------------------------
3845 : * Fetch and validate geometry
3846 : *----------------------------------------------------------------*/
3847 0 : OGRGeometry *poGeom = GetGeometryRef();
3848 0 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
3849 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
3850 : {
3851 : /*-------------------------------------------------------------
3852 : * Generate output for region
3853 : *
3854 : * Note that we want to handle both OGRPolygons and OGRMultiPolygons
3855 : * that's why we use the GetNumRings()/GetRingRef() interface.
3856 : *------------------------------------------------------------*/
3857 0 : int numRingsTotal = GetNumRings();
3858 :
3859 0 : fprintf(fpOut, "REGION %d\n", numRingsTotal);
3860 :
3861 0 : for (int iRing = 0; iRing < numRingsTotal; iRing++)
3862 : {
3863 0 : OGRLinearRing *poRing = GetRingRef(iRing);
3864 :
3865 0 : if (poRing == nullptr)
3866 : {
3867 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3868 : "TABRegion: Object Geometry contains NULL rings!");
3869 0 : return;
3870 : }
3871 :
3872 0 : const int numPoints = poRing->getNumPoints();
3873 0 : fprintf(fpOut, " %d\n", numPoints);
3874 0 : for (int i = 0; i < numPoints; i++)
3875 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
3876 : poRing->getY(i));
3877 : }
3878 : }
3879 : else
3880 : {
3881 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3882 : "TABRegion: Missing or Invalid Geometry!");
3883 0 : return;
3884 : }
3885 :
3886 0 : if (m_bCenterIsSet)
3887 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
3888 :
3889 : // Finish with PEN/BRUSH/etc. clauses
3890 0 : DumpPenDef();
3891 0 : DumpBrushDef();
3892 :
3893 0 : fflush(fpOut);
3894 : }
3895 :
3896 : /**********************************************************************
3897 : * TABRegion::GetCenter()
3898 : *
3899 : * Returns the center/label point of the region.
3900 : * Compute one using OGRPolygonLabelPoint() if it was not explicitly set
3901 : * before.
3902 : *
3903 : * Returns 0 on success, -1 on error.
3904 : **********************************************************************/
3905 88 : int TABRegion::GetCenter(double &dX, double &dY)
3906 : {
3907 88 : if (!m_bCenterIsSet)
3908 : {
3909 : /*-------------------------------------------------------------
3910 : * Calculate label point. If we have a multipolygon then we use
3911 : * the first OGRPolygon in the feature to calculate the point.
3912 : *------------------------------------------------------------*/
3913 88 : OGRGeometry *poGeom = GetGeometryRef();
3914 88 : if (poGeom == nullptr)
3915 0 : return -1;
3916 :
3917 88 : OGRPolygon *poPolygon = nullptr;
3918 :
3919 88 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
3920 : {
3921 6 : OGRMultiPolygon *poMultiPolygon = poGeom->toMultiPolygon();
3922 6 : if (poMultiPolygon->getNumGeometries() > 0)
3923 6 : poPolygon = poMultiPolygon->getGeometryRef(0);
3924 : }
3925 82 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
3926 : {
3927 82 : poPolygon = poGeom->toPolygon();
3928 : }
3929 :
3930 88 : OGRPoint oLabelPoint;
3931 176 : if (poPolygon != nullptr &&
3932 88 : OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE)
3933 : {
3934 88 : m_dCenterX = oLabelPoint.getX();
3935 88 : m_dCenterY = oLabelPoint.getY();
3936 : }
3937 : else
3938 : {
3939 0 : OGREnvelope oEnv;
3940 0 : poGeom->getEnvelope(&oEnv);
3941 0 : m_dCenterX = (oEnv.MaxX + oEnv.MinX) / 2.0;
3942 0 : m_dCenterY = (oEnv.MaxY + oEnv.MinY) / 2.0;
3943 : }
3944 :
3945 88 : m_bCenterIsSet = TRUE;
3946 : }
3947 :
3948 88 : if (!m_bCenterIsSet)
3949 0 : return -1;
3950 :
3951 88 : dX = m_dCenterX;
3952 88 : dY = m_dCenterY;
3953 88 : return 0;
3954 : }
3955 :
3956 : /**********************************************************************
3957 : * TABRegion::SetCenter()
3958 : *
3959 : * Set the X,Y coordinates to use as center/label point for the region.
3960 : **********************************************************************/
3961 504 : void TABRegion::SetCenter(double dX, double dY)
3962 : {
3963 504 : m_dCenterX = dX;
3964 504 : m_dCenterY = dY;
3965 504 : m_bCenterIsSet = TRUE;
3966 504 : }
3967 :
3968 : /*=====================================================================
3969 : * class TABRectangle
3970 : *====================================================================*/
3971 :
3972 : /**********************************************************************
3973 : * TABRectangle::TABRectangle()
3974 : *
3975 : * Constructor.
3976 : **********************************************************************/
3977 949 : TABRectangle::TABRectangle(OGRFeatureDefn *poDefnIn)
3978 : : TABFeature(poDefnIn), m_bRoundCorners(FALSE), m_dRoundXRadius(0.0),
3979 949 : m_dRoundYRadius(0.0)
3980 : {
3981 949 : }
3982 :
3983 : /**********************************************************************
3984 : * TABRectangle::~TABRectangle()
3985 : *
3986 : * Destructor.
3987 : **********************************************************************/
3988 1898 : TABRectangle::~TABRectangle()
3989 : {
3990 1898 : }
3991 :
3992 : /**********************************************************************
3993 : * TABRectangle::CloneTABFeature()
3994 : *
3995 : * Duplicate feature, including stuff specific to each TABFeature type.
3996 : *
3997 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
3998 : * then copies any members specific to its own type.
3999 : **********************************************************************/
4000 0 : TABFeature *TABRectangle::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4001 : {
4002 : /*-----------------------------------------------------------------
4003 : * Alloc new feature and copy the base stuff
4004 : *----------------------------------------------------------------*/
4005 : TABRectangle *poNew =
4006 0 : new TABRectangle(poNewDefn ? poNewDefn : GetDefnRef());
4007 :
4008 0 : CopyTABFeatureBase(poNew);
4009 :
4010 : /*-----------------------------------------------------------------
4011 : * And members specific to this class
4012 : *----------------------------------------------------------------*/
4013 : // ITABFeaturePen
4014 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4015 :
4016 : // ITABFeatureBrush
4017 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4018 :
4019 0 : poNew->m_bRoundCorners = m_bRoundCorners;
4020 0 : poNew->m_dRoundXRadius = m_dRoundXRadius;
4021 0 : poNew->m_dRoundYRadius = m_dRoundYRadius;
4022 :
4023 0 : return poNew;
4024 : }
4025 :
4026 : /**********************************************************************
4027 : * TABRectangle::ValidateMapInfoType()
4028 : *
4029 : * Check the feature's geometry part and return the corresponding
4030 : * mapinfo object type code. The m_nMapInfoType member will also
4031 : * be updated for further calls to GetMapInfoType();
4032 : *
4033 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4034 : * is expected for this object class.
4035 : **********************************************************************/
4036 0 : TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4037 : {
4038 : /*-----------------------------------------------------------------
4039 : * Fetch and validate geometry
4040 : *----------------------------------------------------------------*/
4041 0 : OGRGeometry *poGeom = GetGeometryRef();
4042 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4043 : {
4044 0 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4045 0 : m_nMapInfoType = TAB_GEOM_ROUNDRECT;
4046 : else
4047 0 : m_nMapInfoType = TAB_GEOM_RECT;
4048 : }
4049 : else
4050 : {
4051 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4052 : "TABRectangle: Missing or Invalid Geometry!");
4053 0 : m_nMapInfoType = TAB_GEOM_NONE;
4054 : }
4055 :
4056 : /*-----------------------------------------------------------------
4057 : * Decide if coordinates should be compressed or not.
4058 : *----------------------------------------------------------------*/
4059 : // __TODO__ For now we always write uncompressed for this class...
4060 : // ValidateCoordType(poMapFile);
4061 0 : UpdateMBR(poMapFile);
4062 :
4063 0 : return m_nMapInfoType;
4064 : }
4065 :
4066 : /**********************************************************************
4067 : * TABRectangle::UpdateMBR()
4068 : *
4069 : * Update the feature MBR members using the geometry
4070 : *
4071 : * Returns 0 on success, or -1 if there is no geometry in object
4072 : **********************************************************************/
4073 0 : int TABRectangle::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4074 : {
4075 0 : OGREnvelope sEnvelope;
4076 :
4077 : /*-----------------------------------------------------------------
4078 : * Fetch and validate geometry
4079 : *----------------------------------------------------------------*/
4080 0 : OGRGeometry *poGeom = GetGeometryRef();
4081 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4082 0 : poGeom->getEnvelope(&sEnvelope);
4083 : else
4084 : {
4085 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4086 : "TABRectangle: Missing or Invalid Geometry!");
4087 0 : return -1;
4088 : }
4089 :
4090 : /*-----------------------------------------------------------------
4091 : * Note that we will simply use the rectangle's MBR and don't really
4092 : * read the polygon geometry... this should be OK unless the
4093 : * polygon geometry was not really a rectangle.
4094 : *----------------------------------------------------------------*/
4095 0 : m_dXMin = sEnvelope.MinX;
4096 0 : m_dYMin = sEnvelope.MinY;
4097 0 : m_dXMax = sEnvelope.MaxX;
4098 0 : m_dYMax = sEnvelope.MaxY;
4099 :
4100 0 : if (poMapFile)
4101 : {
4102 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4103 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4104 : }
4105 :
4106 0 : return 0;
4107 : }
4108 :
4109 : /**********************************************************************
4110 : * TABRectangle::ReadGeometryFromMAPFile()
4111 : *
4112 : * Fill the geometry and representation (color, etc...) part of the
4113 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4114 : *
4115 : * It is assumed that poMAPFile currently points to the beginning of
4116 : * a map object.
4117 : *
4118 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4119 : * been called.
4120 : **********************************************************************/
4121 8 : int TABRectangle::ReadGeometryFromMAPFile(
4122 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4123 : GBool bCoordBlockDataOnly /*=FALSE*/,
4124 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4125 : {
4126 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4127 8 : if (bCoordBlockDataOnly)
4128 0 : return 0;
4129 :
4130 : /*-----------------------------------------------------------------
4131 : * Fetch and validate geometry type
4132 : *----------------------------------------------------------------*/
4133 8 : m_nMapInfoType = poObjHdr->m_nType;
4134 :
4135 8 : if (m_nMapInfoType != TAB_GEOM_RECT && m_nMapInfoType != TAB_GEOM_RECT_C &&
4136 4 : m_nMapInfoType != TAB_GEOM_ROUNDRECT &&
4137 0 : m_nMapInfoType != TAB_GEOM_ROUNDRECT_C)
4138 : {
4139 0 : CPLError(
4140 : CE_Failure, CPLE_AssertionFailed,
4141 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4142 0 : m_nMapInfoType, m_nMapInfoType);
4143 0 : return -1;
4144 : }
4145 :
4146 : /*-----------------------------------------------------------------
4147 : * Read object information
4148 : *----------------------------------------------------------------*/
4149 : TABMAPObjRectEllipse *poRectHdr =
4150 8 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4151 :
4152 : // Read the corners radius
4153 :
4154 8 : if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4155 4 : m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4156 : {
4157 : // Read the corner's diameters
4158 4 : poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth,
4159 4 : poRectHdr->m_nCornerHeight, m_dRoundXRadius,
4160 4 : m_dRoundYRadius);
4161 :
4162 : // Divide by 2 since we store the corner's radius
4163 4 : m_dRoundXRadius /= 2.0;
4164 4 : m_dRoundYRadius /= 2.0;
4165 :
4166 4 : m_bRoundCorners = TRUE;
4167 : }
4168 : else
4169 : {
4170 4 : m_bRoundCorners = FALSE;
4171 4 : m_dRoundXRadius = 0.0;
4172 4 : m_dRoundYRadius = 0.0;
4173 : }
4174 :
4175 : // A rectangle is defined by its MBR
4176 :
4177 8 : double dXMin = 0.0;
4178 8 : double dYMin = 0.0;
4179 8 : double dXMax = 0.0;
4180 8 : double dYMax = 0.0;
4181 8 : poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4182 : dYMin);
4183 8 : poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4184 : dYMax);
4185 :
4186 8 : m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index
4187 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4188 :
4189 8 : m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index
4190 8 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4191 :
4192 : /*-----------------------------------------------------------------
4193 : * Call SetMBR() and GetMBR() now to make sure that min values are
4194 : * really smaller than max values.
4195 : *----------------------------------------------------------------*/
4196 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
4197 8 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4198 :
4199 : /* Copy int MBR to feature class members */
4200 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4201 : poObjHdr->m_nMaxY);
4202 :
4203 : /*-----------------------------------------------------------------
4204 : * Create and fill geometry object
4205 : *----------------------------------------------------------------*/
4206 8 : OGRPolygon *poPolygon = new OGRPolygon;
4207 8 : OGRLinearRing *poRing = new OGRLinearRing();
4208 8 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
4209 : {
4210 : /*-------------------------------------------------------------
4211 : * For rounded rectangles, we generate arcs with 45 line
4212 : * segments for each corner. We start with lower-left corner
4213 : * and proceed counterclockwise
4214 : * We also have to make sure that rounding radius is not too
4215 : * large for the MBR in the generated polygon... however, we
4216 : * always return the true X/Y radius (not adjusted) since this
4217 : * is the way MapInfo seems to do it when a radius bigger than
4218 : * the MBR is passed from TBA to MIF.
4219 : *------------------------------------------------------------*/
4220 : const double dXRadius =
4221 4 : std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
4222 : const double dYRadius =
4223 4 : std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
4224 4 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
4225 : dYRadius, M_PI, 3.0 * M_PI / 2.0);
4226 4 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
4227 : dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
4228 4 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
4229 : dYRadius, 0.0, M_PI / 2.0);
4230 4 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
4231 : dYRadius, M_PI / 2.0, M_PI);
4232 :
4233 4 : TABCloseRing(poRing);
4234 : }
4235 : else
4236 : {
4237 4 : poRing->addPoint(dXMin, dYMin);
4238 4 : poRing->addPoint(dXMax, dYMin);
4239 4 : poRing->addPoint(dXMax, dYMax);
4240 4 : poRing->addPoint(dXMin, dYMax);
4241 4 : poRing->addPoint(dXMin, dYMin);
4242 : }
4243 :
4244 8 : poPolygon->addRingDirectly(poRing);
4245 8 : SetGeometryDirectly(poPolygon);
4246 :
4247 8 : return 0;
4248 : }
4249 :
4250 : /**********************************************************************
4251 : * TABRectangle::WriteGeometryToMAPFile()
4252 : *
4253 : * Write the geometry and representation (color, etc...) part of the
4254 : * feature to the .MAP object pointed to by poMAPFile.
4255 : *
4256 : * It is assumed that poMAPFile currently points to a valid map object.
4257 : *
4258 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4259 : * been called.
4260 : **********************************************************************/
4261 0 : int TABRectangle::WriteGeometryToMAPFile(
4262 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4263 : GBool bCoordBlockDataOnly /*=FALSE*/,
4264 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4265 : {
4266 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4267 0 : if (bCoordBlockDataOnly)
4268 0 : return 0;
4269 :
4270 : /*-----------------------------------------------------------------
4271 : * We assume that ValidateMapInfoType() was called already and that
4272 : * the type in poObjHdr->m_nType is valid.
4273 : *----------------------------------------------------------------*/
4274 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4275 :
4276 : /*-----------------------------------------------------------------
4277 : * Fetch and validate geometry and update MBR
4278 : * Note that we will simply use the geometry's MBR and don't really
4279 : * read the polygon geometry... this should be OK unless the
4280 : * polygon geometry was not really a rectangle.
4281 : *----------------------------------------------------------------*/
4282 0 : if (UpdateMBR(poMapFile) != 0)
4283 0 : return -1; /* Error already reported */
4284 :
4285 : /*-----------------------------------------------------------------
4286 : * Copy object information
4287 : *----------------------------------------------------------------*/
4288 : TABMAPObjRectEllipse *poRectHdr =
4289 0 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4290 :
4291 0 : if (m_nMapInfoType == TAB_GEOM_ROUNDRECT ||
4292 0 : m_nMapInfoType == TAB_GEOM_ROUNDRECT_C)
4293 : {
4294 0 : poMapFile->Coordsys2IntDist(
4295 0 : m_dRoundXRadius * 2.0, m_dRoundYRadius * 2.0,
4296 0 : poRectHdr->m_nCornerWidth, poRectHdr->m_nCornerHeight);
4297 : }
4298 : else
4299 : {
4300 0 : poRectHdr->m_nCornerWidth = 0;
4301 0 : poRectHdr->m_nCornerHeight = 0;
4302 : }
4303 :
4304 : // A rectangle is defined by its MBR (values were set in UpdateMBR())
4305 0 : poRectHdr->m_nMinX = m_nXMin;
4306 0 : poRectHdr->m_nMinY = m_nYMin;
4307 0 : poRectHdr->m_nMaxX = m_nXMax;
4308 0 : poRectHdr->m_nMaxY = m_nYMax;
4309 :
4310 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4311 0 : poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
4312 :
4313 0 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4314 0 : poRectHdr->m_nBrushId =
4315 0 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
4316 :
4317 0 : if (CPLGetLastErrorType() == CE_Failure)
4318 0 : return -1;
4319 :
4320 0 : return 0;
4321 : }
4322 :
4323 : /**********************************************************************
4324 : * TABRectangle::GetStyleString() const
4325 : *
4326 : * Return style string for this feature.
4327 : *
4328 : * Style String is built only once during the first call to GetStyleString().
4329 : **********************************************************************/
4330 18 : const char *TABRectangle::GetStyleString() const
4331 : {
4332 18 : if (m_pszStyleString == nullptr)
4333 : {
4334 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4335 : // to use temporary buffers
4336 10 : char *pszPen = CPLStrdup(GetPenStyleString());
4337 10 : char *pszBrush = CPLStrdup(GetBrushStyleString());
4338 :
4339 10 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4340 :
4341 10 : CPLFree(pszPen);
4342 10 : CPLFree(pszBrush);
4343 : }
4344 :
4345 18 : return m_pszStyleString;
4346 : }
4347 :
4348 : /**********************************************************************
4349 : * TABRectangle::DumpMIF()
4350 : *
4351 : * Dump feature geometry in a format similar to .MIF REGIONs.
4352 : **********************************************************************/
4353 0 : void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/)
4354 : {
4355 0 : if (fpOut == nullptr)
4356 0 : fpOut = stdout;
4357 :
4358 : /*-----------------------------------------------------------------
4359 : * Output RECT or ROUNDRECT parameters
4360 : *----------------------------------------------------------------*/
4361 0 : double dXMin = 0.0;
4362 0 : double dYMin = 0.0;
4363 0 : double dXMax = 0.0;
4364 0 : double dYMax = 0.0;
4365 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4366 :
4367 0 : if (m_bRoundCorners)
4368 0 : fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g %.15g %.15g)\n",
4369 : dXMin, dYMin, dXMax, dYMax, m_dRoundXRadius, m_dRoundYRadius);
4370 : else
4371 0 : fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4372 : dYMax);
4373 :
4374 : /*-----------------------------------------------------------------
4375 : * Fetch and validate geometry
4376 : *----------------------------------------------------------------*/
4377 0 : OGRGeometry *poGeom = GetGeometryRef();
4378 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4379 : {
4380 : /*-------------------------------------------------------------
4381 : * Generate rectangle output as a region
4382 : * We could also output as a RECT or ROUNDRECT in a real MIF generator
4383 : *------------------------------------------------------------*/
4384 0 : OGRPolygon *poPolygon = poGeom->toPolygon();
4385 0 : int numIntRings = poPolygon->getNumInteriorRings();
4386 0 : fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4387 : // In this loop, iRing=-1 for the outer ring.
4388 0 : for (int iRing = -1; iRing < numIntRings; iRing++)
4389 : {
4390 0 : OGRLinearRing *poRing = nullptr;
4391 :
4392 0 : if (iRing == -1)
4393 0 : poRing = poPolygon->getExteriorRing();
4394 : else
4395 0 : poRing = poPolygon->getInteriorRing(iRing);
4396 :
4397 0 : if (poRing == nullptr)
4398 : {
4399 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4400 : "TABRectangle: Object Geometry contains NULL rings!");
4401 0 : return;
4402 : }
4403 :
4404 0 : const int numPoints = poRing->getNumPoints();
4405 0 : fprintf(fpOut, " %d\n", numPoints);
4406 0 : for (int i = 0; i < numPoints; i++)
4407 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4408 : poRing->getY(i));
4409 : }
4410 : }
4411 : else
4412 : {
4413 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4414 : "TABRectangle: Missing or Invalid Geometry!");
4415 0 : return;
4416 : }
4417 :
4418 : // Finish with PEN/BRUSH/etc. clauses
4419 0 : DumpPenDef();
4420 0 : DumpBrushDef();
4421 :
4422 0 : fflush(fpOut);
4423 : }
4424 :
4425 : /*=====================================================================
4426 : * class TABEllipse
4427 : *====================================================================*/
4428 :
4429 : /**********************************************************************
4430 : * TABEllipse::TABEllipse()
4431 : *
4432 : * Constructor.
4433 : **********************************************************************/
4434 401 : TABEllipse::TABEllipse(OGRFeatureDefn *poDefnIn)
4435 : : TABFeature(poDefnIn), m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0),
4436 401 : m_dYRadius(0.0)
4437 : {
4438 401 : }
4439 :
4440 : /**********************************************************************
4441 : * TABEllipse::~TABEllipse()
4442 : *
4443 : * Destructor.
4444 : **********************************************************************/
4445 802 : TABEllipse::~TABEllipse()
4446 : {
4447 802 : }
4448 :
4449 : /**********************************************************************
4450 : * TABEllipse::CloneTABFeature()
4451 : *
4452 : * Duplicate feature, including stuff specific to each TABFeature type.
4453 : *
4454 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
4455 : * then copies any members specific to its own type.
4456 : **********************************************************************/
4457 0 : TABFeature *TABEllipse::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4458 : {
4459 : /*-----------------------------------------------------------------
4460 : * Alloc new feature and copy the base stuff
4461 : *----------------------------------------------------------------*/
4462 0 : TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : GetDefnRef());
4463 :
4464 0 : CopyTABFeatureBase(poNew);
4465 :
4466 : /*-----------------------------------------------------------------
4467 : * And members specific to this class
4468 : *----------------------------------------------------------------*/
4469 : // ITABFeaturePen
4470 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4471 :
4472 : // ITABFeatureBrush
4473 0 : *(poNew->GetBrushDefRef()) = *GetBrushDefRef();
4474 :
4475 0 : poNew->m_dCenterX = m_dCenterX;
4476 0 : poNew->m_dCenterY = m_dCenterY;
4477 0 : poNew->m_dXRadius = m_dXRadius;
4478 0 : poNew->m_dYRadius = m_dYRadius;
4479 :
4480 0 : return poNew;
4481 : }
4482 :
4483 : /**********************************************************************
4484 : * TABEllipse::ValidateMapInfoType()
4485 : *
4486 : * Check the feature's geometry part and return the corresponding
4487 : * mapinfo object type code. The m_nMapInfoType member will also
4488 : * be updated for further calls to GetMapInfoType();
4489 : *
4490 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4491 : * is expected for this object class.
4492 : **********************************************************************/
4493 0 : TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4494 : {
4495 : /*-----------------------------------------------------------------
4496 : * Fetch and validate geometry
4497 : *----------------------------------------------------------------*/
4498 0 : OGRGeometry *poGeom = GetGeometryRef();
4499 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4500 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4501 : {
4502 0 : m_nMapInfoType = TAB_GEOM_ELLIPSE;
4503 : }
4504 : else
4505 : {
4506 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4507 : "TABEllipse: Missing or Invalid Geometry!");
4508 0 : m_nMapInfoType = TAB_GEOM_NONE;
4509 : }
4510 :
4511 : /*-----------------------------------------------------------------
4512 : * Decide if coordinates should be compressed or not.
4513 : *----------------------------------------------------------------*/
4514 : // __TODO__ For now we always write uncompressed for this class...
4515 : // ValidateCoordType(poMapFile);
4516 0 : UpdateMBR(poMapFile);
4517 :
4518 0 : return m_nMapInfoType;
4519 : }
4520 :
4521 : /**********************************************************************
4522 : * TABEllipse::UpdateMBR()
4523 : *
4524 : * Update the feature MBR members using the geometry
4525 : *
4526 : * Returns 0 on success, or -1 if there is no geometry in object
4527 : **********************************************************************/
4528 0 : int TABEllipse::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4529 : {
4530 0 : OGREnvelope sEnvelope;
4531 :
4532 : /*-----------------------------------------------------------------
4533 : * Fetch and validate geometry... Polygon and point are accepted.
4534 : * Note that we will simply use the ellipse's MBR and don't really
4535 : * read the polygon geometry... this should be OK unless the
4536 : * polygon geometry was not really an ellipse.
4537 : *----------------------------------------------------------------*/
4538 0 : OGRGeometry *poGeom = GetGeometryRef();
4539 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
4540 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4541 0 : poGeom->getEnvelope(&sEnvelope);
4542 : else
4543 : {
4544 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4545 : "TABEllipse: Missing or Invalid Geometry!");
4546 0 : return -1;
4547 : }
4548 :
4549 : /*-----------------------------------------------------------------
4550 : * We use the center of the MBR as the ellipse center, and the
4551 : * X/Y radius to define the MBR size. If X/Y radius are null then
4552 : * we'll try to use the MBR to recompute them.
4553 : *----------------------------------------------------------------*/
4554 0 : const double dXCenter = (sEnvelope.MaxX + sEnvelope.MinX) / 2.0;
4555 0 : const double dYCenter = (sEnvelope.MaxY + sEnvelope.MinY) / 2.0;
4556 0 : if (m_dXRadius == 0.0 && m_dYRadius == 0.0)
4557 : {
4558 0 : m_dXRadius = std::abs(sEnvelope.MaxX - sEnvelope.MinX) / 2.0;
4559 0 : m_dYRadius = std::abs(sEnvelope.MaxY - sEnvelope.MinY) / 2.0;
4560 : }
4561 :
4562 0 : m_dXMin = dXCenter - m_dXRadius;
4563 0 : m_dYMin = dYCenter - m_dYRadius;
4564 0 : m_dXMax = dXCenter + m_dXRadius;
4565 0 : m_dYMax = dYCenter + m_dYRadius;
4566 :
4567 0 : if (poMapFile)
4568 : {
4569 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4570 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4571 : }
4572 :
4573 0 : return 0;
4574 : }
4575 :
4576 : /**********************************************************************
4577 : * TABEllipse::ReadGeometryFromMAPFile()
4578 : *
4579 : * Fill the geometry and representation (color, etc...) part of the
4580 : * feature from the contents of the .MAP object pointed to by poMAPFile.
4581 : *
4582 : * It is assumed that poMAPFile currently points to the beginning of
4583 : * a map object.
4584 : *
4585 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4586 : * been called.
4587 : **********************************************************************/
4588 4 : int TABEllipse::ReadGeometryFromMAPFile(
4589 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4590 : GBool bCoordBlockDataOnly /*=FALSE*/,
4591 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4592 : {
4593 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4594 4 : if (bCoordBlockDataOnly)
4595 0 : return 0;
4596 :
4597 : /*-----------------------------------------------------------------
4598 : * Fetch and validate geometry type
4599 : *----------------------------------------------------------------*/
4600 4 : m_nMapInfoType = poObjHdr->m_nType;
4601 :
4602 4 : if (m_nMapInfoType != TAB_GEOM_ELLIPSE &&
4603 0 : m_nMapInfoType != TAB_GEOM_ELLIPSE_C)
4604 : {
4605 0 : CPLError(
4606 : CE_Failure, CPLE_AssertionFailed,
4607 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
4608 0 : m_nMapInfoType, m_nMapInfoType);
4609 0 : return -1;
4610 : }
4611 :
4612 : /*-----------------------------------------------------------------
4613 : * Read object information
4614 : *----------------------------------------------------------------*/
4615 : TABMAPObjRectEllipse *poRectHdr =
4616 4 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4617 :
4618 : // An ellipse is defined by its MBR
4619 :
4620 4 : double dXMin = 0.0;
4621 4 : double dYMin = 0.0;
4622 4 : double dXMax = 0.0;
4623 4 : double dYMax = 0.0;
4624 4 : poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, dXMin,
4625 : dYMin);
4626 4 : poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, dXMax,
4627 : dYMax);
4628 :
4629 4 : m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index
4630 4 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
4631 :
4632 4 : m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index
4633 4 : poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef);
4634 :
4635 : /*-----------------------------------------------------------------
4636 : * Save info about the ellipse def. inside class members
4637 : *----------------------------------------------------------------*/
4638 4 : m_dCenterX = (dXMin + dXMax) / 2.0;
4639 4 : m_dCenterY = (dYMin + dYMax) / 2.0;
4640 4 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
4641 4 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
4642 :
4643 4 : SetMBR(dXMin, dYMin, dXMax, dYMax);
4644 :
4645 4 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
4646 : poObjHdr->m_nMaxY);
4647 :
4648 : /*-----------------------------------------------------------------
4649 : * Create and fill geometry object
4650 : *----------------------------------------------------------------*/
4651 4 : OGRPolygon *poPolygon = new OGRPolygon;
4652 4 : OGRLinearRing *poRing = new OGRLinearRing();
4653 :
4654 : /*-----------------------------------------------------------------
4655 : * For the OGR geometry, we generate an ellipse with 2 degrees line
4656 : * segments.
4657 : *----------------------------------------------------------------*/
4658 4 : TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
4659 : 0.0, 2.0 * M_PI);
4660 4 : TABCloseRing(poRing);
4661 :
4662 4 : poPolygon->addRingDirectly(poRing);
4663 4 : SetGeometryDirectly(poPolygon);
4664 :
4665 4 : return 0;
4666 : }
4667 :
4668 : /**********************************************************************
4669 : * TABEllipse::WriteGeometryToMAPFile()
4670 : *
4671 : * Write the geometry and representation (color, etc...) part of the
4672 : * feature to the .MAP object pointed to by poMAPFile.
4673 : *
4674 : * It is assumed that poMAPFile currently points to a valid map object.
4675 : *
4676 : * Returns 0 on success, -1 on error, in which case CPLError() will have
4677 : * been called.
4678 : **********************************************************************/
4679 0 : int TABEllipse::WriteGeometryToMAPFile(
4680 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
4681 : GBool bCoordBlockDataOnly /*=FALSE*/,
4682 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
4683 : {
4684 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
4685 0 : if (bCoordBlockDataOnly)
4686 0 : return 0;
4687 :
4688 : /*-----------------------------------------------------------------
4689 : * We assume that ValidateMapInfoType() was called already and that
4690 : * the type in poObjHdr->m_nType is valid.
4691 : *----------------------------------------------------------------*/
4692 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
4693 :
4694 : /*-----------------------------------------------------------------
4695 : * Fetch and validate geometry... Polygon and point are accepted.
4696 : * Note that we will simply use the ellipse's MBR and don't really
4697 : * read the polygon geometry... this should be OK unless the
4698 : * polygon geometry was not really an ellipse.
4699 : *
4700 : * We use the center of the MBR as the ellipse center, and the
4701 : * X/Y radius to define the MBR size. If X/Y radius are null then
4702 : * we'll try to use the MBR to recompute them.
4703 : *----------------------------------------------------------------*/
4704 0 : if (UpdateMBR(poMapFile) != 0)
4705 0 : return -1; /* Error already reported */
4706 :
4707 : /*-----------------------------------------------------------------
4708 : * Copy object information
4709 : *----------------------------------------------------------------*/
4710 : TABMAPObjRectEllipse *poRectHdr =
4711 0 : cpl::down_cast<TABMAPObjRectEllipse *>(poObjHdr);
4712 :
4713 : // Reset RoundRect Corner members... just in case (unused for ellipse)
4714 0 : poRectHdr->m_nCornerWidth = 0;
4715 0 : poRectHdr->m_nCornerHeight = 0;
4716 :
4717 : // An ellipse is defined by its MBR (values were set in UpdateMBR())
4718 0 : poRectHdr->m_nMinX = m_nXMin;
4719 0 : poRectHdr->m_nMinY = m_nYMin;
4720 0 : poRectHdr->m_nMaxX = m_nXMax;
4721 0 : poRectHdr->m_nMaxY = m_nYMax;
4722 :
4723 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
4724 0 : poRectHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
4725 :
4726 0 : m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef);
4727 0 : poRectHdr->m_nBrushId =
4728 0 : static_cast<GByte>(m_nBrushDefIndex); // Brush index
4729 :
4730 0 : if (CPLGetLastErrorType() == CE_Failure)
4731 0 : return -1;
4732 :
4733 0 : return 0;
4734 : }
4735 :
4736 : /**********************************************************************
4737 : * TABEllipse::GetStyleString() const
4738 : *
4739 : * Return style string for this feature.
4740 : *
4741 : * Style String is built only once during the first call to GetStyleString().
4742 : **********************************************************************/
4743 9 : const char *TABEllipse::GetStyleString() const
4744 : {
4745 9 : if (m_pszStyleString == nullptr)
4746 : {
4747 : // Since GetPen/BrushStyleString() use CPLSPrintf(), we need
4748 : // to use temporary buffers
4749 5 : char *pszPen = CPLStrdup(GetPenStyleString());
4750 5 : char *pszBrush = CPLStrdup(GetBrushStyleString());
4751 :
4752 5 : m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen));
4753 :
4754 5 : CPLFree(pszPen);
4755 5 : CPLFree(pszBrush);
4756 : }
4757 :
4758 9 : return m_pszStyleString;
4759 : }
4760 :
4761 : /**********************************************************************
4762 : * TABEllipse::DumpMIF()
4763 : *
4764 : * Dump feature geometry in a format similar to .MIF REGIONs.
4765 : **********************************************************************/
4766 0 : void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/)
4767 : {
4768 0 : if (fpOut == nullptr)
4769 0 : fpOut = stdout;
4770 :
4771 : /*-----------------------------------------------------------------
4772 : * Output ELLIPSE parameters
4773 : *----------------------------------------------------------------*/
4774 0 : double dXMin = 0.0;
4775 0 : double dYMin = 0.0;
4776 0 : double dXMax = 0.0;
4777 0 : double dYMax = 0.0;
4778 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
4779 0 : fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax,
4780 : dYMax);
4781 :
4782 : /*-----------------------------------------------------------------
4783 : * Fetch and validate geometry
4784 : *----------------------------------------------------------------*/
4785 0 : OGRGeometry *poGeom = GetGeometryRef();
4786 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
4787 : {
4788 : /*-------------------------------------------------------------
4789 : * Generate ellipse output as a region
4790 : * We could also output as an ELLIPSE in a real MIF generator
4791 : *------------------------------------------------------------*/
4792 0 : OGRPolygon *poPolygon = poGeom->toPolygon();
4793 0 : int numIntRings = poPolygon->getNumInteriorRings();
4794 0 : fprintf(fpOut, "REGION %d\n", numIntRings + 1);
4795 : // In this loop, iRing=-1 for the outer ring.
4796 0 : for (int iRing = -1; iRing < numIntRings; iRing++)
4797 : {
4798 0 : OGRLinearRing *poRing = nullptr;
4799 :
4800 0 : if (iRing == -1)
4801 0 : poRing = poPolygon->getExteriorRing();
4802 : else
4803 0 : poRing = poPolygon->getInteriorRing(iRing);
4804 :
4805 0 : if (poRing == nullptr)
4806 : {
4807 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4808 : "TABEllipse: Object Geometry contains NULL rings!");
4809 0 : return;
4810 : }
4811 :
4812 0 : int numPoints = poRing->getNumPoints();
4813 0 : fprintf(fpOut, " %d\n", numPoints);
4814 0 : for (int i = 0; i < numPoints; i++)
4815 0 : fprintf(fpOut, "%.15g %.15g\n", poRing->getX(i),
4816 : poRing->getY(i));
4817 : }
4818 : }
4819 : else
4820 : {
4821 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4822 : "TABEllipse: Missing or Invalid Geometry!");
4823 0 : return;
4824 : }
4825 :
4826 : // Finish with PEN/BRUSH/etc. clauses
4827 0 : DumpPenDef();
4828 0 : DumpBrushDef();
4829 :
4830 0 : fflush(fpOut);
4831 : }
4832 :
4833 : /*=====================================================================
4834 : * class TABArc
4835 : *====================================================================*/
4836 :
4837 : /**********************************************************************
4838 : * TABArc::TABArc()
4839 : *
4840 : * Constructor.
4841 : **********************************************************************/
4842 680 : TABArc::TABArc(OGRFeatureDefn *poDefnIn)
4843 : : TABFeature(poDefnIn), m_dStartAngle(0.0), m_dEndAngle(0.0),
4844 680 : m_dCenterX(0.0), m_dCenterY(0.0), m_dXRadius(0.0), m_dYRadius(0.0)
4845 : {
4846 680 : }
4847 :
4848 : /**********************************************************************
4849 : * TABArc::~TABArc()
4850 : *
4851 : * Destructor.
4852 : **********************************************************************/
4853 1360 : TABArc::~TABArc()
4854 : {
4855 1360 : }
4856 :
4857 : /**********************************************************************
4858 : * TABArc::CloneTABFeature()
4859 : *
4860 : * Duplicate feature, including stuff specific to each TABFeature type.
4861 : *
4862 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
4863 : * then copies any members specific to its own type.
4864 : **********************************************************************/
4865 0 : TABFeature *TABArc::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
4866 : {
4867 : /*-----------------------------------------------------------------
4868 : * Alloc new feature and copy the base stuff
4869 : *----------------------------------------------------------------*/
4870 0 : TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef());
4871 :
4872 0 : CopyTABFeatureBase(poNew);
4873 :
4874 : /*-----------------------------------------------------------------
4875 : * And members specific to this class
4876 : *----------------------------------------------------------------*/
4877 : // ITABFeaturePen
4878 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
4879 :
4880 0 : poNew->SetStartAngle(GetStartAngle());
4881 0 : poNew->SetEndAngle(GetEndAngle());
4882 :
4883 0 : poNew->m_dCenterX = m_dCenterX;
4884 0 : poNew->m_dCenterY = m_dCenterY;
4885 0 : poNew->m_dXRadius = m_dXRadius;
4886 0 : poNew->m_dYRadius = m_dYRadius;
4887 :
4888 0 : return poNew;
4889 : }
4890 :
4891 : /**********************************************************************
4892 : * TABArc::ValidateMapInfoType()
4893 : *
4894 : * Check the feature's geometry part and return the corresponding
4895 : * mapinfo object type code. The m_nMapInfoType member will also
4896 : * be updated for further calls to GetMapInfoType();
4897 : *
4898 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
4899 : * is expected for this object class.
4900 : **********************************************************************/
4901 0 : TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
4902 : {
4903 : /*-----------------------------------------------------------------
4904 : * Fetch and validate geometry
4905 : *----------------------------------------------------------------*/
4906 0 : OGRGeometry *poGeom = GetGeometryRef();
4907 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ||
4908 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4909 : {
4910 0 : m_nMapInfoType = TAB_GEOM_ARC;
4911 : }
4912 : else
4913 : {
4914 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4915 : "TABArc: Missing or Invalid Geometry!");
4916 0 : m_nMapInfoType = TAB_GEOM_NONE;
4917 : }
4918 :
4919 : /*-----------------------------------------------------------------
4920 : * Decide if coordinates should be compressed or not.
4921 : *----------------------------------------------------------------*/
4922 : // __TODO__ For now we always write uncompressed for this class...
4923 : // ValidateCoordType(poMapFile);
4924 0 : UpdateMBR(poMapFile);
4925 :
4926 0 : return m_nMapInfoType;
4927 : }
4928 :
4929 : /**********************************************************************
4930 : * TABArc::UpdateMBR()
4931 : *
4932 : * Update the feature MBR members using the geometry
4933 : *
4934 : * Returns 0 on success, or -1 if there is no geometry in object
4935 : **********************************************************************/
4936 0 : int TABArc::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
4937 : {
4938 0 : OGREnvelope sEnvelope;
4939 :
4940 0 : OGRGeometry *poGeom = GetGeometryRef();
4941 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString))
4942 : {
4943 : /*-------------------------------------------------------------
4944 : * POLYGON geometry:
4945 : * Note that we will simply use the ellipse's MBR and don't really
4946 : * read the polygon geometry... this should be OK unless the
4947 : * polygon geometry was not really an ellipse.
4948 : * In the case of a polygon geometry. the m_dCenterX/Y values MUST
4949 : * have been set by the caller.
4950 : *------------------------------------------------------------*/
4951 0 : poGeom->getEnvelope(&sEnvelope);
4952 : }
4953 0 : else if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
4954 : {
4955 : /*-------------------------------------------------------------
4956 : * In the case of a POINT GEOMETRY, we will make sure the
4957 : * feature's m_dCenterX/Y are in sync with the point's X,Y coords.
4958 : *
4959 : * In this case we have to reconstruct the arc inside a temporary
4960 : * geometry object in order to find its real MBR.
4961 : *------------------------------------------------------------*/
4962 0 : OGRPoint *poPoint = poGeom->toPoint();
4963 0 : m_dCenterX = poPoint->getX();
4964 0 : m_dCenterY = poPoint->getY();
4965 :
4966 0 : OGRLineString oTmpLine;
4967 0 : int numPts = 0;
4968 0 : if (m_dEndAngle < m_dStartAngle)
4969 0 : numPts = static_cast<int>(
4970 0 : std::abs(((m_dEndAngle + 360) - m_dStartAngle) / 2) + 1);
4971 : else
4972 0 : numPts = static_cast<int>(
4973 0 : std::abs((m_dEndAngle - m_dStartAngle) / 2) + 1);
4974 0 : numPts = std::max(2, numPts);
4975 :
4976 0 : TABGenerateArc(&oTmpLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
4977 0 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
4978 0 : m_dEndAngle * M_PI / 180.0);
4979 :
4980 0 : oTmpLine.getEnvelope(&sEnvelope);
4981 : }
4982 : else
4983 : {
4984 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
4985 : "TABArc: Missing or Invalid Geometry!");
4986 0 : return -1;
4987 : }
4988 :
4989 : // Update the Arc's MBR
4990 0 : m_dXMin = sEnvelope.MinX;
4991 0 : m_dYMin = sEnvelope.MinY;
4992 0 : m_dXMax = sEnvelope.MaxX;
4993 0 : m_dYMax = sEnvelope.MaxY;
4994 :
4995 0 : if (poMapFile)
4996 : {
4997 0 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
4998 0 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
4999 : }
5000 :
5001 0 : return 0;
5002 : }
5003 :
5004 : /**********************************************************************
5005 : * TABArc::ReadGeometryFromMAPFile()
5006 : *
5007 : * Fill the geometry and representation (color, etc...) part of the
5008 : * feature from the contents of the .MAP object pointed to by poMAPFile.
5009 : *
5010 : * It is assumed that poMAPFile currently points to the beginning of
5011 : * a map object.
5012 : *
5013 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5014 : * been called.
5015 : **********************************************************************/
5016 8 : int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5017 : TABMAPObjHdr *poObjHdr,
5018 : GBool bCoordBlockDataOnly /*=FALSE*/,
5019 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5020 : {
5021 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5022 8 : if (bCoordBlockDataOnly)
5023 0 : return 0;
5024 :
5025 : /*-----------------------------------------------------------------
5026 : * Fetch and validate geometry type
5027 : *----------------------------------------------------------------*/
5028 8 : m_nMapInfoType = poObjHdr->m_nType;
5029 :
5030 8 : if (m_nMapInfoType != TAB_GEOM_ARC && m_nMapInfoType != TAB_GEOM_ARC_C)
5031 : {
5032 0 : CPLError(
5033 : CE_Failure, CPLE_AssertionFailed,
5034 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5035 0 : m_nMapInfoType, m_nMapInfoType);
5036 0 : return -1;
5037 : }
5038 :
5039 : /*-----------------------------------------------------------------
5040 : * Read object information
5041 : *----------------------------------------------------------------*/
5042 8 : TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5043 :
5044 : /*-------------------------------------------------------------
5045 : * Start/End angles
5046 : * Since the angles are specified for integer coordinates, and
5047 : * that these coordinates can have the X axis reversed, we have to
5048 : * adjust the angle values for the change in the X axis
5049 : * direction.
5050 : *
5051 : * This should be necessary only when X axis is flipped.
5052 : * __TODO__ Why is order of start/end values reversed as well???
5053 : *------------------------------------------------------------*/
5054 :
5055 : /*-------------------------------------------------------------
5056 : * OK, Arc angles again!!!!!!!!!!!!
5057 : * After some tests in 1999-11, it appeared that the angle values
5058 : * ALWAYS had to be flipped (read order= end angle followed by
5059 : * start angle), no matter which quadrant the file is in.
5060 : * This does not make any sense, so I suspect that there is something
5061 : * that we are missing here!
5062 : *
5063 : * 2000-01-14.... Again!!! Based on some sample data files:
5064 : * File Ver Quadr ReflXAxis Read_Order Adjust_Angle
5065 : * test_symb.tab 300 2 1 end,start X=yes Y=no
5066 : * alltypes.tab: 300 1 0 start,end X=no Y=no
5067 : * arcs.tab: 300 2 0 end,start X=yes Y=no
5068 : *
5069 : * Until we prove it wrong, the rule would be:
5070 : * -> Quadrant 1 and 3, angles order = start, end
5071 : * -> Quadrant 2 and 4, angles order = end, start
5072 : * + Always adjust angles for x and y axis based on quadrant.
5073 : *
5074 : * This was confirmed using some more files in which the quadrant was
5075 : * manually changed, but whether these are valid results is
5076 : * disputable.
5077 : *
5078 : * The ReflectXAxis flag seems to have no effect here...
5079 : *------------------------------------------------------------*/
5080 :
5081 : /*-------------------------------------------------------------
5082 : * In version 100 .tab files (version 400 .map), it is possible
5083 : * to have a quadrant value of 0 and it should be treated the
5084 : * same way as quadrant 3
5085 : *------------------------------------------------------------*/
5086 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1 ||
5087 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5088 0 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5089 : {
5090 : // Quadrants 1 and 3 ... read order = start, end
5091 8 : m_dStartAngle = poArcHdr->m_nStartAngle / 10.0;
5092 8 : m_dEndAngle = poArcHdr->m_nEndAngle / 10.0;
5093 : }
5094 : else
5095 : {
5096 : // Quadrants 2 and 4 ... read order = end, start
5097 0 : m_dStartAngle = poArcHdr->m_nEndAngle / 10.0;
5098 0 : m_dEndAngle = poArcHdr->m_nStartAngle / 10.0;
5099 : }
5100 :
5101 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 2 ||
5102 16 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5103 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5104 : {
5105 : // X axis direction is flipped... adjust angle
5106 0 : m_dStartAngle = (m_dStartAngle <= 180.0) ? (180.0 - m_dStartAngle)
5107 0 : : (540.0 - m_dStartAngle);
5108 0 : m_dEndAngle = (m_dEndAngle <= 180.0) ? (180.0 - m_dEndAngle)
5109 0 : : (540.0 - m_dEndAngle);
5110 : }
5111 :
5112 8 : if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
5113 : {
5114 0 : CPLError(CE_Failure, CPLE_AppDefined,
5115 : "Wrong start and end angles: %f %f", m_dStartAngle,
5116 : m_dEndAngle);
5117 0 : return -1;
5118 : }
5119 :
5120 8 : if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 3 ||
5121 16 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 4 ||
5122 8 : poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 0)
5123 : {
5124 : // Y axis direction is flipped... this reverses angle direction
5125 : // Unfortunately we never found any file that contains this case,
5126 : // but this should be the behavior to expect!!!
5127 : //
5128 : // 2000-01-14: some files in which quadrant was set to 3 and 4
5129 : // manually seemed to confirm that this is the right thing to do.
5130 0 : m_dStartAngle = 360.0 - m_dStartAngle;
5131 0 : m_dEndAngle = 360.0 - m_dEndAngle;
5132 : }
5133 :
5134 : // An arc is defined by its defining ellipse's MBR:
5135 :
5136 8 : double dXMin = 0.0;
5137 8 : double dYMin = 0.0;
5138 8 : double dXMax = 0.0;
5139 8 : double dYMax = 0.0;
5140 :
5141 8 : poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX,
5142 : poArcHdr->m_nArcEllipseMinY, dXMin, dYMin);
5143 8 : poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX,
5144 : poArcHdr->m_nArcEllipseMaxY, dXMax, dYMax);
5145 :
5146 8 : m_dCenterX = (dXMin + dXMax) / 2.0;
5147 8 : m_dCenterY = (dYMin + dYMax) / 2.0;
5148 8 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
5149 8 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
5150 :
5151 : // Read the Arc's MBR and use that as this feature's MBR
5152 8 : poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, dXMin, dYMin);
5153 8 : poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, dXMax, dYMax);
5154 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
5155 :
5156 8 : m_nPenDefIndex = poArcHdr->m_nPenId; // Pen index
5157 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5158 :
5159 : /*-----------------------------------------------------------------
5160 : * Create and fill geometry object
5161 : * For the OGR geometry, we generate an arc with 2 degrees line
5162 : * segments.
5163 : *----------------------------------------------------------------*/
5164 8 : OGRLineString *poLine = new OGRLineString;
5165 :
5166 : const int numPts = std::max(
5167 16 : 2,
5168 8 : (m_dEndAngle < m_dStartAngle
5169 8 : ? static_cast<int>(
5170 0 : std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
5171 8 : : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
5172 8 : 1)));
5173 :
5174 8 : TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
5175 8 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
5176 8 : m_dEndAngle * M_PI / 180.0);
5177 :
5178 8 : SetGeometryDirectly(poLine);
5179 :
5180 8 : return 0;
5181 : }
5182 :
5183 : /**********************************************************************
5184 : * TABArc::WriteGeometryToMAPFile()
5185 : *
5186 : * Write the geometry and representation (color, etc...) part of the
5187 : * feature to the .MAP object pointed to by poMAPFile.
5188 : *
5189 : * It is assumed that poMAPFile currently points to a valid map object.
5190 : *
5191 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5192 : * been called.
5193 : **********************************************************************/
5194 0 : int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5195 : TABMAPObjHdr *poObjHdr,
5196 : GBool bCoordBlockDataOnly /*=FALSE*/,
5197 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
5198 : {
5199 : /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */
5200 0 : if (bCoordBlockDataOnly)
5201 0 : return 0;
5202 :
5203 : /*-----------------------------------------------------------------
5204 : * We assume that ValidateMapInfoType() was called already and that
5205 : * the type in poObjHdr->m_nType is valid.
5206 : *----------------------------------------------------------------*/
5207 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5208 :
5209 : /*-----------------------------------------------------------------
5210 : * Fetch and validate geometry
5211 : * In the case of ARCs, this is all done inside UpdateMBR()
5212 : *----------------------------------------------------------------*/
5213 0 : if (UpdateMBR(poMapFile) != 0)
5214 0 : return -1; /* Error already reported */
5215 :
5216 : /*-----------------------------------------------------------------
5217 : * Copy object information
5218 : *----------------------------------------------------------------*/
5219 0 : TABMAPObjArc *poArcHdr = cpl::down_cast<TABMAPObjArc *>(poObjHdr);
5220 :
5221 : /*-------------------------------------------------------------
5222 : * Start/End angles
5223 : * Since we ALWAYS produce files in quadrant 1 then we can
5224 : * ignore the special angle conversion required by flipped axis.
5225 : *
5226 : * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile()
5227 : *------------------------------------------------------------*/
5228 0 : CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1);
5229 :
5230 0 : poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle * 10.0);
5231 0 : poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle * 10.0);
5232 :
5233 : // An arc is defined by its defining ellipse's MBR:
5234 0 : poMapFile->Coordsys2Int(m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5235 0 : poArcHdr->m_nArcEllipseMinX,
5236 0 : poArcHdr->m_nArcEllipseMinY);
5237 0 : poMapFile->Coordsys2Int(m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5238 0 : poArcHdr->m_nArcEllipseMaxX,
5239 0 : poArcHdr->m_nArcEllipseMaxY);
5240 :
5241 : // Pass the Arc's actual MBR (values were set in UpdateMBR())
5242 0 : poArcHdr->m_nMinX = m_nXMin;
5243 0 : poArcHdr->m_nMinY = m_nYMin;
5244 0 : poArcHdr->m_nMaxX = m_nXMax;
5245 0 : poArcHdr->m_nMaxY = m_nYMax;
5246 :
5247 0 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5248 0 : poArcHdr->m_nPenId = static_cast<GByte>(m_nPenDefIndex); // Pen index
5249 :
5250 0 : if (CPLGetLastErrorType() == CE_Failure)
5251 0 : return -1;
5252 :
5253 0 : return 0;
5254 : }
5255 :
5256 : /**********************************************************************
5257 : * TABArc::SetStart/EndAngle()
5258 : *
5259 : * Set the start/end angle values in degrees, making sure the values are
5260 : * always in the range [0..360]
5261 : **********************************************************************/
5262 0 : void TABArc::SetStartAngle(double dAngle)
5263 : {
5264 0 : dAngle = fmod(dAngle, 360.0);
5265 0 : if (dAngle < 0.0)
5266 0 : dAngle += 360.0;
5267 :
5268 0 : m_dStartAngle = dAngle;
5269 0 : }
5270 :
5271 0 : void TABArc::SetEndAngle(double dAngle)
5272 : {
5273 0 : dAngle = fmod(dAngle, 360.0);
5274 0 : if (dAngle < 0.0)
5275 0 : dAngle += 360.0;
5276 :
5277 0 : m_dEndAngle = dAngle;
5278 0 : }
5279 :
5280 : /**********************************************************************
5281 : * TABArc::GetStyleString() const
5282 : *
5283 : * Return style string for this feature.
5284 : *
5285 : * Style String is built only once during the first call to GetStyleString().
5286 : **********************************************************************/
5287 14 : const char *TABArc::GetStyleString() const
5288 : {
5289 14 : if (m_pszStyleString == nullptr)
5290 : {
5291 10 : m_pszStyleString = CPLStrdup(GetPenStyleString());
5292 : }
5293 :
5294 14 : return m_pszStyleString;
5295 : }
5296 :
5297 : /**********************************************************************
5298 : * TABArc::DumpMIF()
5299 : *
5300 : * Dump feature geometry in a format similar to .MIF REGIONs.
5301 : **********************************************************************/
5302 0 : void TABArc::DumpMIF(FILE *fpOut /*=NULL*/)
5303 : {
5304 0 : if (fpOut == nullptr)
5305 0 : fpOut = stdout;
5306 :
5307 : /*-----------------------------------------------------------------
5308 : * Output ARC parameters
5309 : *----------------------------------------------------------------*/
5310 0 : fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g %d %d)\n",
5311 0 : m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius,
5312 0 : m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius,
5313 0 : static_cast<int>(m_dStartAngle), static_cast<int>(m_dEndAngle));
5314 :
5315 : /*-----------------------------------------------------------------
5316 : * Fetch and validate geometry
5317 : *----------------------------------------------------------------*/
5318 0 : OGRGeometry *poGeom = GetGeometryRef();
5319 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
5320 : {
5321 : /*-------------------------------------------------------------
5322 : * Generate arc output as a simple polyline
5323 : * We could also output as an ELLIPSE in a real MIF generator
5324 : *------------------------------------------------------------*/
5325 0 : OGRLineString *poLine = poGeom->toLineString();
5326 0 : const int numPoints = poLine->getNumPoints();
5327 0 : fprintf(fpOut, "PLINE %d\n", numPoints);
5328 0 : for (int i = 0; i < numPoints; i++)
5329 0 : fprintf(fpOut, "%.15g %.15g\n", poLine->getX(i), poLine->getY(i));
5330 : }
5331 : else
5332 : {
5333 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5334 : "TABArc: Missing or Invalid Geometry!");
5335 0 : return;
5336 : }
5337 :
5338 : // Finish with PEN/BRUSH/etc. clauses
5339 0 : DumpPenDef();
5340 :
5341 0 : fflush(fpOut);
5342 : }
5343 :
5344 : /*=====================================================================
5345 : * class TABText
5346 : *====================================================================*/
5347 :
5348 : /**********************************************************************
5349 : * TABText::TABText()
5350 : *
5351 : * Constructor.
5352 : **********************************************************************/
5353 309 : TABText::TABText(OGRFeatureDefn *poDefnIn)
5354 : : TABFeature(poDefnIn), m_pszString(nullptr), m_dAngle(0.0), m_dHeight(0.0),
5355 : m_dWidth(0.0), m_dfLineEndX(0.0), m_dfLineEndY(0.0), m_bLineEndSet(FALSE),
5356 : m_rgbForeground(0x000000), m_rgbBackground(0xffffff),
5357 : m_rgbOutline(0xffffff), m_rgbShadow(0x808080), m_nTextAlignment(0),
5358 309 : m_nFontStyle(0)
5359 : {
5360 309 : }
5361 :
5362 : /**********************************************************************
5363 : * TABText::~TABText()
5364 : *
5365 : * Destructor.
5366 : **********************************************************************/
5367 618 : TABText::~TABText()
5368 : {
5369 309 : CPLFree(m_pszString);
5370 618 : }
5371 :
5372 : /**********************************************************************
5373 : * TABText::CloneTABFeature()
5374 : *
5375 : * Duplicate feature, including stuff specific to each TABFeature type.
5376 : *
5377 : * This method calls the generic TABFeature::CopyTABFeatureBase() and
5378 : * then copies any members specific to its own type.
5379 : **********************************************************************/
5380 0 : TABFeature *TABText::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
5381 : {
5382 : /*-----------------------------------------------------------------
5383 : * Alloc new feature and copy the base stuff
5384 : *----------------------------------------------------------------*/
5385 0 : TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef());
5386 :
5387 0 : CopyTABFeatureBase(poNew);
5388 :
5389 : /*-----------------------------------------------------------------
5390 : * And members specific to this class
5391 : *----------------------------------------------------------------*/
5392 : // ITABFeaturePen
5393 0 : *(poNew->GetPenDefRef()) = *GetPenDefRef();
5394 :
5395 : // ITABFeatureFont
5396 0 : *(poNew->GetFontDefRef()) = *GetFontDefRef();
5397 :
5398 0 : poNew->SetTextString(GetTextString());
5399 0 : poNew->SetTextAngle(GetTextAngle());
5400 0 : poNew->SetTextBoxHeight(GetTextBoxHeight());
5401 0 : poNew->SetTextBoxWidth(GetTextBoxWidth());
5402 0 : poNew->SetFontStyleTABValue(GetFontStyleTABValue());
5403 0 : poNew->SetFontBGColor(GetFontBGColor());
5404 0 : poNew->SetFontFGColor(GetFontFGColor());
5405 0 : poNew->SetFontOColor(GetFontOColor());
5406 0 : poNew->SetFontSColor(GetFontSColor());
5407 :
5408 0 : poNew->SetTextJustification(GetTextJustification());
5409 0 : poNew->SetTextSpacing(GetTextSpacing());
5410 : // Note: Text arrow/line coordinates are not transported... but
5411 : // we ignore them most of the time anyways.
5412 0 : poNew->SetTextLineType(TABTLNoLine);
5413 :
5414 0 : return poNew;
5415 : }
5416 :
5417 : /**********************************************************************
5418 : * TABText::ValidateMapInfoType()
5419 : *
5420 : * Check the feature's geometry part and return the corresponding
5421 : * mapinfo object type code. The m_nMapInfoType member will also
5422 : * be updated for further calls to GetMapInfoType();
5423 : *
5424 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
5425 : * is expected for this object class.
5426 : **********************************************************************/
5427 4 : TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
5428 : {
5429 : /*-----------------------------------------------------------------
5430 : * Fetch and validate geometry
5431 : *----------------------------------------------------------------*/
5432 4 : OGRGeometry *poGeom = GetGeometryRef();
5433 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5434 : {
5435 4 : m_nMapInfoType = TAB_GEOM_TEXT;
5436 : }
5437 : else
5438 : {
5439 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5440 : "TABText: Missing or Invalid Geometry!");
5441 0 : m_nMapInfoType = TAB_GEOM_NONE;
5442 : }
5443 :
5444 : /*-----------------------------------------------------------------
5445 : * Decide if coordinates should be compressed or not.
5446 : *----------------------------------------------------------------*/
5447 : // __TODO__ For now we always write uncompressed for this class...
5448 : // ValidateCoordType(poMapFile);
5449 4 : UpdateMBR(poMapFile);
5450 :
5451 4 : return m_nMapInfoType;
5452 : }
5453 :
5454 : /**********************************************************************
5455 : * TABText::ReadGeometryFromMAPFile()
5456 : *
5457 : * Fill the geometry and representation (color, etc...) part of the
5458 : * feature from the contents of the .MAP object pointed to by poMAPFile.
5459 : *
5460 : * It is assumed that poMAPFile currently points to the beginning of
5461 : * a map object.
5462 : *
5463 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5464 : * been called.
5465 : **********************************************************************/
5466 8 : int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile,
5467 : TABMAPObjHdr *poObjHdr,
5468 : GBool bCoordBlockDataOnly /*=FALSE*/,
5469 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5470 : {
5471 : /*-----------------------------------------------------------------
5472 : * Fetch and validate geometry type
5473 : *----------------------------------------------------------------*/
5474 8 : m_nMapInfoType = poObjHdr->m_nType;
5475 :
5476 8 : if (m_nMapInfoType != TAB_GEOM_TEXT && m_nMapInfoType != TAB_GEOM_TEXT_C)
5477 : {
5478 0 : CPLError(
5479 : CE_Failure, CPLE_AssertionFailed,
5480 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
5481 0 : m_nMapInfoType, m_nMapInfoType);
5482 0 : return -1;
5483 : }
5484 :
5485 : /*=============================================================
5486 : * TEXT
5487 : *============================================================*/
5488 :
5489 : /*-----------------------------------------------------------------
5490 : * Read object information
5491 : *----------------------------------------------------------------*/
5492 8 : TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5493 :
5494 8 : const GInt32 nCoordBlockPtr =
5495 : poTextHdr->m_nCoordBlockPtr; // String position
5496 8 : const int nStringLen = poTextHdr->m_nCoordDataSize; // String length
5497 8 : m_nTextAlignment = poTextHdr->m_nTextAlignment; // just./spacing/arrow
5498 :
5499 : /*-------------------------------------------------------------
5500 : * Text Angle, in tenths of degree.
5501 : * Contrary to arc start/end angles, no conversion based on
5502 : * origin quadrant is required here.
5503 : *------------------------------------------------------------*/
5504 8 : m_dAngle = poTextHdr->m_nAngle / 10.0;
5505 :
5506 8 : m_nFontStyle = poTextHdr->m_nFontStyle; // Font style
5507 :
5508 8 : m_rgbForeground = (poTextHdr->m_nFGColorR * 256 * 256 +
5509 8 : poTextHdr->m_nFGColorG * 256 + poTextHdr->m_nFGColorB);
5510 8 : m_rgbBackground = (poTextHdr->m_nBGColorR * 256 * 256 +
5511 8 : poTextHdr->m_nBGColorG * 256 + poTextHdr->m_nBGColorB);
5512 8 : m_rgbOutline = m_rgbBackground;
5513 : // In MapInfo, the shadow color is always gray (128,128,128)
5514 8 : m_rgbShadow = 0x808080;
5515 :
5516 : // arrow endpoint
5517 8 : poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY,
5518 8 : m_dfLineEndX, m_dfLineEndY);
5519 8 : m_bLineEndSet = TRUE;
5520 :
5521 : // Text Height
5522 8 : double dJunk = 0.0;
5523 8 : poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight);
5524 :
5525 8 : if (!bCoordBlockDataOnly)
5526 : {
5527 8 : m_nFontDefIndex = poTextHdr->m_nFontId; // Font name index
5528 8 : poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef);
5529 : }
5530 :
5531 : // MBR after rotation
5532 8 : double dXMin = 0.0;
5533 8 : double dYMin = 0.0;
5534 8 : double dXMax = 0.0;
5535 8 : double dYMax = 0.0;
5536 8 : poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, dXMin,
5537 : dYMin);
5538 8 : poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, dXMax,
5539 : dYMax);
5540 :
5541 8 : if (!bCoordBlockDataOnly)
5542 : {
5543 8 : m_nPenDefIndex = poTextHdr->m_nPenId; // Pen index for line
5544 8 : poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef);
5545 : }
5546 :
5547 : /*-------------------------------------------------------------
5548 : * Read text string from the coord. block
5549 : * Note that the string may contain binary '\n' and '\\' chars
5550 : * that we keep to an unescaped form internally. This is to
5551 : * be like OGR drivers. See bug 1107 for details.
5552 : *------------------------------------------------------------*/
5553 : char *pszTmpString =
5554 8 : static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
5555 :
5556 8 : if (nStringLen > 0)
5557 : {
5558 8 : TABMAPCoordBlock *poCoordBlock = nullptr;
5559 :
5560 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5561 0 : poCoordBlock = *ppoCoordBlock;
5562 : else
5563 8 : poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr);
5564 16 : if (poCoordBlock == nullptr ||
5565 8 : poCoordBlock->ReadBytes(
5566 : nStringLen, reinterpret_cast<GByte *>(pszTmpString)) != 0)
5567 : {
5568 0 : CPLError(CE_Failure, CPLE_FileIO,
5569 : "Failed reading text string at offset %d", nCoordBlockPtr);
5570 0 : CPLFree(pszTmpString);
5571 0 : return -1;
5572 : }
5573 :
5574 : /* Return a ref to coord block so that caller can continue reading
5575 : * after the end of this object (used by index splitting)
5576 : */
5577 8 : if (ppoCoordBlock)
5578 0 : *ppoCoordBlock = poCoordBlock;
5579 : }
5580 :
5581 8 : pszTmpString[nStringLen] = '\0';
5582 :
5583 8 : if (!poMapFile->GetEncoding().empty())
5584 : {
5585 : char *pszUtf8String =
5586 1 : CPLRecode(pszTmpString, poMapFile->GetEncoding(), CPL_ENC_UTF8);
5587 1 : CPLFree(pszTmpString);
5588 1 : pszTmpString = pszUtf8String;
5589 : }
5590 :
5591 8 : CPLFree(m_pszString);
5592 8 : m_pszString = pszTmpString; // This string was Escaped before 20050714
5593 :
5594 : /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
5595 : */
5596 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
5597 8 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5598 :
5599 : /* Copy int MBR to feature class members */
5600 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
5601 : poObjHdr->m_nMaxY);
5602 :
5603 : /*-----------------------------------------------------------------
5604 : * Create an OGRPoint Geometry...
5605 : * The point X,Y values will be the coords of the lower-left corner before
5606 : * rotation is applied. (Note that the rotation in MapInfo is done around
5607 : * the upper-left corner)
5608 : * We need to calculate the true lower left corner of the text based
5609 : * on the MBR after rotation, the text height and the rotation angle.
5610 : *----------------------------------------------------------------*/
5611 8 : double dSin = sin(m_dAngle * M_PI / 180.0);
5612 8 : double dCos = cos(m_dAngle * M_PI / 180.0);
5613 8 : double dX = 0.0;
5614 8 : double dY = 0.0;
5615 8 : if (dSin > 0.0 && dCos > 0.0)
5616 : {
5617 7 : dX = dXMin + m_dHeight * dSin;
5618 7 : dY = dYMin;
5619 : }
5620 1 : else if (dSin > 0.0 && dCos < 0.0)
5621 : {
5622 0 : dX = dXMax;
5623 0 : dY = dYMin - m_dHeight * dCos;
5624 : }
5625 1 : else if (dSin < 0.0 && dCos < 0.0)
5626 : {
5627 0 : dX = dXMax + m_dHeight * dSin;
5628 0 : dY = dYMax;
5629 : }
5630 : else // dSin < 0 && dCos > 0
5631 : {
5632 1 : dX = dXMin;
5633 1 : dY = dYMax - m_dHeight * dCos;
5634 : }
5635 :
5636 8 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
5637 :
5638 8 : SetGeometryDirectly(poGeometry);
5639 :
5640 : /*-----------------------------------------------------------------
5641 : * Compute Text Width: the width of the Text MBR before rotation
5642 : * in ground units... unfortunately this value is not stored in the
5643 : * file, so we have to compute it with the MBR after rotation and
5644 : * the height of the MBR before rotation:
5645 : * With W = Width of MBR before rotation
5646 : * H = Height of MBR before rotation
5647 : * dX = Width of MBR after rotation
5648 : * dY = Height of MBR after rotation
5649 : * teta = rotation angle
5650 : *
5651 : * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
5652 : * W = H * (dX - H * sin(teta)) / (H * cos(teta))
5653 : *
5654 : * and for other teta values, use:
5655 : * W = H * (dY - H * cos(teta)) / (H * sin(teta))
5656 : *----------------------------------------------------------------*/
5657 8 : dSin = std::abs(dSin);
5658 8 : dCos = std::abs(dCos);
5659 8 : if (m_dHeight == 0.0)
5660 0 : m_dWidth = 0.0;
5661 8 : else if (dCos > dSin)
5662 8 : m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
5663 8 : (m_dHeight * dCos);
5664 : else
5665 0 : m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
5666 0 : (m_dHeight * dSin);
5667 8 : m_dWidth = std::abs(m_dWidth);
5668 :
5669 8 : return 0;
5670 : }
5671 :
5672 : /**********************************************************************
5673 : * TABText::WriteGeometryToMAPFile()
5674 : *
5675 : * Write the geometry and representation (color, etc...) part of the
5676 : * feature to the .MAP object pointed to by poMAPFile.
5677 : *
5678 : * It is assumed that poMAPFile currently points to a valid map object.
5679 : *
5680 : * Returns 0 on success, -1 on error, in which case CPLError() will have
5681 : * been called.
5682 : **********************************************************************/
5683 4 : int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile,
5684 : TABMAPObjHdr *poObjHdr,
5685 : GBool bCoordBlockDataOnly /*=FALSE*/,
5686 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
5687 : {
5688 : GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax;
5689 :
5690 : /*-----------------------------------------------------------------
5691 : * We assume that ValidateMapInfoType() was called already and that
5692 : * the type in poObjHdr->m_nType is valid.
5693 : *----------------------------------------------------------------*/
5694 4 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
5695 :
5696 : /*-----------------------------------------------------------------
5697 : * Fetch and validate geometry
5698 : *----------------------------------------------------------------*/
5699 4 : OGRGeometry *poGeom = GetGeometryRef();
5700 4 : OGRPoint *poPoint = nullptr;
5701 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5702 4 : poPoint = poGeom->toPoint();
5703 : else
5704 : {
5705 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
5706 : "TABText: Missing or Invalid Geometry!");
5707 0 : return -1;
5708 : }
5709 :
5710 4 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
5711 :
5712 : /*-----------------------------------------------------------------
5713 : * Write string to a coord block first...
5714 : * Note that the string may contain unescaped '\n' and '\\'
5715 : * that we have to keep like that for the MAP file.
5716 : * See MapTools bug 1107 for more details.
5717 : *----------------------------------------------------------------*/
5718 4 : TABMAPCoordBlock *poCoordBlock = nullptr;
5719 4 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
5720 0 : poCoordBlock = *ppoCoordBlock;
5721 : else
5722 4 : poCoordBlock = poMapFile->GetCurCoordBlock();
5723 4 : poCoordBlock->StartNewFeature();
5724 4 : GInt32 nCoordBlockPtr = poCoordBlock->GetCurAddress();
5725 :
5726 : // This string was escaped before 20050714
5727 8 : CPLString oTmpString(m_pszString ? m_pszString : "");
5728 4 : if (!poMapFile->GetEncoding().empty())
5729 : {
5730 0 : oTmpString.Recode(CPL_ENC_UTF8, poMapFile->GetEncoding());
5731 : }
5732 :
5733 4 : int nStringLen = static_cast<int>(oTmpString.length());
5734 :
5735 4 : if (nStringLen > 0)
5736 : {
5737 3 : poCoordBlock->WriteBytes(
5738 3 : nStringLen, reinterpret_cast<const GByte *>(oTmpString.c_str()));
5739 : }
5740 : else
5741 : {
5742 1 : nCoordBlockPtr = 0;
5743 : }
5744 :
5745 : /*-----------------------------------------------------------------
5746 : * Copy object information
5747 : *----------------------------------------------------------------*/
5748 4 : TABMAPObjText *poTextHdr = cpl::down_cast<TABMAPObjText *>(poObjHdr);
5749 :
5750 4 : poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr; // String position
5751 4 : poTextHdr->m_nCoordDataSize = nStringLen; // String length
5752 4 : poTextHdr->m_nTextAlignment = m_nTextAlignment; // just./spacing/arrow
5753 :
5754 : /*-----------------------------------------------------------------
5755 : * Text Angle, (written in tenths of degrees)
5756 : * Contrary to arc start/end angles, no conversion based on
5757 : * origin quadrant is required here.
5758 : *----------------------------------------------------------------*/
5759 4 : poTextHdr->m_nAngle = ROUND_INT(m_dAngle * 10.0);
5760 :
5761 4 : poTextHdr->m_nFontStyle = m_nFontStyle; // Font style/effect
5762 :
5763 4 : poTextHdr->m_nFGColorR = static_cast<GByte>(COLOR_R(m_rgbForeground));
5764 4 : poTextHdr->m_nFGColorG = static_cast<GByte>(COLOR_G(m_rgbForeground));
5765 4 : poTextHdr->m_nFGColorB = static_cast<GByte>(COLOR_B(m_rgbForeground));
5766 :
5767 4 : poTextHdr->m_nBGColorR = static_cast<GByte>(COLOR_R(m_rgbBackground));
5768 4 : poTextHdr->m_nBGColorG = static_cast<GByte>(COLOR_G(m_rgbBackground));
5769 4 : poTextHdr->m_nBGColorB = static_cast<GByte>(COLOR_B(m_rgbBackground));
5770 :
5771 : /*-----------------------------------------------------------------
5772 : * The OGRPoint's X,Y values were the coords of the lower-left corner
5773 : * before rotation was applied. (Note that the rotation in MapInfo is
5774 : * done around the upper-left corner)
5775 : * The Feature's MBR is the MBR of the text after rotation... that's
5776 : * what MapInfo uses to define the text location.
5777 : *----------------------------------------------------------------*/
5778 4 : double dXMin = 0.0;
5779 4 : double dYMin = 0.0;
5780 4 : double dXMax = 0.0;
5781 4 : double dYMax = 0.0;
5782 : // Make sure Feature MBR is in sync with other params
5783 :
5784 4 : UpdateMBR();
5785 4 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5786 :
5787 4 : poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin);
5788 4 : poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax);
5789 :
5790 : // Label line end point
5791 4 : double dX = 0.0;
5792 4 : double dY = 0.0;
5793 4 : GetTextLineEndPoint(dX, dY); // Make sure a default line end point is set
5794 4 : poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, poTextHdr->m_nLineEndX,
5795 4 : poTextHdr->m_nLineEndY);
5796 :
5797 : // Text Height
5798 4 : poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY);
5799 4 : poTextHdr->m_nHeight = nY;
5800 :
5801 4 : if (!bCoordBlockDataOnly)
5802 : {
5803 : // Font name
5804 4 : m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef);
5805 4 : poTextHdr->m_nFontId =
5806 4 : static_cast<GByte>(m_nFontDefIndex); // Font name index
5807 : }
5808 :
5809 : // MBR after rotation
5810 4 : poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax);
5811 :
5812 4 : if (!bCoordBlockDataOnly)
5813 : {
5814 4 : m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef);
5815 4 : poTextHdr->m_nPenId =
5816 4 : static_cast<GByte>(m_nPenDefIndex); // Pen index for line/arrow
5817 : }
5818 :
5819 4 : if (CPLGetLastErrorType() == CE_Failure)
5820 0 : return -1;
5821 :
5822 : /* Return a ref to coord block so that caller can continue writing
5823 : * after the end of this object (used by index splitting)
5824 : */
5825 4 : if (ppoCoordBlock)
5826 0 : *ppoCoordBlock = poCoordBlock;
5827 :
5828 4 : return 0;
5829 : }
5830 :
5831 : /**********************************************************************
5832 : * TABText::GetTextString()
5833 : *
5834 : * Return ref to text string value.
5835 : *
5836 : * Returned string is a reference to the internal string buffer and should
5837 : * not be modified or freed by the caller.
5838 : **********************************************************************/
5839 300 : const char *TABText::GetTextString() const
5840 : {
5841 300 : if (m_pszString == nullptr)
5842 0 : return "";
5843 :
5844 300 : return m_pszString;
5845 : }
5846 :
5847 : /**********************************************************************
5848 : * TABText::SetTextString()
5849 : *
5850 : * Set new text string value.
5851 : *
5852 : * Note: The text string may contain "\n" chars or "\\" chars
5853 : * and we expect to receive them in a 2 chars escaped form as
5854 : * described in the MIF format specs.
5855 : **********************************************************************/
5856 3 : void TABText::SetTextString(const char *pszNewStr)
5857 : {
5858 3 : CPLFree(m_pszString);
5859 3 : m_pszString = CPLStrdup(pszNewStr);
5860 3 : }
5861 :
5862 : /**********************************************************************
5863 : * TABText::GetTextAngle()
5864 : *
5865 : * Return text angle in degrees.
5866 : **********************************************************************/
5867 10 : double TABText::GetTextAngle() const
5868 : {
5869 10 : return m_dAngle;
5870 : }
5871 :
5872 211 : void TABText::SetTextAngle(double dAngle)
5873 : {
5874 : // Make sure angle is in the range [0..360]
5875 211 : dAngle = fmod(dAngle, 360.0);
5876 211 : if (dAngle < 0.0)
5877 0 : dAngle += 360.0;
5878 211 : m_dAngle = dAngle;
5879 211 : UpdateMBR();
5880 211 : }
5881 :
5882 : /**********************************************************************
5883 : * TABText::GetTextBoxHeight()
5884 : *
5885 : * Return text height in Y axis coord. units of the text box before rotation.
5886 : **********************************************************************/
5887 10 : double TABText::GetTextBoxHeight() const
5888 : {
5889 10 : return m_dHeight;
5890 : }
5891 :
5892 3 : void TABText::SetTextBoxHeight(double dHeight)
5893 : {
5894 3 : m_dHeight = dHeight;
5895 3 : UpdateMBR();
5896 3 : }
5897 :
5898 : /**********************************************************************
5899 : * TABText::GetTextBoxWidth()
5900 : *
5901 : * Return text width in X axis coord. units. of the text box before rotation.
5902 : *
5903 : * If value has not been set, then we force a default value that assumes
5904 : * that one char's box width is 60% of its height... and we ignore
5905 : * the multiline case. This should not matter when the user PROPERLY sets
5906 : * the value.
5907 : **********************************************************************/
5908 12 : double TABText::GetTextBoxWidth() const
5909 : {
5910 12 : if (m_dWidth == 0.0 && m_pszString)
5911 : {
5912 3 : m_dWidth = 0.6 * m_dHeight * strlen(m_pszString);
5913 : }
5914 12 : return m_dWidth;
5915 : }
5916 :
5917 0 : void TABText::SetTextBoxWidth(double dWidth)
5918 : {
5919 0 : m_dWidth = dWidth;
5920 0 : UpdateMBR();
5921 0 : }
5922 :
5923 : /**********************************************************************
5924 : * TABText::GetTextLineEndPoint()
5925 : *
5926 : * Return X,Y coordinates of the text label line end point.
5927 : * Default is the center of the text MBR.
5928 : **********************************************************************/
5929 4 : void TABText::GetTextLineEndPoint(double &dX, double &dY)
5930 : {
5931 4 : if (!m_bLineEndSet)
5932 : {
5933 : // Set default location at center of text MBR
5934 4 : double dXMin = 0.0;
5935 4 : double dYMin = 0.0;
5936 4 : double dXMax = 0.0;
5937 4 : double dYMax = 0.0;
5938 4 : UpdateMBR();
5939 4 : GetMBR(dXMin, dYMin, dXMax, dYMax);
5940 4 : m_dfLineEndX = (dXMin + dXMax) / 2.0;
5941 4 : m_dfLineEndY = (dYMin + dYMax) / 2.0;
5942 4 : m_bLineEndSet = TRUE;
5943 : }
5944 :
5945 : // Return values
5946 4 : dX = m_dfLineEndX;
5947 4 : dY = m_dfLineEndY;
5948 4 : }
5949 :
5950 178 : void TABText::SetTextLineEndPoint(double dX, double dY)
5951 : {
5952 178 : m_dfLineEndX = dX;
5953 178 : m_dfLineEndY = dY;
5954 178 : m_bLineEndSet = TRUE;
5955 178 : }
5956 :
5957 : /**********************************************************************
5958 : * TABText::UpdateMBR()
5959 : *
5960 : * Update the feature MBR using the text origin (OGRPoint geometry), the
5961 : * rotation angle, and the Width/height before rotation.
5962 : *
5963 : * This function cannot perform properly unless all the above have been set.
5964 : *
5965 : * Returns 0 on success, or -1 if there is no geometry in object
5966 : **********************************************************************/
5967 226 : int TABText::UpdateMBR(TABMAPFile *poMapFile /*=NULL*/)
5968 : {
5969 226 : OGRGeometry *poGeom = GetGeometryRef();
5970 226 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
5971 : {
5972 12 : OGRPoint *poPoint = poGeom->toPoint();
5973 :
5974 12 : const double dX0 = poPoint->getX();
5975 12 : const double dY0 = poPoint->getY();
5976 :
5977 12 : const double dSin = sin(m_dAngle * M_PI / 180.0);
5978 12 : const double dCos = cos(m_dAngle * M_PI / 180.0);
5979 :
5980 12 : GetTextBoxWidth(); // Force default width value if necessary.
5981 :
5982 12 : const double dX[4] = {dX0, dX0 + m_dWidth, dX0 + m_dWidth, dX0};
5983 12 : const double dY[4] = {dY0, dY0, dY0 + m_dHeight, dY0 + m_dHeight};
5984 :
5985 12 : SetMBR(dX0, dY0, dX0, dY0);
5986 60 : for (int i = 0; i < 4; i++)
5987 : {
5988 : // Rotate one of the box corners
5989 48 : const double dX1 =
5990 48 : dX0 + (dX[i] - dX0) * dCos - (dY[i] - dY0) * dSin;
5991 48 : const double dY1 =
5992 48 : dY0 + (dX[i] - dX0) * dSin + (dY[i] - dY0) * dCos;
5993 :
5994 : // And update feature MBR with rotated coordinate
5995 48 : if (dX1 < m_dXMin)
5996 9 : m_dXMin = dX1;
5997 48 : if (dX1 > m_dXMax)
5998 9 : m_dXMax = dX1;
5999 48 : if (dY1 < m_dYMin)
6000 0 : m_dYMin = dY1;
6001 48 : if (dY1 > m_dYMax)
6002 18 : m_dYMax = dY1;
6003 : }
6004 :
6005 12 : if (poMapFile)
6006 : {
6007 4 : poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin);
6008 4 : poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax);
6009 : }
6010 :
6011 12 : return 0;
6012 : }
6013 :
6014 214 : return -1;
6015 : }
6016 :
6017 : /**********************************************************************
6018 : * TABText::GetFontBGColor()
6019 : *
6020 : * Return background color.
6021 : **********************************************************************/
6022 8 : GInt32 TABText::GetFontBGColor() const
6023 : {
6024 8 : return m_rgbBackground;
6025 : }
6026 :
6027 258 : void TABText::SetFontBGColor(GInt32 rgbColor)
6028 : {
6029 258 : m_rgbBackground = rgbColor;
6030 258 : }
6031 :
6032 : /**********************************************************************
6033 : * TABText::GetFontOColor()
6034 : *
6035 : * Return outline color.
6036 : **********************************************************************/
6037 1 : GInt32 TABText::GetFontOColor() const
6038 : {
6039 1 : return m_rgbOutline;
6040 : }
6041 :
6042 1 : void TABText::SetFontOColor(GInt32 rgbColor)
6043 : {
6044 1 : m_rgbOutline = rgbColor;
6045 1 : }
6046 :
6047 : /**********************************************************************
6048 : * TABText::GetFontSColor()
6049 : *
6050 : * Return shadow color.
6051 : **********************************************************************/
6052 0 : GInt32 TABText::GetFontSColor() const
6053 : {
6054 0 : return m_rgbShadow;
6055 : }
6056 :
6057 0 : void TABText::SetFontSColor(GInt32 rgbColor)
6058 : {
6059 0 : m_rgbShadow = rgbColor;
6060 0 : }
6061 :
6062 : /**********************************************************************
6063 : * TABText::GetFontFGColor()
6064 : *
6065 : * Return foreground color.
6066 : **********************************************************************/
6067 10 : GInt32 TABText::GetFontFGColor() const
6068 : {
6069 10 : return m_rgbForeground;
6070 : }
6071 :
6072 265 : void TABText::SetFontFGColor(GInt32 rgbColor)
6073 : {
6074 265 : m_rgbForeground = rgbColor;
6075 265 : }
6076 :
6077 : /**********************************************************************
6078 : * TABText::GetTextJustification()
6079 : *
6080 : * Return text justification. Default is TABTJLeft
6081 : **********************************************************************/
6082 10 : TABTextJust TABText::GetTextJustification() const
6083 : {
6084 10 : TABTextJust eJust = TABTJLeft;
6085 :
6086 10 : if (m_nTextAlignment & 0x0200)
6087 9 : eJust = TABTJCenter;
6088 1 : else if (m_nTextAlignment & 0x0400)
6089 0 : eJust = TABTJRight;
6090 :
6091 10 : return eJust;
6092 : }
6093 :
6094 222 : void TABText::SetTextJustification(TABTextJust eJustification)
6095 : {
6096 : // Flush current value... default is TABTJLeft
6097 222 : m_nTextAlignment &= ~0x0600;
6098 : // ... and set new one.
6099 222 : if (eJustification == TABTJCenter)
6100 222 : m_nTextAlignment |= 0x0200;
6101 0 : else if (eJustification == TABTJRight)
6102 0 : m_nTextAlignment |= 0x0400;
6103 222 : }
6104 :
6105 : /**********************************************************************
6106 : * TABText::GetTextSpacing()
6107 : *
6108 : * Return text vertical spacing factor. Default is TABTSSingle
6109 : **********************************************************************/
6110 0 : TABTextSpacing TABText::GetTextSpacing() const
6111 : {
6112 0 : TABTextSpacing eSpacing = TABTSSingle;
6113 :
6114 0 : if (m_nTextAlignment & 0x0800)
6115 0 : eSpacing = TABTS1_5;
6116 0 : else if (m_nTextAlignment & 0x1000)
6117 0 : eSpacing = TABTSDouble;
6118 :
6119 0 : return eSpacing;
6120 : }
6121 :
6122 237 : void TABText::SetTextSpacing(TABTextSpacing eSpacing)
6123 : {
6124 : // Flush current value... default is TABTSSingle
6125 237 : m_nTextAlignment &= ~0x1800;
6126 : // ... and set new one.
6127 237 : if (eSpacing == TABTS1_5)
6128 0 : m_nTextAlignment |= 0x0800;
6129 237 : else if (eSpacing == TABTSDouble)
6130 237 : m_nTextAlignment |= 0x1000;
6131 237 : }
6132 :
6133 : /**********************************************************************
6134 : * TABText::GetTextLineType()
6135 : *
6136 : * Return text line (arrow) type. Default is TABTLNoLine
6137 : **********************************************************************/
6138 0 : TABTextLineType TABText::GetTextLineType() const
6139 : {
6140 0 : TABTextLineType eLine = TABTLNoLine;
6141 :
6142 0 : if (m_nTextAlignment & 0x2000)
6143 0 : eLine = TABTLSimple;
6144 0 : else if (m_nTextAlignment & 0x4000)
6145 0 : eLine = TABTLArrow;
6146 :
6147 0 : return eLine;
6148 : }
6149 :
6150 178 : void TABText::SetTextLineType(TABTextLineType eLineType)
6151 : {
6152 : // Flush current value... default is TABTLNoLine
6153 178 : m_nTextAlignment &= ~0x6000;
6154 : // ... and set new one.
6155 178 : if (eLineType == TABTLSimple)
6156 178 : m_nTextAlignment |= 0x2000;
6157 0 : else if (eLineType == TABTLArrow)
6158 0 : m_nTextAlignment |= 0x4000;
6159 178 : }
6160 :
6161 : /**********************************************************************
6162 : * TABText::QueryFontStyle()
6163 : *
6164 : * Return TRUE if the specified font style attribute is turned ON,
6165 : * or FALSE otherwise. See enum TABFontStyle for the list of styles
6166 : * that can be queried on.
6167 : **********************************************************************/
6168 385 : GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) const
6169 : {
6170 385 : return (m_nFontStyle & static_cast<int>(eStyleToQuery)) ? TRUE : FALSE;
6171 : }
6172 :
6173 264 : void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn)
6174 : {
6175 264 : if (bStyleOn)
6176 264 : m_nFontStyle |= static_cast<int>(eStyleToToggle);
6177 : else
6178 0 : m_nFontStyle &= ~static_cast<int>(eStyleToToggle);
6179 264 : }
6180 :
6181 : /**********************************************************************
6182 : * TABText::GetFontStyleMIFValue()
6183 : *
6184 : * Return the Font Style value for this object using the style values
6185 : * that are used in a MIF FONT() clause. See MIF specs (appendix A).
6186 : *
6187 : * The reason why we have to differentiate between the TAB and the MIF font
6188 : * style values is that in TAB, TABFSBox is included in the style value
6189 : * as code 0x100, but in MIF it is not included, instead it is implied by
6190 : * the presence of the BG color in the FONT() clause (the BG color is
6191 : * present only when TABFSBox or TABFSHalo is set).
6192 : * This also has the effect of shifting all the other style values > 0x100
6193 : * by 1 byte.
6194 : **********************************************************************/
6195 0 : int TABText::GetFontStyleMIFValue() const
6196 : {
6197 : // The conversion is simply to remove bit 0x100 from the value and shift
6198 : // down all values past this bit.
6199 0 : return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00 - 0x0100)) / 2;
6200 : }
6201 :
6202 261 : void TABText::SetFontStyleMIFValue(int nStyle, GBool bBGColorSet)
6203 : {
6204 261 : m_nFontStyle = static_cast<GInt16>((nStyle & 0xff) + (nStyle & 0x7f00) * 2);
6205 : // When BG color is set, then either BOX or HALO should be set.
6206 261 : if (bBGColorSet && !QueryFontStyle(TABFSHalo))
6207 254 : ToggleFontStyle(TABFSBox, TRUE);
6208 261 : }
6209 :
6210 10 : int TABText::IsFontBGColorUsed() const
6211 : {
6212 : // Font BG color is used only when BOX is set.
6213 10 : return QueryFontStyle(TABFSBox);
6214 : }
6215 :
6216 10 : int TABText::IsFontOColorUsed() const
6217 : {
6218 : // Font outline color is used only when HALO is set.
6219 10 : return QueryFontStyle(TABFSHalo);
6220 : }
6221 :
6222 10 : int TABText::IsFontSColorUsed() const
6223 : {
6224 : // Font shadow color is used only when Shadow is set.
6225 10 : return QueryFontStyle(TABFSShadow);
6226 : }
6227 :
6228 10 : int TABText::IsFontBold() const
6229 : {
6230 : // Font bold is used only when Bold is set.
6231 10 : return QueryFontStyle(TABFSBold);
6232 : }
6233 :
6234 10 : int TABText::IsFontItalic() const
6235 : {
6236 : // Font italic is used only when Italic is set.
6237 10 : return QueryFontStyle(TABFSItalic);
6238 : }
6239 :
6240 10 : int TABText::IsFontUnderline() const
6241 : {
6242 : // Font underline is used only when Underline is set.
6243 10 : return QueryFontStyle(TABFSUnderline);
6244 : }
6245 :
6246 : /**********************************************************************
6247 : * TABText::GetLabelStyleString()
6248 : *
6249 : * This is not the correct location, it should be in ITABFeatureFont,
6250 : * but it is really more easy to put it here. This fct return a complete
6251 : * string for the representation with the string to display
6252 : **********************************************************************/
6253 10 : const char *TABText::GetLabelStyleString() const
6254 : {
6255 10 : const char *pszStyle = nullptr;
6256 10 : int nStringLen = static_cast<int>(strlen(GetTextString()));
6257 : // ALL Caps, Extpanded need to modify the string value
6258 : char *pszTextString =
6259 10 : static_cast<char *>(CPLMalloc((nStringLen + 1) * sizeof(char)));
6260 : /* char szPattern[20]; */
6261 10 : int nJustification = 1;
6262 :
6263 10 : strcpy(pszTextString, GetTextString());
6264 : /* szPattern[0] = '\0'; */
6265 :
6266 10 : switch (GetTextJustification())
6267 : {
6268 9 : case TABTJCenter:
6269 9 : nJustification = 2;
6270 9 : break;
6271 0 : case TABTJRight:
6272 0 : nJustification = 3;
6273 0 : break;
6274 1 : case TABTJLeft:
6275 : default:
6276 1 : nJustification = 1;
6277 1 : break;
6278 : }
6279 :
6280 : // Compute real font size, taking number of lines ("\\n", "\n") and line
6281 : // spacing into account.
6282 10 : int numLines = 1;
6283 61 : for (int i = 0; pszTextString[i];
6284 51 : numLines +=
6285 102 : ((pszTextString[i] == '\n' ||
6286 51 : (pszTextString[i] == '\\' && pszTextString[i + 1] == 'n')) &&
6287 0 : pszTextString[i + 1] != '\0'),
6288 : ++i)
6289 : ;
6290 :
6291 10 : double dHeight = GetTextBoxHeight() / numLines;
6292 :
6293 : // In all cases, take out 20% of font height to account for line spacing
6294 10 : if (numLines > 1)
6295 : {
6296 0 : switch (GetTextSpacing())
6297 : {
6298 0 : case TABTS1_5:
6299 0 : dHeight *= (0.80 * 0.69);
6300 0 : break;
6301 0 : case TABTSDouble:
6302 0 : dHeight *= (0.66 * 0.69);
6303 0 : break;
6304 0 : default:
6305 0 : dHeight *= 0.69;
6306 : }
6307 : }
6308 : else
6309 : {
6310 10 : dHeight *= 0.69;
6311 : }
6312 :
6313 10 : if (QueryFontStyle(TABFSAllCaps))
6314 0 : for (int i = 0; pszTextString[i]; ++i)
6315 0 : if (isalpha(static_cast<unsigned char>(pszTextString[i])))
6316 0 : pszTextString[i] = static_cast<char>(
6317 0 : CPLToupper(static_cast<unsigned char>(pszTextString[i])));
6318 :
6319 : /* Escape the double quote chars and expand the text */
6320 10 : char *pszTmpTextString = nullptr;
6321 :
6322 10 : if (QueryFontStyle(TABFSExpanded))
6323 : pszTmpTextString = static_cast<char *>(
6324 0 : CPLMalloc(((nStringLen * 4) + 1) * sizeof(char)));
6325 : else
6326 : pszTmpTextString = static_cast<char *>(
6327 10 : CPLMalloc(((nStringLen * 2) + 1) * sizeof(char)));
6328 :
6329 10 : int j = 0;
6330 61 : for (int i = 0; i < nStringLen; ++i, ++j)
6331 : {
6332 51 : if (pszTextString[i] == '"')
6333 : {
6334 0 : pszTmpTextString[j] = '\\';
6335 0 : pszTmpTextString[j + 1] = pszTextString[i];
6336 0 : ++j;
6337 : }
6338 : else
6339 51 : pszTmpTextString[j] = pszTextString[i];
6340 :
6341 51 : if (QueryFontStyle(TABFSExpanded))
6342 : {
6343 0 : pszTmpTextString[j + 1] = ' ';
6344 0 : ++j;
6345 : }
6346 : }
6347 :
6348 10 : pszTmpTextString[j] = '\0';
6349 10 : CPLFree(pszTextString);
6350 : pszTextString = static_cast<char *>(
6351 10 : CPLMalloc((strlen(pszTmpTextString) + 1) * sizeof(char)));
6352 10 : strcpy(pszTextString, pszTmpTextString);
6353 10 : CPLFree(pszTmpTextString);
6354 :
6355 : const char *pszBGColor =
6356 10 : IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", GetFontBGColor()) : "";
6357 : const char *pszOColor =
6358 10 : IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", GetFontOColor()) : "";
6359 : const char *pszSColor =
6360 10 : IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", GetFontSColor()) : "";
6361 10 : const char *pszBold = IsFontBold() ? ",bo:1" : "";
6362 10 : const char *pszItalic = IsFontItalic() ? ",it:1" : "";
6363 10 : const char *pszUnderline = IsFontUnderline() ? ",un:1" : "";
6364 :
6365 10 : pszStyle = CPLSPrintf(
6366 : "LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")",
6367 : pszTextString, GetTextAngle(), dHeight, GetFontFGColor(), pszBGColor,
6368 : pszOColor, pszSColor, pszBold, pszItalic, pszUnderline, nJustification,
6369 : GetFontNameRef());
6370 :
6371 10 : CPLFree(pszTextString);
6372 10 : return pszStyle;
6373 : }
6374 :
6375 : /**********************************************************************
6376 : * TABText::GetStyleString() const
6377 : *
6378 : * Return style string for this feature.
6379 : *
6380 : * Style String is built only once during the first call to GetStyleString().
6381 : **********************************************************************/
6382 10 : const char *TABText::GetStyleString() const
6383 : {
6384 10 : if (m_pszStyleString == nullptr)
6385 : {
6386 10 : m_pszStyleString = CPLStrdup(GetLabelStyleString());
6387 : }
6388 :
6389 10 : return m_pszStyleString;
6390 : }
6391 :
6392 4 : void TABText::SetLabelFromStyleString(const char *pszStyleString)
6393 : {
6394 : // Use the Style Manager to retrieve all the information we need.
6395 4 : auto poStyleMgr = std::make_unique<OGRStyleMgr>(nullptr);
6396 0 : std::unique_ptr<OGRStyleTool> poStylePart;
6397 :
6398 : // Init the StyleMgr with the StyleString.
6399 4 : poStyleMgr->InitStyleString(pszStyleString);
6400 :
6401 : // Retrieve the Symbol info.
6402 4 : const int numParts = poStyleMgr->GetPartCount();
6403 4 : for (int i = 0; i < numParts; i++)
6404 : {
6405 4 : poStylePart.reset(poStyleMgr->GetPart(i));
6406 4 : if (poStylePart == nullptr)
6407 : {
6408 0 : continue;
6409 : }
6410 :
6411 4 : if (poStylePart->GetType() == OGRSTCLabel)
6412 : {
6413 4 : break;
6414 : }
6415 : else
6416 : {
6417 0 : poStylePart.reset();
6418 : }
6419 : }
6420 :
6421 : // If the no Symbol found, do nothing.
6422 4 : if (poStylePart == nullptr)
6423 : {
6424 0 : return;
6425 : }
6426 :
6427 4 : auto poLabelStyle = cpl::down_cast<OGRStyleLabel *>(poStylePart.get());
6428 :
6429 4 : GBool bIsNull = 0;
6430 4 : const char *pszText = poLabelStyle->TextString(bIsNull);
6431 4 : if (!bIsNull && pszText)
6432 : {
6433 3 : SetTextString(pszText);
6434 :
6435 3 : poLabelStyle->SetUnit(OGRSTUMM);
6436 3 : double dfSize = poLabelStyle->Size(bIsNull);
6437 3 : if (!bIsNull)
6438 : {
6439 3 : dfSize /= 1000;
6440 :
6441 : // Compute text box height, taking number of lines ("\\n", "\n") and
6442 : // line spacing into account.
6443 3 : int numLines = 1;
6444 18 : for (int i = 0; pszText[i];
6445 45 : numLines += ((pszText[i] == '\n' ||
6446 15 : (pszText[i] == '\\' && pszText[i + 1] == 'n')) &&
6447 0 : pszText[i + 1] != '\0'),
6448 : ++i)
6449 : ;
6450 :
6451 : // Cf GetLabelStyleString() for 0.69. We should likely also take
6452 : // into account line spacing if we knew how to compute it.
6453 3 : SetTextBoxHeight(dfSize / 0.69 * numLines);
6454 : }
6455 : }
6456 :
6457 4 : if (poLabelStyle->Bold(bIsNull))
6458 3 : ToggleFontStyle(TABFSBold, true);
6459 :
6460 4 : if (poLabelStyle->Italic(bIsNull))
6461 1 : ToggleFontStyle(TABFSItalic, true);
6462 :
6463 4 : if (poLabelStyle->Underline(bIsNull))
6464 1 : ToggleFontStyle(TABFSUnderline, true);
6465 :
6466 4 : const char *pszFontName = poLabelStyle->FontName(bIsNull);
6467 4 : if (!bIsNull && pszFontName)
6468 4 : SetFontName(pszFontName);
6469 :
6470 : // Set the ForeColor
6471 4 : const char *pszForeColor = poLabelStyle->ForeColor(bIsNull);
6472 4 : if (bIsNull)
6473 0 : pszForeColor = nullptr;
6474 4 : if (pszForeColor)
6475 : {
6476 4 : if (pszForeColor[0] == '#')
6477 4 : pszForeColor++;
6478 8 : CPLString osForeColor(pszForeColor);
6479 4 : if (strlen(pszForeColor) > 6)
6480 1 : osForeColor.resize(6);
6481 4 : const int nColor = static_cast<int>(strtol(osForeColor, nullptr, 16));
6482 4 : SetFontFGColor(static_cast<GInt32>(nColor));
6483 : }
6484 :
6485 : // Set the BackgroundColor
6486 4 : const char *pszBackColor = poLabelStyle->BackColor(bIsNull);
6487 4 : if (bIsNull)
6488 0 : pszBackColor = nullptr;
6489 4 : if (pszBackColor)
6490 : {
6491 4 : if (pszBackColor[0] == '#')
6492 4 : pszBackColor++;
6493 8 : CPLString osBackColor(pszBackColor);
6494 4 : if (strlen(pszBackColor) > 6)
6495 1 : osBackColor.resize(6);
6496 4 : const int nColor = static_cast<int>(strtol(osBackColor, nullptr, 16));
6497 4 : ToggleFontStyle(TABFSBox, true);
6498 4 : SetFontBGColor(static_cast<GInt32>(nColor));
6499 : }
6500 :
6501 : // Set the OutlineColor
6502 4 : const char *pszOutlineColor = poLabelStyle->OutlineColor(bIsNull);
6503 4 : if (bIsNull)
6504 3 : pszOutlineColor = nullptr;
6505 4 : if (pszOutlineColor)
6506 : {
6507 1 : if (pszOutlineColor[0] == '#')
6508 1 : pszOutlineColor++;
6509 2 : CPLString osOutlineColor(pszOutlineColor);
6510 1 : if (strlen(pszOutlineColor) > 6)
6511 0 : osOutlineColor.resize(6);
6512 : const int nColor =
6513 1 : static_cast<int>(strtol(osOutlineColor, nullptr, 16));
6514 1 : ToggleFontStyle(TABFSHalo, true);
6515 1 : SetFontOColor(static_cast<GInt32>(nColor));
6516 : }
6517 :
6518 : #if 0
6519 : // Commented out since it is hardcoded to 0x808080.
6520 : // Set the ShadowColor
6521 : const char* pszShadowColor = poLabelStyle->ShadowColor(bIsNull);
6522 : if(bIsNull) pszShadowColor = nullptr;
6523 : if(pszShadowColor)
6524 : {
6525 : if(pszShadowColor[0] == '#')
6526 : pszShadowColor++;
6527 : CPLString osShadowColor(pszShadowColor);
6528 : if( strlen(pszShadowColor) > 6 )
6529 : osShadowColor.resize(6);
6530 : const int nColor =
6531 : static_cast<int>(strtol(osShadowColor, nullptr, 16));
6532 : ToggleFontStyle(TABFSShadow, true);
6533 : SetFontSColor(static_cast<GInt32>(nColor));
6534 : }
6535 : #endif
6536 :
6537 4 : const double dfAngle = poLabelStyle->Angle(bIsNull);
6538 4 : if (!bIsNull)
6539 3 : SetTextAngle(dfAngle);
6540 :
6541 4 : const int nAnchor = poLabelStyle->Anchor(bIsNull);
6542 4 : if (!bIsNull)
6543 : {
6544 3 : switch ((nAnchor - 1) % 3)
6545 : {
6546 0 : case 0:
6547 0 : SetTextJustification(TABTJLeft);
6548 0 : break;
6549 3 : case 1:
6550 3 : SetTextJustification(TABTJCenter);
6551 3 : break;
6552 0 : default /* 2 */:
6553 0 : SetTextJustification(TABTJRight);
6554 0 : break;
6555 : }
6556 : }
6557 : }
6558 :
6559 : /**********************************************************************
6560 : * TABText::DumpMIF()
6561 : *
6562 : * Dump feature geometry in a format similar to .MIF REGIONs.
6563 : **********************************************************************/
6564 0 : void TABText::DumpMIF(FILE *fpOut /*=NULL*/)
6565 : {
6566 0 : if (fpOut == nullptr)
6567 0 : fpOut = stdout;
6568 :
6569 : /*-----------------------------------------------------------------
6570 : * Fetch and validate geometry
6571 : *----------------------------------------------------------------*/
6572 0 : OGRGeometry *poGeom = GetGeometryRef();
6573 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6574 : {
6575 : /*-------------------------------------------------------------
6576 : * Generate output for text object
6577 : *------------------------------------------------------------*/
6578 0 : OGRPoint *poPoint = poGeom->toPoint();
6579 :
6580 0 : fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n",
6581 0 : m_pszString ? m_pszString : "", poPoint->getX(),
6582 : poPoint->getY());
6583 :
6584 0 : fprintf(fpOut, " m_pszString = '%s'\n", m_pszString);
6585 0 : fprintf(fpOut, " m_dAngle = %.15g\n", m_dAngle);
6586 0 : fprintf(fpOut, " m_dHeight = %.15g\n", m_dHeight);
6587 0 : fprintf(fpOut, " m_rgbForeground = 0x%6.6x (%d)\n", m_rgbForeground,
6588 : m_rgbForeground);
6589 0 : fprintf(fpOut, " m_rgbBackground = 0x%6.6x (%d)\n", m_rgbBackground,
6590 : m_rgbBackground);
6591 0 : fprintf(fpOut, " m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment);
6592 0 : fprintf(fpOut, " m_nFontStyle = 0x%4.4x\n", m_nFontStyle);
6593 : }
6594 : else
6595 : {
6596 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6597 : "TABText: Missing or Invalid Geometry!");
6598 0 : return;
6599 : }
6600 :
6601 : // Finish with PEN/BRUSH/etc. clauses
6602 0 : DumpPenDef();
6603 0 : DumpFontDef();
6604 :
6605 0 : fflush(fpOut);
6606 : }
6607 :
6608 : /*=====================================================================
6609 : * class TABMultiPoint
6610 : *====================================================================*/
6611 :
6612 : /**********************************************************************
6613 : * TABMultiPoint::TABMultiPoint()
6614 : *
6615 : * Constructor.
6616 : **********************************************************************/
6617 191 : TABMultiPoint::TABMultiPoint(OGRFeatureDefn *poDefnIn)
6618 : : TABFeature(poDefnIn), m_bCenterIsSet(FALSE), m_dCenterX(0.0),
6619 191 : m_dCenterY(0.0)
6620 : {
6621 191 : }
6622 :
6623 : /**********************************************************************
6624 : * TABMultiPoint::~TABMultiPoint()
6625 : *
6626 : * Destructor.
6627 : **********************************************************************/
6628 382 : TABMultiPoint::~TABMultiPoint()
6629 : {
6630 382 : }
6631 :
6632 : /**********************************************************************
6633 : * TABMultiPoint::CloneTABFeature()
6634 : *
6635 : * Duplicate feature, including stuff specific to each TABFeature type.
6636 : *
6637 : * This method calls the generic TABFeature::CloneTABFeature() and
6638 : * then copies any members specific to its own type.
6639 : **********************************************************************/
6640 0 : TABFeature *TABMultiPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
6641 : {
6642 : /*-----------------------------------------------------------------
6643 : * Alloc new feature and copy the base stuff
6644 : *----------------------------------------------------------------*/
6645 : TABMultiPoint *poNew =
6646 0 : new TABMultiPoint(poNewDefn ? poNewDefn : GetDefnRef());
6647 :
6648 0 : CopyTABFeatureBase(poNew);
6649 :
6650 : /*-----------------------------------------------------------------
6651 : * And members specific to this class
6652 : *----------------------------------------------------------------*/
6653 : // ITABFeatureSymbol
6654 0 : *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef();
6655 :
6656 0 : poNew->m_bCenterIsSet = m_bCenterIsSet;
6657 0 : poNew->m_dCenterX = m_dCenterX;
6658 0 : poNew->m_dCenterY = m_dCenterY;
6659 :
6660 0 : return poNew;
6661 : }
6662 :
6663 : /**********************************************************************
6664 : * TABMultiPoint::ValidateMapInfoType()
6665 : *
6666 : * Check the feature's geometry part and return the corresponding
6667 : * mapinfo object type code. The m_nMapInfoType member will also
6668 : * be updated for further calls to GetMapInfoType();
6669 : *
6670 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
6671 : * is expected for this object class.
6672 : **********************************************************************/
6673 0 : TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
6674 : {
6675 : /*-----------------------------------------------------------------
6676 : * Fetch and validate geometry
6677 : *----------------------------------------------------------------*/
6678 0 : OGRGeometry *poGeom = GetGeometryRef();
6679 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6680 : {
6681 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6682 :
6683 0 : if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES)
6684 0 : m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT;
6685 : else
6686 0 : m_nMapInfoType = TAB_GEOM_MULTIPOINT;
6687 : }
6688 : else
6689 : {
6690 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6691 : "TABMultiPoint: Missing or Invalid Geometry!");
6692 0 : m_nMapInfoType = TAB_GEOM_NONE;
6693 : }
6694 :
6695 : /*-----------------------------------------------------------------
6696 : * Decide if coordinates should be compressed or not.
6697 : *----------------------------------------------------------------*/
6698 0 : ValidateCoordType(poMapFile);
6699 :
6700 0 : return m_nMapInfoType;
6701 : }
6702 :
6703 : /**********************************************************************
6704 : * TABMultiPoint::ReadGeometryFromMAPFile()
6705 : *
6706 : * Fill the geometry and representation (color, etc...) part of the
6707 : * feature from the contents of the .MAP object pointed to by poMAPFile.
6708 : *
6709 : * It is assumed that poMAPFile currently points to the beginning of
6710 : * a map object.
6711 : *
6712 : * Returns 0 on success, -1 on error, in which case CPLError() will have
6713 : * been called.
6714 : **********************************************************************/
6715 8 : int TABMultiPoint::ReadGeometryFromMAPFile(
6716 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6717 : GBool bCoordBlockDataOnly /*=FALSE*/,
6718 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6719 : {
6720 8 : double dXMin = 0.0;
6721 8 : double dYMin = 0.0;
6722 8 : double dXMax = 0.0;
6723 8 : double dYMax = 0.0;
6724 8 : OGRGeometry *poGeometry = nullptr;
6725 8 : GBool bComprCoord = poObjHdr->IsCompressedType();
6726 8 : TABMAPCoordBlock *poCoordBlock = nullptr;
6727 :
6728 : /*-----------------------------------------------------------------
6729 : * Fetch and validate geometry type
6730 : *----------------------------------------------------------------*/
6731 8 : m_nMapInfoType = poObjHdr->m_nType;
6732 :
6733 : /*-----------------------------------------------------------------
6734 : * Read object information
6735 : *----------------------------------------------------------------*/
6736 8 : if (m_nMapInfoType == TAB_GEOM_MULTIPOINT ||
6737 8 : m_nMapInfoType == TAB_GEOM_MULTIPOINT_C ||
6738 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT ||
6739 0 : m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C)
6740 : {
6741 : /*-------------------------------------------------------------
6742 : * Copy data from poObjHdr
6743 : *------------------------------------------------------------*/
6744 : TABMAPObjMultiPoint *poMPointHdr =
6745 8 : cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6746 :
6747 8 : const GUInt32 nMinimumBytesForPoints =
6748 8 : (bComprCoord ? 4 : 8) * poMPointHdr->m_nNumPoints;
6749 8 : if (nMinimumBytesForPoints > 1024 * 1024 &&
6750 0 : nMinimumBytesForPoints > poMapFile->GetFileSize())
6751 : {
6752 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many points");
6753 0 : return -1;
6754 : }
6755 :
6756 : // MBR
6757 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY,
6758 : dXMin, dYMin);
6759 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY,
6760 : dXMax, dYMax);
6761 :
6762 8 : if (!bCoordBlockDataOnly)
6763 : {
6764 8 : m_nSymbolDefIndex = poMPointHdr->m_nSymbolId; // Symbol index
6765 8 : poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef);
6766 : }
6767 :
6768 8 : double dX = 0.0;
6769 8 : double dY = 0.0;
6770 : // Centroid/label point
6771 8 : poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY,
6772 : dX, dY);
6773 8 : SetCenter(dX, dY);
6774 :
6775 : // Compressed coordinate origin (useful only in compressed case!)
6776 8 : m_nComprOrgX = poMPointHdr->m_nComprOrgX;
6777 8 : m_nComprOrgY = poMPointHdr->m_nComprOrgY;
6778 :
6779 : /*-------------------------------------------------------------
6780 : * Read Point Coordinates
6781 : *------------------------------------------------------------*/
6782 8 : OGRMultiPoint *poMultiPoint = new OGRMultiPoint();
6783 8 : poGeometry = poMultiPoint;
6784 :
6785 8 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6786 4 : poCoordBlock = *ppoCoordBlock;
6787 : else
6788 : poCoordBlock =
6789 4 : poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr);
6790 8 : if (poCoordBlock == nullptr)
6791 : {
6792 0 : delete poGeometry;
6793 0 : return -1;
6794 : }
6795 8 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6796 :
6797 24 : for (int iPoint = 0; iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6798 : {
6799 16 : GInt32 nX = 0;
6800 16 : GInt32 nY = 0;
6801 16 : if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0)
6802 : {
6803 0 : CPLError(CE_Failure, CPLE_FileIO,
6804 : "Failed reading coordinate data at offset %d",
6805 : poMPointHdr->m_nCoordBlockPtr);
6806 0 : delete poGeometry;
6807 0 : return -1;
6808 : }
6809 :
6810 16 : poMapFile->Int2Coordsys(nX, nY, dX, dY);
6811 16 : OGRPoint *poPoint = new OGRPoint(dX, dY);
6812 :
6813 16 : if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
6814 : {
6815 0 : CPLAssert(false); // Just in case lower-level lib is modified
6816 : }
6817 8 : }
6818 : }
6819 : else
6820 : {
6821 0 : CPLError(
6822 : CE_Failure, CPLE_AssertionFailed,
6823 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
6824 0 : m_nMapInfoType, m_nMapInfoType);
6825 0 : return -1;
6826 : }
6827 :
6828 8 : SetGeometryDirectly(poGeometry);
6829 :
6830 8 : SetMBR(dXMin, dYMin, dXMax, dYMax);
6831 :
6832 : /* Copy int MBR to feature class members */
6833 8 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
6834 : poObjHdr->m_nMaxY);
6835 :
6836 : /* Return a ref to coord block so that caller can continue reading
6837 : * after the end of this object (used by TABCollection and index splitting)
6838 : */
6839 8 : if (ppoCoordBlock)
6840 4 : *ppoCoordBlock = poCoordBlock;
6841 :
6842 8 : return 0;
6843 : }
6844 :
6845 : /**********************************************************************
6846 : * TABMultiPoint::WriteGeometryToMAPFile()
6847 : *
6848 : * Write the geometry and representation (color, etc...) part of the
6849 : * feature to the .MAP object pointed to by poMAPFile.
6850 : *
6851 : * It is assumed that poMAPFile currently points to a valid map object.
6852 : *
6853 : * Returns 0 on success, -1 on error, in which case CPLError() will have
6854 : * been called.
6855 : **********************************************************************/
6856 0 : int TABMultiPoint::WriteGeometryToMAPFile(
6857 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
6858 : GBool bCoordBlockDataOnly /*=FALSE*/,
6859 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
6860 : {
6861 : GInt32 nX, nY;
6862 :
6863 : /*-----------------------------------------------------------------
6864 : * We assume that ValidateMapInfoType() was called already and that
6865 : * the type in poObjHdr->m_nType is valid.
6866 : *----------------------------------------------------------------*/
6867 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
6868 :
6869 : TABMAPObjMultiPoint *poMPointHdr =
6870 0 : cpl::down_cast<TABMAPObjMultiPoint *>(poObjHdr);
6871 :
6872 : /*-----------------------------------------------------------------
6873 : * Fetch and validate geometry
6874 : *----------------------------------------------------------------*/
6875 0 : OGRGeometry *poGeom = GetGeometryRef();
6876 0 : OGRMultiPoint *poMPoint = nullptr;
6877 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6878 0 : poMPoint = poGeom->toMultiPoint();
6879 : else
6880 : {
6881 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6882 : "TABMultiPoint: Missing or Invalid Geometry!");
6883 0 : return -1;
6884 : }
6885 :
6886 0 : poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries();
6887 :
6888 : /*-----------------------------------------------------------------
6889 : * Write data to coordinate block
6890 : *----------------------------------------------------------------*/
6891 0 : const GBool bCompressed = poObjHdr->IsCompressedType();
6892 :
6893 0 : TABMAPCoordBlock *poCoordBlock = nullptr;
6894 0 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
6895 0 : poCoordBlock = *ppoCoordBlock;
6896 : else
6897 0 : poCoordBlock = poMapFile->GetCurCoordBlock();
6898 0 : poCoordBlock->StartNewFeature();
6899 0 : poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
6900 0 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
6901 :
6902 0 : for (int iPoint = 0, nStatus = 0;
6903 0 : nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++)
6904 : {
6905 0 : poGeom = poMPoint->getGeometryRef(iPoint);
6906 :
6907 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6908 : {
6909 0 : OGRPoint *poPoint = poGeom->toPoint();
6910 :
6911 0 : poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY);
6912 0 : if (iPoint == 0)
6913 : {
6914 : // Default to the first point, we may use explicit value below
6915 0 : poMPointHdr->m_nLabelX = nX;
6916 0 : poMPointHdr->m_nLabelY = nY;
6917 : }
6918 :
6919 0 : if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, bCompressed)) !=
6920 : 0)
6921 : {
6922 : // Failed ... error message has already been produced
6923 0 : return nStatus;
6924 : }
6925 : }
6926 : else
6927 : {
6928 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
6929 : "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
6930 0 : return -1;
6931 : }
6932 : }
6933 :
6934 : /*-----------------------------------------------------------------
6935 : * Copy object information
6936 : *----------------------------------------------------------------*/
6937 :
6938 : // Compressed coordinate origin (useful only in compressed case!)
6939 0 : poMPointHdr->m_nComprOrgX = m_nComprOrgX;
6940 0 : poMPointHdr->m_nComprOrgY = m_nComprOrgY;
6941 :
6942 0 : poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize();
6943 0 : poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
6944 :
6945 : // Center/label point (default value already set above)
6946 0 : double dX = 0.0;
6947 0 : double dY = 0.0;
6948 0 : if (GetCenter(dX, dY) != -1)
6949 : {
6950 0 : poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX,
6951 0 : poMPointHdr->m_nLabelY);
6952 : }
6953 :
6954 0 : if (!bCoordBlockDataOnly)
6955 : {
6956 0 : m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef);
6957 0 : poMPointHdr->m_nSymbolId =
6958 0 : static_cast<GByte>(m_nSymbolDefIndex); // Symbol index
6959 : }
6960 :
6961 0 : if (CPLGetLastErrorType() == CE_Failure)
6962 0 : return -1;
6963 :
6964 : /* Return a ref to coord block so that caller can continue writing
6965 : * after the end of this object (used by index splitting)
6966 : */
6967 0 : if (ppoCoordBlock)
6968 0 : *ppoCoordBlock = poCoordBlock;
6969 :
6970 0 : return 0;
6971 : }
6972 :
6973 : /**********************************************************************
6974 : * TABMultiPoint::GetXY()
6975 : *
6976 : * Return this point's X,Y coordinates.
6977 : **********************************************************************/
6978 0 : int TABMultiPoint::GetXY(int i, double &dX, double &dY)
6979 : {
6980 : /*-----------------------------------------------------------------
6981 : * Fetch and validate geometry
6982 : *----------------------------------------------------------------*/
6983 0 : OGRGeometry *poGeom = GetGeometryRef();
6984 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
6985 : {
6986 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
6987 :
6988 0 : if (i >= 0 && i < poMPoint->getNumGeometries() &&
6989 0 : (poGeom = poMPoint->getGeometryRef(i)) != nullptr &&
6990 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
6991 : {
6992 0 : OGRPoint *poPoint = poGeom->toPoint();
6993 :
6994 0 : dX = poPoint->getX();
6995 0 : dY = poPoint->getY();
6996 : }
6997 : }
6998 : else
6999 : {
7000 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7001 : "TABMultiPoint: Missing or Invalid Geometry!");
7002 0 : dX = 0.0;
7003 0 : dY = 0.0;
7004 0 : return -1;
7005 : }
7006 :
7007 0 : return 0;
7008 : }
7009 :
7010 : /**********************************************************************
7011 : * TABMultiPoint::GetNumPoints()
7012 : *
7013 : * Return the number of points in this multipoint object
7014 : **********************************************************************/
7015 0 : int TABMultiPoint::GetNumPoints()
7016 : {
7017 : /*-----------------------------------------------------------------
7018 : * Fetch and validate geometry
7019 : *----------------------------------------------------------------*/
7020 0 : OGRGeometry *poGeom = GetGeometryRef();
7021 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7022 : {
7023 0 : OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
7024 :
7025 0 : return poMPoint->getNumGeometries();
7026 : }
7027 : else
7028 : {
7029 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7030 : "TABMultiPoint: Missing or Invalid Geometry!");
7031 0 : return 0;
7032 : }
7033 : }
7034 :
7035 : /**********************************************************************
7036 : * TABMultiPoint::GetStyleString() const
7037 : *
7038 : * Return style string for this feature.
7039 : *
7040 : * Style String is built only once during the first call to GetStyleString().
7041 : **********************************************************************/
7042 3 : const char *TABMultiPoint::GetStyleString() const
7043 : {
7044 3 : if (m_pszStyleString == nullptr)
7045 : {
7046 3 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
7047 : }
7048 :
7049 3 : return m_pszStyleString;
7050 : }
7051 :
7052 : /**********************************************************************
7053 : * TABMultiPoint::GetCenter()
7054 : *
7055 : * Returns the center point (or label point?) of the object. Compute one
7056 : * if it was not explicitly set:
7057 : *
7058 : * The default seems to be to use the first point in the collection as
7059 : * the center.. so we'll use that.
7060 : *
7061 : * Returns 0 on success, -1 on error.
7062 : **********************************************************************/
7063 0 : int TABMultiPoint::GetCenter(double &dX, double &dY)
7064 : {
7065 0 : if (!m_bCenterIsSet && GetNumPoints() > 0)
7066 : {
7067 : // The default seems to be to use the first point in the collection
7068 : // as the center... so we'll use that.
7069 0 : if (GetXY(0, m_dCenterX, m_dCenterY) == 0)
7070 0 : m_bCenterIsSet = TRUE;
7071 : }
7072 :
7073 0 : if (!m_bCenterIsSet)
7074 0 : return -1;
7075 :
7076 0 : dX = m_dCenterX;
7077 0 : dY = m_dCenterY;
7078 0 : return 0;
7079 : }
7080 :
7081 : /**********************************************************************
7082 : * TABMultiPoint::SetCenter()
7083 : *
7084 : * Set the X,Y coordinates to use as center point (or label point?)
7085 : **********************************************************************/
7086 173 : void TABMultiPoint::SetCenter(double dX, double dY)
7087 : {
7088 173 : m_dCenterX = dX;
7089 173 : m_dCenterY = dY;
7090 173 : m_bCenterIsSet = TRUE;
7091 173 : }
7092 :
7093 : /**********************************************************************
7094 : * TABMultiPoint::DumpMIF()
7095 : *
7096 : * Dump feature geometry in a format similar to .MIF POINTs.
7097 : **********************************************************************/
7098 0 : void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/)
7099 : {
7100 0 : if (fpOut == nullptr)
7101 0 : fpOut = stdout;
7102 :
7103 : /*-----------------------------------------------------------------
7104 : * Fetch and validate geometry
7105 : *----------------------------------------------------------------*/
7106 0 : OGRGeometry *poGeom = GetGeometryRef();
7107 0 : OGRMultiPoint *poMPoint = nullptr;
7108 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
7109 0 : poMPoint = poGeom->toMultiPoint();
7110 : else
7111 : {
7112 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7113 : "TABMultiPoint: Missing or Invalid Geometry!");
7114 0 : return;
7115 : }
7116 :
7117 : /*-----------------------------------------------------------------
7118 : * Generate output
7119 : *----------------------------------------------------------------*/
7120 0 : fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries());
7121 :
7122 0 : for (int iPoint = 0; iPoint < poMPoint->getNumGeometries(); iPoint++)
7123 : {
7124 0 : poGeom = poMPoint->getGeometryRef(iPoint);
7125 :
7126 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
7127 : {
7128 0 : OGRPoint *poPoint = poGeom->toPoint();
7129 0 : fprintf(fpOut, " %.15g %.15g\n", poPoint->getX(), poPoint->getY());
7130 : }
7131 : else
7132 : {
7133 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7134 : "TABMultiPoint: Invalid Geometry, expecting OGRPoint!");
7135 0 : return;
7136 : }
7137 : }
7138 :
7139 0 : DumpSymbolDef(fpOut);
7140 :
7141 0 : if (m_bCenterIsSet)
7142 0 : fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
7143 :
7144 0 : fflush(fpOut);
7145 : }
7146 :
7147 : /*=====================================================================
7148 : * class TABCollection
7149 : *====================================================================*/
7150 :
7151 : /**********************************************************************
7152 : * TABCollection::TABCollection()
7153 : *
7154 : * Constructor.
7155 : **********************************************************************/
7156 103 : TABCollection::TABCollection(OGRFeatureDefn *poDefnIn)
7157 : : TABFeature(poDefnIn), m_poRegion(nullptr), m_poPline(nullptr),
7158 103 : m_poMpoint(nullptr)
7159 : {
7160 103 : }
7161 :
7162 : /**********************************************************************
7163 : * TABCollection::~TABCollection()
7164 : *
7165 : * Destructor.
7166 : **********************************************************************/
7167 206 : TABCollection::~TABCollection()
7168 : {
7169 103 : EmptyCollection();
7170 206 : }
7171 :
7172 : /**********************************************************************
7173 : * TABCollection::EmptyCollection()
7174 : *
7175 : * Delete/free all collection components.
7176 : **********************************************************************/
7177 204 : void TABCollection::EmptyCollection()
7178 : {
7179 :
7180 204 : if (m_poRegion)
7181 : {
7182 54 : delete m_poRegion;
7183 54 : m_poRegion = nullptr;
7184 : }
7185 :
7186 204 : if (m_poPline)
7187 : {
7188 37 : delete m_poPline;
7189 37 : m_poPline = nullptr;
7190 : }
7191 :
7192 204 : if (m_poMpoint)
7193 : {
7194 6 : delete m_poMpoint;
7195 6 : m_poMpoint = nullptr;
7196 : }
7197 :
7198 : // Empty OGR Geometry Collection as well
7199 204 : SyncOGRGeometryCollection(TRUE, TRUE, TRUE);
7200 204 : }
7201 :
7202 : /**********************************************************************
7203 : * TABCollection::CloneTABFeature()
7204 : *
7205 : * Duplicate feature, including stuff specific to each TABFeature type.
7206 : *
7207 : * This method calls the generic TABFeature::CloneTABFeature() and
7208 : * then copies any members specific to its own type.
7209 : **********************************************************************/
7210 0 : TABFeature *TABCollection::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/)
7211 : {
7212 : /*-----------------------------------------------------------------
7213 : * Alloc new feature and copy the base stuff
7214 : *----------------------------------------------------------------*/
7215 : TABCollection *poNew =
7216 0 : new TABCollection(poNewDefn ? poNewDefn : GetDefnRef());
7217 :
7218 0 : CopyTABFeatureBase(poNew);
7219 :
7220 : /*-----------------------------------------------------------------
7221 : * And members specific to this class
7222 : *----------------------------------------------------------------*/
7223 :
7224 0 : if (m_poRegion)
7225 0 : poNew->SetRegionDirectly(
7226 0 : cpl::down_cast<TABRegion *>(m_poRegion->CloneTABFeature()));
7227 :
7228 0 : if (m_poPline)
7229 0 : poNew->SetPolylineDirectly(
7230 0 : cpl::down_cast<TABPolyline *>(m_poPline->CloneTABFeature()));
7231 :
7232 0 : if (m_poMpoint)
7233 0 : poNew->SetMultiPointDirectly(
7234 0 : cpl::down_cast<TABMultiPoint *>(m_poMpoint->CloneTABFeature()));
7235 :
7236 0 : return poNew;
7237 : }
7238 :
7239 : /**********************************************************************
7240 : * TABCollection::ValidateMapInfoType()
7241 : *
7242 : * Check the feature's geometry part and return the corresponding
7243 : * mapinfo object type code. The m_nMapInfoType member will also
7244 : * be updated for further calls to GetMapInfoType();
7245 : *
7246 : * Returns TAB_GEOM_NONE if the geometry is not compatible with what
7247 : * is expected for this object class.
7248 : **********************************************************************/
7249 0 : TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/)
7250 : {
7251 0 : int nRegionType = TAB_GEOM_NONE;
7252 0 : int nPLineType = TAB_GEOM_NONE;
7253 0 : int nMPointType = TAB_GEOM_NONE;
7254 0 : int nVersion = 650;
7255 :
7256 : /*-----------------------------------------------------------------
7257 : * Fetch and validate geometry
7258 : *----------------------------------------------------------------*/
7259 0 : OGRGeometry *poGeom = GetGeometryRef();
7260 0 : if (poGeom &&
7261 0 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
7262 : {
7263 0 : m_nMapInfoType = TAB_GEOM_COLLECTION;
7264 : }
7265 : else
7266 : {
7267 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
7268 : "TABCollection: Missing or Invalid Geometry!");
7269 0 : m_nMapInfoType = TAB_GEOM_NONE;
7270 : }
7271 :
7272 : /*-----------------------------------------------------------------
7273 : * Decide if coordinates should be compressed or not.
7274 : *----------------------------------------------------------------*/
7275 0 : GBool bComprCoord = ValidateCoordType(poMapFile);
7276 :
7277 : /*-----------------------------------------------------------------
7278 : * Since all members of the collection share the same compressed coord
7279 : * origin, we should force the compressed origin in all components
7280 : * to be the same.
7281 : * This also implies that ValidateMapInfoType() should *NOT* be called
7282 : * again until the collection components are written by WriteGeom...()
7283 : *----------------------------------------------------------------*/
7284 :
7285 : // First pass to figure collection type...
7286 0 : if (m_poRegion)
7287 : {
7288 0 : m_poRegion->ValidateCoordType(poMapFile);
7289 0 : nRegionType = m_poRegion->ValidateMapInfoType(poMapFile);
7290 0 : if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion)
7291 0 : nVersion = TAB_GEOM_GET_VERSION(nRegionType);
7292 : }
7293 :
7294 0 : if (m_poPline)
7295 : {
7296 0 : m_poPline->ValidateCoordType(poMapFile);
7297 0 : nPLineType = m_poPline->ValidateMapInfoType(poMapFile);
7298 0 : if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion)
7299 0 : nVersion = TAB_GEOM_GET_VERSION(nPLineType);
7300 : }
7301 :
7302 0 : if (m_poMpoint)
7303 : {
7304 0 : m_poMpoint->ValidateCoordType(poMapFile);
7305 0 : nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile);
7306 0 : if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion)
7307 0 : nVersion = TAB_GEOM_GET_VERSION(nMPointType);
7308 : }
7309 :
7310 : // Need to upgrade native type of collection?
7311 0 : if (nVersion == 800)
7312 : {
7313 0 : m_nMapInfoType = TAB_GEOM_V800_COLLECTION;
7314 : }
7315 :
7316 : // Make another pass updating native type and coordinates type and origin
7317 : // of each component
7318 0 : if (m_poRegion && nRegionType != TAB_GEOM_NONE)
7319 : {
7320 0 : GInt32 nXMin = 0;
7321 0 : GInt32 nYMin = 0;
7322 0 : GInt32 nXMax = 0;
7323 0 : GInt32 nYMax = 0;
7324 0 : m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7325 0 : m_poRegion->ForceCoordTypeAndOrigin(
7326 : (nVersion == 800 ? TAB_GEOM_V800_REGION : TAB_GEOM_V450_REGION),
7327 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7328 : nYMax);
7329 : }
7330 :
7331 0 : if (m_poPline && nPLineType != TAB_GEOM_NONE)
7332 : {
7333 : GInt32 nXMin, nYMin, nXMax, nYMax;
7334 0 : m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7335 0 : m_poPline->ForceCoordTypeAndOrigin(
7336 : (nVersion == 800 ? TAB_GEOM_V800_MULTIPLINE
7337 : : TAB_GEOM_V450_MULTIPLINE),
7338 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7339 : nYMax);
7340 : }
7341 :
7342 0 : if (m_poMpoint && nMPointType != TAB_GEOM_NONE)
7343 : {
7344 : GInt32 nXMin, nYMin, nXMax, nYMax;
7345 0 : m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax);
7346 0 : m_poMpoint->ForceCoordTypeAndOrigin(
7347 : (nVersion == 800 ? TAB_GEOM_V800_MULTIPOINT : TAB_GEOM_MULTIPOINT),
7348 : bComprCoord, m_nComprOrgX, m_nComprOrgY, nXMin, nYMin, nXMax,
7349 : nYMax);
7350 : }
7351 :
7352 0 : return m_nMapInfoType;
7353 : }
7354 :
7355 : /**********************************************************************
7356 : * TABCollection::ReadLabelAndMBR()
7357 : *
7358 : * Reads the label and MBR elements of the header of a collection component
7359 : *
7360 : * Returns 0 on success, -1 on failure.
7361 : **********************************************************************/
7362 12 : int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7363 : GBool bComprCoord, GInt32 nComprOrgX,
7364 : GInt32 nComprOrgY, GInt32 &pnMinX,
7365 : GInt32 &pnMinY, GInt32 &pnMaxX,
7366 : GInt32 &pnMaxY, GInt32 &pnLabelX,
7367 : GInt32 &pnLabelY)
7368 : {
7369 : //
7370 : // The sections in the collection's coord blocks start with center/label
7371 : // point + MBR that are normally found in the object data blocks
7372 : // of regular region/pline/mulitpoint objects.
7373 : //
7374 :
7375 12 : if (bComprCoord)
7376 : {
7377 : // Region center/label point, relative to compr. coord. origin
7378 : // No it is not relative to the Object block center
7379 12 : pnLabelX = poCoordBlock->ReadInt16();
7380 12 : pnLabelY = poCoordBlock->ReadInt16();
7381 :
7382 12 : TABSaturatedAdd(pnLabelX, nComprOrgX);
7383 12 : TABSaturatedAdd(pnLabelY, nComprOrgY);
7384 :
7385 12 : pnMinX = poCoordBlock->ReadInt16(); // Read MBR
7386 12 : pnMinY = poCoordBlock->ReadInt16();
7387 12 : pnMaxX = poCoordBlock->ReadInt16();
7388 12 : pnMaxY = poCoordBlock->ReadInt16();
7389 12 : TABSaturatedAdd(pnMinX, nComprOrgX);
7390 12 : TABSaturatedAdd(pnMinY, nComprOrgY);
7391 12 : TABSaturatedAdd(pnMaxX, nComprOrgX);
7392 12 : TABSaturatedAdd(pnMaxY, nComprOrgY);
7393 : }
7394 : else
7395 : {
7396 : // Region center/label point, relative to compr. coord. origin
7397 : // No it is not relative to the Object block center
7398 0 : pnLabelX = poCoordBlock->ReadInt32();
7399 0 : pnLabelY = poCoordBlock->ReadInt32();
7400 :
7401 0 : pnMinX = poCoordBlock->ReadInt32(); // Read MBR
7402 0 : pnMinY = poCoordBlock->ReadInt32();
7403 0 : pnMaxX = poCoordBlock->ReadInt32();
7404 0 : pnMaxY = poCoordBlock->ReadInt32();
7405 : }
7406 :
7407 12 : return 0;
7408 : }
7409 :
7410 : /**********************************************************************
7411 : * TABCollection::WriteLabelAndMBR()
7412 : *
7413 : * Writes the label and MBR elements of the header of a collection component
7414 : *
7415 : * Returns 0 on success, -1 on failure.
7416 : **********************************************************************/
7417 0 : int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock,
7418 : GBool bComprCoord, GInt32 nMinX,
7419 : GInt32 nMinY, GInt32 nMaxX, GInt32 nMaxY,
7420 : GInt32 nLabelX, GInt32 nLabelY)
7421 : {
7422 : //
7423 : // The sections in the collection's coord blocks start with center/label
7424 : // point + MBR that are normally found in the object data blocks
7425 : // of regular region/pline/mulitpoint objects.
7426 : //
7427 :
7428 0 : int nStatus = 0;
7429 0 : if ((nStatus =
7430 0 : poCoordBlock->WriteIntCoord(nLabelX, nLabelY, bComprCoord)) != 0 ||
7431 0 : (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, bComprCoord)) !=
7432 0 : 0 ||
7433 0 : (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, bComprCoord)) != 0)
7434 : {
7435 : // Failed ... error message has already been produced
7436 0 : return nStatus;
7437 : }
7438 :
7439 0 : return 0;
7440 : }
7441 :
7442 : /**********************************************************************
7443 : * TABCollection::ReadGeometryFromMAPFile()
7444 : *
7445 : * Fill the geometry and representation (color, etc...) part of the
7446 : * feature from the contents of the .MAP object pointed to by poMAPFile.
7447 : *
7448 : * It is assumed that poMAPFile currently points to the beginning of
7449 : * a map object.
7450 : *
7451 : * Returns 0 on success, -1 on error, in which case CPLError() will have
7452 : * been called.
7453 : **********************************************************************/
7454 4 : int TABCollection::ReadGeometryFromMAPFile(
7455 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7456 : GBool bCoordBlockDataOnly /*=FALSE*/,
7457 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7458 : {
7459 4 : const GBool bComprCoord = poObjHdr->IsCompressedType();
7460 :
7461 : /*-----------------------------------------------------------------
7462 : * Fetch and validate geometry type
7463 : *----------------------------------------------------------------*/
7464 4 : m_nMapInfoType = poObjHdr->m_nType;
7465 :
7466 4 : if (m_nMapInfoType != TAB_GEOM_COLLECTION &&
7467 4 : m_nMapInfoType != TAB_GEOM_COLLECTION_C &&
7468 0 : m_nMapInfoType != TAB_GEOM_V800_COLLECTION &&
7469 0 : m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C)
7470 : {
7471 0 : CPLError(
7472 : CE_Failure, CPLE_AssertionFailed,
7473 : "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)",
7474 0 : m_nMapInfoType, m_nMapInfoType);
7475 0 : return -1;
7476 : }
7477 :
7478 4 : int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7479 :
7480 : // Make sure collection is empty
7481 4 : EmptyCollection();
7482 :
7483 : /*-------------------------------------------------------------
7484 : * Copy data from poObjHdr
7485 : *------------------------------------------------------------*/
7486 : TABMAPObjCollection *poCollHdr =
7487 4 : cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7488 :
7489 : // MBR
7490 4 : double dXMin = 0.0;
7491 4 : double dYMin = 0.0;
7492 4 : double dXMax = 0.0;
7493 4 : double dYMax = 0.0;
7494 4 : poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, dXMin,
7495 : dYMin);
7496 4 : poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, dXMax,
7497 : dYMax);
7498 :
7499 4 : SetMBR(dXMin, dYMin, dXMax, dYMax);
7500 :
7501 4 : SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
7502 : poObjHdr->m_nMaxY);
7503 :
7504 4 : int nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr;
7505 4 : TABMAPCoordBlock *poCoordBlock = nullptr;
7506 4 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7507 0 : poCoordBlock = *ppoCoordBlock;
7508 : else
7509 4 : poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr);
7510 :
7511 : // Compressed coordinate origin (useful only in compressed case!)
7512 4 : m_nComprOrgX = poCollHdr->m_nComprOrgX;
7513 4 : m_nComprOrgY = poCollHdr->m_nComprOrgY;
7514 :
7515 : /*-----------------------------------------------------------------
7516 : * Region Component
7517 : *----------------------------------------------------------------*/
7518 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumRegSections > 0)
7519 : {
7520 : //
7521 : // Build fake coord section header to pass to TABRegion::ReadGeom...()
7522 : //
7523 4 : TABMAPObjPLine oRegionHdr;
7524 :
7525 4 : oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7526 4 : oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7527 :
7528 : //
7529 : // The region section in the coord block starts with center/label
7530 : // point + MBR that are normally found in the object data blocks
7531 : // of regular region objects.
7532 : //
7533 :
7534 : // In V800 the mini-header starts with a copy of num_parts
7535 4 : if (nVersion >= 800)
7536 : {
7537 : // int numParts = poCoordBlock->ReadInt32();
7538 0 : CPLAssert(poCoordBlock->ReadInt32() ==
7539 : poCollHdr->m_nNumRegSections);
7540 : }
7541 :
7542 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oRegionHdr.m_nComprOrgX,
7543 : oRegionHdr.m_nComprOrgY, oRegionHdr.m_nMinX,
7544 : oRegionHdr.m_nMinY, oRegionHdr.m_nMaxX,
7545 : oRegionHdr.m_nMaxY, oRegionHdr.m_nLabelX,
7546 : oRegionHdr.m_nLabelY);
7547 :
7548 : // Set CoordBlockPtr so that TABRegion continues reading here
7549 4 : oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7550 :
7551 4 : if (bComprCoord)
7552 4 : oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C;
7553 : else
7554 0 : oRegionHdr.m_nType = TAB_GEOM_V450_REGION;
7555 4 : if (nVersion == 800)
7556 0 : oRegionHdr.m_nType = static_cast<TABGeomType>(
7557 0 : oRegionHdr.m_nType +
7558 : (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION));
7559 :
7560 4 : oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections;
7561 4 : oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId;
7562 4 : oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId;
7563 4 : oRegionHdr.m_bSmooth = 0; // TODO
7564 :
7565 : //
7566 : // Use a TABRegion to read/store the Region coord data
7567 : //
7568 4 : m_poRegion = new TABRegion(GetDefnRef());
7569 4 : if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr,
7570 : bCoordBlockDataOnly,
7571 4 : &poCoordBlock) != 0)
7572 0 : return -1;
7573 :
7574 : // Set new coord block ptr for next object
7575 : /*if (poCoordBlock)
7576 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7577 : }
7578 :
7579 : /*-----------------------------------------------------------------
7580 : * PLine Component
7581 : *----------------------------------------------------------------*/
7582 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumPLineSections > 0)
7583 : {
7584 : //
7585 : // Build fake coord section header to pass to TABPolyline::ReadGeom..()
7586 : //
7587 4 : TABMAPObjPLine oPLineHdr;
7588 :
7589 4 : oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7590 4 : oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7591 :
7592 : //
7593 : // The pline section in the coord block starts with center/label
7594 : // point + MBR that are normally found in the object data blocks
7595 : // of regular pline objects.
7596 : //
7597 :
7598 : // In V800 the mini-header starts with a copy of num_parts
7599 4 : if (nVersion >= 800)
7600 : {
7601 : // int numParts = poCoordBlock->ReadInt32();
7602 0 : CPLAssert(poCoordBlock->ReadInt32() ==
7603 : poCollHdr->m_nNumPLineSections);
7604 : }
7605 :
7606 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oPLineHdr.m_nComprOrgX,
7607 : oPLineHdr.m_nComprOrgY, oPLineHdr.m_nMinX,
7608 : oPLineHdr.m_nMinY, oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY,
7609 : oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY);
7610 :
7611 : // Set CoordBlockPtr so that TABRegion continues reading here
7612 4 : oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7613 :
7614 4 : if (bComprCoord)
7615 4 : oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C;
7616 : else
7617 0 : oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE;
7618 4 : if (nVersion == 800)
7619 0 : oPLineHdr.m_nType = static_cast<TABGeomType>(
7620 0 : oPLineHdr.m_nType +
7621 : (TAB_GEOM_V800_MULTIPLINE - TAB_GEOM_V450_MULTIPLINE));
7622 :
7623 4 : oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections;
7624 4 : oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId;
7625 4 : oPLineHdr.m_bSmooth = 0; // TODO
7626 :
7627 : //
7628 : // Use a TABPolyline to read/store the Polyline coord data
7629 : //
7630 4 : m_poPline = new TABPolyline(GetDefnRef());
7631 4 : if (m_poPline->ReadGeometryFromMAPFile(
7632 4 : poMapFile, &oPLineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7633 0 : return -1;
7634 :
7635 : // Set new coord block ptr for next object
7636 : /*if (poCoordBlock)
7637 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7638 : }
7639 :
7640 : /*-----------------------------------------------------------------
7641 : * MultiPoint Component
7642 : *----------------------------------------------------------------*/
7643 4 : if (poCoordBlock != nullptr && poCollHdr->m_nNumMultiPoints > 0)
7644 : {
7645 : //
7646 : // Build fake coord section header to pass to TABMultiPoint::ReadGeom()
7647 : //
7648 4 : TABMAPObjMultiPoint oMPointHdr;
7649 :
7650 4 : oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX;
7651 4 : oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY;
7652 :
7653 : //
7654 : // The pline section in the coord block starts with center/label
7655 : // point + MBR that are normally found in the object data blocks
7656 : // of regular pline objects.
7657 : //
7658 4 : ReadLabelAndMBR(poCoordBlock, bComprCoord, oMPointHdr.m_nComprOrgX,
7659 : oMPointHdr.m_nComprOrgY, oMPointHdr.m_nMinX,
7660 : oMPointHdr.m_nMinY, oMPointHdr.m_nMaxX,
7661 : oMPointHdr.m_nMaxY, oMPointHdr.m_nLabelX,
7662 : oMPointHdr.m_nLabelY);
7663 :
7664 : // Set CoordBlockPtr so that TABRegion continues reading here
7665 4 : oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7666 :
7667 4 : if (bComprCoord)
7668 4 : oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C;
7669 : else
7670 0 : oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT;
7671 4 : if (nVersion == 800)
7672 0 : oMPointHdr.m_nType = static_cast<TABGeomType>(
7673 0 : oMPointHdr.m_nType +
7674 : (TAB_GEOM_V800_MULTIPOINT - TAB_GEOM_MULTIPOINT));
7675 :
7676 4 : oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints;
7677 4 : oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId;
7678 :
7679 : //
7680 : // Use a TABMultiPoint to read/store the coord data
7681 : //
7682 4 : m_poMpoint = new TABMultiPoint(GetDefnRef());
7683 4 : if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr,
7684 : bCoordBlockDataOnly,
7685 4 : &poCoordBlock) != 0)
7686 0 : return -1;
7687 :
7688 : // Set new coord block ptr for next object (not really useful here)
7689 : /*if (poCoordBlock)
7690 : nCurCoordBlockPtr = poCoordBlock->GetCurAddress();*/
7691 : }
7692 :
7693 : /*-----------------------------------------------------------------
7694 : * Set the main OGRFeature Geometry
7695 : * (this is actually duplicating geometries from each member)
7696 : *----------------------------------------------------------------*/
7697 4 : if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0)
7698 0 : return -1;
7699 :
7700 : /* Return a ref to coord block so that caller can continue reading
7701 : * after the end of this object (used by index splitting)
7702 : */
7703 4 : if (ppoCoordBlock)
7704 0 : *ppoCoordBlock = poCoordBlock;
7705 :
7706 4 : return 0;
7707 : }
7708 :
7709 : /**********************************************************************
7710 : * TABCollection::WriteGeometryToMAPFile()
7711 : *
7712 : * Write the geometry and representation (color, etc...) part of the
7713 : * feature to the .MAP object pointed to by poMAPFile.
7714 : *
7715 : * It is assumed that poMAPFile currently points to a valid map object.
7716 : *
7717 : * Returns 0 on success, -1 on error, in which case CPLError() will have
7718 : * been called.
7719 : **********************************************************************/
7720 0 : int TABCollection::WriteGeometryToMAPFile(
7721 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
7722 : GBool bCoordBlockDataOnly /*=FALSE*/,
7723 : TABMAPCoordBlock **ppoCoordBlock /*=NULL*/)
7724 : {
7725 : /*-----------------------------------------------------------------
7726 : * Note that the current implementation does not allow setting the
7727 : * Geometry via OGRFeature::SetGeometry(). The geometries must be set
7728 : * via the SetRegion/Pline/MpointDirectly() methods which will take
7729 : * care of keeping the OGRFeature's geometry in sync.
7730 : *
7731 : * TODO: If we ever want to support sync'ing changes from the OGRFeature's
7732 : * geometry to the m_poRegion/Pline/Mpoint then a call should be added
7733 : * here, or perhaps in ValidateMapInfoType(), or even better in
7734 : * custom TABCollection::SetGeometry*()... but then this last option
7735 : * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR.
7736 : *----------------------------------------------------------------*/
7737 :
7738 : /*-----------------------------------------------------------------
7739 : * We assume that ValidateMapInfoType() was called already and that
7740 : * the type in poObjHdr->m_nType is valid.
7741 : *----------------------------------------------------------------*/
7742 0 : CPLAssert(m_nMapInfoType == poObjHdr->m_nType);
7743 :
7744 : TABMAPObjCollection *poCollHdr =
7745 0 : cpl::down_cast<TABMAPObjCollection *>(poObjHdr);
7746 :
7747 : /*-----------------------------------------------------------------
7748 : * Write data to coordinate block for each component...
7749 : *
7750 : * Note that at this point, the caller (TABFile) has called
7751 : * TABCollection::ValidateMapInfoType() which in turn has called
7752 : * each component's respective ValidateMapInfoType() and
7753 : * ForceCoordTypeAndCoordOrigin() so the objects are ready to have
7754 : * their respective WriteGeometryToMapFile() called.
7755 : *----------------------------------------------------------------*/
7756 0 : const GBool bCompressed = poObjHdr->IsCompressedType();
7757 : // TODO: ??? Do we need to track overall collection coord data size???
7758 0 : int nTotalFeatureDataSize = 0;
7759 :
7760 0 : const int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType);
7761 :
7762 0 : TABMAPCoordBlock *poCoordBlock = nullptr;
7763 0 : if (ppoCoordBlock != nullptr && *ppoCoordBlock != nullptr)
7764 0 : poCoordBlock = *ppoCoordBlock;
7765 : else
7766 0 : poCoordBlock = poMapFile->GetCurCoordBlock();
7767 0 : poCoordBlock->StartNewFeature();
7768 0 : poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress();
7769 0 : poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY);
7770 :
7771 : /*-----------------------------------------------------------------
7772 : * Region component
7773 : *----------------------------------------------------------------*/
7774 0 : if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE)
7775 : {
7776 0 : CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION ||
7777 : m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C ||
7778 : m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION ||
7779 : m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C);
7780 :
7781 0 : TABMAPObjPLine *poRegionHdr = cpl::down_cast<TABMAPObjPLine *>(
7782 0 : TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1));
7783 :
7784 : // Update count of objects by type in header
7785 0 : if (!bCoordBlockDataOnly)
7786 0 : poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType());
7787 :
7788 : // Write a placeholder for centroid/label point and MBR mini-header
7789 : // and we'll come back later to write the real values.
7790 : //
7791 : // Note that the call to WriteGeometryToMAPFile() below will call
7792 : // StartNewFeature() as well, so we need to track the current
7793 : // value before calling it
7794 :
7795 0 : poCoordBlock->StartNewFeature();
7796 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7797 :
7798 : // In V800 the mini-header starts with a copy of num_parts
7799 0 : if (nVersion >= 800)
7800 : {
7801 0 : poCoordBlock->WriteInt32(0);
7802 : }
7803 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7804 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7805 :
7806 0 : if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr,
7807 : bCoordBlockDataOnly,
7808 0 : &poCoordBlock) != 0)
7809 : {
7810 0 : CPLError(CE_Failure, CPLE_FileIO,
7811 : "Failed writing Region part in collection.");
7812 0 : delete poRegionHdr;
7813 0 : return -1;
7814 : }
7815 :
7816 0 : nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize;
7817 :
7818 : // Come back to write the real values in the mini-header
7819 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7820 0 : poCoordBlock->StartNewFeature();
7821 :
7822 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7823 : {
7824 0 : delete poRegionHdr;
7825 0 : return -1;
7826 : }
7827 :
7828 : // In V800 the mini-header starts with a copy of num_parts
7829 0 : if (nVersion >= 800)
7830 : {
7831 0 : poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections);
7832 : }
7833 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poRegionHdr->m_nMinX,
7834 : poRegionHdr->m_nMinY, poRegionHdr->m_nMaxX,
7835 : poRegionHdr->m_nMaxY, poRegionHdr->m_nLabelX,
7836 : poRegionHdr->m_nLabelY);
7837 :
7838 : // And finally move the pointer back to the end of this component
7839 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7840 : {
7841 0 : delete poRegionHdr;
7842 0 : return -1;
7843 : }
7844 :
7845 : // Copy other header members to the main collection header
7846 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7847 : // mini-header???
7848 0 : poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize;
7849 0 : poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections;
7850 :
7851 0 : if (!bCoordBlockDataOnly)
7852 : {
7853 0 : poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId;
7854 0 : poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId;
7855 : // TODO: Smooth flag = poRegionHdr->m_bSmooth;
7856 : }
7857 :
7858 0 : delete poRegionHdr;
7859 : }
7860 : else
7861 : {
7862 : // No Region component. Set corresponding header fields to 0
7863 :
7864 0 : poCollHdr->m_nRegionDataSize = 0;
7865 0 : poCollHdr->m_nNumRegSections = 0;
7866 0 : poCollHdr->m_nRegionPenId = 0;
7867 0 : poCollHdr->m_nRegionBrushId = 0;
7868 : }
7869 :
7870 : /*-----------------------------------------------------------------
7871 : * PLine component
7872 : *----------------------------------------------------------------*/
7873 0 : if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE)
7874 : {
7875 0 : CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE ||
7876 : m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C ||
7877 : m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE ||
7878 : m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C);
7879 :
7880 0 : TABMAPObjPLine *poPlineHdr = cpl::down_cast<TABMAPObjPLine *>(
7881 0 : TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1));
7882 :
7883 : // Update count of objects by type in header
7884 0 : if (!bCoordBlockDataOnly)
7885 0 : poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType());
7886 :
7887 : // Write a placeholder for centroid/label point and MBR mini-header
7888 : // and we'll come back later to write the real values.
7889 : //
7890 : // Note that the call to WriteGeometryToMAPFile() below will call
7891 : // StartNewFeature() as well, so we need to track the current
7892 : // value before calling it
7893 :
7894 0 : poCoordBlock->StartNewFeature();
7895 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7896 :
7897 : // In V800 the mini-header starts with a copy of num_parts
7898 0 : if (nVersion >= 800)
7899 : {
7900 0 : poCoordBlock->WriteInt32(0);
7901 : }
7902 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7903 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7904 :
7905 0 : if (m_poPline->WriteGeometryToMAPFile(
7906 0 : poMapFile, poPlineHdr, bCoordBlockDataOnly, &poCoordBlock) != 0)
7907 : {
7908 0 : CPLError(CE_Failure, CPLE_FileIO,
7909 : "Failed writing Region part in collection.");
7910 0 : delete poPlineHdr;
7911 0 : return -1;
7912 : }
7913 :
7914 0 : nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize;
7915 :
7916 : // Come back to write the real values in the mini-header
7917 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
7918 0 : poCoordBlock->StartNewFeature();
7919 :
7920 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
7921 : {
7922 0 : delete poPlineHdr;
7923 0 : return -1;
7924 : }
7925 :
7926 : // In V800 the mini-header starts with a copy of num_parts
7927 0 : if (nVersion >= 800)
7928 : {
7929 0 : poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections);
7930 : }
7931 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poPlineHdr->m_nMinX,
7932 : poPlineHdr->m_nMinY, poPlineHdr->m_nMaxX,
7933 : poPlineHdr->m_nMaxY, poPlineHdr->m_nLabelX,
7934 : poPlineHdr->m_nLabelY);
7935 :
7936 : // And finally move the pointer back to the end of this component
7937 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
7938 : {
7939 0 : delete poPlineHdr;
7940 0 : return -1;
7941 : }
7942 :
7943 : // Copy other header members to the main collection header
7944 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
7945 : // mini-header???
7946 0 : poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize;
7947 0 : poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections;
7948 0 : if (!bCoordBlockDataOnly)
7949 : {
7950 0 : poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId;
7951 : // TODO: Smooth flag = poPlineHdr->m_bSmooth;
7952 : }
7953 :
7954 0 : delete poPlineHdr;
7955 : }
7956 : else
7957 : {
7958 : // No Polyline component. Set corresponding header fields to 0
7959 :
7960 0 : poCollHdr->m_nPolylineDataSize = 0;
7961 0 : poCollHdr->m_nNumPLineSections = 0;
7962 0 : poCollHdr->m_nPolylinePenId = 0;
7963 : }
7964 :
7965 : /*-----------------------------------------------------------------
7966 : * MultiPoint component
7967 : *----------------------------------------------------------------*/
7968 0 : if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE)
7969 : {
7970 0 : CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT ||
7971 : m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C ||
7972 : m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT ||
7973 : m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C);
7974 :
7975 : TABMAPObjMultiPoint *poMpointHdr =
7976 0 : cpl::down_cast<TABMAPObjMultiPoint *>(
7977 0 : TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1));
7978 :
7979 : // Update count of objects by type in header
7980 0 : if (!bCoordBlockDataOnly)
7981 0 : poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType());
7982 :
7983 : // Write a placeholder for centroid/label point and MBR mini-header
7984 : // and we'll come back later to write the real values.
7985 : //
7986 : // Note that the call to WriteGeometryToMAPFile() below will call
7987 : // StartNewFeature() as well, so we need to track the current
7988 : // value before calling it
7989 :
7990 0 : poCoordBlock->StartNewFeature();
7991 0 : int nMiniHeaderPtr = poCoordBlock->GetCurAddress();
7992 :
7993 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, 0, 0, 0, 0, 0, 0);
7994 0 : nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize();
7995 :
7996 0 : if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr,
7997 : bCoordBlockDataOnly,
7998 0 : &poCoordBlock) != 0)
7999 : {
8000 0 : CPLError(CE_Failure, CPLE_FileIO,
8001 : "Failed writing Region part in collection.");
8002 0 : delete poMpointHdr;
8003 0 : return -1;
8004 : }
8005 :
8006 0 : nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize;
8007 :
8008 : // Come back to write the real values in the mini-header
8009 0 : int nEndOfObjectPtr = poCoordBlock->GetCurAddress();
8010 0 : poCoordBlock->StartNewFeature();
8011 :
8012 0 : if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0)
8013 : {
8014 0 : delete poMpointHdr;
8015 0 : return -1;
8016 : }
8017 :
8018 0 : WriteLabelAndMBR(poCoordBlock, bCompressed, poMpointHdr->m_nMinX,
8019 : poMpointHdr->m_nMinY, poMpointHdr->m_nMaxX,
8020 : poMpointHdr->m_nMaxY, poMpointHdr->m_nLabelX,
8021 : poMpointHdr->m_nLabelY);
8022 :
8023 : // And finally move the pointer back to the end of this component
8024 0 : if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0)
8025 : {
8026 0 : delete poMpointHdr;
8027 0 : return -1;
8028 : }
8029 :
8030 : // Copy other header members to the main collection header
8031 : // TODO: Does m_nRegionDataSize need to include the centroid+mbr
8032 : // mini-header???
8033 0 : poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize;
8034 0 : poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints;
8035 0 : if (!bCoordBlockDataOnly)
8036 : {
8037 0 : poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId;
8038 : }
8039 :
8040 0 : delete poMpointHdr;
8041 : }
8042 : else
8043 : {
8044 : // No Multipoint component. Set corresponding header fields to 0
8045 :
8046 0 : poCollHdr->m_nMPointDataSize = 0;
8047 0 : poCollHdr->m_nNumMultiPoints = 0;
8048 0 : poCollHdr->m_nMultiPointSymbolId = 0;
8049 : }
8050 :
8051 : /*-----------------------------------------------------------------
8052 : * Copy object information
8053 : *----------------------------------------------------------------*/
8054 :
8055 : // Compressed coordinate origin (useful only in compressed case!)
8056 0 : poCollHdr->m_nComprOrgX = m_nComprOrgX;
8057 0 : poCollHdr->m_nComprOrgY = m_nComprOrgY;
8058 :
8059 0 : poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize;
8060 :
8061 0 : poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax);
8062 :
8063 0 : if (CPLGetLastErrorType() == CE_Failure)
8064 0 : return -1;
8065 :
8066 : /* Return a ref to coord block so that caller can continue writing
8067 : * after the end of this object (used by index splitting)
8068 : */
8069 0 : if (ppoCoordBlock)
8070 0 : *ppoCoordBlock = poCoordBlock;
8071 :
8072 0 : return 0;
8073 : }
8074 :
8075 : /**********************************************************************
8076 : * TABCollection::SyncOGRGeometryCollection()
8077 : *
8078 : * Copy the region/pline/multipoint's geometries to the OGRFeature's
8079 : * geometry.
8080 : **********************************************************************/
8081 208 : int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion,
8082 : GBool bSyncPline,
8083 : GBool bSyncMpoint)
8084 : {
8085 208 : OGRGeometry *poThisGeom = GetGeometryRef();
8086 208 : OGRGeometryCollection *poGeomColl = nullptr;
8087 :
8088 : // poGeometry is defined in the OGRFeature class
8089 208 : if (poThisGeom == nullptr)
8090 : {
8091 103 : poGeomColl = new OGRGeometryCollection();
8092 : }
8093 105 : else if (wkbFlatten(poThisGeom->getGeometryType()) == wkbGeometryCollection)
8094 : {
8095 105 : poGeomColl = poThisGeom->toGeometryCollection();
8096 : }
8097 : else
8098 : {
8099 0 : CPLError(
8100 : CE_Failure, CPLE_AssertionFailed,
8101 : "TABCollection: Invalid Geometry. Type must be OGRCollection.");
8102 0 : return -1;
8103 : }
8104 :
8105 : /*-----------------------------------------------------------------
8106 : * Start by removing geometries that need to be replaced
8107 : * In theory there should be a single geometry of each type, but
8108 : * just in case, we'll loop over the whole collection and delete all
8109 : * instances of each type if there are some.
8110 : *----------------------------------------------------------------*/
8111 208 : int numGeometries = poGeomColl->getNumGeometries();
8112 220 : for (int i = 0; i < numGeometries; i++)
8113 : {
8114 12 : OGRGeometry *poGeom = poGeomColl->getGeometryRef(i);
8115 12 : if (!poGeom)
8116 0 : continue;
8117 :
8118 24 : if ((bSyncRegion &&
8119 12 : (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
8120 6 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) ||
8121 6 : (bSyncPline &&
8122 6 : (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ||
8123 30 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) ||
8124 6 : (bSyncMpoint &&
8125 6 : (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)))
8126 : {
8127 : // Remove this geometry
8128 12 : poGeomColl->removeGeometry(i);
8129 :
8130 : // Unless this was the last geometry, we need to restart
8131 : // scanning the collection since we modified it
8132 12 : if (i != numGeometries - 1)
8133 : {
8134 6 : i = 0;
8135 6 : numGeometries = poGeomColl->getNumGeometries();
8136 : }
8137 : }
8138 : }
8139 :
8140 : /*-----------------------------------------------------------------
8141 : * Copy TAB Feature geometries to OGRGeometryCollection
8142 : *----------------------------------------------------------------*/
8143 208 : if (bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
8144 4 : poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
8145 :
8146 208 : if (bSyncPline && m_poPline && m_poPline->GetGeometryRef() != nullptr)
8147 4 : poGeomColl->addGeometry(m_poPline->GetGeometryRef());
8148 :
8149 208 : if (bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
8150 4 : poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
8151 :
8152 208 : if (poThisGeom == nullptr)
8153 103 : SetGeometryDirectly(poGeomColl);
8154 :
8155 208 : return 0;
8156 : }
8157 :
8158 : /**********************************************************************
8159 : * TABCollection::SetRegionDirectly()
8160 : *
8161 : * Set the region component of the collection, deleting the current
8162 : * region component if there is one. The object is then owned by the
8163 : * TABCollection object. Passing NULL just deletes it.
8164 : *
8165 : * Note that an intentional side-effect is that calling this method
8166 : * with the same poRegion pointer that is already owned by this object
8167 : * will force resync'ing the OGR Geometry member.
8168 : **********************************************************************/
8169 0 : int TABCollection::SetRegionDirectly(TABRegion *poRegion)
8170 : {
8171 0 : if (m_poRegion && m_poRegion != poRegion)
8172 0 : delete m_poRegion;
8173 0 : m_poRegion = poRegion;
8174 :
8175 : // Update OGRGeometryCollection component as well
8176 0 : return SyncOGRGeometryCollection(TRUE, FALSE, FALSE);
8177 : }
8178 :
8179 : /**********************************************************************
8180 : * TABCollection::SetPolylineDirectly()
8181 : *
8182 : * Set the polyline component of the collection, deleting the current
8183 : * polyline component if there is one. The object is then owned by the
8184 : * TABCollection object. Passing NULL just deletes it.
8185 : *
8186 : * Note that an intentional side-effect is that calling this method
8187 : * with the same poPline pointer that is already owned by this object
8188 : * will force resync'ing the OGR Geometry member.
8189 : **********************************************************************/
8190 0 : int TABCollection::SetPolylineDirectly(TABPolyline *poPline)
8191 : {
8192 0 : if (m_poPline && m_poPline != poPline)
8193 0 : delete m_poPline;
8194 0 : m_poPline = poPline;
8195 :
8196 : // Update OGRGeometryCollection component as well
8197 0 : return SyncOGRGeometryCollection(FALSE, TRUE, FALSE);
8198 : }
8199 :
8200 : /**********************************************************************
8201 : * TABCollection::SetMultiPointDirectly()
8202 : *
8203 : * Set the multipoint component of the collection, deleting the current
8204 : * multipoint component if there is one. The object is then owned by the
8205 : * TABCollection object. Passing NULL just deletes it.
8206 : *
8207 : * Note that an intentional side-effect is that calling this method
8208 : * with the same poMpoint pointer that is already owned by this object
8209 : * will force resync'ing the OGR Geometry member.
8210 : **********************************************************************/
8211 0 : int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint)
8212 : {
8213 0 : if (m_poMpoint && m_poMpoint != poMpoint)
8214 0 : delete m_poMpoint;
8215 0 : m_poMpoint = poMpoint;
8216 :
8217 : // Update OGRGeometryCollection component as well
8218 0 : return SyncOGRGeometryCollection(FALSE, FALSE, TRUE);
8219 : }
8220 :
8221 : /**********************************************************************
8222 : * TABCollection::GetStyleString() const
8223 : *
8224 : * Return style string for this feature.
8225 : *
8226 : * Style String is built only once during the first call to GetStyleString().
8227 : **********************************************************************/
8228 3 : const char *TABCollection::GetStyleString() const
8229 : {
8230 3 : if (m_pszStyleString == nullptr)
8231 : {
8232 3 : m_pszStyleString = CPLStrdup(GetSymbolStyleString());
8233 : }
8234 :
8235 3 : return m_pszStyleString;
8236 : }
8237 :
8238 : /**********************************************************************
8239 : * TABCollection::DumpMIF()
8240 : *
8241 : * Dump feature geometry
8242 : **********************************************************************/
8243 0 : void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/)
8244 : {
8245 0 : if (fpOut == nullptr)
8246 0 : fpOut = stdout;
8247 :
8248 : /*-----------------------------------------------------------------
8249 : * Generate output
8250 : *----------------------------------------------------------------*/
8251 0 : int numParts = 0;
8252 0 : if (m_poRegion)
8253 0 : numParts++;
8254 0 : if (m_poPline)
8255 0 : numParts++;
8256 0 : if (m_poMpoint)
8257 0 : numParts++;
8258 :
8259 0 : fprintf(fpOut, "COLLECTION %d\n", numParts);
8260 :
8261 0 : if (m_poRegion)
8262 0 : m_poRegion->DumpMIF(fpOut);
8263 :
8264 0 : if (m_poPline)
8265 0 : m_poPline->DumpMIF(fpOut);
8266 :
8267 0 : if (m_poMpoint)
8268 0 : m_poMpoint->DumpMIF(fpOut);
8269 :
8270 0 : DumpSymbolDef(fpOut);
8271 :
8272 0 : fflush(fpOut);
8273 0 : }
8274 :
8275 : /*=====================================================================
8276 : * class TABDebugFeature
8277 : *====================================================================*/
8278 :
8279 : /**********************************************************************
8280 : * TABDebugFeature::TABDebugFeature()
8281 : *
8282 : * Constructor.
8283 : **********************************************************************/
8284 0 : TABDebugFeature::TABDebugFeature(OGRFeatureDefn *poDefnIn)
8285 0 : : TABFeature(poDefnIn), m_nSize(0), m_nCoordDataPtr(0), m_nCoordDataSize(0)
8286 : {
8287 0 : memset(m_abyBuf, 0, sizeof(m_abyBuf));
8288 0 : }
8289 :
8290 : /**********************************************************************
8291 : * TABDebugFeature::~TABDebugFeature()
8292 : *
8293 : * Destructor.
8294 : **********************************************************************/
8295 0 : TABDebugFeature::~TABDebugFeature()
8296 : {
8297 0 : }
8298 :
8299 : /**********************************************************************
8300 : * TABDebugFeature::ReadGeometryFromMAPFile()
8301 : *
8302 : * Fill the geometry and representation (color, etc...) part of the
8303 : * feature from the contents of the .MAP object pointed to by poMAPFile.
8304 : *
8305 : * It is assumed that poMAPFile currently points to the beginning of
8306 : * a map object.
8307 : *
8308 : * Returns 0 on success, -1 on error, in which case CPLError() will have
8309 : * been called.
8310 : **********************************************************************/
8311 0 : int TABDebugFeature::ReadGeometryFromMAPFile(
8312 : TABMAPFile *poMapFile, TABMAPObjHdr *poObjHdr,
8313 : GBool /*bCoordBlockDataOnly=FALSE*/,
8314 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8315 : {
8316 : /*-----------------------------------------------------------------
8317 : * Fetch geometry type
8318 : *----------------------------------------------------------------*/
8319 0 : m_nMapInfoType = poObjHdr->m_nType;
8320 :
8321 0 : TABMAPObjectBlock *poObjBlock = poMapFile->GetCurObjBlock();
8322 0 : TABMAPHeaderBlock *poHeader = poMapFile->GetHeaderBlock();
8323 :
8324 : /*-----------------------------------------------------------------
8325 : * If object type has coords in a type 3 block, then its position
8326 : * follows
8327 : *----------------------------------------------------------------*/
8328 0 : if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType))
8329 : {
8330 0 : m_nCoordDataPtr = poObjBlock->ReadInt32();
8331 0 : m_nCoordDataSize = poObjBlock->ReadInt32();
8332 : }
8333 : else
8334 : {
8335 0 : m_nCoordDataPtr = -1;
8336 0 : m_nCoordDataSize = 0;
8337 : }
8338 :
8339 0 : m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType);
8340 0 : if (m_nSize > 0)
8341 : {
8342 0 : poObjBlock->GotoByteRel(-5); // Go back to beginning of header
8343 0 : poObjBlock->ReadBytes(
8344 0 : std::min(m_nSize, static_cast<int>(sizeof(m_abyBuf))), m_abyBuf);
8345 : }
8346 :
8347 0 : return 0;
8348 : }
8349 :
8350 : /**********************************************************************
8351 : * TABDebugFeature::WriteGeometryToMAPFile()
8352 : *
8353 : * Write the geometry and representation (color, etc...) part of the
8354 : * feature to the .MAP object pointed to by poMAPFile.
8355 : *
8356 : * It is assumed that poMAPFile currently points to a valid map object.
8357 : *
8358 : * Returns 0 on success, -1 on error, in which case CPLError() will have
8359 : * been called.
8360 : **********************************************************************/
8361 0 : int TABDebugFeature::WriteGeometryToMAPFile(
8362 : TABMAPFile * /*poMapFile*/, TABMAPObjHdr * /*poObjHdr*/,
8363 : GBool /*bCoordBlockDataOnly=FALSE*/,
8364 : TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/)
8365 : {
8366 : // Nothing to do here!
8367 :
8368 0 : CPLError(CE_Failure, CPLE_NotSupported,
8369 : "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n");
8370 :
8371 0 : return -1;
8372 : }
8373 :
8374 : /**********************************************************************
8375 : * TABDebugFeature::DumpMIF()
8376 : *
8377 : * Dump feature contents... available only in DEBUG mode.
8378 : **********************************************************************/
8379 0 : void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/)
8380 : {
8381 0 : if (fpOut == nullptr)
8382 0 : fpOut = stdout;
8383 :
8384 0 : fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n",
8385 0 : GetMapInfoType());
8386 0 : fprintf(fpOut, " Object size: %d bytes\n", m_nSize);
8387 0 : fprintf(fpOut, " m_nCoordDataPtr = %d\n", m_nCoordDataPtr);
8388 0 : fprintf(fpOut, " m_nCoordDataSize = %d\n", m_nCoordDataSize);
8389 0 : fprintf(fpOut, " ");
8390 :
8391 0 : for (int i = 0; i < m_nSize; i++)
8392 0 : fprintf(fpOut, " %2.2x", m_abyBuf[i]);
8393 :
8394 0 : fprintf(fpOut, " \n");
8395 :
8396 0 : fflush(fpOut);
8397 0 : }
8398 :
8399 : /*=====================================================================
8400 : * class ITABFeaturePen
8401 : *====================================================================*/
8402 :
8403 : /**********************************************************************
8404 : * ITABFeaturePen::ITABFeaturePen()
8405 : **********************************************************************/
8406 :
8407 : // MI default is PEN(1, 2, 0)
8408 : static const TABPenDef MITABcsDefaultPen = MITAB_PEN_DEFAULT;
8409 :
8410 7550 : ITABFeaturePen::ITABFeaturePen()
8411 7550 : : m_nPenDefIndex(-1), m_sPenDef(MITABcsDefaultPen)
8412 : {
8413 7550 : }
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 95 : GByte ITABFeaturePen::GetPenWidthPixel() const
8440 : {
8441 95 : return m_sPenDef.nPixelWidth;
8442 : }
8443 :
8444 27 : void ITABFeaturePen::SetPenWidthPixel(GByte val)
8445 : {
8446 27 : const GByte nPixelWidthMin = 1;
8447 27 : const GByte nPixelWidthMax = 7;
8448 27 : m_sPenDef.nPixelWidth =
8449 27 : std::min(std::max(val, nPixelWidthMin), nPixelWidthMax);
8450 27 : m_sPenDef.nPointWidth = 0;
8451 27 : }
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 2421 : void ITABFeaturePen::SetPenWidthMIF(int val)
8481 : {
8482 2421 : 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 2421 : m_sPenDef.nPixelWidth =
8490 2421 : static_cast<GByte>(std::min(std::max(val, 1), 7));
8491 2421 : m_sPenDef.nPointWidth = 0;
8492 : }
8493 2421 : }
8494 :
8495 : /**********************************************************************
8496 : * ITABFeaturePen::GetPenStyleString()
8497 : *
8498 : * Return a PEN() string. All representations info for the pen are here.
8499 : **********************************************************************/
8500 95 : const char *ITABFeaturePen::GetPenStyleString() const
8501 : {
8502 95 : const char *pszStyle = nullptr;
8503 95 : int nOGRStyle = 0;
8504 : char szPattern[20];
8505 :
8506 95 : szPattern[0] = '\0';
8507 :
8508 : // For now, I only add the 25 first styles
8509 95 : switch (GetPenPattern())
8510 : {
8511 0 : case 1:
8512 0 : nOGRStyle = 1;
8513 0 : break;
8514 95 : case 2:
8515 95 : nOGRStyle = 0;
8516 95 : 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 95 : 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 95 : 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 95 : pszStyle = CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\""
8642 : "mapinfo-pen-%d,ogr-pen-%d\",cap:r,j:r)",
8643 95 : GetPenWidthPixel(), m_sPenDef.rgbColor,
8644 95 : GetPenPattern(), nOGRStyle);
8645 : }
8646 :
8647 95 : return pszStyle;
8648 : }
8649 :
8650 : /**********************************************************************
8651 : * ITABFeaturePen::SetPenFromStyleString()
8652 : *
8653 : * Init the Pen properties from a style string.
8654 : **********************************************************************/
8655 30 : void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString)
8656 : {
8657 30 : GBool bIsNull = 0;
8658 :
8659 : // Use the Style Manager to retrieve all the information we need.
8660 30 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8661 30 : OGRStyleTool *poStylePart = nullptr;
8662 :
8663 : // Init the StyleMgr with the StyleString.
8664 30 : poStyleMgr->InitStyleString(pszStyleString);
8665 :
8666 : // Retrieve the Pen info.
8667 30 : const int numParts = poStyleMgr->GetPartCount();
8668 47 : for (int i = 0; i < numParts; i++)
8669 : {
8670 45 : poStylePart = poStyleMgr->GetPart(i);
8671 45 : if (poStylePart == nullptr)
8672 0 : continue;
8673 :
8674 45 : if (poStylePart->GetType() == OGRSTCPen)
8675 : {
8676 28 : break;
8677 : }
8678 : else
8679 : {
8680 17 : delete poStylePart;
8681 17 : poStylePart = nullptr;
8682 : }
8683 : }
8684 :
8685 : // If the no Pen found, do nothing.
8686 30 : if (poStylePart == nullptr)
8687 : {
8688 2 : delete poStyleMgr;
8689 2 : return;
8690 : }
8691 :
8692 28 : 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 28 : poPenStyle->SetUnit(OGRSTUPoints, 1);
8703 :
8704 : // Get the Pen Id or pattern
8705 28 : const char *pszPenName = poPenStyle->Id(bIsNull);
8706 28 : if (bIsNull)
8707 1 : pszPenName = nullptr;
8708 :
8709 : // Set the width
8710 28 : if (poPenStyle->Width(bIsNull) != 0.0)
8711 : {
8712 27 : const double nPenWidth = poPenStyle->Width(bIsNull);
8713 : // Width < 10 is a pixel
8714 27 : if (nPenWidth > 10)
8715 0 : SetPenWidthPoint(nPenWidth);
8716 : else
8717 27 : SetPenWidthPixel(static_cast<GByte>(nPenWidth));
8718 : }
8719 :
8720 : // Set the color
8721 28 : const char *pszPenColor = poPenStyle->Color(bIsNull);
8722 28 : if (pszPenColor != nullptr)
8723 : {
8724 28 : if (pszPenColor[0] == '#')
8725 28 : pszPenColor++;
8726 : // The Pen color is an Hexa string that need to be convert in a int
8727 : const GInt32 nPenColor =
8728 28 : static_cast<int>(strtol(pszPenColor, nullptr, 16));
8729 28 : SetPenColor(nPenColor);
8730 : }
8731 :
8732 28 : const char *pszPenPattern = nullptr;
8733 :
8734 : // Set the Id of the Pen, use Pattern if necessary.
8735 28 : if (pszPenName &&
8736 27 : (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")))
8737 : {
8738 27 : const char *pszPenId = strstr(pszPenName, "mapinfo-pen-");
8739 27 : if (pszPenId != nullptr)
8740 : {
8741 27 : const int nPenId = atoi(pszPenId + 12);
8742 27 : 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 27 : }
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 28 : delete poStyleMgr;
8814 28 : delete poStylePart;
8815 :
8816 28 : 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 2504 : ITABFeatureBrush::ITABFeatureBrush()
8852 2504 : : m_nBrushDefIndex(-1), m_sBrushDef(MITABcsDefaultBrush)
8853 : {
8854 2504 : }
8855 :
8856 : /**********************************************************************
8857 : * ITABFeatureBrush::GetBrushStyleString()
8858 : *
8859 : * Return a Brush() string. All representations info for the Brush are here.
8860 : **********************************************************************/
8861 50 : const char *ITABFeatureBrush::GetBrushStyleString() const
8862 : {
8863 50 : const char *pszStyle = nullptr;
8864 50 : int nOGRStyle = 0;
8865 : /* char szPattern[20]; */
8866 : //* szPattern[0] = '\0'; */
8867 :
8868 50 : if (m_sBrushDef.nFillPattern == 1)
8869 49 : nOGRStyle = 1;
8870 1 : else if (m_sBrushDef.nFillPattern == 3)
8871 0 : nOGRStyle = 2;
8872 1 : else if (m_sBrushDef.nFillPattern == 4)
8873 0 : nOGRStyle = 3;
8874 1 : else if (m_sBrushDef.nFillPattern == 5)
8875 0 : nOGRStyle = 5;
8876 1 : else if (m_sBrushDef.nFillPattern == 6)
8877 0 : nOGRStyle = 4;
8878 1 : else if (m_sBrushDef.nFillPattern == 7)
8879 0 : nOGRStyle = 6;
8880 1 : else if (m_sBrushDef.nFillPattern == 8)
8881 0 : nOGRStyle = 7;
8882 :
8883 50 : if (GetBrushTransparent())
8884 : {
8885 : /* Omit BG Color for transparent brushes */
8886 2 : pszStyle = CPLSPrintf(
8887 : "BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8888 2 : m_sBrushDef.rgbFGColor, m_sBrushDef.nFillPattern, nOGRStyle);
8889 : }
8890 : else
8891 : {
8892 48 : pszStyle = CPLSPrintf(
8893 : "BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")",
8894 48 : m_sBrushDef.rgbFGColor, m_sBrushDef.rgbBGColor,
8895 48 : m_sBrushDef.nFillPattern, nOGRStyle);
8896 : }
8897 :
8898 50 : return pszStyle;
8899 : }
8900 :
8901 : /**********************************************************************
8902 : * ITABFeatureBrush::SetBrushFromStyleString()
8903 : *
8904 : * Set all Brush elements from a StyleString.
8905 : * Use StyleMgr to do so.
8906 : **********************************************************************/
8907 17 : void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString)
8908 : {
8909 17 : GBool bIsNull = 0;
8910 :
8911 : // Use the Style Manager to retrieve all the information we need.
8912 17 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
8913 17 : OGRStyleTool *poStylePart = nullptr;
8914 :
8915 : // Init the StyleMgr with the StyleString.
8916 17 : poStyleMgr->InitStyleString(pszStyleString);
8917 :
8918 : // Retrieve the Brush info.
8919 17 : const int numParts = poStyleMgr->GetPartCount();
8920 17 : for (int i = 0; i < numParts; i++)
8921 : {
8922 17 : poStylePart = poStyleMgr->GetPart(i);
8923 17 : if (poStylePart == nullptr)
8924 0 : continue;
8925 :
8926 17 : if (poStylePart->GetType() == OGRSTCBrush)
8927 : {
8928 17 : break;
8929 : }
8930 : else
8931 : {
8932 0 : delete poStylePart;
8933 0 : poStylePart = nullptr;
8934 : }
8935 : }
8936 :
8937 : // If the no Brush found, do nothing.
8938 17 : if (poStylePart == nullptr)
8939 : {
8940 0 : delete poStyleMgr;
8941 0 : return;
8942 : }
8943 :
8944 17 : OGRStyleBrush *poBrushStyle = cpl::down_cast<OGRStyleBrush *>(poStylePart);
8945 :
8946 : // Set the Brush Id (FillPattern)
8947 17 : const char *pszBrushId = poBrushStyle->Id(bIsNull);
8948 17 : if (bIsNull)
8949 2 : pszBrushId = nullptr;
8950 17 : bool bHasBrushId = false;
8951 :
8952 17 : if (pszBrushId && (strstr(pszBrushId, "mapinfo-brush-") ||
8953 0 : strstr(pszBrushId, "ogr-brush-")))
8954 : {
8955 15 : if (strstr(pszBrushId, "mapinfo-brush-"))
8956 : {
8957 15 : const int nBrushId = atoi(pszBrushId + 14);
8958 15 : SetBrushPattern(static_cast<GByte>(nBrushId));
8959 15 : bHasBrushId = true;
8960 : }
8961 0 : else if (strstr(pszBrushId, "ogr-brush-"))
8962 : {
8963 0 : int nBrushId = atoi(pszBrushId + 10);
8964 0 : if (nBrushId > 1)
8965 0 : nBrushId++;
8966 0 : SetBrushPattern(static_cast<GByte>(nBrushId));
8967 0 : bHasBrushId = true;
8968 : }
8969 : }
8970 :
8971 : // Set the BackColor, if not set, then it is transparent
8972 17 : const char *pszBrushColor = poBrushStyle->BackColor(bIsNull);
8973 17 : if (bIsNull)
8974 1 : pszBrushColor = nullptr;
8975 :
8976 17 : if (pszBrushColor)
8977 : {
8978 16 : if (pszBrushColor[0] == '#')
8979 16 : pszBrushColor++;
8980 16 : if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
8981 1 : pszBrushColor[7] == '0')
8982 : {
8983 1 : SetBrushTransparent(1);
8984 : }
8985 : else
8986 : {
8987 30 : CPLString osBrushColor(pszBrushColor);
8988 15 : if (strlen(pszBrushColor) > 6)
8989 0 : osBrushColor.resize(6);
8990 : const int nBrushColor =
8991 15 : static_cast<int>(strtol(osBrushColor, nullptr, 16));
8992 15 : SetBrushBGColor(static_cast<GInt32>(nBrushColor));
8993 : }
8994 : }
8995 : else
8996 : {
8997 1 : SetBrushTransparent(1);
8998 : }
8999 :
9000 : // Set the ForeColor
9001 17 : pszBrushColor = poBrushStyle->ForeColor(bIsNull);
9002 17 : if (bIsNull)
9003 0 : pszBrushColor = nullptr;
9004 :
9005 17 : if (pszBrushColor)
9006 : {
9007 17 : if (pszBrushColor[0] == '#')
9008 17 : pszBrushColor++;
9009 17 : if (strlen(pszBrushColor) == 8 && pszBrushColor[6] == '0' &&
9010 1 : pszBrushColor[7] == '0')
9011 : {
9012 1 : if (!bHasBrushId)
9013 1 : SetBrushPattern(static_cast<GByte>(1)); // No-fill
9014 : }
9015 : else
9016 : {
9017 16 : if (!bHasBrushId)
9018 1 : SetBrushPattern(static_cast<GByte>(2)); // Solid-fill
9019 : }
9020 :
9021 34 : CPLString osBrushColor(pszBrushColor);
9022 17 : if (strlen(pszBrushColor) > 6)
9023 1 : osBrushColor.resize(6);
9024 : const int nBrushColor =
9025 17 : static_cast<int>(strtol(osBrushColor, nullptr, 16));
9026 17 : SetBrushFGColor(static_cast<GInt32>(nBrushColor));
9027 : }
9028 :
9029 17 : delete poStyleMgr;
9030 17 : delete poStylePart;
9031 :
9032 17 : return;
9033 : }
9034 :
9035 : /**********************************************************************
9036 : * ITABFeatureBrush::DumpBrushDef()
9037 : *
9038 : * Dump Brush definition information.
9039 : **********************************************************************/
9040 0 : void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/)
9041 : {
9042 0 : if (fpOut == nullptr)
9043 0 : fpOut = stdout;
9044 :
9045 0 : fprintf(fpOut, " m_nBrushDefIndex = %d\n", m_nBrushDefIndex);
9046 0 : fprintf(fpOut, " m_sBrushDef.nRefCount = %d\n", m_sBrushDef.nRefCount);
9047 0 : fprintf(fpOut, " m_sBrushDef.nFillPattern = %d\n",
9048 0 : static_cast<int>(m_sBrushDef.nFillPattern));
9049 0 : fprintf(fpOut, " m_sBrushDef.bTransparentFill = %d\n",
9050 0 : static_cast<int>(m_sBrushDef.bTransparentFill));
9051 0 : fprintf(fpOut, " m_sBrushDef.rgbFGColor = 0x%6.6x (%d)\n",
9052 : m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor);
9053 0 : fprintf(fpOut, " m_sBrushDef.rgbBGColor = 0x%6.6x (%d)\n",
9054 : m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor);
9055 :
9056 0 : fflush(fpOut);
9057 0 : }
9058 :
9059 : /*=====================================================================
9060 : * class ITABFeatureFont
9061 : *====================================================================*/
9062 :
9063 : /**********************************************************************
9064 : * ITABFeatureFont::ITABFeatureFont()
9065 : **********************************************************************/
9066 :
9067 : // MI default is Font("Arial", 0, 0, 0)
9068 : static const TABFontDef MITABcsDefaultFont = MITAB_FONT_DEFAULT;
9069 :
9070 1638 : ITABFeatureFont::ITABFeatureFont()
9071 1638 : : m_nFontDefIndex(-1), m_sFontDef(MITABcsDefaultFont)
9072 : {
9073 1638 : }
9074 :
9075 : /**********************************************************************
9076 : * ITABFeatureFont::SetFontName()
9077 : **********************************************************************/
9078 1582 : void ITABFeatureFont::SetFontName(const char *pszName)
9079 : {
9080 1582 : strncpy(m_sFontDef.szFontName, pszName, sizeof(m_sFontDef.szFontName) - 1);
9081 1582 : m_sFontDef.szFontName[sizeof(m_sFontDef.szFontName) - 1] = '\0';
9082 1582 : }
9083 :
9084 : /**********************************************************************
9085 : * ITABFeatureFont::DumpFontDef()
9086 : *
9087 : * Dump Font definition information.
9088 : **********************************************************************/
9089 0 : void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/)
9090 : {
9091 0 : if (fpOut == nullptr)
9092 0 : fpOut = stdout;
9093 :
9094 0 : fprintf(fpOut, " m_nFontDefIndex = %d\n", m_nFontDefIndex);
9095 0 : fprintf(fpOut, " m_sFontDef.nRefCount = %d\n", m_sFontDef.nRefCount);
9096 0 : fprintf(fpOut, " m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName);
9097 :
9098 0 : fflush(fpOut);
9099 0 : }
9100 :
9101 : /*=====================================================================
9102 : * class ITABFeatureSymbol
9103 : *====================================================================*/
9104 :
9105 : /**********************************************************************
9106 : * ITABFeatureSymbol::ITABFeatureSymbol()
9107 : **********************************************************************/
9108 :
9109 : // MI default is Symbol(35, 0, 12)
9110 : static const TABSymbolDef MITABcsDefaultSymbol = MITAB_SYMBOL_DEFAULT;
9111 :
9112 531864 : ITABFeatureSymbol::ITABFeatureSymbol()
9113 531864 : : m_nSymbolDefIndex(-1), m_sSymbolDef(MITABcsDefaultSymbol)
9114 : {
9115 531864 : }
9116 :
9117 : /**********************************************************************
9118 : * ITABFeatureSymbol::GetSymbolStyleString()
9119 : *
9120 : * Return a Symbol() string. All representations info for the Symbol are here.
9121 : **********************************************************************/
9122 33 : const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) const
9123 : {
9124 33 : const char *pszStyle = nullptr;
9125 33 : int nOGRStyle = 0;
9126 : /* char szPattern[20]; */
9127 33 : int nAngle = 0;
9128 : /* szPattern[0] = '\0'; */
9129 :
9130 33 : switch (m_sSymbolDef.nSymbolNo)
9131 : {
9132 0 : case 31:
9133 : // this is actually a "null" symbol in MapInfo!
9134 0 : nOGRStyle = 0;
9135 0 : break;
9136 0 : case 32: // filled square
9137 0 : nOGRStyle = 5;
9138 0 : break;
9139 0 : case 33: // filled diamond
9140 0 : nAngle = 45;
9141 0 : nOGRStyle = 5;
9142 0 : break;
9143 0 : case 34: // filled circle
9144 0 : nOGRStyle = 3;
9145 0 : break;
9146 33 : case 35: // filled star
9147 33 : nOGRStyle = 9;
9148 33 : break;
9149 0 : case 36: // filled upward pointing triangle
9150 0 : nOGRStyle = 7;
9151 0 : break;
9152 0 : case 37: // filled downward pointing triangle
9153 0 : nAngle = 180;
9154 0 : nOGRStyle = 7;
9155 0 : break;
9156 0 : case 38: // hollow square
9157 0 : nOGRStyle = 4;
9158 0 : break;
9159 0 : case 39: // hollow diamond
9160 0 : nAngle = 45;
9161 0 : nOGRStyle = 4;
9162 0 : break;
9163 0 : case 40: // hollow circle
9164 0 : nOGRStyle = 2;
9165 0 : break;
9166 0 : case 41: // hollow star
9167 0 : nOGRStyle = 8;
9168 0 : break;
9169 0 : case 42: // hollow upward pointing triangle
9170 0 : nOGRStyle = 6;
9171 0 : break;
9172 0 : case 43: // hollow downward pointing triangle
9173 0 : nAngle = 180;
9174 0 : nOGRStyle = 6;
9175 0 : break;
9176 0 : case 44: // filled square (with shadow)
9177 0 : nOGRStyle = 5;
9178 0 : break;
9179 0 : case 45: // filled upward triangle (with shadow)
9180 0 : nOGRStyle = 7;
9181 0 : break;
9182 0 : case 46: // filled circle (with shadow)
9183 0 : nOGRStyle = 3;
9184 0 : break;
9185 0 : case 49: // crossed lines
9186 0 : nOGRStyle = 0;
9187 0 : break;
9188 0 : case 50: // X crossed lines
9189 0 : nOGRStyle = 1;
9190 0 : break;
9191 : }
9192 :
9193 33 : nAngle += static_cast<int>(dfAngle);
9194 :
9195 66 : pszStyle = CPLSPrintf(
9196 : "SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", nAngle,
9197 33 : m_sSymbolDef.rgbColor, m_sSymbolDef.nPointSize, m_sSymbolDef.nSymbolNo,
9198 : nOGRStyle);
9199 :
9200 33 : return pszStyle;
9201 : }
9202 :
9203 : /**********************************************************************
9204 : * ITABFeatureSymbol::SetSymbolFromStyleString()
9205 : *
9206 : * Set all Symbol var from a OGRStyleSymbol.
9207 : **********************************************************************/
9208 109 : void ITABFeatureSymbol::SetSymbolFromStyle(OGRStyleSymbol *poSymbolStyle)
9209 : {
9210 109 : GBool bIsNull = 0;
9211 :
9212 : // Set the Symbol Id (SymbolNo)
9213 109 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9214 109 : if (bIsNull)
9215 0 : pszSymbolId = nullptr;
9216 :
9217 109 : if (pszSymbolId)
9218 : {
9219 109 : if (STARTS_WITH(pszSymbolId, "mapinfo-sym-"))
9220 : {
9221 105 : const int nSymbolId = atoi(pszSymbolId + 12);
9222 105 : SetSymbolNo(static_cast<GByte>(nSymbolId));
9223 : }
9224 4 : else if (STARTS_WITH(pszSymbolId, "ogr-sym-"))
9225 : {
9226 0 : const int nSymbolId = atoi(pszSymbolId + 8);
9227 :
9228 : // The OGR symbol is not the MapInfo one
9229 : // Here's some mapping
9230 0 : switch (nSymbolId)
9231 : {
9232 0 : case 0:
9233 0 : SetSymbolNo(49);
9234 0 : break;
9235 0 : case 1:
9236 0 : SetSymbolNo(50);
9237 0 : break;
9238 0 : case 2:
9239 0 : SetSymbolNo(40);
9240 0 : break;
9241 0 : case 3:
9242 0 : SetSymbolNo(34);
9243 0 : break;
9244 0 : case 4:
9245 0 : SetSymbolNo(38);
9246 0 : break;
9247 0 : case 5:
9248 0 : SetSymbolNo(32);
9249 0 : break;
9250 0 : case 6:
9251 0 : SetSymbolNo(42);
9252 0 : break;
9253 0 : case 7:
9254 0 : SetSymbolNo(36);
9255 0 : break;
9256 0 : case 8:
9257 0 : SetSymbolNo(41);
9258 0 : break;
9259 0 : case 9:
9260 0 : SetSymbolNo(35);
9261 0 : break;
9262 0 : case 10: // vertical bar -- no mapinfo equivalent, so use
9263 : // crosshairs as closest match
9264 0 : SetSymbolNo(49);
9265 0 : break;
9266 : }
9267 : }
9268 : }
9269 :
9270 : // Set SymbolSize
9271 109 : const double dSymbolSize = poSymbolStyle->Size(bIsNull);
9272 109 : if (dSymbolSize != 0.0)
9273 : {
9274 109 : SetSymbolSize(static_cast<GInt16>(dSymbolSize));
9275 : }
9276 :
9277 : // Set Symbol Color
9278 109 : const char *pszSymbolColor = poSymbolStyle->Color(bIsNull);
9279 109 : if (pszSymbolColor)
9280 : {
9281 109 : if (pszSymbolColor[0] == '#')
9282 109 : pszSymbolColor++;
9283 : int nSymbolColor =
9284 109 : static_cast<int>(strtol(pszSymbolColor, nullptr, 16));
9285 109 : SetSymbolColor(static_cast<GInt32>(nSymbolColor));
9286 : }
9287 109 : }
9288 :
9289 : /**********************************************************************
9290 : * ITABFeatureSymbol::SetSymbolFromStyleString()
9291 : *
9292 : * Set all Symbol var from a StyleString. Use StyleMgr to do so.
9293 : **********************************************************************/
9294 109 : void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString)
9295 : {
9296 : // Use the Style Manager to retrieve all the information we need.
9297 109 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9298 109 : OGRStyleTool *poStylePart = nullptr;
9299 :
9300 : // Init the StyleMgr with the StyleString.
9301 109 : poStyleMgr->InitStyleString(pszStyleString);
9302 :
9303 : // Retrieve the Symbol info.
9304 109 : const int numParts = poStyleMgr->GetPartCount();
9305 109 : for (int i = 0; i < numParts; i++)
9306 : {
9307 109 : poStylePart = poStyleMgr->GetPart(i);
9308 109 : if (poStylePart == nullptr)
9309 0 : continue;
9310 :
9311 109 : if (poStylePart->GetType() == OGRSTCSymbol)
9312 : {
9313 109 : break;
9314 : }
9315 : else
9316 : {
9317 0 : delete poStylePart;
9318 0 : poStylePart = nullptr;
9319 : }
9320 : }
9321 :
9322 : // If the no Symbol found, do nothing.
9323 109 : if (poStylePart == nullptr)
9324 : {
9325 0 : delete poStyleMgr;
9326 0 : return;
9327 : }
9328 :
9329 : OGRStyleSymbol *poSymbolStyle =
9330 109 : cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9331 :
9332 : // With Symbol, we always want to output points
9333 : //
9334 : // It's very important to set the output unit of the feature.
9335 : // The default value is meter. If we don't do it all numerical values
9336 : // will be assumed to be converted from the input unit to meter when we
9337 : // will get them via GetParam...() functions.
9338 : // See OGRStyleTool::Parse() for more details.
9339 109 : poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37));
9340 :
9341 109 : SetSymbolFromStyle(poSymbolStyle);
9342 :
9343 109 : delete poStyleMgr;
9344 109 : delete poStylePart;
9345 :
9346 109 : return;
9347 : }
9348 :
9349 : /**********************************************************************
9350 : * ITABFeatureSymbol::GetSymbolFeatureClass()
9351 : *
9352 : * Return the feature class needed to represent the style string.
9353 : **********************************************************************/
9354 : TABFeatureClass
9355 109 : ITABFeatureSymbol::GetSymbolFeatureClass(const char *pszStyleString)
9356 : {
9357 : // Use the Style Manager to retrieve all the information we need.
9358 109 : OGRStyleMgr *poStyleMgr = new OGRStyleMgr(nullptr);
9359 109 : OGRStyleTool *poStylePart = nullptr;
9360 :
9361 : // Init the StyleMgr with the StyleString.
9362 109 : poStyleMgr->InitStyleString(pszStyleString);
9363 :
9364 : // Retrieve the Symbol info.
9365 109 : const int numParts = poStyleMgr->GetPartCount();
9366 109 : for (int i = 0; i < numParts; i++)
9367 : {
9368 109 : poStylePart = poStyleMgr->GetPart(i);
9369 109 : if (poStylePart == nullptr)
9370 : {
9371 0 : continue;
9372 : }
9373 :
9374 109 : if (poStylePart->GetType() == OGRSTCSymbol)
9375 : {
9376 109 : break;
9377 : }
9378 : else
9379 : {
9380 0 : delete poStylePart;
9381 0 : poStylePart = nullptr;
9382 : }
9383 : }
9384 :
9385 109 : TABFeatureClass result = TABFCPoint;
9386 :
9387 : // If the no Symbol found, do nothing.
9388 109 : if (poStylePart == nullptr)
9389 : {
9390 0 : delete poStyleMgr;
9391 0 : return result;
9392 : }
9393 :
9394 : OGRStyleSymbol *poSymbolStyle =
9395 109 : cpl::down_cast<OGRStyleSymbol *>(poStylePart);
9396 :
9397 109 : GBool bIsNull = 0;
9398 :
9399 : // Set the Symbol Id (SymbolNo)
9400 109 : const char *pszSymbolId = poSymbolStyle->Id(bIsNull);
9401 109 : if (bIsNull)
9402 0 : pszSymbolId = nullptr;
9403 :
9404 109 : if (pszSymbolId)
9405 : {
9406 109 : if (STARTS_WITH(pszSymbolId, "font-sym-"))
9407 : {
9408 2 : result = TABFCFontPoint;
9409 : }
9410 107 : else if (STARTS_WITH(pszSymbolId, "mapinfo-custom-sym-"))
9411 : {
9412 2 : result = TABFCCustomPoint;
9413 : }
9414 : }
9415 :
9416 109 : delete poStyleMgr;
9417 109 : delete poStylePart;
9418 :
9419 109 : return result;
9420 : }
9421 :
9422 : /**********************************************************************
9423 : * ITABFeatureSymbol::DumpSymbolDef()
9424 : *
9425 : * Dump Symbol definition information.
9426 : **********************************************************************/
9427 0 : void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/)
9428 : {
9429 0 : if (fpOut == nullptr)
9430 0 : fpOut = stdout;
9431 :
9432 0 : fprintf(fpOut, " m_nSymbolDefIndex = %d\n", m_nSymbolDefIndex);
9433 0 : fprintf(fpOut, " m_sSymbolDef.nRefCount = %d\n", m_sSymbolDef.nRefCount);
9434 0 : fprintf(fpOut, " m_sSymbolDef.nSymbolNo = %d\n", m_sSymbolDef.nSymbolNo);
9435 0 : fprintf(fpOut, " m_sSymbolDef.nPointSize = %d\n", m_sSymbolDef.nPointSize);
9436 0 : fprintf(fpOut, " m_sSymbolDef._unknown_ = %d\n",
9437 0 : static_cast<int>(m_sSymbolDef._nUnknownValue_));
9438 0 : fprintf(fpOut, " m_sSymbolDef.rgbColor = 0x%6.6x (%d)\n",
9439 : m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor);
9440 :
9441 0 : fflush(fpOut);
9442 0 : }
|