1 : /**********************************************************************
2 : * $Id: mitab_imapinfofile.cpp,v 1.31 2010-01-07 20:39:12 aboudreault Exp $
3 : *
4 : * Name: mitab_imapinfo
5 : * Project: MapInfo mid/mif Tab Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the IMapInfoFile class, super class of
8 : * of MIFFile and TABFile
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2008, Daniel Morissette
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : **********************************************************************
32 : *
33 : * $Log: mitab_imapinfofile.cpp,v $
34 : * Revision 1.31 2010-01-07 20:39:12 aboudreault
35 : * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141)
36 : *
37 : * Revision 1.30 2009-01-23 16:50:27 aboudreault
38 : * Fixed wrong return value of IMapInfoFile::SetCharset() method (bug 1987)
39 : *
40 : * Revision 1.29 2008/11/27 20:50:22 aboudreault
41 : * Improved support for OGR date/time types. New Read/Write methods (bug 1948)
42 : * Added support of OGR date/time types for MIF features.
43 : *
44 : * Revision 1.28 2008/11/17 22:06:21 aboudreault
45 : * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with
46 : * OGR and fixed reading/writing support for these types.
47 : *
48 : * Revision 1.27 2008/09/26 14:40:24 aboudreault
49 : * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948)
50 : *
51 : * Revision 1.26 2008/03/07 20:16:17 dmorissette
52 : * Fixed typos in comments
53 : *
54 : * Revision 1.25 2008/03/05 20:35:39 dmorissette
55 : * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859)
56 : *
57 : * Revision 1.24 2007/06/21 14:00:23 dmorissette
58 : * Added missing cast in isspace() calls to avoid failed assertion on Windows
59 : * (MITAB bug 1737, GDAL ticket 1678))
60 : *
61 : * Revision 1.23 2007/06/12 14:43:19 dmorissette
62 : * Use iswspace instead of sispace in IMapInfoFile::SmartOpen() (bug 1737)
63 : *
64 : * Revision 1.22 2007/06/12 13:52:37 dmorissette
65 : * Added IMapInfoFile::SetCharset() method (bug 1734)
66 : *
67 : * Revision 1.21 2005/05/19 21:10:50 fwarmerdam
68 : * changed to use OGRLayers spatial filter support
69 : *
70 : * Revision 1.20 2005/05/19 15:27:00 jlacroix
71 : * Implement a method to set the StyleString of a TABFeature.
72 : * This is done via the ITABFeaturePen, Brush and Symbol classes.
73 : *
74 : * Revision 1.19 2004/06/30 20:29:04 dmorissette
75 : * Fixed refs to old address danmo@videotron.ca
76 : *
77 : * Revision 1.18 2003/12/19 07:55:55 fwarmerdam
78 : * treat 3D features as 2D on write
79 : *
80 : * Revision 1.17 2001/09/14 19:14:43 warmerda
81 : * added attribute query support
82 : *
83 : * Revision 1.16 2001/09/14 03:23:55 warmerda
84 : * Substantial upgrade to support spatial queries using spatial indexes
85 : *
86 : * Revision 1.15 2001/07/03 23:11:21 daniel
87 : * Test for NULL geometries if spatial filter enabled in GetNextFeature().
88 : *
89 : * Revision 1.14 2001/03/09 04:16:02 daniel
90 : * Added TABSeamless for reading seamless TAB files
91 : *
92 : * Revision 1.13 2001/02/27 19:59:05 daniel
93 : * Enabled spatial filter in IMapInfoFile::GetNextFeature(), and avoid
94 : * unnecessary feature cloning in GetNextFeature() and GetFeature()
95 : *
96 : * Revision 1.12 2001/02/06 22:03:24 warmerda
97 : * fixed memory leak of whole features in CreateFeature
98 : *
99 : * Revision 1.11 2001/01/23 21:23:42 daniel
100 : * Added projection bounds lookup table, called from TABFile::SetProjInfo()
101 : *
102 : * Revision 1.10 2001/01/22 16:03:58 warmerda
103 : * expanded tabs
104 : *
105 : * Revision 1.9 2000/11/30 20:27:56 warmerda
106 : * make variable length string fields 254 wide, not 255
107 : *
108 : * Revision 1.8 2000/02/28 03:11:35 warmerda
109 : * fix support for zero width fields
110 : *
111 : * Revision 1.7 2000/02/02 20:14:03 warmerda
112 : * made safer when encountering geometryless features
113 : *
114 : * Revision 1.6 2000/01/26 18:17:35 warmerda
115 : * added CreateField method
116 : *
117 : * Revision 1.5 2000/01/15 22:30:44 daniel
118 : * Switch to MIT/X-Consortium OpenSource license
119 : *
120 : * Revision 1.4 2000/01/11 19:06:25 daniel
121 : * Added support for conversion of collections in CreateFeature()
122 : *
123 : * Revision 1.3 1999/12/14 02:14:50 daniel
124 : * Added static SmartOpen() method + TABView support
125 : *
126 : * Revision 1.2 1999/11/08 19:15:44 stephane
127 : * Add headers method
128 : *
129 : * Revision 1.1 1999/11/08 04:17:27 stephane
130 : * First Revision
131 : *
132 : **********************************************************************/
133 :
134 : #include "mitab.h"
135 : #include "mitab_utils.h"
136 :
137 : #ifdef __HP_aCC
138 : # include <wchar.h> /* iswspace() */
139 : #else
140 : # include <wctype.h> /* iswspace() */
141 : #endif
142 :
143 : /**********************************************************************
144 : * IMapInfoFile::IMapInfoFile()
145 : *
146 : * Constructor.
147 : **********************************************************************/
148 52 : IMapInfoFile::IMapInfoFile()
149 : {
150 52 : m_nCurFeatureId = 0;
151 52 : m_poCurFeature = NULL;
152 52 : m_bBoundsSet = FALSE;
153 52 : m_pszCharset = NULL;
154 52 : }
155 :
156 :
157 : /**********************************************************************
158 : * IMapInfoFile::~IMapInfoFile()
159 : *
160 : * Destructor.
161 : **********************************************************************/
162 52 : IMapInfoFile::~IMapInfoFile()
163 : {
164 52 : if (m_poCurFeature)
165 : {
166 0 : delete m_poCurFeature;
167 0 : m_poCurFeature = NULL;
168 : }
169 :
170 52 : CPLFree(m_pszCharset);
171 52 : m_pszCharset = NULL;
172 52 : }
173 :
174 : /**********************************************************************
175 : * IMapInfoFile::SmartOpen()
176 : *
177 : * Use this static method to automatically open any flavour of MapInfo
178 : * dataset. This method will detect the file type, create an object
179 : * of the right type, and open the file.
180 : *
181 : * Call GetFileClass() on the returned object if you need to find out
182 : * its exact type. (To access format-specific methods for instance)
183 : *
184 : * Returns the new object ptr. , or NULL if the open failed.
185 : **********************************************************************/
186 828 : IMapInfoFile *IMapInfoFile::SmartOpen(const char *pszFname,
187 : GBool bTestOpenNoError /*=FALSE*/)
188 : {
189 828 : IMapInfoFile *poFile = NULL;
190 828 : int nLen = 0;
191 :
192 828 : if (pszFname)
193 828 : nLen = strlen(pszFname);
194 :
195 852 : if (nLen > 4 && (EQUAL(pszFname + nLen-4, ".MIF") ||
196 : EQUAL(pszFname + nLen-4, ".MID") ) )
197 : {
198 : /*-------------------------------------------------------------
199 : * MIF/MID file
200 : *------------------------------------------------------------*/
201 24 : poFile = new MIFFile;
202 : }
203 804 : else if (nLen > 4 && EQUAL(pszFname + nLen-4, ".TAB"))
204 : {
205 : /*-------------------------------------------------------------
206 : * .TAB file ... is it a TABFileView or a TABFile?
207 : * We have to read the .tab header to find out.
208 : *------------------------------------------------------------*/
209 : FILE *fp;
210 : const char *pszLine;
211 6 : char *pszAdjFname = CPLStrdup(pszFname);
212 6 : GBool bFoundFields = FALSE, bFoundView=FALSE, bFoundSeamless=FALSE;
213 :
214 6 : TABAdjustFilenameExtension(pszAdjFname);
215 6 : fp = VSIFOpen(pszAdjFname, "r");
216 70 : while(fp && (pszLine = CPLReadLine(fp)) != NULL)
217 : {
218 58 : while (isspace((unsigned char)*pszLine)) pszLine++;
219 58 : if (EQUALN(pszLine, "Fields", 6))
220 6 : bFoundFields = TRUE;
221 52 : else if (EQUALN(pszLine, "create view", 11))
222 0 : bFoundView = TRUE;
223 52 : else if (EQUALN(pszLine, "\"\\IsSeamless\" = \"TRUE\"", 21))
224 0 : bFoundSeamless = TRUE;
225 : }
226 :
227 6 : if (bFoundView)
228 0 : poFile = new TABView;
229 6 : else if (bFoundFields && bFoundSeamless)
230 0 : poFile = new TABSeamless;
231 6 : else if (bFoundFields)
232 6 : poFile = new TABFile;
233 :
234 6 : if (fp)
235 6 : VSIFClose(fp);
236 :
237 6 : CPLFree(pszAdjFname);
238 : }
239 :
240 : /*-----------------------------------------------------------------
241 : * Perform the open() call
242 : *----------------------------------------------------------------*/
243 828 : if (poFile && poFile->Open(pszFname, "r", bTestOpenNoError) != 0)
244 : {
245 0 : delete poFile;
246 0 : poFile = NULL;
247 : }
248 :
249 828 : if (!bTestOpenNoError && poFile == NULL)
250 : {
251 : CPLError(CE_Failure, CPLE_FileIO,
252 0 : "%s could not be opened as a MapInfo dataset.", pszFname);
253 : }
254 :
255 828 : return poFile;
256 : }
257 :
258 :
259 :
260 : /**********************************************************************
261 : * IMapInfoFile::GetNextFeature()
262 : *
263 : * Standard OGR GetNextFeature implementation. This method is used
264 : * to retreive the next OGRFeature.
265 : **********************************************************************/
266 138 : OGRFeature *IMapInfoFile::GetNextFeature()
267 : {
268 : OGRFeature *poFeatureRef;
269 : OGRGeometry *poGeom;
270 : int nFeatureId;
271 :
272 352 : while( (nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1 )
273 : {
274 194 : poFeatureRef = GetFeatureRef(nFeatureId);
275 194 : if (poFeatureRef == NULL)
276 0 : return NULL;
277 194 : else if( (m_poFilterGeom == NULL ||
278 : ((poGeom = poFeatureRef->GetGeometryRef()) != NULL &&
279 : FilterGeometry( poGeom )))
280 : && (m_poAttrQuery == NULL
281 : || m_poAttrQuery->Evaluate( poFeatureRef )) )
282 : {
283 : // Avoid cloning feature... return the copy owned by the class
284 118 : CPLAssert(poFeatureRef == m_poCurFeature);
285 118 : m_poCurFeature = NULL;
286 118 : return poFeatureRef;
287 : }
288 : }
289 20 : return NULL;
290 : }
291 :
292 : /**********************************************************************
293 : * IMapInfoFile::CreateFeature()
294 : *
295 : * Standard OGR CreateFeature implementation. This method is used
296 : * to create a new feature in current dataset
297 : **********************************************************************/
298 70 : OGRErr IMapInfoFile::CreateFeature(OGRFeature *poFeature)
299 : {
300 : TABFeature *poTABFeature;
301 : OGRGeometry *poGeom;
302 : OGRwkbGeometryType eGType;
303 : OGRErr eErr;
304 70 : TABPoint *poTABPointFeature = NULL;
305 70 : TABRegion *poTABRegionFeature = NULL;
306 70 : TABPolyline *poTABPolylineFeature = NULL;
307 :
308 : /*-----------------------------------------------------------------
309 : * MITAB won't accept new features unless they are in a type derived
310 : * from TABFeature... so we have to do our best to map to the right
311 : * feature type based on the geometry type.
312 : *----------------------------------------------------------------*/
313 70 : poGeom = poFeature->GetGeometryRef();
314 70 : if( poGeom != NULL )
315 52 : eGType = poGeom->getGeometryType();
316 : else
317 18 : eGType = wkbNone;
318 :
319 70 : switch( wkbFlatten(eGType) )
320 : {
321 : /*-------------------------------------------------------------
322 : * POINT
323 : *------------------------------------------------------------*/
324 : case wkbPoint:
325 8 : poTABFeature = new TABPoint(poFeature->GetDefnRef());
326 8 : if(poFeature->GetStyleString())
327 : {
328 0 : poTABPointFeature = (TABPoint*)poTABFeature;
329 : poTABPointFeature->SetSymbolFromStyleString(
330 0 : poFeature->GetStyleString());
331 : }
332 8 : break;
333 : /*-------------------------------------------------------------
334 : * REGION
335 : *------------------------------------------------------------*/
336 : case wkbPolygon:
337 : case wkbMultiPolygon:
338 42 : poTABFeature = new TABRegion(poFeature->GetDefnRef());
339 42 : if(poFeature->GetStyleString())
340 : {
341 0 : poTABRegionFeature = (TABRegion*)poTABFeature;
342 : poTABRegionFeature->SetPenFromStyleString(
343 0 : poFeature->GetStyleString());
344 :
345 : poTABRegionFeature->SetBrushFromStyleString(
346 0 : poFeature->GetStyleString());
347 : }
348 42 : break;
349 : /*-------------------------------------------------------------
350 : * LINE/PLINE/MULTIPLINE
351 : *------------------------------------------------------------*/
352 : case wkbLineString:
353 : case wkbMultiLineString:
354 2 : poTABFeature = new TABPolyline(poFeature->GetDefnRef());
355 2 : if(poFeature->GetStyleString())
356 : {
357 0 : poTABPolylineFeature = (TABPolyline*)poTABFeature;
358 : poTABPolylineFeature->SetPenFromStyleString(
359 0 : poFeature->GetStyleString());
360 : }
361 2 : break;
362 : /*-------------------------------------------------------------
363 : * Collection types that are not directly supported... convert
364 : * to multiple features in output file through recursive calls.
365 : *------------------------------------------------------------*/
366 : case wkbGeometryCollection:
367 : case wkbMultiPoint:
368 : {
369 0 : OGRErr eStatus = OGRERR_NONE;
370 : int i;
371 0 : OGRGeometryCollection *poColl = (OGRGeometryCollection*)poGeom;
372 0 : OGRFeature *poTmpFeature = poFeature->Clone();
373 :
374 0 : for (i=0; eStatus==OGRERR_NONE && i<poColl->getNumGeometries(); i++)
375 : {
376 0 : poTmpFeature->SetGeometry(poColl->getGeometryRef(i));
377 0 : eStatus = CreateFeature(poTmpFeature);
378 : }
379 0 : delete poTmpFeature;
380 0 : return eStatus;
381 : }
382 : break;
383 : /*-------------------------------------------------------------
384 : * Unsupported type.... convert to MapInfo geometry NONE
385 : *------------------------------------------------------------*/
386 : case wkbUnknown:
387 : default:
388 18 : poTABFeature = new TABFeature(poFeature->GetDefnRef());
389 : break;
390 : }
391 :
392 70 : if( poGeom != NULL )
393 52 : poTABFeature->SetGeometryDirectly(poGeom->clone());
394 :
395 240 : for (int i=0; i< poFeature->GetDefnRef()->GetFieldCount();i++)
396 : {
397 170 : poTABFeature->SetField(i,poFeature->GetRawFieldRef( i ));
398 : }
399 :
400 :
401 70 : eErr = CreateFeature(poTABFeature);
402 :
403 70 : delete poTABFeature;
404 :
405 70 : return eErr;
406 : }
407 :
408 : /**********************************************************************
409 : * IMapInfoFile::GetFeature()
410 : *
411 : * Standard OGR GetFeature implementation. This method is used
412 : * to get the wanted (nFeatureId) feature, a NULL value will be
413 : * returned on error.
414 : **********************************************************************/
415 0 : OGRFeature *IMapInfoFile::GetFeature(long nFeatureId)
416 : {
417 : OGRFeature *poFeatureRef;
418 :
419 0 : poFeatureRef = GetFeatureRef(nFeatureId);
420 0 : if (poFeatureRef)
421 : {
422 : // Avoid cloning feature... return the copy owned by the class
423 0 : CPLAssert(poFeatureRef == m_poCurFeature);
424 0 : m_poCurFeature = NULL;
425 :
426 0 : return poFeatureRef;
427 : }
428 : else
429 0 : return NULL;
430 : }
431 :
432 : /************************************************************************/
433 : /* CreateField() */
434 : /* */
435 : /* Create a native field based on a generic OGR definition. */
436 : /************************************************************************/
437 :
438 50 : OGRErr IMapInfoFile::CreateField( OGRFieldDefn *poField, int bApproxOK )
439 :
440 : {
441 : TABFieldType eTABType;
442 50 : int nWidth = poField->GetWidth();
443 :
444 50 : if( poField->GetType() == OFTInteger )
445 : {
446 14 : eTABType = TABFInteger;
447 14 : if( nWidth == 0 )
448 6 : nWidth = 12;
449 : }
450 36 : else if( poField->GetType() == OFTReal )
451 : {
452 14 : if( nWidth == 0 && poField->GetPrecision() == 0)
453 : {
454 10 : eTABType = TABFFloat;
455 10 : nWidth = 32;
456 : }
457 : else
458 : {
459 4 : eTABType = TABFDecimal;
460 : }
461 : }
462 22 : else if( poField->GetType() == OFTDate )
463 : {
464 0 : eTABType = TABFDate;
465 0 : if( nWidth == 0 )
466 0 : nWidth = 10;
467 : }
468 22 : else if( poField->GetType() == OFTTime )
469 : {
470 0 : eTABType = TABFTime;
471 0 : if( nWidth == 0 )
472 0 : nWidth = 9;
473 : }
474 22 : else if( poField->GetType() == OFTDateTime )
475 : {
476 0 : eTABType = TABFDateTime;
477 0 : if( nWidth == 0 )
478 0 : nWidth = 19;
479 : }
480 22 : else if( poField->GetType() == OFTString )
481 : {
482 22 : eTABType = TABFChar;
483 22 : if( nWidth == 0 )
484 22 : nWidth = 254;
485 : else
486 0 : nWidth = MIN(254,nWidth);
487 : }
488 : else
489 : {
490 : CPLError( CE_Failure, CPLE_AppDefined,
491 : "IMapInfoFile::CreateField() called with unsupported field"
492 : " type %d.\n"
493 : "Note that Mapinfo files don't support list field types.\n",
494 0 : poField->GetType() );
495 :
496 0 : return OGRERR_FAILURE;
497 : }
498 :
499 50 : if( AddFieldNative( poField->GetNameRef(), eTABType,
500 50 : nWidth, poField->GetPrecision(), FALSE, FALSE, bApproxOK ) > -1 )
501 50 : return OGRERR_NONE;
502 : else
503 0 : return OGRERR_FAILURE;
504 : }
505 :
506 :
507 : /**********************************************************************
508 : * IMapInfoFile::SetCharset()
509 : *
510 : * Set the charset for the tab header.
511 : *
512 : *
513 : * Returns 0 on success, -1 on error.
514 : **********************************************************************/
515 0 : int IMapInfoFile::SetCharset(const char* pszCharset)
516 : {
517 0 : if(pszCharset && strlen(pszCharset) > 0)
518 : {
519 0 : CPLFree(m_pszCharset);
520 0 : m_pszCharset = CPLStrdup(pszCharset);
521 0 : return 0;
522 : }
523 0 : return -1;
524 : }
525 :
|