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