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