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 373 : int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp)
50 : {
51 : #ifdef MITAB_USE_OFTDATETIME
52 373 : int nYear = 0;
53 373 : int nMonth = 0;
54 373 : int nDay = 0;
55 373 : int nHour = 0;
56 373 : int nMin = 0;
57 373 : int nSec = 0;
58 373 : int nMS = 0;
59 : // int nTZFlag = 0;
60 : #endif
61 :
62 373 : const int nFields = GetFieldCount();
63 :
64 373 : char **papszToken = fp->GetTokenizedNextLine();
65 373 : 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 373 : 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 373 : if (CSLCount(papszToken) < nFields)
83 : {
84 0 : CSLDestroy(papszToken);
85 0 : return -1;
86 : }
87 :
88 1516 : for (int i = 0; i < nFields; i++)
89 : {
90 1143 : const auto poFDefn = GetFieldDefnRef(i);
91 1143 : 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 372 : case OFTInteger:
128 : {
129 372 : 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 364 : SetField(i, papszToken[i]);
140 : }
141 372 : break;
142 : }
143 405 : case OFTString:
144 : {
145 810 : CPLString osValue(papszToken[i]);
146 405 : if (!fp->GetEncoding().empty())
147 : {
148 80 : osValue.Recode(fp->GetEncoding(), CPL_ENC_UTF8);
149 : }
150 405 : SetField(i, osValue);
151 405 : break;
152 : }
153 348 : default:
154 348 : SetField(i, papszToken[i]);
155 : }
156 : }
157 :
158 373 : CSLDestroy(papszToken);
159 :
160 373 : 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 554 : int TABRegion::ReadGeometryFromMIFFile(MIDDATAFile *fp)
954 : {
955 554 : m_bSmooth = FALSE;
956 :
957 : /*=============================================================
958 : * REGION (Similar to PLINE MULTIPLE)
959 : *============================================================*/
960 : CPLStringList aosTokens(
961 1108 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
962 :
963 554 : int numLineSections = (aosTokens.size() == 2) ? atoi(aosTokens[1]) : 0;
964 554 : aosTokens.Clear();
965 :
966 554 : if (numLineSections < 0 ||
967 554 : 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 554 : OGRPolygon **tabPolygons = nullptr;
975 554 : const int MAX_INITIAL_SECTIONS = 100000;
976 1108 : const int numInitialLineSections = (numLineSections < MAX_INITIAL_SECTIONS)
977 554 : ? numLineSections
978 : : MAX_INITIAL_SECTIONS;
979 554 : if (numLineSections > 0)
980 : {
981 : tabPolygons = static_cast<OGRPolygon **>(
982 550 : VSI_MALLOC2_VERBOSE(numInitialLineSections, sizeof(OGRPolygon *)));
983 550 : if (tabPolygons == nullptr)
984 0 : return -1;
985 : }
986 :
987 554 : const char *pszLine = nullptr;
988 :
989 1038 : for (int iSection = 0; iSection < numLineSections; iSection++)
990 : {
991 550 : if (iSection == MAX_INITIAL_SECTIONS)
992 : {
993 : OGRPolygon **newTabPolygons =
994 0 : static_cast<OGRPolygon **>(VSI_REALLOC_VERBOSE(
995 : tabPolygons, numLineSections * sizeof(OGRPolygon *)));
996 0 : if (newTabPolygons == nullptr)
997 : {
998 0 : iSection--;
999 0 : for (; iSection >= 0; --iSection)
1000 0 : delete tabPolygons[iSection];
1001 0 : VSIFree(tabPolygons);
1002 66 : return -1;
1003 : }
1004 0 : tabPolygons = newTabPolygons;
1005 : }
1006 :
1007 550 : int numSectionVertices = 0;
1008 :
1009 550 : tabPolygons[iSection] = new OGRPolygon();
1010 :
1011 550 : if ((pszLine = fp->GetLine()) != nullptr)
1012 : {
1013 546 : numSectionVertices = atoi(pszLine);
1014 : }
1015 550 : if (numSectionVertices < 2)
1016 : {
1017 10 : CPLError(CE_Failure, CPLE_FileIO,
1018 : "Invalid number of points (%d) in REGION "
1019 : "segment.",
1020 : numSectionVertices);
1021 20 : for (; iSection >= 0; --iSection)
1022 10 : delete tabPolygons[iSection];
1023 10 : VSIFree(tabPolygons);
1024 10 : return -1;
1025 : }
1026 :
1027 540 : auto poRing = std::make_unique<OGRLinearRing>();
1028 :
1029 540 : const int MAX_INITIAL_POINTS = 100000;
1030 1080 : const int nInitialNumPoints = (numSectionVertices < MAX_INITIAL_POINTS)
1031 540 : ? numSectionVertices
1032 : : MAX_INITIAL_POINTS;
1033 : /* Do not allocate too much memory to begin with */
1034 540 : poRing->setNumPoints(nInitialNumPoints);
1035 540 : if (poRing->getNumPoints() != nInitialNumPoints)
1036 : {
1037 0 : for (; iSection >= 0; --iSection)
1038 0 : delete tabPolygons[iSection];
1039 0 : VSIFree(tabPolygons);
1040 0 : return -1;
1041 : }
1042 8873 : for (int i = 0; i < numSectionVertices; i++)
1043 : {
1044 8389 : if (i == MAX_INITIAL_POINTS)
1045 : {
1046 0 : poRing->setNumPoints(numSectionVertices);
1047 0 : if (poRing->getNumPoints() != numSectionVertices)
1048 : {
1049 0 : for (; iSection >= 0; --iSection)
1050 0 : delete tabPolygons[iSection];
1051 0 : VSIFree(tabPolygons);
1052 0 : return -1;
1053 : }
1054 : }
1055 :
1056 : aosTokens =
1057 8389 : CSLTokenizeStringComplex(fp->GetLine(), " ,\t", TRUE, FALSE);
1058 8389 : if (aosTokens.size() < 2)
1059 : {
1060 112 : for (; iSection >= 0; --iSection)
1061 56 : delete tabPolygons[iSection];
1062 56 : VSIFree(tabPolygons);
1063 56 : return -1;
1064 : }
1065 :
1066 8333 : const double dX = fp->GetXTrans(CPLAtof(aosTokens[0]));
1067 8333 : const double dY = fp->GetYTrans(CPLAtof(aosTokens[1]));
1068 8333 : poRing->setPoint(i, dX, dY);
1069 8333 : aosTokens.Clear();
1070 : }
1071 :
1072 484 : poRing->closeRings();
1073 :
1074 484 : tabPolygons[iSection]->addRingDirectly(poRing.release());
1075 : }
1076 :
1077 0 : std::unique_ptr<OGRGeometry> poGeometry;
1078 488 : if (numLineSections == 1)
1079 : {
1080 484 : poGeometry.reset(tabPolygons[0]);
1081 484 : tabPolygons[0] = nullptr;
1082 : }
1083 4 : else if (numLineSections > 1)
1084 : {
1085 0 : int isValidGeometry = FALSE;
1086 0 : const char *papszOptions[] = {"METHOD=DEFAULT", nullptr};
1087 0 : poGeometry.reset(OGRGeometryFactory::organizePolygons(
1088 : reinterpret_cast<OGRGeometry **>(tabPolygons), numLineSections,
1089 : &isValidGeometry, papszOptions));
1090 :
1091 0 : if (!isValidGeometry)
1092 : {
1093 0 : CPLError(
1094 : CE_Warning, CPLE_AppDefined,
1095 : "Geometry of polygon cannot be translated to Simple Geometry. "
1096 : "All polygons will be contained in a multipolygon.\n");
1097 : }
1098 : }
1099 :
1100 488 : VSIFree(tabPolygons);
1101 :
1102 488 : if (poGeometry)
1103 : {
1104 484 : OGREnvelope sEnvelope;
1105 484 : poGeometry->getEnvelope(&sEnvelope);
1106 484 : SetGeometryDirectly(poGeometry.release());
1107 :
1108 484 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1109 : }
1110 :
1111 2469 : while (((pszLine = fp->GetLine()) != nullptr) &&
1112 1206 : fp->IsValidFeature(pszLine) == FALSE)
1113 : {
1114 775 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1115 :
1116 775 : if (aosTokens.size() > 1)
1117 : {
1118 652 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1119 : {
1120 :
1121 325 : if (aosTokens.size() == 4)
1122 : {
1123 325 : SetPenWidthMIF(atoi(aosTokens[1]));
1124 325 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1125 325 : SetPenColor(atoi(aosTokens[3]));
1126 : }
1127 : }
1128 327 : else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
1129 : {
1130 325 : if (aosTokens.size() >= 3)
1131 : {
1132 325 : SetBrushFGColor(atoi(aosTokens[2]));
1133 325 : SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
1134 :
1135 325 : if (aosTokens.size() == 4)
1136 325 : SetBrushBGColor(atoi(aosTokens[3]));
1137 : else
1138 0 : SetBrushTransparent(TRUE);
1139 : }
1140 : }
1141 2 : else if (STARTS_WITH_CI(aosTokens[0], "CENTER"))
1142 : {
1143 2 : if (aosTokens.size() == 3)
1144 : {
1145 2 : SetCenter(fp->GetXTrans(CPLAtof(aosTokens[1])),
1146 2 : fp->GetYTrans(CPLAtof(aosTokens[2])));
1147 : }
1148 : }
1149 : }
1150 775 : aosTokens.Clear();
1151 : }
1152 :
1153 488 : return 0;
1154 : }
1155 :
1156 : /**********************************************************************
1157 : * TABRegion::WriteGeometryToMIFFile()
1158 : *
1159 : * Write the geometry and representation (color, etc...) part of the
1160 : * feature to the .MIF file
1161 : *
1162 : * Returns 0 on success, -1 on error, in which case CPLError() will have
1163 : * been called.
1164 : **********************************************************************/
1165 21 : int TABRegion::WriteGeometryToMIFFile(MIDDATAFile *fp)
1166 : {
1167 21 : OGRGeometry *poGeom = GetGeometryRef();
1168 :
1169 21 : if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
1170 0 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon))
1171 : {
1172 : /*=============================================================
1173 : * REGIONs are similar to PLINE MULTIPLE
1174 : *
1175 : * We accept both OGRPolygons (with one or multiple rings) and
1176 : * OGRMultiPolygons as input.
1177 : *============================================================*/
1178 : int i, iRing, numRingsTotal, numPoints;
1179 :
1180 21 : numRingsTotal = GetNumRings();
1181 :
1182 21 : fp->WriteLine("Region %d\n", numRingsTotal);
1183 :
1184 42 : for (iRing = 0; iRing < numRingsTotal; iRing++)
1185 : {
1186 21 : OGRLinearRing *poRing = GetRingRef(iRing);
1187 21 : if (poRing == nullptr)
1188 : {
1189 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1190 : "TABRegion: Object Geometry contains NULL rings!");
1191 0 : return -1;
1192 : }
1193 :
1194 21 : numPoints = poRing->getNumPoints();
1195 :
1196 21 : fp->WriteLine(" %d\n", numPoints);
1197 516 : for (i = 0; i < numPoints; i++)
1198 : {
1199 495 : fp->WriteLine("%.15g %.15g\n", poRing->getX(i),
1200 : poRing->getY(i));
1201 : }
1202 : }
1203 :
1204 21 : if (GetPenPattern())
1205 42 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(),
1206 21 : GetPenPattern(), GetPenColor());
1207 :
1208 21 : if (GetBrushPattern())
1209 : {
1210 21 : if (GetBrushTransparent() == 0)
1211 21 : fp->WriteLine(" Brush (%d,%d,%d)\n", GetBrushPattern(),
1212 : GetBrushFGColor(), GetBrushBGColor());
1213 : else
1214 0 : fp->WriteLine(" Brush (%d,%d)\n", GetBrushPattern(),
1215 : GetBrushFGColor());
1216 : }
1217 :
1218 21 : if (m_bCenterIsSet)
1219 : {
1220 0 : fp->WriteLine(" Center %.15g %.15g\n", m_dCenterX, m_dCenterY);
1221 : }
1222 : }
1223 : else
1224 : {
1225 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1226 : "TABRegion: Object contains an invalid Geometry!");
1227 0 : return -1;
1228 : }
1229 :
1230 21 : return 0;
1231 : }
1232 :
1233 : /**********************************************************************
1234 : *
1235 : **********************************************************************/
1236 941 : int TABRectangle::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1237 : {
1238 : CPLStringList aosTokens(
1239 1882 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1240 :
1241 941 : if (aosTokens.size() < 5)
1242 : {
1243 20 : return -1;
1244 : }
1245 :
1246 921 : double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1247 921 : double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1248 921 : double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1249 921 : double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1250 :
1251 : /*-----------------------------------------------------------------
1252 : * Call SetMBR() and GetMBR() now to make sure that min values are
1253 : * really smaller than max values.
1254 : *----------------------------------------------------------------*/
1255 921 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1256 921 : GetMBR(dXMin, dYMin, dXMax, dYMax);
1257 :
1258 921 : m_bRoundCorners = FALSE;
1259 921 : m_dRoundXRadius = 0.0;
1260 921 : m_dRoundYRadius = 0.0;
1261 :
1262 921 : if (STARTS_WITH_CI(aosTokens[0], "ROUNDRECT"))
1263 : {
1264 436 : m_bRoundCorners = TRUE;
1265 436 : if (aosTokens.size() == 6)
1266 : {
1267 434 : m_dRoundXRadius = CPLAtof(aosTokens[5]) / 2.0;
1268 434 : m_dRoundYRadius = m_dRoundXRadius;
1269 : }
1270 : else
1271 : {
1272 : aosTokens =
1273 2 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1274 2 : if (aosTokens.size() == 1)
1275 0 : m_dRoundXRadius = m_dRoundYRadius = CPLAtof(aosTokens[0]) / 2.0;
1276 : }
1277 : }
1278 921 : aosTokens.Clear();
1279 :
1280 : /*-----------------------------------------------------------------
1281 : * Create and fill geometry object
1282 : *----------------------------------------------------------------*/
1283 :
1284 921 : OGRPolygon *poPolygon = new OGRPolygon;
1285 921 : OGRLinearRing *poRing = new OGRLinearRing();
1286 921 : if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0)
1287 : {
1288 : /*-------------------------------------------------------------
1289 : * For rounded rectangles, we generate arcs with 45 line
1290 : * segments for each corner. We start with lower-left corner
1291 : * and proceed counterclockwise
1292 : * We also have to make sure that rounding radius is not too
1293 : * large for the MBR however, we
1294 : * always return the true X/Y radius (not adjusted) since this
1295 : * is the way MapInfo seems to do it when a radius bigger than
1296 : * the MBR is passed from TBA to MIF.
1297 : *------------------------------------------------------------*/
1298 : const double dXRadius =
1299 434 : std::min(m_dRoundXRadius, (dXMax - dXMin) / 2.0);
1300 : const double dYRadius =
1301 434 : std::min(m_dRoundYRadius, (dYMax - dYMin) / 2.0);
1302 434 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMin + dYRadius, dXRadius,
1303 : dYRadius, M_PI, 3.0 * M_PI / 2.0);
1304 434 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMin + dYRadius, dXRadius,
1305 : dYRadius, 3.0 * M_PI / 2.0, 2.0 * M_PI);
1306 434 : TABGenerateArc(poRing, 45, dXMax - dXRadius, dYMax - dYRadius, dXRadius,
1307 : dYRadius, 0.0, M_PI / 2.0);
1308 434 : TABGenerateArc(poRing, 45, dXMin + dXRadius, dYMax - dYRadius, dXRadius,
1309 : dYRadius, M_PI / 2.0, M_PI);
1310 :
1311 434 : TABCloseRing(poRing);
1312 : }
1313 : else
1314 : {
1315 487 : poRing->addPoint(dXMin, dYMin);
1316 487 : poRing->addPoint(dXMax, dYMin);
1317 487 : poRing->addPoint(dXMax, dYMax);
1318 487 : poRing->addPoint(dXMin, dYMax);
1319 487 : poRing->addPoint(dXMin, dYMin);
1320 : }
1321 :
1322 921 : poPolygon->addRingDirectly(poRing);
1323 921 : SetGeometryDirectly(poPolygon);
1324 :
1325 921 : const char *pszLine = nullptr;
1326 7118 : while (((pszLine = fp->GetLine()) != nullptr) &&
1327 3520 : fp->IsValidFeature(pszLine) == FALSE)
1328 : {
1329 2677 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1330 :
1331 2677 : if (aosTokens.size() > 1)
1332 : {
1333 1776 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1334 : {
1335 903 : if (aosTokens.size() == 4)
1336 : {
1337 895 : SetPenWidthMIF(atoi(aosTokens[1]));
1338 895 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1339 895 : SetPenColor(atoi(aosTokens[3]));
1340 : }
1341 : }
1342 873 : else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
1343 : {
1344 873 : if (aosTokens.size() >= 3)
1345 : {
1346 869 : SetBrushFGColor(atoi(aosTokens[2]));
1347 869 : SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
1348 :
1349 869 : if (aosTokens.size() == 4)
1350 865 : SetBrushBGColor(atoi(aosTokens[3]));
1351 : else
1352 4 : SetBrushTransparent(TRUE);
1353 : }
1354 : }
1355 : }
1356 2677 : aosTokens.Clear();
1357 : }
1358 :
1359 921 : return 0;
1360 : }
1361 :
1362 : /**********************************************************************
1363 : *
1364 : **********************************************************************/
1365 0 : int TABRectangle::WriteGeometryToMIFFile(MIDDATAFile *fp)
1366 : {
1367 : /*-----------------------------------------------------------------
1368 : * Fetch and validate geometry
1369 : *----------------------------------------------------------------*/
1370 0 : OGRGeometry *poGeom = GetGeometryRef();
1371 0 : OGRPolygon *poPolygon = nullptr;
1372 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
1373 0 : poPolygon = poGeom->toPolygon();
1374 : else
1375 : {
1376 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1377 : "TABRectangle: Missing or Invalid Geometry!");
1378 0 : return -1;
1379 : }
1380 : /*-----------------------------------------------------------------
1381 : * Note that we will simply use the rectangle's MBR and don't really
1382 : * read the polygon geometry... this should be OK unless the
1383 : * polygon geometry was not really a rectangle.
1384 : *----------------------------------------------------------------*/
1385 0 : OGREnvelope sEnvelope;
1386 0 : poPolygon->getEnvelope(&sEnvelope);
1387 :
1388 0 : if (m_bRoundCorners == TRUE)
1389 : {
1390 0 : fp->WriteLine("Roundrect %.15g %.15g %.15g %.15g %.15g\n",
1391 : sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX,
1392 0 : sEnvelope.MaxY, m_dRoundXRadius * 2.0);
1393 : }
1394 : else
1395 : {
1396 0 : fp->WriteLine("Rect %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
1397 : sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1398 : }
1399 :
1400 0 : if (GetPenPattern())
1401 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
1402 : GetPenColor());
1403 :
1404 0 : if (GetBrushPattern())
1405 : {
1406 0 : if (GetBrushTransparent() == 0)
1407 0 : fp->WriteLine(" Brush (%d,%d,%d)\n", GetBrushPattern(),
1408 : GetBrushFGColor(), GetBrushBGColor());
1409 : else
1410 0 : fp->WriteLine(" Brush (%d,%d)\n", GetBrushPattern(),
1411 : GetBrushFGColor());
1412 : }
1413 0 : return 0;
1414 : }
1415 :
1416 : /**********************************************************************
1417 : *
1418 : **********************************************************************/
1419 397 : int TABEllipse::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1420 : {
1421 : CPLStringList aosTokens(
1422 794 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1423 :
1424 397 : if (aosTokens.size() != 5)
1425 : {
1426 10 : return -1;
1427 : }
1428 :
1429 387 : double dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1430 387 : double dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1431 387 : double dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1432 387 : double dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1433 387 : aosTokens.Clear();
1434 :
1435 : /*-----------------------------------------------------------------
1436 : * Save info about the ellipse def. inside class members
1437 : *----------------------------------------------------------------*/
1438 387 : m_dCenterX = (dXMin + dXMax) / 2.0;
1439 387 : m_dCenterY = (dYMin + dYMax) / 2.0;
1440 387 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
1441 387 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
1442 :
1443 387 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1444 :
1445 : /*-----------------------------------------------------------------
1446 : * Create and fill geometry object
1447 : *----------------------------------------------------------------*/
1448 387 : OGRPolygon *poPolygon = new OGRPolygon;
1449 387 : OGRLinearRing *poRing = new OGRLinearRing();
1450 :
1451 : /*-----------------------------------------------------------------
1452 : * For the OGR geometry, we generate an ellipse with 2 degrees line
1453 : * segments.
1454 : *----------------------------------------------------------------*/
1455 387 : TABGenerateArc(poRing, 180, m_dCenterX, m_dCenterY, m_dXRadius, m_dYRadius,
1456 : 0.0, 2.0 * M_PI);
1457 387 : TABCloseRing(poRing);
1458 :
1459 387 : poPolygon->addRingDirectly(poRing);
1460 387 : SetGeometryDirectly(poPolygon);
1461 :
1462 387 : const char *pszLine = nullptr;
1463 2973 : while (((pszLine = fp->GetLine()) != nullptr) &&
1464 1470 : fp->IsValidFeature(pszLine) == FALSE)
1465 : {
1466 1116 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1467 :
1468 1116 : if (aosTokens.size() > 1)
1469 : {
1470 743 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1471 : {
1472 379 : if (aosTokens.size() == 4)
1473 : {
1474 375 : SetPenWidthMIF(atoi(aosTokens[1]));
1475 375 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1476 375 : SetPenColor(atoi(aosTokens[3]));
1477 : }
1478 : }
1479 364 : else if (STARTS_WITH_CI(aosTokens[0], "BRUSH"))
1480 : {
1481 364 : if (aosTokens.size() >= 3)
1482 : {
1483 362 : SetBrushFGColor(atoi(aosTokens[2]));
1484 362 : SetBrushPattern(static_cast<GByte>(atoi(aosTokens[1])));
1485 :
1486 362 : if (aosTokens.size() == 4)
1487 360 : SetBrushBGColor(atoi(aosTokens[3]));
1488 : else
1489 2 : SetBrushTransparent(TRUE);
1490 : }
1491 : }
1492 : }
1493 1116 : aosTokens.Clear();
1494 : }
1495 387 : return 0;
1496 : }
1497 :
1498 : /**********************************************************************
1499 : *
1500 : **********************************************************************/
1501 0 : int TABEllipse::WriteGeometryToMIFFile(MIDDATAFile *fp)
1502 : {
1503 0 : OGREnvelope sEnvelope;
1504 0 : OGRGeometry *poGeom = GetGeometryRef();
1505 0 : if ((poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) ||
1506 0 : (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint))
1507 0 : poGeom->getEnvelope(&sEnvelope);
1508 : else
1509 : {
1510 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1511 : "TABEllipse: Missing or Invalid Geometry!");
1512 0 : return -1;
1513 : }
1514 :
1515 0 : fp->WriteLine("Ellipse %.15g %.15g %.15g %.15g\n", sEnvelope.MinX,
1516 : sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1517 :
1518 0 : if (GetPenPattern())
1519 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
1520 : GetPenColor());
1521 :
1522 0 : if (GetBrushPattern())
1523 : {
1524 0 : if (GetBrushTransparent() == 0)
1525 0 : fp->WriteLine(" Brush (%d,%d,%d)\n", GetBrushPattern(),
1526 : GetBrushFGColor(), GetBrushBGColor());
1527 : else
1528 0 : fp->WriteLine(" Brush (%d,%d)\n", GetBrushPattern(),
1529 : GetBrushFGColor());
1530 : }
1531 0 : return 0;
1532 : }
1533 :
1534 : /**********************************************************************
1535 : *
1536 : **********************************************************************/
1537 672 : int TABArc::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1538 : {
1539 672 : double dXMin = 0.0;
1540 672 : double dXMax = 0.0;
1541 672 : double dYMin = 0.0;
1542 672 : double dYMax = 0.0;
1543 :
1544 : CPLStringList aosTokens(
1545 1344 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1546 :
1547 672 : if (aosTokens.size() == 5)
1548 : {
1549 346 : dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1550 346 : dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1551 346 : dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1552 346 : dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1553 :
1554 : aosTokens =
1555 346 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1556 346 : if (aosTokens.size() != 2)
1557 : {
1558 8 : return -1;
1559 : }
1560 :
1561 338 : m_dStartAngle = CPLAtof(aosTokens[0]);
1562 338 : m_dEndAngle = CPLAtof(aosTokens[1]);
1563 : }
1564 326 : else if (aosTokens.size() == 7)
1565 : {
1566 304 : dXMin = fp->GetXTrans(CPLAtof(aosTokens[1]));
1567 304 : dXMax = fp->GetXTrans(CPLAtof(aosTokens[3]));
1568 304 : dYMin = fp->GetYTrans(CPLAtof(aosTokens[2]));
1569 304 : dYMax = fp->GetYTrans(CPLAtof(aosTokens[4]));
1570 304 : m_dStartAngle = CPLAtof(aosTokens[5]);
1571 304 : m_dEndAngle = CPLAtof(aosTokens[6]);
1572 : }
1573 : else
1574 : {
1575 22 : return -1;
1576 : }
1577 :
1578 642 : aosTokens.Clear();
1579 :
1580 642 : if (fabs(m_dEndAngle - m_dStartAngle) >= 721)
1581 : {
1582 0 : CPLError(CE_Failure, CPLE_AppDefined,
1583 : "Wrong start and end angles: %f %f", m_dStartAngle,
1584 : m_dEndAngle);
1585 0 : return -1;
1586 : }
1587 :
1588 : /*-------------------------------------------------------------
1589 : * Start/End angles
1590 : * Since the angles are specified for integer coordinates, and
1591 : * that these coordinates can have the X axis reversed, we have to
1592 : * adjust the angle values for the change in the X axis
1593 : * direction.
1594 : *
1595 : * This should be necessary only when X axis is flipped.
1596 : * __TODO__ Why is order of start/end values reversed as well???
1597 : *------------------------------------------------------------*/
1598 :
1599 642 : if (fp->GetXMultiplier() <= 0.0)
1600 : {
1601 0 : m_dStartAngle = 360.0 - m_dStartAngle;
1602 0 : m_dEndAngle = 360.0 - m_dEndAngle;
1603 : }
1604 :
1605 642 : m_dCenterX = (dXMin + dXMax) / 2.0;
1606 642 : m_dCenterY = (dYMin + dYMax) / 2.0;
1607 642 : m_dXRadius = std::abs((dXMax - dXMin) / 2.0);
1608 642 : m_dYRadius = std::abs((dYMax - dYMin) / 2.0);
1609 :
1610 : /*-----------------------------------------------------------------
1611 : * Create and fill geometry object
1612 : * For the OGR geometry, we generate an arc with 2 degrees line
1613 : * segments.
1614 : *----------------------------------------------------------------*/
1615 642 : OGRLineString *poLine = new OGRLineString;
1616 :
1617 : int numPts = std::max(
1618 1284 : 2,
1619 642 : (m_dEndAngle < m_dStartAngle
1620 642 : ? static_cast<int>(
1621 0 : std::abs(((m_dEndAngle + 360.0) - m_dStartAngle) / 2.0) + 1)
1622 642 : : static_cast<int>(std::abs((m_dEndAngle - m_dStartAngle) / 2.0) +
1623 642 : 1)));
1624 :
1625 642 : TABGenerateArc(poLine, numPts, m_dCenterX, m_dCenterY, m_dXRadius,
1626 642 : m_dYRadius, m_dStartAngle * M_PI / 180.0,
1627 642 : m_dEndAngle * M_PI / 180.0);
1628 :
1629 642 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1630 642 : SetGeometryDirectly(poLine);
1631 :
1632 642 : const char *pszLine = nullptr;
1633 3176 : while (((pszLine = fp->GetLine()) != nullptr) &&
1634 1574 : fp->IsValidFeature(pszLine) == FALSE)
1635 : {
1636 960 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1637 :
1638 960 : if (aosTokens.size() > 1)
1639 : {
1640 328 : if (STARTS_WITH_CI(aosTokens[0], "PEN"))
1641 : {
1642 :
1643 328 : if (aosTokens.size() == 4)
1644 : {
1645 324 : SetPenWidthMIF(atoi(aosTokens[1]));
1646 324 : SetPenPattern(static_cast<GByte>(atoi(aosTokens[2])));
1647 324 : SetPenColor(atoi(aosTokens[3]));
1648 : }
1649 : }
1650 : }
1651 960 : aosTokens.Clear();
1652 : }
1653 642 : return 0;
1654 : }
1655 :
1656 : /**********************************************************************
1657 : *
1658 : **********************************************************************/
1659 0 : int TABArc::WriteGeometryToMIFFile(MIDDATAFile *fp)
1660 : {
1661 : /*-------------------------------------------------------------
1662 : * Start/End angles
1663 : * Since we ALWAYS produce files in quadrant 1 then we can
1664 : * ignore the special angle conversion required by flipped axis.
1665 : *------------------------------------------------------------*/
1666 :
1667 : // Write the Arc's actual MBR
1668 0 : fp->WriteLine("Arc %.15g %.15g %.15g %.15g\n", m_dCenterX - m_dXRadius,
1669 0 : m_dCenterY - m_dYRadius, m_dCenterX + m_dXRadius,
1670 0 : m_dCenterY + m_dYRadius);
1671 :
1672 0 : fp->WriteLine(" %.15g %.15g\n", m_dStartAngle, m_dEndAngle);
1673 :
1674 0 : if (GetPenPattern())
1675 0 : fp->WriteLine(" Pen (%d,%d,%d)\n", GetPenWidthMIF(), GetPenPattern(),
1676 : GetPenColor());
1677 :
1678 0 : return 0;
1679 : }
1680 :
1681 : /**********************************************************************
1682 : *
1683 : **********************************************************************/
1684 297 : int TABText::ReadGeometryFromMIFFile(MIDDATAFile *fp)
1685 : {
1686 297 : const char *pszString = nullptr;
1687 297 : int bXYBoxRead = 0;
1688 :
1689 : CPLStringList aosTokens(
1690 594 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS));
1691 297 : if (aosTokens.size() == 1)
1692 : {
1693 : aosTokens =
1694 3 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1695 3 : const int tokenLen = aosTokens.size();
1696 3 : if (tokenLen == 4)
1697 : {
1698 0 : pszString = nullptr;
1699 0 : bXYBoxRead = 1;
1700 : }
1701 3 : else if (tokenLen == 0)
1702 : {
1703 3 : pszString = nullptr;
1704 : }
1705 0 : else if (tokenLen != 1)
1706 : {
1707 0 : return -1;
1708 : }
1709 : else
1710 : {
1711 0 : pszString = aosTokens[0];
1712 : }
1713 : }
1714 294 : else if (aosTokens.size() == 2)
1715 : {
1716 294 : pszString = aosTokens[1];
1717 : }
1718 : else
1719 : {
1720 0 : return -1;
1721 : }
1722 :
1723 : /*-------------------------------------------------------------
1724 : * Note: The text string may contain escaped "\n" chars, and we
1725 : * sstore them in memory in the UnEscaped form to be OGR
1726 : * compliant. See Maptools bug 1107 for more details.
1727 : *------------------------------------------------------------*/
1728 297 : char *pszTmpString = CPLStrdup(pszString);
1729 297 : m_pszString = TABUnEscapeString(pszTmpString, TRUE);
1730 297 : if (pszTmpString != m_pszString)
1731 0 : CPLFree(pszTmpString);
1732 297 : if (!fp->GetEncoding().empty())
1733 : {
1734 : char *pszUtf8String =
1735 1 : CPLRecode(m_pszString, fp->GetEncoding(), CPL_ENC_UTF8);
1736 1 : CPLFree(m_pszString);
1737 1 : m_pszString = pszUtf8String;
1738 : }
1739 297 : if (!bXYBoxRead)
1740 : {
1741 : aosTokens =
1742 297 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
1743 : }
1744 :
1745 297 : if (aosTokens.size() != 4)
1746 : {
1747 17 : return -1;
1748 : }
1749 :
1750 280 : double dXMin = fp->GetXTrans(CPLAtof(aosTokens[0]));
1751 280 : double dXMax = fp->GetXTrans(CPLAtof(aosTokens[2]));
1752 280 : double dYMin = fp->GetYTrans(CPLAtof(aosTokens[1]));
1753 280 : double dYMax = fp->GetYTrans(CPLAtof(aosTokens[3]));
1754 :
1755 280 : m_dHeight = dYMax - dYMin; // SetTextBoxHeight(dYMax - dYMin);
1756 280 : m_dWidth = dXMax - dXMin; // SetTextBoxWidth(dXMax - dXMin);
1757 :
1758 280 : if (m_dHeight < 0.0)
1759 0 : m_dHeight *= -1.0;
1760 280 : if (m_dWidth < 0.0)
1761 0 : m_dWidth *= -1.0;
1762 :
1763 : /* Set/retrieve the MBR to make sure Mins are smaller than Maxs
1764 : */
1765 :
1766 280 : SetMBR(dXMin, dYMin, dXMax, dYMax);
1767 280 : GetMBR(dXMin, dYMin, dXMax, dYMax);
1768 :
1769 280 : const char *pszLine = nullptr;
1770 3169 : while (((pszLine = fp->GetLine()) != nullptr) &&
1771 1526 : fp->IsValidFeature(pszLine) == FALSE)
1772 : {
1773 1363 : aosTokens = CSLTokenizeStringComplex(pszLine, "() ,", TRUE, FALSE);
1774 :
1775 1363 : if (aosTokens.size() > 1)
1776 : {
1777 1134 : if (STARTS_WITH_CI(aosTokens[0], "FONT"))
1778 : {
1779 270 : if (aosTokens.size() >= 5)
1780 : {
1781 261 : SetFontName(aosTokens[1]);
1782 261 : SetFontFGColor(atoi(aosTokens[4]));
1783 261 : if (aosTokens.size() == 6)
1784 : {
1785 254 : SetFontBGColor(atoi(aosTokens[5]));
1786 254 : SetFontStyleMIFValue(atoi(aosTokens[2]), TRUE);
1787 : }
1788 : else
1789 7 : SetFontStyleMIFValue(atoi(aosTokens[2]));
1790 :
1791 : // papsztoken[3] = Size ???
1792 : }
1793 : }
1794 864 : else if (STARTS_WITH_CI(aosTokens[0], "SPACING"))
1795 : {
1796 237 : if (aosTokens.size() >= 2)
1797 : {
1798 237 : if (STARTS_WITH_CI(aosTokens[1], "2"))
1799 : {
1800 237 : SetTextSpacing(TABTSDouble);
1801 : }
1802 0 : else if (STARTS_WITH_CI(aosTokens[1], "1.5"))
1803 : {
1804 0 : SetTextSpacing(TABTS1_5);
1805 : }
1806 : }
1807 :
1808 237 : if (aosTokens.size() == 7)
1809 : {
1810 0 : if (STARTS_WITH_CI(aosTokens[2], "LAbel"))
1811 : {
1812 0 : if (STARTS_WITH_CI(aosTokens[4], "simple"))
1813 : {
1814 0 : SetTextLineType(TABTLSimple);
1815 0 : SetTextLineEndPoint(
1816 0 : fp->GetXTrans(CPLAtof(aosTokens[5])),
1817 0 : fp->GetYTrans(CPLAtof(aosTokens[6])));
1818 : }
1819 0 : else if (STARTS_WITH_CI(aosTokens[4], "arrow"))
1820 : {
1821 0 : SetTextLineType(TABTLArrow);
1822 0 : SetTextLineEndPoint(
1823 0 : fp->GetXTrans(CPLAtof(aosTokens[5])),
1824 0 : fp->GetYTrans(CPLAtof(aosTokens[6])));
1825 : }
1826 : }
1827 : }
1828 : }
1829 627 : else if (STARTS_WITH_CI(aosTokens[0], "Justify"))
1830 : {
1831 224 : if (aosTokens.size() == 2)
1832 : {
1833 224 : if (STARTS_WITH_CI(aosTokens[1], "Center"))
1834 : {
1835 219 : SetTextJustification(TABTJCenter);
1836 : }
1837 5 : else if (STARTS_WITH_CI(aosTokens[1], "Right"))
1838 : {
1839 0 : SetTextJustification(TABTJRight);
1840 : }
1841 : }
1842 : }
1843 403 : else if (STARTS_WITH_CI(aosTokens[0], "Angle"))
1844 : {
1845 208 : if (aosTokens.size() == 2)
1846 : {
1847 208 : SetTextAngle(CPLAtof(aosTokens[1]));
1848 : }
1849 : }
1850 195 : else if (STARTS_WITH_CI(aosTokens[0], "LAbel"))
1851 : {
1852 195 : if (aosTokens.size() == 5)
1853 : {
1854 178 : if (STARTS_WITH_CI(aosTokens[2], "simple"))
1855 : {
1856 178 : SetTextLineType(TABTLSimple);
1857 356 : SetTextLineEndPoint(
1858 178 : fp->GetXTrans(CPLAtof(aosTokens[3])),
1859 178 : fp->GetYTrans(CPLAtof(aosTokens[4])));
1860 : }
1861 0 : else if (STARTS_WITH_CI(aosTokens[2], "arrow"))
1862 : {
1863 0 : SetTextLineType(TABTLArrow);
1864 0 : SetTextLineEndPoint(
1865 0 : fp->GetXTrans(CPLAtof(aosTokens[3])),
1866 0 : fp->GetYTrans(CPLAtof(aosTokens[4])));
1867 : }
1868 : }
1869 : // What I do with the XY coordinate
1870 : }
1871 : }
1872 : }
1873 : /*-----------------------------------------------------------------
1874 : * Create an OGRPoint Geometry...
1875 : * The point X,Y values will be the coords of the lower-left corner before
1876 : * rotation is applied. (Note that the rotation in MapInfo is done around
1877 : * the upper-left corner)
1878 : * We need to calculate the true lower left corner of the text based
1879 : * on the MBR after rotation, the text height and the rotation angle.
1880 : *---------------------------------------------------------------- */
1881 280 : double dSin = sin(m_dAngle * M_PI / 180.0);
1882 280 : double dCos = cos(m_dAngle * M_PI / 180.0);
1883 280 : double dX = 0.0;
1884 280 : double dY = 0.0;
1885 280 : if (dSin > 0.0 && dCos > 0.0)
1886 : {
1887 208 : dX = dXMin + m_dHeight * dSin;
1888 208 : dY = dYMin;
1889 : }
1890 72 : else if (dSin > 0.0 && dCos < 0.0)
1891 : {
1892 0 : dX = dXMax;
1893 0 : dY = dYMin - m_dHeight * dCos;
1894 : }
1895 72 : else if (dSin < 0.0 && dCos < 0.0)
1896 : {
1897 0 : dX = dXMax + m_dHeight * dSin;
1898 0 : dY = dYMax;
1899 : }
1900 : else // dSin < 0 && dCos > 0
1901 : {
1902 72 : dX = dXMin;
1903 72 : dY = dYMax - m_dHeight * dCos;
1904 : }
1905 :
1906 280 : OGRGeometry *poGeometry = new OGRPoint(dX, dY);
1907 :
1908 280 : SetGeometryDirectly(poGeometry);
1909 :
1910 : /*-----------------------------------------------------------------
1911 : * Compute Text Width: the width of the Text MBR before rotation
1912 : * in ground units... unfortunately this value is not stored in the
1913 : * file, so we have to compute it with the MBR after rotation and
1914 : * the height of the MBR before rotation:
1915 : * With W = Width of MBR before rotation
1916 : * H = Height of MBR before rotation
1917 : * dX = Width of MBR after rotation
1918 : * dY = Height of MBR after rotation
1919 : * teta = rotation angle
1920 : *
1921 : * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use:
1922 : * W = H * (dX - H * sin(teta)) / (H * cos(teta))
1923 : *
1924 : * and for other teta values, use:
1925 : * W = H * (dY - H * cos(teta)) / (H * sin(teta))
1926 : *---------------------------------------------------------------- */
1927 280 : dSin = std::abs(dSin);
1928 280 : dCos = std::abs(dCos);
1929 280 : if (m_dHeight == 0.0)
1930 0 : m_dWidth = 0.0;
1931 280 : else if (dCos > dSin)
1932 280 : m_dWidth = m_dHeight * ((dXMax - dXMin) - m_dHeight * dSin) /
1933 280 : (m_dHeight * dCos);
1934 : else
1935 0 : m_dWidth = m_dHeight * ((dYMax - dYMin) - m_dHeight * dCos) /
1936 0 : (m_dHeight * dSin);
1937 280 : m_dWidth = std::abs(m_dWidth);
1938 :
1939 280 : return 0;
1940 : }
1941 :
1942 : /**********************************************************************
1943 : *
1944 : **********************************************************************/
1945 0 : int TABText::WriteGeometryToMIFFile(MIDDATAFile *fp)
1946 : {
1947 : /*-------------------------------------------------------------
1948 : * Note: The text string may contain unescaped "\n" chars or
1949 : * "\\" chars and we expect to receive them in an unescaped
1950 : * form. Those characters are unescaped in memory to be like
1951 : * other OGR drivers. See MapTools bug 1107 for more details.
1952 : *------------------------------------------------------------*/
1953 : char *pszTmpString;
1954 0 : if (fp->GetEncoding().empty())
1955 : {
1956 0 : pszTmpString = TABEscapeString(m_pszString);
1957 : }
1958 : else
1959 : {
1960 : char *pszEncString =
1961 0 : CPLRecode(m_pszString, CPL_ENC_UTF8, fp->GetEncoding());
1962 0 : pszTmpString = TABEscapeString(pszEncString);
1963 0 : if (pszTmpString != pszEncString)
1964 0 : CPLFree(pszEncString);
1965 : }
1966 :
1967 0 : if (pszTmpString == nullptr)
1968 0 : fp->WriteLine("Text \"\"\n");
1969 : else
1970 0 : fp->WriteLine("Text \"%s\"\n", pszTmpString);
1971 0 : if (pszTmpString != m_pszString)
1972 0 : CPLFree(pszTmpString);
1973 :
1974 0 : double dXMin = 0.0;
1975 0 : double dYMin = 0.0;
1976 0 : double dXMax = 0.0;
1977 0 : double dYMax = 0.0;
1978 0 : UpdateMBR();
1979 0 : GetMBR(dXMin, dYMin, dXMax, dYMax);
1980 0 : fp->WriteLine(" %.15g %.15g %.15g %.15g\n", dXMin, dYMin, dXMax, dYMax);
1981 :
1982 0 : if (IsFontBGColorUsed())
1983 0 : fp->WriteLine(" Font (\"%s\",%d,%d,%d,%d)\n", GetFontNameRef(),
1984 : GetFontStyleMIFValue(), 0, GetFontFGColor(),
1985 : GetFontBGColor());
1986 : else
1987 0 : fp->WriteLine(" Font (\"%s\",%d,%d,%d)\n", GetFontNameRef(),
1988 : GetFontStyleMIFValue(), 0, GetFontFGColor());
1989 :
1990 0 : switch (GetTextSpacing())
1991 : {
1992 0 : case TABTS1_5:
1993 0 : fp->WriteLine(" Spacing 1.5\n");
1994 0 : break;
1995 0 : case TABTSDouble:
1996 0 : fp->WriteLine(" Spacing 2.0\n");
1997 0 : break;
1998 0 : case TABTSSingle:
1999 : default:
2000 0 : break;
2001 : }
2002 :
2003 0 : switch (GetTextJustification())
2004 : {
2005 0 : case TABTJCenter:
2006 0 : fp->WriteLine(" Justify Center\n");
2007 0 : break;
2008 0 : case TABTJRight:
2009 0 : fp->WriteLine(" Justify Right\n");
2010 0 : break;
2011 0 : case TABTJLeft:
2012 : default:
2013 0 : break;
2014 : }
2015 :
2016 0 : if (std::abs(GetTextAngle()) > 0.000001)
2017 0 : fp->WriteLine(" Angle %.15g\n", GetTextAngle());
2018 :
2019 0 : switch (GetTextLineType())
2020 : {
2021 0 : case TABTLSimple:
2022 0 : if (m_bLineEndSet)
2023 0 : fp->WriteLine(" Label Line Simple %.15g %.15g \n",
2024 : m_dfLineEndX, m_dfLineEndY);
2025 0 : break;
2026 0 : case TABTLArrow:
2027 0 : if (m_bLineEndSet)
2028 0 : fp->WriteLine(" Label Line Arrow %.15g %.15g \n",
2029 : m_dfLineEndX, m_dfLineEndY);
2030 0 : break;
2031 0 : case TABTLNoLine:
2032 : default:
2033 0 : break;
2034 : }
2035 0 : return 0;
2036 : }
2037 :
2038 : /**********************************************************************
2039 : *
2040 : **********************************************************************/
2041 183 : int TABMultiPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp)
2042 : {
2043 : char **papszToken =
2044 183 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
2045 :
2046 183 : if (CSLCount(papszToken) != 2)
2047 : {
2048 4 : CSLDestroy(papszToken);
2049 4 : return -1;
2050 : }
2051 :
2052 179 : int nNumPoint = atoi(papszToken[1]);
2053 179 : OGRMultiPoint *poMultiPoint = new OGRMultiPoint;
2054 :
2055 179 : CSLDestroy(papszToken);
2056 179 : papszToken = nullptr;
2057 :
2058 : // Get each point and add them to the multipoint feature
2059 495 : for (int i = 0; i < nNumPoint; i++)
2060 : {
2061 : papszToken =
2062 344 : CSLTokenizeString2(fp->GetLine(), " \t", CSLT_HONOURSTRINGS);
2063 344 : if (CSLCount(papszToken) != 2)
2064 : {
2065 28 : CSLDestroy(papszToken);
2066 28 : delete poMultiPoint;
2067 28 : return -1;
2068 : }
2069 :
2070 316 : const double dfX = fp->GetXTrans(CPLAtof(papszToken[0]));
2071 316 : const double dfY = fp->GetXTrans(CPLAtof(papszToken[1]));
2072 316 : OGRPoint *poPoint = new OGRPoint(dfX, dfY);
2073 316 : if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE)
2074 : {
2075 0 : CPLAssert(false); // Just in case OGR is modified
2076 : }
2077 :
2078 : // Set center
2079 316 : if (i == 0)
2080 : {
2081 165 : SetCenter(dfX, dfY);
2082 : }
2083 316 : CSLDestroy(papszToken);
2084 : }
2085 :
2086 151 : OGREnvelope sEnvelope;
2087 151 : poMultiPoint->getEnvelope(&sEnvelope);
2088 151 : if (SetGeometryDirectly(poMultiPoint) != OGRERR_NONE)
2089 : {
2090 0 : CPLAssert(false); // Just in case OGR is modified
2091 : }
2092 :
2093 151 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
2094 :
2095 : // Read optional SYMBOL line...
2096 :
2097 151 : const char *pszLine = nullptr;
2098 596 : while (((pszLine = fp->GetLine()) != nullptr) &&
2099 293 : fp->IsValidFeature(pszLine) == FALSE)
2100 : {
2101 152 : papszToken = CSLTokenizeStringComplex(pszLine, " ,()\t", TRUE, FALSE);
2102 152 : if (CSLCount(papszToken) == 4 && EQUAL(papszToken[0], "SYMBOL"))
2103 : {
2104 0 : SetSymbolNo(static_cast<GInt16>(atoi(papszToken[1])));
2105 0 : SetSymbolColor(atoi(papszToken[2]));
2106 0 : SetSymbolSize(static_cast<GInt16>(atoi(papszToken[3])));
2107 : }
2108 152 : CSLDestroy(papszToken);
2109 : }
2110 :
2111 151 : return 0;
2112 : }
2113 :
2114 : /**********************************************************************
2115 : *
2116 : **********************************************************************/
2117 0 : int TABMultiPoint::WriteGeometryToMIFFile(MIDDATAFile *fp)
2118 : {
2119 : /*-----------------------------------------------------------------
2120 : * Fetch and validate geometry
2121 : *----------------------------------------------------------------*/
2122 0 : OGRGeometry *poGeom = GetGeometryRef();
2123 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
2124 : {
2125 0 : OGRMultiPoint *poMultiPoint = poGeom->toMultiPoint();
2126 0 : const int nNumPoints = poMultiPoint->getNumGeometries();
2127 :
2128 0 : fp->WriteLine("MultiPoint %d\n", nNumPoints);
2129 :
2130 0 : for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
2131 : {
2132 : /*------------------------------------------------------------
2133 : * Validate each point
2134 : *-----------------------------------------------------------*/
2135 0 : poGeom = poMultiPoint->getGeometryRef(iPoint);
2136 0 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
2137 : {
2138 0 : OGRPoint *poPoint = poGeom->toPoint();
2139 0 : fp->WriteLine("%.15g %.15g\n", poPoint->getX(),
2140 : poPoint->getY());
2141 : }
2142 : else
2143 : {
2144 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2145 : "TABMultiPoint: Missing or Invalid Geometry!");
2146 0 : return -1;
2147 : }
2148 : }
2149 : // Write symbol
2150 0 : fp->WriteLine(" Symbol (%d,%d,%d)\n", GetSymbolNo(),
2151 0 : GetSymbolColor(), GetSymbolSize());
2152 : }
2153 :
2154 0 : return 0;
2155 : }
2156 :
2157 : /**********************************************************************
2158 : *
2159 : **********************************************************************/
2160 99 : int TABCollection::ReadGeometryFromMIFFile(MIDDATAFile *fp)
2161 : {
2162 : /*-----------------------------------------------------------------
2163 : * Fetch number of parts in "COLLECTION %d" line
2164 : *----------------------------------------------------------------*/
2165 : char **papszToken =
2166 99 : CSLTokenizeString2(fp->GetLastLine(), " \t", CSLT_HONOURSTRINGS);
2167 :
2168 99 : if (CSLCount(papszToken) != 2)
2169 : {
2170 2 : CSLDestroy(papszToken);
2171 2 : return -1;
2172 : }
2173 :
2174 97 : int numParts = atoi(papszToken[1]);
2175 97 : CSLDestroy(papszToken);
2176 97 : papszToken = nullptr;
2177 :
2178 : // Make sure collection is empty
2179 97 : EmptyCollection();
2180 :
2181 97 : const char *pszLine = fp->GetLine();
2182 :
2183 : /*-----------------------------------------------------------------
2184 : * Read each part and add them to the feature
2185 : *----------------------------------------------------------------*/
2186 184 : for (int i = 0; i < numParts; i++)
2187 : {
2188 182 : if (pszLine == nullptr)
2189 : {
2190 26 : CPLError(
2191 : CE_Failure, CPLE_FileIO,
2192 : "Unexpected EOF while reading TABCollection from MIF file.");
2193 26 : return -1;
2194 : }
2195 :
2196 156 : while (*pszLine == ' ' || *pszLine == '\t')
2197 0 : pszLine++; // skip leading spaces
2198 :
2199 156 : if (*pszLine == '\0')
2200 : {
2201 2 : pszLine = fp->GetLine();
2202 2 : continue; // Skip blank lines
2203 : }
2204 :
2205 154 : if (STARTS_WITH_CI(pszLine, "REGION"))
2206 : {
2207 88 : delete m_poRegion;
2208 88 : m_poRegion = new TABRegion(GetDefnRef());
2209 88 : if (m_poRegion->ReadGeometryFromMIFFile(fp) != 0)
2210 : {
2211 38 : CPLError(CE_Failure, CPLE_NotSupported,
2212 : "TABCollection: Error reading REGION part.");
2213 38 : delete m_poRegion;
2214 38 : m_poRegion = nullptr;
2215 38 : return -1;
2216 : }
2217 : }
2218 66 : else if (STARTS_WITH_CI(pszLine, "LINE") ||
2219 25 : STARTS_WITH_CI(pszLine, "PLINE"))
2220 : {
2221 41 : delete m_poPline;
2222 41 : m_poPline = new TABPolyline(GetDefnRef());
2223 41 : if (m_poPline->ReadGeometryFromMIFFile(fp) != 0)
2224 : {
2225 8 : CPLError(CE_Failure, CPLE_NotSupported,
2226 : "TABCollection: Error reading PLINE part.");
2227 8 : delete m_poPline;
2228 8 : m_poPline = nullptr;
2229 8 : return -1;
2230 : }
2231 : }
2232 25 : else if (STARTS_WITH_CI(pszLine, "MULTIPOINT"))
2233 : {
2234 20 : delete m_poMpoint;
2235 20 : m_poMpoint = new TABMultiPoint(GetDefnRef());
2236 20 : if (m_poMpoint->ReadGeometryFromMIFFile(fp) != 0)
2237 : {
2238 18 : CPLError(CE_Failure, CPLE_NotSupported,
2239 : "TABCollection: Error reading MULTIPOINT part.");
2240 18 : delete m_poMpoint;
2241 18 : m_poMpoint = nullptr;
2242 18 : return -1;
2243 : }
2244 : }
2245 : else
2246 : {
2247 5 : CPLError(CE_Failure, CPLE_FileIO,
2248 : "Reading TABCollection from MIF failed, expecting one "
2249 : "of REGION, PLINE or MULTIPOINT, got: '%s'",
2250 : pszLine);
2251 5 : return -1;
2252 : }
2253 :
2254 85 : pszLine = fp->GetLastLine();
2255 : }
2256 :
2257 : /*-----------------------------------------------------------------
2258 : * Set the main OGRFeature Geometry
2259 : * (this is actually duplicating geometries from each member)
2260 : *----------------------------------------------------------------*/
2261 : // use addGeometry() rather than addGeometryDirectly() as this clones
2262 : // the added geometry so won't leave dangling ptrs when the above features
2263 : // are deleted
2264 :
2265 2 : OGRGeometryCollection *poGeomColl = new OGRGeometryCollection();
2266 2 : if (m_poRegion && m_poRegion->GetGeometryRef() != nullptr)
2267 2 : poGeomColl->addGeometry(m_poRegion->GetGeometryRef());
2268 :
2269 2 : if (m_poPline && m_poPline->GetGeometryRef() != nullptr)
2270 2 : poGeomColl->addGeometry(m_poPline->GetGeometryRef());
2271 :
2272 2 : if (m_poMpoint && m_poMpoint->GetGeometryRef() != nullptr)
2273 2 : poGeomColl->addGeometry(m_poMpoint->GetGeometryRef());
2274 :
2275 2 : OGREnvelope sEnvelope;
2276 2 : poGeomColl->getEnvelope(&sEnvelope);
2277 2 : this->SetGeometryDirectly(poGeomColl);
2278 :
2279 2 : SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
2280 :
2281 2 : return 0;
2282 : }
2283 :
2284 : /**********************************************************************
2285 : *
2286 : **********************************************************************/
2287 0 : int TABCollection::WriteGeometryToMIFFile(MIDDATAFile *fp)
2288 : {
2289 0 : int numParts = 0;
2290 0 : if (m_poRegion)
2291 0 : numParts++;
2292 0 : if (m_poPline)
2293 0 : numParts++;
2294 0 : if (m_poMpoint)
2295 0 : numParts++;
2296 :
2297 0 : fp->WriteLine("COLLECTION %d\n", numParts);
2298 :
2299 0 : if (m_poRegion)
2300 : {
2301 0 : if (m_poRegion->WriteGeometryToMIFFile(fp) != 0)
2302 0 : return -1;
2303 : }
2304 :
2305 0 : if (m_poPline)
2306 : {
2307 0 : if (m_poPline->WriteGeometryToMIFFile(fp) != 0)
2308 0 : return -1;
2309 : }
2310 :
2311 0 : if (m_poMpoint)
2312 : {
2313 0 : if (m_poMpoint->WriteGeometryToMIFFile(fp) != 0)
2314 0 : return -1;
2315 : }
2316 :
2317 0 : return 0;
2318 : }
2319 :
2320 : /**********************************************************************
2321 : *
2322 : **********************************************************************/
2323 0 : int TABDebugFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp)
2324 : {
2325 : // Go to the first line of the next feature.
2326 0 : printf("%s\n", fp->GetLastLine()); /*ok*/
2327 :
2328 0 : const char *pszLine = nullptr;
2329 0 : while (((pszLine = fp->GetLine()) != nullptr) &&
2330 0 : fp->IsValidFeature(pszLine) == FALSE)
2331 : {
2332 : }
2333 :
2334 0 : return 0;
2335 : }
2336 :
2337 : /**********************************************************************
2338 : *
2339 : **********************************************************************/
2340 0 : int TABDebugFeature::WriteGeometryToMIFFile(MIDDATAFile * /* fp */)
2341 : {
2342 0 : return -1;
2343 : }
|