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