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 R/W Fcts for (Mid/Mif) in feature classes
7 : * specific to MapInfo files.
8 : * Author: Stephane Villeneuve, stephane.v@videotron.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999-2002, Stephane Villeneuve
12 : *
13 : * SPDX-License-Identifier: MIT
14 : **********************************************************************/
15 :
16 : #include "cpl_port.h"
17 : #include "mitab.h"
18 :
19 : #include <cctype>
20 : #include <cmath>
21 : #include <cstdio>
22 : #include <cstdlib>
23 : #include <cstring>
24 : #include <algorithm>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi.h"
30 : #include "mitab_priv.h"
31 : #include "mitab_utils.h"
32 : #include "ogr_core.h"
33 : #include "ogr_feature.h"
34 : #include "ogr_geometry.h"
35 :
36 : /*=====================================================================
37 : * class TABFeature
38 : *====================================================================*/
39 :
40 : /**********************************************************************
41 : * TABFeature::ReadRecordFromMIDFile()
42 : *
43 : * This method is used to read the Record (Attributes) for all type of
44 : * features included in a mid/mif file.
45 : *
46 : * Returns 0 on success, -1 on error, in which case CPLError() will have
47 : * been called.
48 : **********************************************************************/
49 377 : int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp)
50 : {
51 : #ifdef MITAB_USE_OFTDATETIME
52 377 : int nYear = 0;
53 377 : int nMonth = 0;
54 377 : int nDay = 0;
55 377 : int nHour = 0;
56 377 : int nMin = 0;
57 377 : int nSec = 0;
58 377 : int nMS = 0;
59 : // int nTZFlag = 0;
60 : #endif
61 :
62 377 : const int nFields = GetFieldCount();
63 :
64 377 : char **papszToken = fp->GetTokenizedNextLine();
65 377 : if (papszToken == nullptr)
66 : {
67 0 : CPLError(
68 : CE_Failure, CPLE_FileIO,
69 : "Unexpected EOF while reading attribute record from MID file.");
70 0 : return -1;
71 : }
72 :
73 : // Ensure that a blank line in a mid file is treated as one field
74 : // containing an empty string.
75 377 : if (nFields == 1 && CSLCount(papszToken) == 0)
76 8 : papszToken = CSLAddString(papszToken, "");
77 :
78 : // Make sure we found at least the expected number of field values.
79 : // Note that it is possible to have a stray delimiter at the end of
80 : // the line (mif/mid files from Geomedia), so don't produce an error
81 : // if we find more tokens than expected.
82 377 : if (CSLCount(papszToken) < nFields)
83 : {
84 0 : CSLDestroy(papszToken);
85 0 : return -1;
86 : }
87 :
88 1548 : for (int i = 0; i < nFields; i++)
89 : {
90 1171 : const auto poFDefn = GetFieldDefnRef(i);
91 1171 : switch (poFDefn->GetType())
92 : {
93 : #ifdef MITAB_USE_OFTDATETIME
94 6 : case OFTTime:
95 : {
96 6 : if (strlen(papszToken[i]) == 9)
97 : {
98 6 : sscanf(papszToken[i], "%2d%2d%2d%3d", &nHour, &nMin, &nSec,
99 : &nMS);
100 6 : SetField(i, nYear, nMonth, nDay, nHour, nMin,
101 6 : static_cast<float>(nSec + nMS / 1000.0f), 0);
102 : }
103 6 : break;
104 : }
105 6 : case OFTDate:
106 : {
107 6 : if (strlen(papszToken[i]) == 8)
108 : {
109 6 : sscanf(papszToken[i], "%4d%2d%2d", &nYear, &nMonth, &nDay);
110 6 : SetField(i, nYear, nMonth, nDay, nHour, nMin,
111 : static_cast<float>(nSec), 0);
112 : }
113 6 : break;
114 : }
115 6 : case OFTDateTime:
116 : {
117 6 : if (strlen(papszToken[i]) == 17)
118 : {
119 6 : sscanf(papszToken[i], "%4d%2d%2d%2d%2d%2d%3d", &nYear,
120 : &nMonth, &nDay, &nHour, &nMin, &nSec, &nMS);
121 6 : SetField(i, nYear, nMonth, nDay, nHour, nMin,
122 6 : static_cast<float>(nSec + nMS / 1000.0f), 0);
123 : }
124 6 : break;
125 : }
126 : #endif
127 380 : case OFTInteger:
128 : {
129 380 : if (poFDefn->GetSubType() == OFSTBoolean)
130 : {
131 8 : char ch = papszToken[i][0];
132 13 : SetField(i, (ch == 'T' || ch == 't' || ch == 'Y' ||
133 5 : ch == 'y' || ch == '1')
134 : ? 1
135 : : 0);
136 : }
137 : else
138 : {
139 372 : SetField(i, papszToken[i]);
140 : }
141 380 : break;
142 : }
143 417 : case OFTString:
144 : {
145 834 : CPLString osValue(papszToken[i]);
146 417 : if (!fp->GetEncoding().empty())
147 : {
148 92 : osValue.Recode(fp->GetEncoding(), CPL_ENC_UTF8);
149 : }
150 417 : SetField(i, osValue);
151 417 : break;
152 : }
153 356 : default:
154 356 : SetField(i, papszToken[i]);
155 : }
156 : }
157 :
158 377 : CSLDestroy(papszToken);
159 :
160 377 : return 0;
161 : }
162 :
163 : /**********************************************************************
164 : * TABFeature::WriteRecordToMIDFile()
165 : *
166 : * This method is used to write the Record (Attributes) for all type
167 : * of feature included in a mid file.
168 : *
169 : * Return 0 on success, -1 on error
170 : **********************************************************************/
171 118 : int TABFeature::WriteRecordToMIDFile(MIDDATAFile *fp)
172 : {
173 118 : CPLAssert(fp);
174 :
175 : #ifdef MITAB_USE_OFTDATETIME
176 : char szBuffer[20];
177 118 : int nYear = 0;
178 118 : int nMonth = 0;
179 118 : int nDay = 0;
180 118 : int nHour = 0;
181 118 : int nMin = 0;
182 : // int nMS = 0;
183 118 : int nTZFlag = 0;
184 118 : float fSec = 0.0f;
185 : #endif
186 :
187 118 : const char *delimiter = fp->GetDelimiter();
188 :
189 118 : const int numFields = GetFieldCount();
190 :
191 308 : for (int iField = 0; iField < numFields; iField++)
192 : {
193 190 : if (iField != 0)
194 72 : fp->WriteLine("%s", delimiter);
195 190 : const auto poFDefn = GetFieldDefnRef(iField);
196 :
197 190 : switch (poFDefn->GetType())
198 : {
199 105 : case OFTString:
200 : {
201 210 : CPLString osString(GetFieldAsString(iField));
202 :
203 105 : if (!fp->GetEncoding().empty())
204 : {
205 18 : osString.Recode(CPL_ENC_UTF8, fp->GetEncoding());
206 : }
207 :
208 105 : int nStringLen = static_cast<int>(osString.length());
209 105 : const char *pszString = osString.c_str();
210 : char *pszWorkString = static_cast<char *>(
211 105 : CPLMalloc((2 * (nStringLen) + 1) * sizeof(char)));
212 105 : int j = 0;
213 770 : for (int i = 0; i < nStringLen; ++i)
214 : {
215 665 : if (pszString[i] == '"')
216 : {
217 0 : pszWorkString[j] = pszString[i];
218 0 : ++j;
219 0 : pszWorkString[j] = pszString[i];
220 : }
221 665 : else if (pszString[i] == '\n')
222 : {
223 0 : pszWorkString[j] = '\\';
224 0 : ++j;
225 0 : pszWorkString[j] = 'n';
226 : }
227 : else
228 665 : pszWorkString[j] = pszString[i];
229 665 : ++j;
230 : }
231 :
232 105 : pszWorkString[j] = '\0';
233 105 : fp->WriteLine("\"%s\"", pszWorkString);
234 105 : CPLFree(pszWorkString);
235 105 : break;
236 : }
237 : #ifdef MITAB_USE_OFTDATETIME
238 2 : case OFTTime:
239 : {
240 2 : if (!IsFieldSetAndNotNull(iField))
241 : {
242 0 : szBuffer[0] = '\0';
243 : }
244 : else
245 : {
246 2 : GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, &nHour,
247 : &nMin, &fSec, &nTZFlag);
248 2 : snprintf(szBuffer, sizeof(szBuffer), "%2.2d%2.2d%2.2d%3.3d",
249 : nHour, nMin, static_cast<int>(fSec),
250 : OGR_GET_MS(fSec));
251 : }
252 2 : fp->WriteLine("%s", szBuffer);
253 2 : break;
254 : }
255 2 : case OFTDate:
256 : {
257 2 : if (!IsFieldSetAndNotNull(iField))
258 : {
259 0 : szBuffer[0] = '\0';
260 : }
261 : else
262 : {
263 2 : GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, &nHour,
264 : &nMin, &fSec, &nTZFlag);
265 2 : snprintf(szBuffer, sizeof(szBuffer), "%4.4d%2.2d%2.2d",
266 : nYear, nMonth, nDay);
267 : }
268 2 : fp->WriteLine("%s", szBuffer);
269 2 : break;
270 : }
271 2 : case OFTDateTime:
272 : {
273 2 : if (!IsFieldSetAndNotNull(iField))
274 : {
275 0 : szBuffer[0] = '\0';
276 : }
277 : else
278 : {
279 2 : GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, &nHour,
280 : &nMin, &fSec, &nTZFlag);
281 2 : snprintf(szBuffer, sizeof(szBuffer),
282 : "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d%3.3d", nYear,
283 : nMonth, nDay, nHour, nMin, static_cast<int>(fSec),
284 : OGR_GET_MS(fSec));
285 : }
286 2 : fp->WriteLine("%s", szBuffer);
287 2 : break;
288 : }
289 : #endif
290 51 : case OFTInteger:
291 : {
292 51 : if (poFDefn->GetSubType() == OFSTBoolean)
293 : {
294 2 : fp->WriteLine("%c", GetFieldAsInteger(iField) ? 'T' : 'F');
295 : }
296 : else
297 : {
298 49 : fp->WriteLine("%s", GetFieldAsString(iField));
299 : }
300 51 : break;
301 : }
302 28 : default:
303 28 : fp->WriteLine("%s", GetFieldAsString(iField));
304 : }
305 : }
306 :
307 118 : fp->WriteLine("\n");
308 :
309 118 : return 0;
310 : }
311 :
312 : /**********************************************************************
313 : * TABFeature::ReadGeometryFromMIFFile()
314 : *
315 : * In derived classes, this method should be reimplemented to
316 : * fill the geometry and representation (color, etc...) part of the
317 : * feature from the contents of the .MAP object pointed to by poMAPFile.
318 : *
319 : * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile
320 : * currently points to the beginning of a map object.
321 : *
322 : * The current implementation does nothing since instances of TABFeature
323 : * objects contain no geometry (i.e. TAB_GEOM_NONE).
324 : *
325 : * Returns 0 on success, -1 on error, in which case CPLError() will have
326 : * been called.
327 : **********************************************************************/
328 22 : int TABFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp)
329 : {
330 22 : const char *pszLine = nullptr;
331 :
332 : /* Go to the first line of the next feature */
333 :
334 37 : while (((pszLine = fp->GetLine()) != nullptr) &&
335 15 : fp->IsValidFeature(pszLine) == FALSE)
336 : ;
337 :
338 22 : return 0;
339 : }
340 :
341 : /**********************************************************************
342 : * TABFeature::WriteGeometryToMIFFile()
343 : *
344 : *
345 : * In derived classes, this method should be reimplemented to
346 : * write the geometry and representation (color, etc...) part of the
347 : * feature to the .MAP object pointed to by poMAPFile.
348 : *
349 : * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile
350 : * currently points to a valid map object.
351 : *
352 : * The current implementation does nothing since instances of TABFeature
353 : * objects contain no geometry.
354 : *
355 : * Returns 0 on success, -1 on error, in which case CPLError() will have
356 : * been called.
357 : **********************************************************************/
358 15 : int TABFeature::WriteGeometryToMIFFile(MIDDATAFile *fp)
359 : {
360 15 : fp->WriteLine("NONE\n");
361 15 : return 0;
362 : }
363 :
364 : /**********************************************************************
365 : *
366 : **********************************************************************/
367 752 : int TABPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
368 : {
369 : char **papszToken =
370 752 : CSLTokenizeString2(fp->GetSavedLine(), " \t", CSLT_HONOURSTRINGS);
371 :
372 752 : if (CSLCount(papszToken) != 3)
373 : {
374 0 : CSLDestroy(papszToken);
375 0 : return -1;
376 : }
377 :
378 752 : const double dfX = fp->GetXTrans(CPLAtof(papszToken[1]));
379 752 : const double dfY = fp->GetYTrans(CPLAtof(papszToken[2]));
380 :
381 752 : CSLDestroy(papszToken);
382 752 : papszToken = nullptr;
383 :
384 : // Read optional SYMBOL line...
385 752 : const char *pszLine = fp->GetLastLine();
386 752 : if (pszLine != nullptr)
387 746 : papszToken = CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
388 781 : if (papszToken != nullptr && CSLCount(papszToken) == 4 &&
389 29 : EQUAL(papszToken[0], "SYMBOL"))
390 : {
391 29 : SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
392 29 : SetSymbolColor(atoi(papszToken[2]));
393 29 : SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
394 : }
395 :
396 752 : CSLDestroy(papszToken);
397 752 : papszToken = nullptr;
398 :
399 : // scan until we reach 1st line of next feature
400 : // Since SYMBOL is optional, we have to test IsValidFeature() on that
401 : // line as well.
402 1502 : while (pszLine && fp->IsValidFeature(pszLine) == FALSE)
403 : {
404 750 : pszLine = fp->GetLine();
405 : }
406 :
407 752 : OGRGeometry *poGeometry = new OGRPoint(dfX, dfY);
408 :
409 752 : SetGeometryDirectly(poGeometry);
410 :
411 752 : SetMBR(dfX, dfY, dfX, dfY);
412 :
413 752 : return 0;
414 : }
415 :
416 : /**********************************************************************
417 : *
418 : **********************************************************************/
419 82 : int TABPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
420 : {
421 : /*-----------------------------------------------------------------
422 : * Fetch and validate geometry
423 : *----------------------------------------------------------------*/
424 82 : OGRGeometry *poGeom = GetGeometryRef();
425 82 : OGRPoint *poPoint = nullptr;
426 82 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
427 82 : poPoint = poGeom->toPoint();
428 : else
429 : {
430 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
431 : "TABPoint: Missing or Invalid Geometry!");
432 0 : return -1;
433 : }
434 :
435 82 : fp->WriteLine("Point %.15g %.15g\n", poPoint->getX(), poPoint->getY());
436 82 : fp->WriteLine(" Symbol (%d,%d,%d)\n", GetSymbolNo(), GetSymbolColor(),
437 82 : GetSymbolSize());
438 :
439 82 : return 0;
440 : }
441 :
442 : /**********************************************************************
443 : *
444 : **********************************************************************/
445 635 : int TABFontPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
446 : {
447 : char **papszToken =
448 635 : CSLTokenizeString2(fp->GetSavedLine(), " \t", CSLT_HONOURSTRINGS);
449 :
450 635 : if (CSLCount(papszToken) != 3)
451 : {
452 0 : CSLDestroy(papszToken);
453 0 : return -1;
454 : }
455 :
456 635 : const double dfX = fp->GetXTrans(CPLAtof(papszToken[1]));
457 635 : const double dfY = fp->GetYTrans(CPLAtof(papszToken[2]));
458 :
459 635 : CSLDestroy(papszToken);
460 :
461 : papszToken =
462 635 : CSLTokenizeStringComplex(fp->GetLastLine(), " ,()\t", TRUE, FALSE);
463 :
464 635 : if (CSLCount(papszToken) != 7)
465 : {
466 0 : CSLDestroy(papszToken);
467 0 : return -1;
468 : }
469 :
470 635 : SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
471 635 : SetSymbolColor(atoi(papszToken[2]));
472 635 : SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
473 635 : SetFontName(papszToken[4]);
474 635 : SetFontStyleMIFValue(atoi(papszToken[5]));
475 635 : SetSymbolAngle(CPLAtof(papszToken[6]));
476 :
477 635 : CSLDestroy(papszToken);
478 :
479 635 : OGRGeometry *poGeometry = new OGRPoint(dfX, dfY);
480 :
481 635 : SetGeometryDirectly(poGeometry);
482 :
483 635 : SetMBR(dfX, dfY, dfX, dfY);
484 :
485 : /* Go to the first line of the next feature */
486 :
487 635 : const char *pszLine = nullptr;
488 1902 : while (((pszLine = fp->GetLine()) != nullptr) &&
489 1257 : fp->IsValidFeature(pszLine) == FALSE)
490 : ;
491 635 : return 0;
492 : }
493 :
494 : /**********************************************************************
495 : *
496 : **********************************************************************/
497 0 : int TABFontPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
498 : {
499 : /*-----------------------------------------------------------------
500 : * Fetch and validate geometry
501 : *----------------------------------------------------------------*/
502 0 : OGRGeometry *poGeom = GetGeometryRef();
503 0 : OGRPoint *poPoint = nullptr;
504 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
505 0 : poPoint = poGeom->toPoint();
506 : else
507 : {
508 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
509 : "TABFontPoint: Missing or Invalid Geometry!");
510 0 : return -1;
511 : }
512 :
513 0 : fp->WriteLine("Point %.15g %.15g\n", poPoint->getX(), poPoint->getY());
514 0 : fp->WriteLine(" Symbol (%d,%d,%d,\"%s\",%d,%.15g)\n", GetSymbolNo(),
515 0 : GetSymbolColor(), GetSymbolSize(), GetFontNameRef(),
516 : GetFontStyleMIFValue(), GetSymbolAngle());
517 :
518 0 : return 0;
519 : }
520 :
521 : /**********************************************************************
522 : *
523 : **********************************************************************/
524 678 : int TABCustomPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
525 : {
526 : char **papszToken =
527 678 : CSLTokenizeString2(fp->GetSavedLine(), " \t", CSLT_HONOURSTRINGS);
528 :
529 678 : if (CSLCount(papszToken) != 3)
530 : {
531 0 : CSLDestroy(papszToken);
532 0 : return -1;
533 : }
534 :
535 678 : double dfX = fp->GetXTrans(CPLAtof(papszToken[1]));
536 678 : double dfY = fp->GetYTrans(CPLAtof(papszToken[2]));
537 :
538 678 : CSLDestroy(papszToken);
539 :
540 : papszToken =
541 678 : CSLTokenizeStringComplex(fp->GetLastLine(), " ,()\t", TRUE, FALSE);
542 678 : if (CSLCount(papszToken) != 5)
543 : {
544 :
545 0 : CSLDestroy(papszToken);
546 0 : return -1;
547 : }
548 :
549 678 : SetFontName(papszToken[1]);
550 678 : SetSymbolColor(atoi(papszToken[2]));
551 678 : SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
552 678 : m_nCustomStyle = static_cast<GByte>(atoi(papszToken[4]));
553 :
554 678 : CSLDestroy(papszToken);
555 :
556 678 : OGRGeometry *poGeometry = new OGRPoint(dfX, dfY);
557 :
558 678 : SetGeometryDirectly(poGeometry);
559 :
560 678 : SetMBR(dfX, dfY, dfX, dfY);
561 :
562 : /* Go to the first line of the next feature */
563 :
564 678 : const char *pszLine = nullptr;
565 2030 : while (((pszLine = fp->GetLine()) != nullptr) &&
566 1339 : fp->IsValidFeature(pszLine) == FALSE)
567 : ;
568 :
569 678 : return 0;
570 : }
571 :
572 : /**********************************************************************
573 : *
574 : **********************************************************************/
575 0 : int TABCustomPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
576 : {
577 : /*-----------------------------------------------------------------
578 : * Fetch and validate geometry
579 : *----------------------------------------------------------------*/
580 0 : OGRGeometry *poGeom = GetGeometryRef();
581 0 : OGRPoint *poPoint = nullptr;
582 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
583 0 : poPoint = poGeom->toPoint();
584 : else
585 : {
586 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
587 : "TABCustomPoint: Missing or Invalid Geometry!");
588 0 : return -1;
589 : }
590 :
591 0 : fp->WriteLine("Point %.15g %.15g\n", poPoint->getX(), poPoint->getY());
592 0 : fp->WriteLine(" Symbol (\"%s\",%d,%d,%d)\n", GetFontNameRef(),
593 0 : GetSymbolColor(), GetSymbolSize(), m_nCustomStyle);
594 :
595 0 : return 0;
596 : }
597 :
598 : /**********************************************************************
599 : *
600 : **********************************************************************/
601 2433 : int TABPolyline::ReadGeometryFromMIFFile(MIDDATAFile *fp)
602 : {
603 : char **papszToken =
604 2433 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
605 :
606 2433 : if (CSLCount(papszToken) < 1)
607 : {
608 0 : CSLDestroy(papszToken);
609 0 : return -1;
610 : }
611 :
612 2433 : const char *pszLine = nullptr;
613 2433 : OGRLineString *poLine = nullptr;
614 2433 : OGRMultiLineString *poMultiLine = nullptr;
615 2433 : GBool bMultiple = FALSE;
616 2433 : int nNumPoints = 0;
617 2433 : int nNumSec = 0;
618 2433 : OGREnvelope sEnvelope;
619 :
620 2433 : if (STARTS_WITH_CI(papszToken[0], "LINE"))
621 : {
622 666 : if (CSLCount(papszToken) != 5)
623 : {
624 16 : CSLDestroy(papszToken);
625 16 : return -1;
626 : }
627 :
628 650 : poLine = new OGRLineString();
629 650 : poLine->setNumPoints(2);
630 650 : poLine->setPoint(0, fp->GetXTrans(CPLAtof(papszToken[1])),
631 650 : fp->GetYTrans(CPLAtof(papszToken[2])));
632 650 : poLine->setPoint(1, fp->GetXTrans(CPLAtof(papszToken[3])),
633 650 : fp->GetYTrans(CPLAtof(papszToken[4])));
634 650 : poLine->getEnvelope(&sEnvelope);
635 650 : SetGeometryDirectly(poLine);
636 650 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
637 : }
638 1767 : else if (STARTS_WITH_CI(papszToken[0], "PLINE"))
639 : {
640 1767 : switch (CSLCount(papszToken))
641 : {
642 614 : case 1:
643 614 : bMultiple = FALSE;
644 614 : pszLine = fp->GetLine();
645 614 : if (pszLine == nullptr)
646 : {
647 6 : CSLDestroy(papszToken);
648 6 : return -1;
649 : }
650 608 : nNumPoints = atoi(pszLine);
651 608 : break;
652 598 : case 2:
653 598 : bMultiple = FALSE;
654 598 : nNumPoints = atoi(papszToken[1]);
655 598 : break;
656 555 : case 3:
657 555 : if (STARTS_WITH_CI(papszToken[1], "MULTIPLE"))
658 : {
659 555 : bMultiple = TRUE;
660 555 : nNumSec = atoi(papszToken[2]);
661 555 : pszLine = fp->GetLine();
662 555 : if (pszLine == nullptr)
663 : {
664 2 : CSLDestroy(papszToken);
665 2 : return -1;
666 : }
667 553 : nNumPoints = atoi(pszLine);
668 : }
669 : else
670 : {
671 0 : CSLDestroy(papszToken);
672 0 : return -1;
673 : }
674 553 : break;
675 0 : case 4:
676 0 : if (STARTS_WITH_CI(papszToken[1], "MULTIPLE"))
677 : {
678 0 : bMultiple = TRUE;
679 0 : nNumSec = atoi(papszToken[2]);
680 0 : nNumPoints = atoi(papszToken[3]);
681 : }
682 : else
683 : {
684 0 : CSLDestroy(papszToken);
685 0 : return -1;
686 : }
687 0 : break;
688 0 : default:
689 0 : CSLDestroy(papszToken);
690 0 : return -1;
691 : }
692 :
693 1759 : if (bMultiple)
694 : {
695 553 : poMultiLine = new OGRMultiLineString();
696 1615 : for (int j = 0; j < nNumSec; j++)
697 : {
698 1092 : if (j != 0)
699 : {
700 539 : pszLine = fp->GetLine();
701 539 : if (pszLine == nullptr)
702 : {
703 2 : delete poMultiLine;
704 2 : CSLDestroy(papszToken);
705 2 : return -1;
706 : }
707 537 : nNumPoints = atoi(pszLine);
708 : }
709 1090 : if (nNumPoints < 2)
710 : {
711 4 : CPLError(CE_Failure, CPLE_FileIO,
712 : "Invalid number of vertices (%d) in PLINE "
713 : "MULTIPLE segment.",
714 : nNumPoints);
715 4 : delete poMultiLine;
716 4 : CSLDestroy(papszToken);
717 4 : return -1;
718 : }
719 1086 : poLine = new OGRLineString();
720 1086 : const int MAX_INITIAL_POINTS = 100000;
721 2172 : const int nInitialNumPoints = (nNumPoints < MAX_INITIAL_POINTS)
722 1086 : ? nNumPoints
723 : : MAX_INITIAL_POINTS;
724 : /* Do not allocate too much memory to begin with */
725 1086 : poLine->setNumPoints(nInitialNumPoints);
726 1086 : if (poLine->getNumPoints() != nInitialNumPoints)
727 : {
728 0 : delete poLine;
729 0 : delete poMultiLine;
730 0 : CSLDestroy(papszToken);
731 0 : return -1;
732 : }
733 3222 : for (int i = 0; i < nNumPoints; i++)
734 : {
735 2160 : if (i == MAX_INITIAL_POINTS)
736 : {
737 0 : poLine->setNumPoints(nNumPoints);
738 0 : if (poLine->getNumPoints() != nNumPoints)
739 : {
740 0 : delete poLine;
741 0 : delete poMultiLine;
742 0 : CSLDestroy(papszToken);
743 0 : return -1;
744 : }
745 : }
746 2160 : CSLDestroy(papszToken);
747 2160 : papszToken = CSLTokenizeString2(fp->GetLine(), " \t",
748 : CSLT_HONOURSTRINGS);
749 2160 : if (CSLCount(papszToken) != 2)
750 : {
751 24 : CSLDestroy(papszToken);
752 24 : delete poLine;
753 24 : delete poMultiLine;
754 24 : return -1;
755 : }
756 2136 : poLine->setPoint(i, fp->GetXTrans(CPLAtof(papszToken[0])),
757 2136 : fp->GetYTrans(CPLAtof(papszToken[1])));
758 : }
759 1062 : if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE)
760 : {
761 0 : CPLAssert(false); // Just in case OGR is modified
762 : }
763 : }
764 523 : poMultiLine->getEnvelope(&sEnvelope);
765 523 : if (SetGeometryDirectly(poMultiLine) != OGRERR_NONE)
766 : {
767 0 : CPLAssert(false); // Just in case OGR is modified
768 : }
769 523 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
770 : sEnvelope.MaxY);
771 : }
772 : else
773 : {
774 1206 : if (nNumPoints < 2)
775 : {
776 11 : CPLError(CE_Failure, CPLE_FileIO,
777 : "Invalid number of vertices (%d) in PLINE "
778 : "segment.",
779 : nNumPoints);
780 11 : CSLDestroy(papszToken);
781 11 : return -1;
782 : }
783 1195 : poLine = new OGRLineString();
784 1195 : const int MAX_INITIAL_POINTS = 100000;
785 2390 : const int nInitialNumPoints = (nNumPoints < MAX_INITIAL_POINTS)
786 1195 : ? nNumPoints
787 : : MAX_INITIAL_POINTS;
788 : /* Do not allocate too much memory to begin with */
789 1195 : poLine->setNumPoints(nInitialNumPoints);
790 1195 : if (poLine->getNumPoints() != nInitialNumPoints)
791 : {
792 0 : delete poLine;
793 0 : CSLDestroy(papszToken);
794 0 : return -1;
795 : }
796 3555 : for (int i = 0; i < nNumPoints; i++)
797 : {
798 2384 : if (i == MAX_INITIAL_POINTS)
799 : {
800 0 : poLine->setNumPoints(nNumPoints);
801 0 : if (poLine->getNumPoints() != nNumPoints)
802 : {
803 0 : delete poLine;
804 0 : CSLDestroy(papszToken);
805 0 : return -1;
806 : }
807 : }
808 2384 : CSLDestroy(papszToken);
809 2384 : papszToken = CSLTokenizeString2(fp->GetLine(), " \t",
810 : CSLT_HONOURSTRINGS);
811 :
812 2384 : if (CSLCount(papszToken) != 2)
813 : {
814 24 : CSLDestroy(papszToken);
815 24 : delete poLine;
816 24 : return -1;
817 : }
818 2360 : poLine->setPoint(i, fp->GetXTrans(CPLAtof(papszToken[0])),
819 2360 : fp->GetYTrans(CPLAtof(papszToken[1])));
820 : }
821 1171 : poLine->getEnvelope(&sEnvelope);
822 1171 : SetGeometryDirectly(poLine);
823 1171 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
824 : sEnvelope.MaxY);
825 : }
826 : }
827 :
828 2344 : CSLDestroy(papszToken);
829 2344 : papszToken = nullptr;
830 :
831 11299 : while (((pszLine = fp->GetLine()) != nullptr) &&
832 5618 : fp->IsValidFeature(pszLine) == FALSE)
833 : {
834 3337 : papszToken = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
835 :
836 3337 : if (CSLCount(papszToken) >= 1)
837 : {
838 1053 : if (STARTS_WITH_CI(papszToken[0], "PEN"))
839 : {
840 :
841 521 : if (CSLCount(papszToken) == 4)
842 : {
843 515 : SetPenWidthMIF(atoi(papszToken[1]));
844 515 : SetPenPattern(static_cast<GByte>(atoi(papszToken[2])));
845 515 : SetPenColor(atoi(papszToken[3]));
846 : }
847 : }
848 532 : else if (STARTS_WITH_CI(papszToken[0], "SMOOTH"))
849 : {
850 501 : m_bSmooth = TRUE;
851 : }
852 : }
853 3337 : CSLDestroy(papszToken);
854 : }
855 2344 : return 0;
856 : }
857 :
858 : /**********************************************************************
859 : *
860 : **********************************************************************/
861 0 : int TABPolyline::WriteGeometryToMIFFile(MIDDATAFile *fp)
862 : {
863 0 : OGRMultiLineString *poMultiLine = nullptr;
864 0 : OGRLineString *poLine = nullptr;
865 : int nNumPoints, i;
866 :
867 : /*-----------------------------------------------------------------
868 : * Fetch and validate geometry
869 : *----------------------------------------------------------------*/
870 0 : OGRGeometry *poGeom = GetGeometryRef();
871 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
872 : {
873 : /*-------------------------------------------------------------
874 : * Simple polyline
875 : *------------------------------------------------------------*/
876 0 : poLine = poGeom->toLineString();
877 0 : nNumPoints = poLine->getNumPoints();
878 0 : if (nNumPoints == 2)
879 : {
880 0 : fp->WriteLine("Line %.15g %.15g %.15g %.15g\n", poLine->getX(0),
881 : poLine->getY(0), poLine->getX(1), poLine->getY(1));
882 : }
883 : else
884 : {
885 :
886 0 : fp->WriteLine("Pline %d\n", nNumPoints);
887 0 : for (i = 0; i < nNumPoints; i++)
888 : {
889 0 : fp->WriteLine("%.15g %.15g\n", poLine->getX(i),
890 : poLine->getY(i));
891 : }
892 : }
893 : }
894 0 : else if (poGeom &&
895 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
896 : {
897 : /*-------------------------------------------------------------
898 : * Multiple polyline... validate all components
899 : *------------------------------------------------------------*/
900 : int iLine, numLines;
901 0 : poMultiLine = poGeom->toMultiLineString();
902 0 : numLines = poMultiLine->getNumGeometries();
903 :
904 0 : fp->WriteLine("PLINE MULTIPLE %d\n", numLines);
905 :
906 0 : for (iLine = 0; iLine < numLines; iLine++)
907 : {
908 0 : poGeom = poMultiLine->getGeometryRef(iLine);
909 0 : if (poGeom &&
910 0 : wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
911 : {
912 0 : poLine = poGeom->toLineString();
913 0 : nNumPoints = poLine->getNumPoints();
914 :
915 0 : fp->WriteLine(" %d\n", nNumPoints);
916 0 : for (i = 0; i < nNumPoints; i++)
917 : {
918 0 : fp->WriteLine("%.15g %.15g\n", poLine->getX(i),
919 : poLine->getY(i));
920 : }
921 : }
922 : else
923 : {
924 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
925 : "TABPolyline: Object contains an invalid Geometry!");
926 : }
927 : }
928 : }
929 : else
930 : {
931 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
932 : "TABPolyline: Missing or Invalid Geometry!");
933 : }
934 :
935 0 : if (GetPenPattern())
936 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
937 : GetPenColor());
938 0 : if (m_bSmooth)
939 0 : fp->WriteLine(" Smooth\n");
940 :
941 0 : return 0;
942 : }
943 :
944 : /**********************************************************************
945 : * TABRegion::ReadGeometryFromMIFFile()
946 : *
947 : * Fill the geometry and representation (color, etc...) part of the
948 : * feature from the contents of the .MIF file
949 : *
950 : * Returns 0 on success, -1 on error, in which case CPLError() will have
951 : * been called.
952 : **********************************************************************/
953 560 : int TABRegion::ReadGeometryFromMIFFile(MIDDATAFile *fp)
954 : {
955 560 : m_bSmooth = FALSE;
956 :
957 : /*=============================================================
958 : * REGION (Similar to PLINE MULTIPLE)
959 : *============================================================*/
960 : CPLStringList aosTokens(
961 1120 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
962 :
963 560 : int numLineSections = (aosTokens.size() == 2) ? atoi(aosTokens[1]) : 0;
964 560 : aosTokens.Clear();
965 :
966 560 : if (numLineSections < 0 ||
967 560 : numLineSections > INT_MAX / static_cast<int>(sizeof(OGRPolygon *)))
968 : {
969 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of sections: %d",
970 : numLineSections);
971 0 : return -1;
972 : }
973 :
974 1120 : std::vector<std::unique_ptr<OGRGeometry>> apoPolys;
975 560 : constexpr int MAX_INITIAL_SECTIONS = 100000;
976 : const int numInitialLineSections =
977 560 : std::min(numLineSections, MAX_INITIAL_SECTIONS);
978 560 : apoPolys.reserve(numInitialLineSections);
979 :
980 560 : const char *pszLine = nullptr;
981 :
982 1049 : for (int iSection = 0; iSection < numLineSections; iSection++)
983 : {
984 556 : int numSectionVertices = 0;
985 :
986 556 : if ((pszLine = fp->GetLine()) != nullptr)
987 : {
988 552 : numSectionVertices = atoi(pszLine);
989 : }
990 556 : if (numSectionVertices < 2)
991 : {
992 10 : CPLError(CE_Failure, CPLE_FileIO,
993 : "Invalid number of points (%d) in REGION "
994 : "segment.",
995 : numSectionVertices);
996 67 : return -1;
997 : }
998 :
999 546 : auto poRing = std::make_unique<OGRLinearRing>();
1000 :
1001 546 : constexpr int MAX_INITIAL_POINTS = 100000;
1002 : const int nInitialNumPoints =
1003 546 : std::min(numSectionVertices, MAX_INITIAL_POINTS);
1004 : /* Do not allocate too much memory to begin with */
1005 546 : poRing->setNumPoints(nInitialNumPoints);
1006 546 : if (poRing->getNumPoints() != nInitialNumPoints)
1007 : {
1008 0 : return -1;
1009 : }
1010 8912 : for (int i = 0; i < numSectionVertices; i++)
1011 : {
1012 8423 : if (i == MAX_INITIAL_POINTS)
1013 : {
1014 0 : poRing->setNumPoints(numSectionVertices);
1015 0 : if (poRing->getNumPoints() != numSectionVertices)
1016 : {
1017 0 : return -1;
1018 : }
1019 : }
1020 :
1021 : aosTokens =
1022 8423 : CSLTokenizeStringComplex(fp->GetLine(), " ,\t", TRUE, FALSE);
1023 8423 : if (aosTokens.size() < 2)
1024 : {
1025 57 : return -1;
1026 : }
1027 :
1028 8366 : const double dX = fp->GetXTrans(CPLAtof(aosTokens[0]));
1029 8366 : const double dY = fp->GetYTrans(CPLAtof(aosTokens[1]));
1030 8366 : poRing->setPoint(i, dX, dY);
1031 8366 : aosTokens.Clear();
1032 : }
1033 :
1034 489 : poRing->closeRings();
1035 :
1036 489 : auto poPoly = std::make_unique<OGRPolygon>();
1037 489 : poPoly->addRing(std::move(poRing));
1038 489 : apoPolys.push_back(std::move(poPoly));
1039 : }
1040 :
1041 0 : std::unique_ptr<OGRGeometry> poGeometry;
1042 493 : if (apoPolys.size() == 1)
1043 : {
1044 489 : poGeometry = std::move(apoPolys[0]);
1045 : }
1046 4 : else if (apoPolys.size() > 1)
1047 : {
1048 0 : bool isValidGeometry = false;
1049 0 : const char *const apszOptions[] = {"METHOD=DEFAULT", nullptr};
1050 0 : poGeometry = OGRGeometryFactory::organizePolygons(
1051 0 : apoPolys, &isValidGeometry, apszOptions);
1052 :
1053 0 : if (!isValidGeometry)
1054 : {
1055 0 : CPLError(
1056 : CE_Warning, CPLE_AppDefined,
1057 : "Geometry of polygon cannot be translated to Simple Geometry. "
1058 : "All polygons will be contained in a multipolygon.\n");
1059 : }
1060 : }
1061 :
1062 493 : if (poGeometry)
1063 : {
1064 489 : OGREnvelope sEnvelope;
1065 489 : poGeometry->getEnvelope(&sEnvelope);
1066 489 : SetGeometry(std::move(poGeometry));
1067 :
1068 489 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1069 : }
1070 :
1071 2497 : while (((pszLine = fp->GetLine()) != nullptr) &&
1072 1219 : fp->IsValidFeature(pszLine) == FALSE)
1073 : {
1074 785 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1075 :
1076 785 : if (aosTokens.size() > 1)
1077 : {
1078 662 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1079 : {
1080 :
1081 330 : if (aosTokens.size() == 4)
1082 : {
1083 330 : SetPenWidthMIF(atoi(aosTokens[1]));
1084 330 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1085 330 : SetPenColor(atoi(aosTokens[3]));
1086 : }
1087 : }
1088 332 : else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
1089 : {
1090 330 : if (aosTokens.size() >= 3)
1091 : {
1092 330 : SetBrushFGColor(atoi(aosTokens[2]));
1093 330 : SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
1094 :
1095 330 : if (aosTokens.size() == 4)
1096 330 : SetBrushBGColor(atoi(aosTokens[3]));
1097 : else
1098 0 : SetBrushTransparent(TRUE);
1099 : }
1100 : }
1101 2 : else if (STARTS_WITH_CI(aosTokens[0], "CENTER"))
1102 : {
1103 2 : if (aosTokens.size() == 3)
1104 : {
1105 2 : SetCenter(fp->GetXTrans(CPLAtof(aosTokens[1])),
1106 2 : fp->GetYTrans(CPLAtof(aosTokens[2])));
1107 : }
1108 : }
1109 : }
1110 785 : aosTokens.Clear();
1111 : }
1112 :
1113 493 : return 0;
1114 : }
1115 :
1116 : /**********************************************************************
1117 : * TABRegion::WriteGeometryToMIFFile()
1118 : *
1119 : * Write the geometry and representation (color, etc...) part of the
1120 : * feature to the .MIF file
1121 : *
1122 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1123 : * been called.
1124 : **********************************************************************/
1125 21 : int TABRegion::WriteGeometryToMIFFile(MIDDATAFile *fp)
1126 : {
1127 21 : OGRGeometry *poGeom = GetGeometryRef();
1128 :
1129 21 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
1130 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
1131 : {
1132 : /*=============================================================
1133 : * REGIONs are similar to PLINE MULTIPLE
1134 : *
1135 : * We accept both OGRPolygons (with one or multiple rings) and
1136 : * OGRMultiPolygons as input.
1137 : *============================================================*/
1138 : int i, iRing, numRingsTotal, numPoints;
1139 :
1140 21 : numRingsTotal = GetNumRings();
1141 :
1142 21 : fp->WriteLine("Region %d\n", numRingsTotal);
1143 :
1144 42 : for (iRing = 0; iRing < numRingsTotal; iRing++)
1145 : {
1146 21 : OGRLinearRing *poRing = GetRingRef(iRing);
1147 21 : if (poRing == nullptr)
1148 : {
1149 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1150 : "TABRegion: Object Geometry contains NULL rings!");
1151 0 : return -1;
1152 : }
1153 :
1154 21 : numPoints = poRing->getNumPoints();
1155 :
1156 21 : fp->WriteLine(" %d\n", numPoints);
1157 516 : for (i = 0; i < numPoints; i++)
1158 : {
1159 495 : fp->WriteLine("%.15g %.15g\n", poRing->getX(i),
1160 : poRing->getY(i));
1161 : }
1162 : }
1163 :
1164 21 : if (GetPenPattern())
1165 42 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(),
1166 21 : GetPenPattern(), GetPenColor());
1167 :
1168 21 : if (GetBrushPattern())
1169 : {
1170 21 : if (GetBrushTransparent() == 0)
1171 21 : fp->WriteLine(" Brush (%d,%d,%d)\n", GetBrushPattern(),
1172 : GetBrushFGColor(), GetBrushBGColor());
1173 : else
1174 0 : fp->WriteLine(" Brush (%d,%d)\n", GetBrushPattern(),
1175 : GetBrushFGColor());
1176 : }
1177 :
1178 21 : if (m_bCenterIsSet)
1179 : {
1180 0 : fp->WriteLine(" Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
1181 : }
1182 : }
1183 : else
1184 : {
1185 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1186 : "TABRegion: Object contains an invalid Geometry!");
1187 0 : return -1;
1188 : }
1189 :
1190 21 : return 0;
1191 : }
1192 :
1193 : /**********************************************************************
1194 : *
1195 : **********************************************************************/
1196 941 : int TABRectangle::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1197 : {
1198 : CPLStringList aosTokens(
1199 1882 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1200 :
1201 941 : if (aosTokens.size() < 5)
1202 : {
1203 20 : return -1;
1204 : }
1205 :
1206 921 : double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1207 921 : double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1208 921 : double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1209 921 : double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1210 :
1211 : /*-----------------------------------------------------------------
1212 : * Call SetMBR() and GetMBR() now to make sure that min values are
1213 : * really smaller than max values.
1214 : *----------------------------------------------------------------*/
1215 921 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1216 921 : GetMBR(dXMin, dYMin, dXMax, dYMax);
1217 :
1218 921 : m_bRoundCorners = FALSE;
1219 921 : m_dRoundXRadius = 0.0;
1220 921 : m_dRoundYRadius = 0.0;
1221 :
1222 921 : if (STARTS_WITH_CI(aosTokens[0], "ROUNDRECT"))
1223 : {
1224 436 : m_bRoundCorners = TRUE;
1225 436 : if (aosTokens.size() == 6)
1226 : {
1227 434 : m_dRoundXRadius = CPLAtof(aosTokens[5]) / 2.0;
1228 434 : m_dRoundYRadius = m_dRoundXRadius;
1229 : }
1230 : else
1231 : {
1232 : aosTokens =
1233 2 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1234 2 : if (aosTokens.size() == 1)
1235 0 : m_dRoundXRadius = m_dRoundYRadius = CPLAtof(aosTokens[0]) / 2.0;
1236 : }
1237 : }
1238 921 : aosTokens.Clear();
1239 :
1240 : /*-----------------------------------------------------------------
1241 : * Create and fill geometry object
1242 : *----------------------------------------------------------------*/
1243 :
1244 921 : OGRPolygon *poPolygon = new OGRPolygon;
1245 921 : OGRLinearRing *poRing = new OGRLinearRing();
1246 921 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
1247 : {
1248 : /*-------------------------------------------------------------
1249 : * For rounded rectangles, we generate arcs with 45 line
1250 : * segments for each corner. We start with lower-left corner
1251 : * and proceed counterclockwise
1252 : * We also have to make sure that rounding radius is not too
1253 : * large for the MBR however, we
1254 : * always return the true X/Y radius (not adjusted) since this
1255 : * is the way MapInfo seems to do it when a radius bigger than
1256 : * the MBR is passed from TBA to MIF.
1257 : *------------------------------------------------------------*/
1258 : const double dXRadius =
1259 434 : std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
1260 : const double dYRadius =
1261 434 : std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
1262 434 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
1263 : dYRadius, M_PI, 3.0 * M_PI / 2.0);
1264 434 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
1265 : dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
1266 434 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
1267 : dYRadius, 0.0, M_PI / 2.0);
1268 434 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
1269 : dYRadius, M_PI / 2.0, M_PI);
1270 :
1271 434 : TABCloseRing(poRing);
1272 : }
1273 : else
1274 : {
1275 487 : poRing->addPoint(dXMin, dYMin);
1276 487 : poRing->addPoint(dXMax, dYMin);
1277 487 : poRing->addPoint(dXMax, dYMax);
1278 487 : poRing->addPoint(dXMin, dYMax);
1279 487 : poRing->addPoint(dXMin, dYMin);
1280 : }
1281 :
1282 921 : poPolygon->addRingDirectly(poRing);
1283 921 : SetGeometryDirectly(poPolygon);
1284 :
1285 921 : const char *pszLine = nullptr;
1286 7118 : while (((pszLine = fp->GetLine()) != nullptr) &&
1287 3520 : fp->IsValidFeature(pszLine) == FALSE)
1288 : {
1289 2677 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1290 :
1291 2677 : if (aosTokens.size() > 1)
1292 : {
1293 1776 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1294 : {
1295 903 : if (aosTokens.size() == 4)
1296 : {
1297 895 : SetPenWidthMIF(atoi(aosTokens[1]));
1298 895 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1299 895 : SetPenColor(atoi(aosTokens[3]));
1300 : }
1301 : }
1302 873 : else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
1303 : {
1304 873 : if (aosTokens.size() >= 3)
1305 : {
1306 869 : SetBrushFGColor(atoi(aosTokens[2]));
1307 869 : SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
1308 :
1309 869 : if (aosTokens.size() == 4)
1310 865 : SetBrushBGColor(atoi(aosTokens[3]));
1311 : else
1312 4 : SetBrushTransparent(TRUE);
1313 : }
1314 : }
1315 : }
1316 2677 : aosTokens.Clear();
1317 : }
1318 :
1319 921 : return 0;
1320 : }
1321 :
1322 : /**********************************************************************
1323 : *
1324 : **********************************************************************/
1325 0 : int TABRectangle::WriteGeometryToMIFFile(MIDDATAFile *fp)
1326 : {
1327 : /*-----------------------------------------------------------------
1328 : * Fetch and validate geometry
1329 : *----------------------------------------------------------------*/
1330 0 : OGRGeometry *poGeom = GetGeometryRef();
1331 0 : OGRPolygon *poPolygon = nullptr;
1332 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
1333 0 : poPolygon = poGeom->toPolygon();
1334 : else
1335 : {
1336 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1337 : "TABRectangle: Missing or Invalid Geometry!");
1338 0 : return -1;
1339 : }
1340 : /*-----------------------------------------------------------------
1341 : * Note that we will simply use the rectangle's MBR and don't really
1342 : * read the polygon geometry... this should be OK unless the
1343 : * polygon geometry was not really a rectangle.
1344 : *----------------------------------------------------------------*/
1345 0 : OGREnvelope sEnvelope;
1346 0 : poPolygon->getEnvelope(&sEnvelope);
1347 :
1348 0 : if (m_bRoundCorners == TRUE)
1349 : {
1350 0 : fp->WriteLine("Roundrect %.15g %.15g %.15g %.15g %.15g\n",
1351 : sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
1352 0 : sEnvelope.MaxY, m_dRoundXRadius * 2.0);
1353 : }
1354 : else
1355 : {
1356 0 : fp->WriteLine("Rect %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
1357 : sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1358 : }
1359 :
1360 0 : if (GetPenPattern())
1361 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
1362 : GetPenColor());
1363 :
1364 0 : if (GetBrushPattern())
1365 : {
1366 0 : if (GetBrushTransparent() == 0)
1367 0 : fp->WriteLine(" Brush (%d,%d,%d)\n", GetBrushPattern(),
1368 : GetBrushFGColor(), GetBrushBGColor());
1369 : else
1370 0 : fp->WriteLine(" Brush (%d,%d)\n", GetBrushPattern(),
1371 : GetBrushFGColor());
1372 : }
1373 0 : return 0;
1374 : }
1375 :
1376 : /**********************************************************************
1377 : *
1378 : **********************************************************************/
1379 397 : int TABEllipse::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1380 : {
1381 : CPLStringList aosTokens(
1382 794 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1383 :
1384 397 : if (aosTokens.size() != 5)
1385 : {
1386 10 : return -1;
1387 : }
1388 :
1389 387 : double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1390 387 : double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1391 387 : double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1392 387 : double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1393 387 : aosTokens.Clear();
1394 :
1395 : /*-----------------------------------------------------------------
1396 : * Save info about the ellipse def. inside class members
1397 : *----------------------------------------------------------------*/
1398 387 : m_dCenterX = (dXMin + dXMax) / 2.0;
1399 387 : m_dCenterY = (dYMin + dYMax) / 2.0;
1400 387 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
1401 387 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
1402 :
1403 387 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1404 :
1405 : /*-----------------------------------------------------------------
1406 : * Create and fill geometry object
1407 : *----------------------------------------------------------------*/
1408 387 : OGRPolygon *poPolygon = new OGRPolygon;
1409 387 : OGRLinearRing *poRing = new OGRLinearRing();
1410 :
1411 : /*-----------------------------------------------------------------
1412 : * For the OGR geometry, we generate an ellipse with 2 degrees line
1413 : * segments.
1414 : *----------------------------------------------------------------*/
1415 387 : TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
1416 : 0.0, 2.0 * M_PI);
1417 387 : TABCloseRing(poRing);
1418 :
1419 387 : poPolygon->addRingDirectly(poRing);
1420 387 : SetGeometryDirectly(poPolygon);
1421 :
1422 387 : const char *pszLine = nullptr;
1423 2973 : while (((pszLine = fp->GetLine()) != nullptr) &&
1424 1470 : fp->IsValidFeature(pszLine) == FALSE)
1425 : {
1426 1116 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1427 :
1428 1116 : if (aosTokens.size() > 1)
1429 : {
1430 743 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1431 : {
1432 379 : if (aosTokens.size() == 4)
1433 : {
1434 375 : SetPenWidthMIF(atoi(aosTokens[1]));
1435 375 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1436 375 : SetPenColor(atoi(aosTokens[3]));
1437 : }
1438 : }
1439 364 : else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
1440 : {
1441 364 : if (aosTokens.size() >= 3)
1442 : {
1443 362 : SetBrushFGColor(atoi(aosTokens[2]));
1444 362 : SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
1445 :
1446 362 : if (aosTokens.size() == 4)
1447 360 : SetBrushBGColor(atoi(aosTokens[3]));
1448 : else
1449 2 : SetBrushTransparent(TRUE);
1450 : }
1451 : }
1452 : }
1453 1116 : aosTokens.Clear();
1454 : }
1455 387 : return 0;
1456 : }
1457 :
1458 : /**********************************************************************
1459 : *
1460 : **********************************************************************/
1461 0 : int TABEllipse::WriteGeometryToMIFFile(MIDDATAFile *fp)
1462 : {
1463 0 : OGREnvelope sEnvelope;
1464 0 : OGRGeometry *poGeom = GetGeometryRef();
1465 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
1466 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
1467 0 : poGeom->getEnvelope(&sEnvelope);
1468 : else
1469 : {
1470 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1471 : "TABEllipse: Missing or Invalid Geometry!");
1472 0 : return -1;
1473 : }
1474 :
1475 0 : fp->WriteLine("Ellipse %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
1476 : sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1477 :
1478 0 : if (GetPenPattern())
1479 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
1480 : GetPenColor());
1481 :
1482 0 : if (GetBrushPattern())
1483 : {
1484 0 : if (GetBrushTransparent() == 0)
1485 0 : fp->WriteLine(" Brush (%d,%d,%d)\n", GetBrushPattern(),
1486 : GetBrushFGColor(), GetBrushBGColor());
1487 : else
1488 0 : fp->WriteLine(" Brush (%d,%d)\n", GetBrushPattern(),
1489 : GetBrushFGColor());
1490 : }
1491 0 : return 0;
1492 : }
1493 :
1494 : /**********************************************************************
1495 : *
1496 : **********************************************************************/
1497 672 : int TABArc::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1498 : {
1499 672 : double dXMin = 0.0;
1500 672 : double dXMax = 0.0;
1501 672 : double dYMin = 0.0;
1502 672 : double dYMax = 0.0;
1503 :
1504 : CPLStringList aosTokens(
1505 1344 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1506 :
1507 672 : if (aosTokens.size() == 5)
1508 : {
1509 346 : dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1510 346 : dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1511 346 : dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1512 346 : dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1513 :
1514 : aosTokens =
1515 346 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1516 346 : if (aosTokens.size() != 2)
1517 : {
1518 8 : return -1;
1519 : }
1520 :
1521 338 : m_dStartAngle = CPLAtof(aosTokens[0]);
1522 338 : m_dEndAngle = CPLAtof(aosTokens[1]);
1523 : }
1524 326 : else if (aosTokens.size() == 7)
1525 : {
1526 304 : dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1527 304 : dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1528 304 : dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1529 304 : dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1530 304 : m_dStartAngle = CPLAtof(aosTokens[5]);
1531 304 : m_dEndAngle = CPLAtof(aosTokens[6]);
1532 : }
1533 : else
1534 : {
1535 22 : return -1;
1536 : }
1537 :
1538 642 : aosTokens.Clear();
1539 :
1540 642 : if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
1541 : {
1542 0 : CPLError(CE_Failure, CPLE_AppDefined,
1543 : "Wrong start and end angles: %f %f", m_dStartAngle,
1544 : m_dEndAngle);
1545 0 : return -1;
1546 : }
1547 :
1548 : /*-------------------------------------------------------------
1549 : * Start/End angles
1550 : * Since the angles are specified for integer coordinates, and
1551 : * that these coordinates can have the X axis reversed, we have to
1552 : * adjust the angle values for the change in the X axis
1553 : * direction.
1554 : *
1555 : * This should be necessary only when X axis is flipped.
1556 : * __TODO__ Why is order of start/end values reversed as well???
1557 : *------------------------------------------------------------*/
1558 :
1559 642 : if (fp->GetXMultiplier() <= 0.0)
1560 : {
1561 0 : m_dStartAngle = 360.0 - m_dStartAngle;
1562 0 : m_dEndAngle = 360.0 - m_dEndAngle;
1563 : }
1564 :
1565 642 : m_dCenterX = (dXMin + dXMax) / 2.0;
1566 642 : m_dCenterY = (dYMin + dYMax) / 2.0;
1567 642 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
1568 642 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
1569 :
1570 : /*-----------------------------------------------------------------
1571 : * Create and fill geometry object
1572 : * For the OGR geometry, we generate an arc with 2 degrees line
1573 : * segments.
1574 : *----------------------------------------------------------------*/
1575 642 : OGRLineString *poLine = new OGRLineString;
1576 :
1577 : int numPts = std::max(
1578 1284 : 2,
1579 642 : (m_dEndAngle < m_dStartAngle
1580 642 : ? static_cast<int>(
1581 0 : std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
1582 642 : : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
1583 642 : 1)));
1584 :
1585 642 : TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
1586 642 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
1587 642 : m_dEndAngle * M_PI / 180.0);
1588 :
1589 642 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1590 642 : SetGeometryDirectly(poLine);
1591 :
1592 642 : const char *pszLine = nullptr;
1593 3176 : while (((pszLine = fp->GetLine()) != nullptr) &&
1594 1574 : fp->IsValidFeature(pszLine) == FALSE)
1595 : {
1596 960 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1597 :
1598 960 : if (aosTokens.size() > 1)
1599 : {
1600 328 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1601 : {
1602 :
1603 328 : if (aosTokens.size() == 4)
1604 : {
1605 324 : SetPenWidthMIF(atoi(aosTokens[1]));
1606 324 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1607 324 : SetPenColor(atoi(aosTokens[3]));
1608 : }
1609 : }
1610 : }
1611 960 : aosTokens.Clear();
1612 : }
1613 642 : return 0;
1614 : }
1615 :
1616 : /**********************************************************************
1617 : *
1618 : **********************************************************************/
1619 0 : int TABArc::WriteGeometryToMIFFile(MIDDATAFile *fp)
1620 : {
1621 : /*-------------------------------------------------------------
1622 : * Start/End angles
1623 : * Since we ALWAYS produce files in quadrant 1 then we can
1624 : * ignore the special angle conversion required by flipped axis.
1625 : *------------------------------------------------------------*/
1626 :
1627 : // Write the Arc's actual MBR
1628 0 : fp->WriteLine("Arc %.15g %.15g %.15g %.15g\n", m_dCenterX - m_dXRadius,
1629 0 : m_dCenterY - m_dYRadius, m_dCenterX + m_dXRadius,
1630 0 : m_dCenterY + m_dYRadius);
1631 :
1632 0 : fp->WriteLine(" %.15g %.15g\n", m_dStartAngle, m_dEndAngle);
1633 :
1634 0 : if (GetPenPattern())
1635 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
1636 : GetPenColor());
1637 :
1638 0 : return 0;
1639 : }
1640 :
1641 : /**********************************************************************
1642 : *
1643 : **********************************************************************/
1644 297 : int TABText::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1645 : {
1646 297 : const char *pszString = nullptr;
1647 297 : int bXYBoxRead = 0;
1648 :
1649 : CPLStringList aosTokens(
1650 594 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1651 297 : if (aosTokens.size() == 1)
1652 : {
1653 : aosTokens =
1654 3 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1655 3 : const int tokenLen = aosTokens.size();
1656 3 : if (tokenLen == 4)
1657 : {
1658 0 : pszString = nullptr;
1659 0 : bXYBoxRead = 1;
1660 : }
1661 3 : else if (tokenLen == 0)
1662 : {
1663 3 : pszString = nullptr;
1664 : }
1665 0 : else if (tokenLen != 1)
1666 : {
1667 0 : return -1;
1668 : }
1669 : else
1670 : {
1671 0 : pszString = aosTokens[0];
1672 : }
1673 : }
1674 294 : else if (aosTokens.size() == 2)
1675 : {
1676 294 : pszString = aosTokens[1];
1677 : }
1678 : else
1679 : {
1680 0 : return -1;
1681 : }
1682 :
1683 : /*-------------------------------------------------------------
1684 : * Note: The text string may contain escaped "\n" chars, and we
1685 : * sstore them in memory in the UnEscaped form to be OGR
1686 : * compliant. See Maptools bug 1107 for more details.
1687 : *------------------------------------------------------------*/
1688 297 : char *pszTmpString = CPLStrdup(pszString);
1689 297 : m_pszString = TABUnEscapeString(pszTmpString, TRUE);
1690 297 : if (pszTmpString != m_pszString)
1691 0 : CPLFree(pszTmpString);
1692 297 : if (!fp->GetEncoding().empty())
1693 : {
1694 : char *pszUtf8String =
1695 1 : CPLRecode(m_pszString, fp->GetEncoding(), CPL_ENC_UTF8);
1696 1 : CPLFree(m_pszString);
1697 1 : m_pszString = pszUtf8String;
1698 : }
1699 297 : if (!bXYBoxRead)
1700 : {
1701 : aosTokens =
1702 297 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1703 : }
1704 :
1705 297 : if (aosTokens.size() != 4)
1706 : {
1707 17 : return -1;
1708 : }
1709 :
1710 280 : double dXMin = fp->GetXTrans(CPLAtof(aosTokens[0]));
1711 280 : double dXMax = fp->GetXTrans(CPLAtof(aosTokens[2]));
1712 280 : double dYMin = fp->GetYTrans(CPLAtof(aosTokens[1]));
1713 280 : double dYMax = fp->GetYTrans(CPLAtof(aosTokens[3]));
1714 :
1715 280 : m_dHeight = dYMax - dYMin; // SetTextBoxHeight(dYMax - dYMin);
1716 280 : m_dWidth = dXMax - dXMin; // SetTextBoxWidth(dXMax - dXMin);
1717 :
1718 280 : if (m_dHeight < 0.0)
1719 0 : m_dHeight *= -1.0;
1720 280 : if (m_dWidth < 0.0)
1721 0 : m_dWidth *= -1.0;
1722 :
1723 : /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
1724 : */
1725 :
1726 280 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1727 280 : GetMBR(dXMin, dYMin, dXMax, dYMax);
1728 :
1729 280 : const char *pszLine = nullptr;
1730 3169 : while (((pszLine = fp->GetLine()) != nullptr) &&
1731 1526 : fp->IsValidFeature(pszLine) == FALSE)
1732 : {
1733 1363 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1734 :
1735 1363 : if (aosTokens.size() > 1)
1736 : {
1737 1134 : if (STARTS_WITH_CI(aosTokens[0], "FONT"))
1738 : {
1739 270 : if (aosTokens.size() >= 5)
1740 : {
1741 261 : SetFontName(aosTokens[1]);
1742 261 : SetFontFGColor(atoi(aosTokens[4]));
1743 261 : if (aosTokens.size() == 6)
1744 : {
1745 254 : SetFontBGColor(atoi(aosTokens[5]));
1746 254 : SetFontStyleMIFValue(atoi(aosTokens[2]), TRUE);
1747 : }
1748 : else
1749 7 : SetFontStyleMIFValue(atoi(aosTokens[2]));
1750 :
1751 : // papsztoken[3] = Size ???
1752 : }
1753 : }
1754 864 : else if (STARTS_WITH_CI(aosTokens[0], "SPACING"))
1755 : {
1756 237 : if (aosTokens.size() >= 2)
1757 : {
1758 237 : if (STARTS_WITH_CI(aosTokens[1], "2"))
1759 : {
1760 237 : SetTextSpacing(TABTSDouble);
1761 : }
1762 0 : else if (STARTS_WITH_CI(aosTokens[1], "1.5"))
1763 : {
1764 0 : SetTextSpacing(TABTS1_5);
1765 : }
1766 : }
1767 :
1768 237 : if (aosTokens.size() == 7)
1769 : {
1770 0 : if (STARTS_WITH_CI(aosTokens[2], "LAbel"))
1771 : {
1772 0 : if (STARTS_WITH_CI(aosTokens[4], "simple"))
1773 : {
1774 0 : SetTextLineType(TABTLSimple);
1775 0 : SetTextLineEndPoint(
1776 0 : fp->GetXTrans(CPLAtof(aosTokens[5])),
1777 0 : fp->GetYTrans(CPLAtof(aosTokens[6])));
1778 : }
1779 0 : else if (STARTS_WITH_CI(aosTokens[4], "arrow"))
1780 : {
1781 0 : SetTextLineType(TABTLArrow);
1782 0 : SetTextLineEndPoint(
1783 0 : fp->GetXTrans(CPLAtof(aosTokens[5])),
1784 0 : fp->GetYTrans(CPLAtof(aosTokens[6])));
1785 : }
1786 : }
1787 : }
1788 : }
1789 627 : else if (STARTS_WITH_CI(aosTokens[0], "Justify"))
1790 : {
1791 224 : if (aosTokens.size() == 2)
1792 : {
1793 224 : if (STARTS_WITH_CI(aosTokens[1], "Center"))
1794 : {
1795 219 : SetTextJustification(TABTJCenter);
1796 : }
1797 5 : else if (STARTS_WITH_CI(aosTokens[1], "Right"))
1798 : {
1799 0 : SetTextJustification(TABTJRight);
1800 : }
1801 : }
1802 : }
1803 403 : else if (STARTS_WITH_CI(aosTokens[0], "Angle"))
1804 : {
1805 208 : if (aosTokens.size() == 2)
1806 : {
1807 208 : SetTextAngle(CPLAtof(aosTokens[1]));
1808 : }
1809 : }
1810 195 : else if (STARTS_WITH_CI(aosTokens[0], "LAbel"))
1811 : {
1812 195 : if (aosTokens.size() == 5)
1813 : {
1814 178 : if (STARTS_WITH_CI(aosTokens[2], "simple"))
1815 : {
1816 178 : SetTextLineType(TABTLSimple);
1817 356 : SetTextLineEndPoint(
1818 178 : fp->GetXTrans(CPLAtof(aosTokens[3])),
1819 178 : fp->GetYTrans(CPLAtof(aosTokens[4])));
1820 : }
1821 0 : else if (STARTS_WITH_CI(aosTokens[2], "arrow"))
1822 : {
1823 0 : SetTextLineType(TABTLArrow);
1824 0 : SetTextLineEndPoint(
1825 0 : fp->GetXTrans(CPLAtof(aosTokens[3])),
1826 0 : fp->GetYTrans(CPLAtof(aosTokens[4])));
1827 : }
1828 : }
1829 : // What I do with the XY coordinate
1830 : }
1831 : }
1832 : }
1833 : /*-----------------------------------------------------------------
1834 : * Create an OGRPoint Geometry...
1835 : * The point X,Y values will be the coords of the lower-left corner before
1836 : * rotation is applied. (Note that the rotation in MapInfo is done around
1837 : * the upper-left corner)
1838 : * We need to calculate the true lower left corner of the text based
1839 : * on the MBR after rotation, the text height and the rotation angle.
1840 : *---------------------------------------------------------------- */
1841 280 : double dSin = sin(m_dAngle * M_PI / 180.0);
1842 280 : double dCos = cos(m_dAngle * M_PI / 180.0);
1843 280 : double dX = 0.0;
1844 280 : double dY = 0.0;
1845 280 : if (dSin > 0.0 && dCos > 0.0)
1846 : {
1847 208 : dX = dXMin + m_dHeight * dSin;
1848 208 : dY = dYMin;
1849 : }
1850 72 : else if (dSin > 0.0 && dCos < 0.0)
1851 : {
1852 0 : dX = dXMax;
1853 0 : dY = dYMin - m_dHeight * dCos;
1854 : }
1855 72 : else if (dSin < 0.0 && dCos < 0.0)
1856 : {
1857 0 : dX = dXMax + m_dHeight * dSin;
1858 0 : dY = dYMax;
1859 : }
1860 : else // dSin < 0 && dCos > 0
1861 : {
1862 72 : dX = dXMin;
1863 72 : dY = dYMax - m_dHeight * dCos;
1864 : }
1865 :
1866 280 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1867 :
1868 280 : SetGeometryDirectly(poGeometry);
1869 :
1870 : /*-----------------------------------------------------------------
1871 : * Compute Text Width: the width of the Text MBR before rotation
1872 : * in ground units... unfortunately this value is not stored in the
1873 : * file, so we have to compute it with the MBR after rotation and
1874 : * the height of the MBR before rotation:
1875 : * With W = Width of MBR before rotation
1876 : * H = Height of MBR before rotation
1877 : * dX = Width of MBR after rotation
1878 : * dY = Height of MBR after rotation
1879 : * teta = rotation angle
1880 : *
1881 : * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
1882 : * W = H * (dX - H * sin(teta)) / (H * cos(teta))
1883 : *
1884 : * and for other teta values, use:
1885 : * W = H * (dY - H * cos(teta)) / (H * sin(teta))
1886 : *---------------------------------------------------------------- */
1887 280 : dSin = std::abs(dSin);
1888 280 : dCos = std::abs(dCos);
1889 280 : if (m_dHeight == 0.0)
1890 0 : m_dWidth = 0.0;
1891 280 : else if (dCos > dSin)
1892 280 : m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
1893 280 : (m_dHeight * dCos);
1894 : else
1895 0 : m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
1896 0 : (m_dHeight * dSin);
1897 280 : m_dWidth = std::abs(m_dWidth);
1898 :
1899 280 : return 0;
1900 : }
1901 :
1902 : /**********************************************************************
1903 : *
1904 : **********************************************************************/
1905 0 : int TABText::WriteGeometryToMIFFile(MIDDATAFile *fp)
1906 : {
1907 : /*-------------------------------------------------------------
1908 : * Note: The text string may contain unescaped "\n" chars or
1909 : * "\\" chars and we expect to receive them in an unescaped
1910 : * form. Those characters are unescaped in memory to be like
1911 : * other OGR drivers. See MapTools bug 1107 for more details.
1912 : *------------------------------------------------------------*/
1913 : char *pszTmpString;
1914 0 : if (fp->GetEncoding().empty())
1915 : {
1916 0 : pszTmpString = TABEscapeString(m_pszString);
1917 : }
1918 : else
1919 : {
1920 : char *pszEncString =
1921 0 : CPLRecode(m_pszString, CPL_ENC_UTF8, fp->GetEncoding());
1922 0 : pszTmpString = TABEscapeString(pszEncString);
1923 0 : if (pszTmpString != pszEncString)
1924 0 : CPLFree(pszEncString);
1925 : }
1926 :
1927 0 : if (pszTmpString == nullptr)
1928 0 : fp->WriteLine("Text \"\"\n");
1929 : else
1930 0 : fp->WriteLine("Text \"%s\"\n", pszTmpString);
1931 0 : if (pszTmpString != m_pszString)
1932 0 : CPLFree(pszTmpString);
1933 :
1934 0 : double dXMin = 0.0;
1935 0 : double dYMin = 0.0;
1936 0 : double dXMax = 0.0;
1937 0 : double dYMax = 0.0;
1938 0 : UpdateMBR();
1939 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
1940 0 : fp->WriteLine(" %.15g %.15g %.15g %.15g\n", dXMin, dYMin, dXMax, dYMax);
1941 :
1942 0 : if (IsFontBGColorUsed())
1943 0 : fp->WriteLine(" Font (\"%s\",%d,%d,%d,%d)\n", GetFontNameRef(),
1944 : GetFontStyleMIFValue(), 0, GetFontFGColor(),
1945 : GetFontBGColor());
1946 : else
1947 0 : fp->WriteLine(" Font (\"%s\",%d,%d,%d)\n", GetFontNameRef(),
1948 : GetFontStyleMIFValue(), 0, GetFontFGColor());
1949 :
1950 0 : switch (GetTextSpacing())
1951 : {
1952 0 : case TABTS1_5:
1953 0 : fp->WriteLine(" Spacing 1.5\n");
1954 0 : break;
1955 0 : case TABTSDouble:
1956 0 : fp->WriteLine(" Spacing 2.0\n");
1957 0 : break;
1958 0 : case TABTSSingle:
1959 : default:
1960 0 : break;
1961 : }
1962 :
1963 0 : switch (GetTextJustification())
1964 : {
1965 0 : case TABTJCenter:
1966 0 : fp->WriteLine(" Justify Center\n");
1967 0 : break;
1968 0 : case TABTJRight:
1969 0 : fp->WriteLine(" Justify Right\n");
1970 0 : break;
1971 0 : case TABTJLeft:
1972 : default:
1973 0 : break;
1974 : }
1975 :
1976 0 : if (std::abs(GetTextAngle()) > 0.000001)
1977 0 : fp->WriteLine(" Angle %.15g\n", GetTextAngle());
1978 :
1979 0 : switch (GetTextLineType())
1980 : {
1981 0 : case TABTLSimple:
1982 0 : if (m_bLineEndSet)
1983 0 : fp->WriteLine(" Label Line Simple %.15g %.15g \n",
1984 : m_dfLineEndX, m_dfLineEndY);
1985 0 : break;
1986 0 : case TABTLArrow:
1987 0 : if (m_bLineEndSet)
1988 0 : fp->WriteLine(" Label Line Arrow %.15g %.15g \n",
1989 : m_dfLineEndX, m_dfLineEndY);
1990 0 : break;
1991 0 : case TABTLNoLine:
1992 : default:
1993 0 : break;
1994 : }
1995 0 : return 0;
1996 : }
1997 :
1998 : /**********************************************************************
1999 : *
2000 : **********************************************************************/
2001 183 : int TABMultiPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
2002 : {
2003 : char **papszToken =
2004 183 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
2005 :
2006 183 : if (CSLCount(papszToken) != 2)
2007 : {
2008 4 : CSLDestroy(papszToken);
2009 4 : return -1;
2010 : }
2011 :
2012 179 : int nNumPoint = atoi(papszToken[1]);
2013 179 : OGRMultiPoint *poMultiPoint = new OGRMultiPoint;
2014 :
2015 179 : CSLDestroy(papszToken);
2016 179 : papszToken = nullptr;
2017 :
2018 : // Get each point and add them to the multipoint feature
2019 495 : for (int i = 0; i < nNumPoint; i++)
2020 : {
2021 : papszToken =
2022 344 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
2023 344 : if (CSLCount(papszToken) != 2)
2024 : {
2025 28 : CSLDestroy(papszToken);
2026 28 : delete poMultiPoint;
2027 28 : return -1;
2028 : }
2029 :
2030 316 : const double dfX = fp->GetXTrans(CPLAtof(papszToken[0]));
2031 316 : const double dfY = fp->GetXTrans(CPLAtof(papszToken[1]));
2032 316 : OGRPoint *poPoint = new OGRPoint(dfX, dfY);
2033 316 : if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
2034 : {
2035 0 : CPLAssert(false); // Just in case OGR is modified
2036 : }
2037 :
2038 : // Set center
2039 316 : if (i == 0)
2040 : {
2041 165 : SetCenter(dfX, dfY);
2042 : }
2043 316 : CSLDestroy(papszToken);
2044 : }
2045 :
2046 151 : OGREnvelope sEnvelope;
2047 151 : poMultiPoint->getEnvelope(&sEnvelope);
2048 151 : if (SetGeometryDirectly(poMultiPoint) != OGRERR_NONE)
2049 : {
2050 0 : CPLAssert(false); // Just in case OGR is modified
2051 : }
2052 :
2053 151 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
2054 :
2055 : // Read optional SYMBOL line...
2056 :
2057 151 : const char *pszLine = nullptr;
2058 596 : while (((pszLine = fp->GetLine()) != nullptr) &&
2059 293 : fp->IsValidFeature(pszLine) == FALSE)
2060 : {
2061 152 : papszToken = CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
2062 152 : if (CSLCount(papszToken) == 4 && EQUAL(papszToken[0], "SYMBOL"))
2063 : {
2064 0 : SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
2065 0 : SetSymbolColor(atoi(papszToken[2]));
2066 0 : SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
2067 : }
2068 152 : CSLDestroy(papszToken);
2069 : }
2070 :
2071 151 : return 0;
2072 : }
2073 :
2074 : /**********************************************************************
2075 : *
2076 : **********************************************************************/
2077 0 : int TABMultiPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
2078 : {
2079 : /*-----------------------------------------------------------------
2080 : * Fetch and validate geometry
2081 : *----------------------------------------------------------------*/
2082 0 : OGRGeometry *poGeom = GetGeometryRef();
2083 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
2084 : {
2085 0 : OGRMultiPoint *poMultiPoint = poGeom->toMultiPoint();
2086 0 : const int nNumPoints = poMultiPoint->getNumGeometries();
2087 :
2088 0 : fp->WriteLine("MultiPoint %d\n", nNumPoints);
2089 :
2090 0 : for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
2091 : {
2092 : /*------------------------------------------------------------
2093 : * Validate each point
2094 : *-----------------------------------------------------------*/
2095 0 : poGeom = poMultiPoint->getGeometryRef(iPoint);
2096 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
2097 : {
2098 0 : OGRPoint *poPoint = poGeom->toPoint();
2099 0 : fp->WriteLine("%.15g %.15g\n", poPoint->getX(),
2100 : poPoint->getY());
2101 : }
2102 : else
2103 : {
2104 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2105 : "TABMultiPoint: Missing or Invalid Geometry!");
2106 0 : return -1;
2107 : }
2108 : }
2109 : // Write symbol
2110 0 : fp->WriteLine(" Symbol (%d,%d,%d)\n", GetSymbolNo(),
2111 0 : GetSymbolColor(), GetSymbolSize());
2112 : }
2113 :
2114 0 : return 0;
2115 : }
2116 :
2117 : /**********************************************************************
2118 : *
2119 : **********************************************************************/
2120 99 : int TABCollection::ReadGeometryFromMIFFile(MIDDATAFile *fp)
2121 : {
2122 : /*-----------------------------------------------------------------
2123 : * Fetch number of parts in "COLLECTION %d" line
2124 : *----------------------------------------------------------------*/
2125 : char **papszToken =
2126 99 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
2127 :
2128 99 : if (CSLCount(papszToken) != 2)
2129 : {
2130 2 : CSLDestroy(papszToken);
2131 2 : return -1;
2132 : }
2133 :
2134 97 : int numParts = atoi(papszToken[1]);
2135 97 : CSLDestroy(papszToken);
2136 97 : papszToken = nullptr;
2137 :
2138 : // Make sure collection is empty
2139 97 : EmptyCollection();
2140 :
2141 97 : const char *pszLine = fp->GetLine();
2142 :
2143 : /*-----------------------------------------------------------------
2144 : * Read each part and add them to the feature
2145 : *----------------------------------------------------------------*/
2146 184 : for (int i = 0; i < numParts; i++)
2147 : {
2148 182 : if (pszLine == nullptr)
2149 : {
2150 26 : CPLError(
2151 : CE_Failure, CPLE_FileIO,
2152 : "Unexpected EOF while reading TABCollection from MIF file.");
2153 26 : return -1;
2154 : }
2155 :
2156 156 : while (*pszLine == ' ' || *pszLine == '\t')
2157 0 : pszLine++; // skip leading spaces
2158 :
2159 156 : if (*pszLine == '\0')
2160 : {
2161 2 : pszLine = fp->GetLine();
2162 2 : continue; // Skip blank lines
2163 : }
2164 :
2165 154 : if (STARTS_WITH_CI(pszLine, "REGION"))
2166 : {
2167 88 : delete m_poRegion;
2168 88 : m_poRegion = new TABRegion(GetDefnRef());
2169 88 : if (m_poRegion->ReadGeometryFromMIFFile(fp) != 0)
2170 : {
2171 38 : CPLError(CE_Failure, CPLE_NotSupported,
2172 : "TABCollection: Error reading REGION part.");
2173 38 : delete m_poRegion;
2174 38 : m_poRegion = nullptr;
2175 38 : return -1;
2176 : }
2177 : }
2178 66 : else if (STARTS_WITH_CI(pszLine, "LINE") ||
2179 25 : STARTS_WITH_CI(pszLine, "PLINE"))
2180 : {
2181 41 : delete m_poPline;
2182 41 : m_poPline = new TABPolyline(GetDefnRef());
2183 41 : if (m_poPline->ReadGeometryFromMIFFile(fp) != 0)
2184 : {
2185 8 : CPLError(CE_Failure, CPLE_NotSupported,
2186 : "TABCollection: Error reading PLINE part.");
2187 8 : delete m_poPline;
2188 8 : m_poPline = nullptr;
2189 8 : return -1;
2190 : }
2191 : }
2192 25 : else if (STARTS_WITH_CI(pszLine, "MULTIPOINT"))
2193 : {
2194 20 : delete m_poMpoint;
2195 20 : m_poMpoint = new TABMultiPoint(GetDefnRef());
2196 20 : if (m_poMpoint->ReadGeometryFromMIFFile(fp) != 0)
2197 : {
2198 18 : CPLError(CE_Failure, CPLE_NotSupported,
2199 : "TABCollection: Error reading MULTIPOINT part.");
2200 18 : delete m_poMpoint;
2201 18 : m_poMpoint = nullptr;
2202 18 : return -1;
2203 : }
2204 : }
2205 : else
2206 : {
2207 5 : CPLError(CE_Failure, CPLE_FileIO,
2208 : "Reading TABCollection from MIF failed, expecting one "
2209 : "of REGION, PLINE or MULTIPOINT, got: '%s'",
2210 : pszLine);
2211 5 : return -1;
2212 : }
2213 :
2214 85 : pszLine = fp->GetLastLine();
2215 : }
2216 :
2217 : /*-----------------------------------------------------------------
2218 : * Set the main OGRFeature Geometry
2219 : * (this is actually duplicating geometries from each member)
2220 : *----------------------------------------------------------------*/
2221 : // use addGeometry() rather than addGeometryDirectly() as this clones
2222 : // the added geometry so won't leave dangling ptrs when the above features
2223 : // are deleted
2224 :
2225 2 : OGRGeometryCollection *poGeomColl = new OGRGeometryCollection();
2226 2 : if (m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
2227 2 : poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
2228 :
2229 2 : if (m_poPline && m_poPline->GetGeometryRef() != nullptr)
2230 2 : poGeomColl->addGeometry(m_poPline->GetGeometryRef());
2231 :
2232 2 : if (m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
2233 2 : poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
2234 :
2235 2 : OGREnvelope sEnvelope;
2236 2 : poGeomColl->getEnvelope(&sEnvelope);
2237 2 : this->SetGeometryDirectly(poGeomColl);
2238 :
2239 2 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
2240 :
2241 2 : return 0;
2242 : }
2243 :
2244 : /**********************************************************************
2245 : *
2246 : **********************************************************************/
2247 0 : int TABCollection::WriteGeometryToMIFFile(MIDDATAFile *fp)
2248 : {
2249 0 : int numParts = 0;
2250 0 : if (m_poRegion)
2251 0 : numParts++;
2252 0 : if (m_poPline)
2253 0 : numParts++;
2254 0 : if (m_poMpoint)
2255 0 : numParts++;
2256 :
2257 0 : fp->WriteLine("COLLECTION %d\n", numParts);
2258 :
2259 0 : if (m_poRegion)
2260 : {
2261 0 : if (m_poRegion->WriteGeometryToMIFFile(fp) != 0)
2262 0 : return -1;
2263 : }
2264 :
2265 0 : if (m_poPline)
2266 : {
2267 0 : if (m_poPline->WriteGeometryToMIFFile(fp) != 0)
2268 0 : return -1;
2269 : }
2270 :
2271 0 : if (m_poMpoint)
2272 : {
2273 0 : if (m_poMpoint->WriteGeometryToMIFFile(fp) != 0)
2274 0 : return -1;
2275 : }
2276 :
2277 0 : return 0;
2278 : }
2279 :
2280 : /**********************************************************************
2281 : *
2282 : **********************************************************************/
2283 0 : int TABDebugFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp)
2284 : {
2285 : // Go to the first line of the next feature.
2286 0 : printf("%s\n", fp->GetLastLine()); /*ok*/
2287 :
2288 0 : const char *pszLine = nullptr;
2289 0 : while (((pszLine = fp->GetLine()) != nullptr) &&
2290 0 : fp->IsValidFeature(pszLine) == FALSE)
2291 : {
2292 : }
2293 :
2294 0 : return 0;
2295 : }
2296 :
2297 : /**********************************************************************
2298 : *
2299 : **********************************************************************/
2300 0 : int TABDebugFeature::WriteGeometryToMIFFile(MIDDATAFile * /* fp */)
2301 : {
2302 0 : return -1;
2303 : }
|