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