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