1 : /**********************************************************************
2 : * $Id: mitab_miffile.cpp,v 1.58 2011-09-22 21:57:46 dmorissette Exp $
3 : *
4 : * Name: mitab_miffile.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the MIDFile class.
8 : * To be used by external programs to handle reading/writing of
9 : * features from/to MID/MIF datasets.
10 : * Author: Stephane Villeneuve, stephane.v@videotron.ca
11 : *
12 : **********************************************************************
13 : * Copyright (c) 1999-2003, Stephane Villeneuve
14 : *
15 : * Permission is hereby granted, free of charge, to any person obtaining a
16 : * copy of this software and associated documentation files (the "Software"),
17 : * to deal in the Software without restriction, including without limitation
18 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 : * and/or sell copies of the Software, and to permit persons to whom the
20 : * Software is furnished to do so, subject to the following conditions:
21 : *
22 : * The above copyright notice and this permission notice shall be included
23 : * in all copies or substantial portions of the Software.
24 : *
25 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31 : * DEALINGS IN THE SOFTWARE.
32 : **********************************************************************
33 : *
34 : * $Log: mitab_miffile.cpp,v $
35 : * Revision 1.58 2011-09-22 21:57:46 dmorissette
36 : * Fixed problem with tab delimiter used in MIF files (GDAL #4257)
37 : *
38 : * Revision 1.57 2010-10-15 12:06:44 aboudreault
39 : * Fixed crash when trying to get the same mitab mif feature twice (GDAL #3765)
40 : *
41 : * Revision 1.56 2010-10-12 19:02:40 aboudreault
42 : * Fixed incomplet patch to handle differently indented lines in mif files (gdal #3694)
43 : *
44 : * Revision 1.55 2010-10-08 18:50:52 aboudreault
45 : * Fixed handle differently indented lines in mif files. (GDAL bug #3694)
46 : *
47 : * Revision 1.54 2010-07-07 19:00:15 aboudreault
48 : * Cleanup Win32 Compile Warnings (GDAL bug #2930)
49 : *
50 : * Revision 1.53 2010-05-07 19:39:19 aboudreault
51 : * Fixed MIF driver: doesn't create a layer defn at layer creation (bug 2180)
52 : *
53 : * Revision 1.52 2010-01-07 20:39:12 aboudreault
54 : * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141)
55 : *
56 : * Revision 1.51 2009-07-27 14:08:41 dmorissette
57 : * Fixed dataset version check in AddFieldNative for type TABFDateTime
58 : *
59 : * Revision 1.50 2008-12-17 14:55:20 aboudreault
60 : * Fixed mitab mif/mid importer fails when a Text geometry have an empty
61 : * text value (bug 1978)
62 : *
63 : * Revision 1.49 2008/11/17 22:06:21 aboudreault
64 : * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with
65 : * OGR and fixed reading/writing support for these types.
66 : *
67 : * Revision 1.48 2008/09/26 14:40:24 aboudreault
68 : * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948)
69 : *
70 : * Revision 1.47 2008/03/05 20:35:39 dmorissette
71 : * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859)
72 : *
73 : * Revision 1.46 2008/02/01 20:30:59 dmorissette
74 : * Use %.15g instead of %.16g as number precision in .MIF output
75 : *
76 : * Revision 1.45 2008/01/29 21:56:39 dmorissette
77 : * Update dataset version properly for Date/Time/DateTime field types (#1754)
78 : *
79 : * Revision 1.44 2008/01/29 20:46:32 dmorissette
80 : * Added support for v9 Time and DateTime fields (byg 1754)
81 : *
82 : * Revision 1.43 2007/09/14 15:35:21 dmorissette
83 : * Fixed problem with MIF parser being confused by special attribute
84 : * names (bug 1795)
85 : *
86 : * Revision 1.42 2007/06/12 13:52:37 dmorissette
87 : * Added IMapInfoFile::SetCharset() method (bug 1734)
88 : *
89 : * Revision 1.41 2005/10/13 20:12:03 fwarmerdam
90 : * layers with just regions can't be set as type wkbPolygon because they may
91 : * have multipolygons (bug GDAL:958)
92 : * http://bugzilla.remotesensing.org/show_bug.cgi?id=958
93 : *
94 : * Revision 1.40 2005/10/12 14:03:02 fwarmerdam
95 : * Fixed problem with white space parsing in mitab_miffile.cpp (bug GDAL:954)
96 : *
97 : * Revision 1.39 2005/10/04 19:36:10 dmorissette
98 : * Added support for reading collections from MIF files (bug 1126)
99 : *
100 : * Revision 1.38 2004/02/27 21:04:14 fwarmerdam
101 : * dont write MIF header if file is readonly - gdal bugzilla 509
102 : *
103 : * Revision 1.37 2003/12/19 07:54:50 fwarmerdam
104 : * write mif header on close if not already written out
105 : *
106 : * Revision 1.36 2003/08/13 02:49:02 dmorissette
107 : * Use tab as default delimiter if not explicitly specified (Anthony D, bug 37)
108 : *
109 : * Revision 1.35 2003/01/30 22:42:39 daniel
110 : * Fixed crash in ParseMIFHeader() when .mif doesn't contain a DATA line
111 : *
112 : * Revision 1.34 2002/09/23 12:53:29 warmerda
113 : * fix memory leak of m_pszIndex
114 : *
115 : * Revision 1.33 2002/05/08 15:10:48 julien
116 : * Implement MIFFile::SetMIFCoordSys in mitab_capi.cpp (Bug 984)
117 : *
118 : * Revision 1.32 2002/04/26 14:16:49 julien
119 : * Finishing the implementation of Multipoint (support for MIF)
120 : *
121 : * Revision 1.31 2001/09/19 21:39:15 warmerda
122 : * get extents efficiently
123 : *
124 : * Revision 1.30 2001/09/19 14:31:22 warmerda
125 : * added m_nPreloadedId to keep track of preloaded line
126 : *
127 : * Revision 1.29 2001/09/14 19:14:43 warmerda
128 : * added attribute query support
129 : *
130 : * Revision 1.28 2001/08/10 17:49:01 warmerda
131 : * fixed a few memory leaks
132 : *
133 : * Revision 1.27 2001/03/15 03:57:51 daniel
134 : * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
135 : *
136 : * Revision 1.26 2001/03/09 04:14:19 daniel
137 : * Fixed problem creating new files with mixed case extensions (e.g. ".Tab")
138 : *
139 : * Revision 1.25 2001/03/09 03:51:48 daniel
140 : * Fixed writing MIF header: missing break; for decimal fields
141 : *
142 : * Revision 1.24 2001/02/27 19:59:05 daniel
143 : * Enabled spatial filter in IMapInfoFile::GetNextFeature(), and avoid
144 : * unnecessary feature cloning in GetNextFeature() and GetFeature()
145 : *
146 : * Revision 1.23 2001/01/23 21:23:42 daniel
147 : * Added projection bounds lookup table, called from TABFile::SetProjInfo()
148 : *
149 : * Revision 1.22 2001/01/22 16:03:58 warmerda
150 : * expanded tabs
151 : *
152 : * Revision 1.21 2000/12/15 05:38:38 daniel
153 : * Produce Warning instead of an error when nWidth>254 in AddFieldNative()
154 : *
155 : * Revision 1.20 2000/11/14 06:15:37 daniel
156 : * Handle '\t' as spaces in parsing, and fixed GotoFeature() to avoid calling
157 : * ResetReading() when reading forward.
158 : *
159 : * Revision 1.19 2000/07/04 01:50:40 warmerda
160 : * Removed unprotected debugging printf.
161 : *
162 : * Revision 1.18 2000/06/28 00:32:04 warmerda
163 : * Make GetFeatureCountByType() actually work if bForce is TRUE
164 : * Collect detailed (by feature type) feature counts in PreParse().
165 : *
166 : * Revision 1.17 2000/04/27 15:46:25 daniel
167 : * Make SetFeatureDefn() use AddFieldNative(), scan field names for invalid
168 : * chars, and map field width=0 (variable length in OGR) to valid defaults
169 : *
170 : * Revision 1.16 2000/03/27 03:37:59 daniel
171 : * Handle bounds in CoordSys for read and write, + handle point SYMBOL line as
172 : * optional + fixed reading of bounds in PreParseFile()
173 : *
174 : * Revision 1.15 2000/02/28 17:05:06 daniel
175 : * Added support for index and unique directives for read and write
176 : *
177 : * Revision 1.14 2000/01/28 07:32:25 daniel
178 : * Validate char field width (must be <= 254 chars)
179 : *
180 : * Revision 1.13 2000/01/24 19:51:33 warmerda
181 : * AddFieldNative should not fail for read-only datasets
182 : *
183 : * Revision 1.12 2000/01/18 23:13:41 daniel
184 : * Implemented AddFieldNative()
185 : *
186 : * ...
187 : *
188 : * Revision 1.1 1999/11/08 04:16:07 stephane
189 : * First Revision
190 : *
191 : **********************************************************************/
192 :
193 : #include "mitab.h"
194 : #include "mitab_utils.h"
195 : #include <ctype.h>
196 :
197 : /*=====================================================================
198 : * class MIFFile
199 : *====================================================================*/
200 :
201 :
202 : /**********************************************************************
203 : * MIFFile::MIFFile()
204 : *
205 : * Constructor.
206 : **********************************************************************/
207 40 : MIFFile::MIFFile()
208 : {
209 40 : m_pszFname = NULL;
210 40 : m_nVersion = 300;
211 :
212 : // Tab is default delimiter in MIF spec if not explicitly specified. Use
213 : // that by default for read mode. In write mode, we will use "," as
214 : // delimiter since it's more common than tab (we do this in Open())
215 40 : m_pszDelimiter = CPLStrdup("\t");
216 :
217 40 : m_pszUnique = NULL;
218 40 : m_pszIndex = NULL;
219 40 : m_pszCoordSys = NULL;
220 :
221 40 : m_paeFieldType = NULL;
222 40 : m_pabFieldIndexed = NULL;
223 40 : m_pabFieldUnique = NULL;
224 :
225 40 : m_dfXMultiplier = 1.0;
226 40 : m_dfYMultiplier = 1.0;
227 40 : m_dfXDisplacement = 0.0;
228 40 : m_dfYDisplacement = 0.0;
229 :
230 40 : m_poMIDFile = NULL;
231 40 : m_poMIFFile = NULL;
232 40 : m_nPreloadedId = 0;
233 :
234 40 : m_poDefn = NULL;
235 40 : m_poSpatialRef = NULL;
236 :
237 40 : m_nCurFeatureId = 0;
238 40 : m_nFeatureCount = 0;
239 40 : m_nWriteFeatureId = -1;
240 40 : m_poCurFeature = NULL;
241 :
242 40 : m_bPreParsed = FALSE;
243 40 : m_nAttribut = 0;
244 40 : m_bHeaderWrote = FALSE;
245 40 : m_nPoints = m_nLines = m_nRegions = m_nTexts = 0;
246 :
247 40 : m_bExtentsSet = FALSE;
248 40 : }
249 :
250 : /**********************************************************************
251 : * MIFFile::~MIFFile()
252 : *
253 : * Destructor.
254 : **********************************************************************/
255 40 : MIFFile::~MIFFile()
256 : {
257 40 : Close();
258 40 : }
259 :
260 : /**********************************************************************
261 : * MIFFile::Open()
262 : *
263 : * Returns 0 on success, -1 on error.
264 : **********************************************************************/
265 40 : int MIFFile::Open(const char *pszFname, const char *pszAccess,
266 : GBool bTestOpenNoError /*=FALSE*/ )
267 : {
268 40 : char *pszTmpFname = NULL;
269 40 : int nFnameLen = 0;
270 :
271 40 : CPLErrorReset();
272 :
273 40 : if (m_poMIDFile)
274 : {
275 : CPLError(CE_Failure, CPLE_FileIO,
276 0 : "Open() failed: object already contains an open file");
277 :
278 0 : return -1;
279 : }
280 :
281 : /*-----------------------------------------------------------------
282 : * Validate access mode
283 : *----------------------------------------------------------------*/
284 40 : if (EQUALN(pszAccess, "r", 1))
285 : {
286 24 : m_eAccessMode = TABRead;
287 24 : pszAccess = "rt";
288 : }
289 16 : else if (EQUALN(pszAccess, "w", 1))
290 : {
291 16 : m_eAccessMode = TABWrite;
292 16 : pszAccess = "wt";
293 :
294 : // In write mode, use "," as delimiter since it's more common than tab
295 16 : CPLFree(m_pszDelimiter);
296 16 : m_pszDelimiter = CPLStrdup(",");
297 : }
298 : else
299 : {
300 0 : if (!bTestOpenNoError)
301 : CPLError(CE_Failure, CPLE_FileIO,
302 0 : "Open() failed: access mode \"%s\" not supported", pszAccess);
303 : else
304 0 : CPLErrorReset();
305 :
306 0 : return -1;
307 : }
308 :
309 : /*-----------------------------------------------------------------
310 : * Make sure filename has a .MIF or .MID extension...
311 : *----------------------------------------------------------------*/
312 40 : m_pszFname = CPLStrdup(pszFname);
313 40 : nFnameLen = strlen(m_pszFname);
314 40 : if (nFnameLen > 4 && (strcmp(m_pszFname+nFnameLen-4, ".MID")==0 ||
315 : strcmp(m_pszFname+nFnameLen-4, ".MIF")==0 ) )
316 0 : strcpy(m_pszFname+nFnameLen-4, ".MIF");
317 80 : else if (nFnameLen > 4 && (EQUAL(m_pszFname+nFnameLen-4, ".mid") ||
318 : EQUAL(m_pszFname+nFnameLen-4, ".mif") ) )
319 40 : strcpy(m_pszFname+nFnameLen-4, ".mif");
320 : else
321 : {
322 0 : if (!bTestOpenNoError)
323 : CPLError(CE_Failure, CPLE_FileIO,
324 : "Open() failed for %s: invalid filename extension",
325 0 : m_pszFname);
326 : else
327 0 : CPLErrorReset();
328 :
329 0 : CPLFree(m_pszFname);
330 0 : return -1;
331 : }
332 :
333 40 : pszTmpFname = CPLStrdup(m_pszFname);
334 :
335 : /*-----------------------------------------------------------------
336 : * Open .MIF file
337 : *----------------------------------------------------------------*/
338 :
339 : #ifndef _WIN32
340 : /*-----------------------------------------------------------------
341 : * On Unix, make sure extension uses the right cases
342 : * We do it even for write access because if a file with the same
343 : * extension already exists we want to overwrite it.
344 : *----------------------------------------------------------------*/
345 40 : TABAdjustFilenameExtension(pszTmpFname);
346 : #endif
347 :
348 40 : m_poMIFFile = new MIDDATAFile;
349 :
350 40 : if (m_poMIFFile->Open(pszTmpFname, pszAccess) != 0)
351 : {
352 0 : if (!bTestOpenNoError)
353 : CPLError(CE_Failure, CPLE_NotSupported,
354 0 : "Unable to open %s.", pszTmpFname);
355 : else
356 0 : CPLErrorReset();
357 :
358 0 : CPLFree(pszTmpFname);
359 0 : Close();
360 :
361 0 : return -1;
362 : }
363 :
364 : /*-----------------------------------------------------------------
365 : * Open .MID file
366 : *----------------------------------------------------------------*/
367 40 : if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".MIF")==0)
368 0 : strcpy(pszTmpFname+nFnameLen-4, ".MID");
369 : else
370 40 : strcpy(pszTmpFname+nFnameLen-4, ".mid");
371 :
372 : #ifndef _WIN32
373 40 : TABAdjustFilenameExtension(pszTmpFname);
374 : #endif
375 :
376 40 : m_poMIDFile = new MIDDATAFile;
377 :
378 40 : if (m_poMIDFile->Open(pszTmpFname, pszAccess) !=0)
379 : {
380 0 : if (!bTestOpenNoError)
381 : CPLError(CE_Failure, CPLE_NotSupported,
382 0 : "Unable to open %s.", pszTmpFname);
383 : else
384 0 : CPLErrorReset();
385 :
386 0 : CPLFree(pszTmpFname);
387 0 : Close();
388 :
389 0 : return -1;
390 : }
391 :
392 :
393 40 : CPLFree(pszTmpFname);
394 40 : pszTmpFname = NULL;
395 :
396 : /*-----------------------------------------------------------------
397 : * Read MIF File Header
398 : *----------------------------------------------------------------*/
399 40 : if (m_eAccessMode == TABRead && ParseMIFHeader() != 0)
400 : {
401 0 : Close();
402 :
403 0 : if (!bTestOpenNoError)
404 : CPLError(CE_Failure, CPLE_NotSupported,
405 0 : "Failed parsing header in %s.", m_pszFname);
406 : else
407 0 : CPLErrorReset();
408 :
409 0 : return -1;
410 : }
411 :
412 : /*-----------------------------------------------------------------
413 : * In write access, set some defaults
414 : *----------------------------------------------------------------*/
415 40 : if (m_eAccessMode == TABWrite)
416 : {
417 16 : m_nVersion = 300;
418 16 : m_pszCharset = CPLStrdup("Neutral");
419 : }
420 :
421 : /* Put the MID file at the correct location, on the first feature */
422 40 : if (m_eAccessMode == TABRead && (m_poMIDFile->GetLine() == NULL))
423 : {
424 0 : Close();
425 :
426 0 : if (bTestOpenNoError)
427 0 : CPLErrorReset();
428 :
429 0 : return -1;
430 : }
431 :
432 : m_poMIFFile->SetTranslation(m_dfXMultiplier,m_dfYMultiplier,
433 40 : m_dfXDisplacement, m_dfYDisplacement);
434 : m_poMIDFile->SetTranslation(m_dfXMultiplier,m_dfYMultiplier,
435 40 : m_dfXDisplacement, m_dfYDisplacement);
436 40 : m_poMIFFile->SetDelimiter(m_pszDelimiter);
437 40 : m_poMIDFile->SetDelimiter(m_pszDelimiter);
438 :
439 : /*-------------------------------------------------------------
440 : * Set geometry type if the geometry objects are uniform.
441 : *------------------------------------------------------------*/
442 40 : int numPoints=0, numRegions=0, numTexts=0, numLines=0;
443 :
444 40 : if( GetFeatureCountByType( numPoints, numLines, numRegions, numTexts,
445 40 : FALSE ) == 0 )
446 : {
447 0 : numPoints += numTexts;
448 0 : if( numPoints > 0 && numLines == 0 && numRegions == 0 )
449 0 : m_poDefn->SetGeomType( wkbPoint );
450 0 : else if( numPoints == 0 && numLines > 0 && numRegions == 0 )
451 0 : m_poDefn->SetGeomType( wkbLineString );
452 : else
453 : /* we leave it unknown indicating a mixture */;
454 : }
455 :
456 : /* A newly created layer should have OGRFeatureDefn */
457 40 : if (m_poDefn == NULL)
458 : {
459 16 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
460 16 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
461 16 : CPLFree(pszFeatureClassName);
462 : // Ref count defaults to 0... set it to 1
463 16 : m_poDefn->Reference();
464 : }
465 :
466 40 : return 0;
467 : }
468 :
469 : /**********************************************************************
470 : * MIFFile::ParseMIFHeader()
471 : *
472 : * Scan the header of a MIF file, and store any useful information into
473 : * class members. The main piece of information being the fields
474 : * definition that we use to build the OGRFeatureDefn for this file.
475 : *
476 : * This private method should be used only during the Open() call.
477 : *
478 : * Returns 0 on success, -1 on error.
479 : **********************************************************************/
480 24 : int MIFFile::ParseMIFHeader()
481 : {
482 24 : GBool bColumns = FALSE, bAllColumnsRead = FALSE;
483 24 : int nColumns = 0;
484 24 : GBool bCoordSys = FALSE;
485 : char *pszTmp;
486 :
487 :
488 : const char *pszLine;
489 : char **papszToken;
490 :
491 24 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
492 24 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
493 24 : CPLFree(pszFeatureClassName);
494 : // Ref count defaults to 0... set it to 1
495 24 : m_poDefn->Reference();
496 :
497 :
498 24 : if (m_eAccessMode != TABRead)
499 : {
500 : CPLError(CE_Failure, CPLE_NotSupported,
501 0 : "ParseMIDFile() can be used only with Read access.");
502 0 : return -1;
503 : }
504 :
505 :
506 : /*-----------------------------------------------------------------
507 : * Parse header until we find the "Data" line
508 : *----------------------------------------------------------------*/
509 266 : while (((pszLine = m_poMIFFile->GetLine()) != NULL) &&
510 : ((bAllColumnsRead == FALSE) || !EQUALN(pszLine,"Data",4)))
511 : {
512 320 : if (bColumns == TRUE && nColumns >0)
513 : {
514 102 : if (AddFields(pszLine) == 0)
515 : {
516 102 : nColumns--;
517 102 : if (nColumns == 0)
518 : {
519 24 : bAllColumnsRead = TRUE;
520 24 : bColumns = FALSE;
521 : }
522 : }
523 : else
524 : {
525 0 : bColumns = FALSE;
526 : }
527 : }
528 116 : else if (EQUALN(pszLine,"VERSION",7))
529 : {
530 24 : papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE);
531 24 : bColumns = FALSE; bCoordSys = FALSE;
532 24 : if (CSLCount(papszToken) == 2)
533 24 : m_nVersion = atoi(papszToken[1]);
534 :
535 24 : CSLDestroy(papszToken);
536 :
537 : }
538 92 : else if (EQUALN(pszLine,"CHARSET",7))
539 : {
540 24 : papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE);
541 24 : bColumns = FALSE; bCoordSys = FALSE;
542 :
543 24 : if (CSLCount(papszToken) == 2)
544 : {
545 24 : CPLFree(m_pszCharset);
546 24 : m_pszCharset = CPLStrdup(papszToken[1]);
547 : }
548 24 : CSLDestroy(papszToken);
549 :
550 : }
551 68 : else if (EQUALN(pszLine,"DELIMITER",9))
552 : {
553 24 : papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE);
554 24 : bColumns = FALSE; bCoordSys = FALSE;
555 :
556 24 : if (CSLCount(papszToken) == 2)
557 : {
558 24 : CPLFree(m_pszDelimiter);
559 24 : m_pszDelimiter = CPLStrdup(papszToken[1]);
560 : }
561 24 : CSLDestroy(papszToken);
562 :
563 : }
564 44 : else if (EQUALN(pszLine,"UNIQUE",6))
565 : {
566 0 : bColumns = FALSE; bCoordSys = FALSE;
567 :
568 0 : m_pszUnique = CPLStrdup(pszLine + 6);
569 : }
570 44 : else if (EQUALN(pszLine,"INDEX",5))
571 : {
572 0 : bColumns = FALSE; bCoordSys = FALSE;
573 :
574 0 : m_pszIndex = CPLStrdup(pszLine + 5);
575 : }
576 44 : else if (EQUALN(pszLine,"COORDSYS",8) )
577 : {
578 10 : bCoordSys = TRUE;
579 10 : m_pszCoordSys = CPLStrdup(pszLine + 9);
580 :
581 : // Extract bounds if present
582 : char **papszFields;
583 : papszFields = CSLTokenizeStringComplex(m_pszCoordSys, " ,()\t",
584 10 : TRUE, FALSE );
585 10 : int iBounds = CSLFindString( papszFields, "Bounds" );
586 10 : if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
587 : {
588 10 : m_dXMin = atof(papszFields[++iBounds]);
589 10 : m_dYMin = atof(papszFields[++iBounds]);
590 10 : m_dXMax = atof(papszFields[++iBounds]);
591 10 : m_dYMax = atof(papszFields[++iBounds]);
592 10 : m_bBoundsSet = TRUE;
593 : }
594 10 : CSLDestroy( papszFields );
595 : }
596 34 : else if (EQUALN(pszLine,"TRANSFORM",9))
597 : {
598 0 : papszToken = CSLTokenizeStringComplex(pszLine," ,\t",TRUE,FALSE);
599 0 : bColumns = FALSE; bCoordSys = FALSE;
600 :
601 0 : if (CSLCount(papszToken) == 5)
602 : {
603 0 : m_dfXMultiplier = atof(papszToken[1]);
604 0 : m_dfYMultiplier = atof(papszToken[2]);
605 0 : m_dfXDisplacement = atof(papszToken[3]);
606 0 : m_dfYDisplacement = atof(papszToken[4]);
607 :
608 0 : if (m_dfXMultiplier == 0.0)
609 0 : m_dfXMultiplier = 1.0;
610 0 : if (m_dfYMultiplier == 0.0)
611 0 : m_dfYMultiplier = 1.0;
612 : }
613 0 : CSLDestroy(papszToken);
614 : }
615 34 : else if (EQUALN(pszLine,"COLUMNS",7))
616 : {
617 24 : papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE);
618 24 : bCoordSys = FALSE;
619 24 : bColumns = TRUE;
620 24 : if (CSLCount(papszToken) == 2)
621 : {
622 24 : nColumns = atoi(papszToken[1]);
623 24 : m_nAttribut = nColumns;
624 24 : if (nColumns == 0)
625 : {
626 : // Permit to 0 columns
627 0 : bAllColumnsRead = TRUE;
628 0 : bColumns = FALSE;
629 : }
630 : }
631 : else
632 : {
633 0 : bColumns = FALSE;
634 0 : m_nAttribut = 0;
635 : }
636 24 : CSLDestroy(papszToken);
637 : }
638 10 : else if (bCoordSys == TRUE)
639 : {
640 0 : pszTmp = m_pszCoordSys;
641 : m_pszCoordSys = CPLStrdup(CPLSPrintf("%s %s",m_pszCoordSys,
642 0 : pszLine));
643 0 : CPLFree(pszTmp);
644 : //printf("Reading CoordSys\n");
645 : // Reading CoordSys
646 : }
647 :
648 : }
649 :
650 24 : if (!bAllColumnsRead)
651 : {
652 : CPLError(CE_Failure, CPLE_NotSupported,
653 : "COLUMNS keyword not found or invalid number of columns read in %s. File may be corrupt.",
654 0 : m_pszFname);
655 0 : return -1;
656 : }
657 :
658 24 : if ((pszLine = m_poMIFFile->GetLastLine()) == NULL ||
659 : EQUALN(m_poMIFFile->GetLastLine(),"DATA",4) == FALSE)
660 : {
661 : CPLError(CE_Failure, CPLE_NotSupported,
662 : "DATA keyword not found in %s. File may be corrupt.",
663 0 : m_pszFname);
664 0 : return -1;
665 : }
666 :
667 : /*-----------------------------------------------------------------
668 : * Move pointer to first line of first object
669 : *----------------------------------------------------------------*/
670 38 : while (((pszLine = m_poMIFFile->GetLine()) != NULL) &&
671 : m_poMIFFile->IsValidFeature(pszLine) == FALSE)
672 : ;
673 :
674 : /*-----------------------------------------------------------------
675 : * Check for Unique and Indexed flags
676 : *----------------------------------------------------------------*/
677 24 : if (m_pszIndex)
678 : {
679 0 : papszToken = CSLTokenizeStringComplex(m_pszIndex," ,\t",TRUE,FALSE);
680 0 : for(int i=0; papszToken && papszToken[i]; i++)
681 : {
682 0 : int nVal = atoi(papszToken[i]);
683 0 : if (nVal > 0 && nVal <= m_poDefn->GetFieldCount())
684 0 : m_pabFieldIndexed[nVal-1] = TRUE;
685 : }
686 0 : CSLDestroy(papszToken);
687 : }
688 :
689 24 : if (m_pszUnique)
690 : {
691 0 : papszToken = CSLTokenizeStringComplex(m_pszUnique," ,\t",TRUE,FALSE);
692 0 : for(int i=0; papszToken && papszToken[i]; i++)
693 : {
694 0 : int nVal = atoi(papszToken[i]);
695 0 : if (nVal > 0 && nVal <= m_poDefn->GetFieldCount())
696 0 : m_pabFieldUnique[nVal-1] = TRUE;
697 : }
698 0 : CSLDestroy(papszToken);
699 : }
700 :
701 24 : return 0;
702 :
703 : }
704 :
705 : /************************************************************************/
706 : /* AddFields() */
707 : /************************************************************************/
708 :
709 102 : int MIFFile::AddFields(const char *pszLine)
710 : {
711 : char **papszToken;
712 102 : int nStatus = 0,numTok;
713 :
714 102 : CPLAssert(m_bHeaderWrote == FALSE);
715 102 : papszToken = CSLTokenizeStringComplex(pszLine," (,)\t",TRUE,FALSE);
716 102 : numTok = CSLCount(papszToken);
717 :
718 150 : if (numTok >= 3 && EQUAL(papszToken[1], "char"))
719 : {
720 : /*-------------------------------------------------
721 : * CHAR type
722 : *------------------------------------------------*/
723 48 : nStatus = AddFieldNative(papszToken[0], TABFChar,
724 96 : atoi(papszToken[2]));
725 : }
726 82 : else if (numTok >= 2 && EQUAL(papszToken[1], "integer"))
727 : {
728 28 : if (numTok == 2)
729 : {
730 : /*-------------------------------------------------
731 : * INTEGER type without a specified width
732 : *------------------------------------------------*/
733 20 : nStatus = AddFieldNative(papszToken[0], TABFInteger);
734 : }
735 8 : else if (numTok > 2)
736 : {
737 : /*-------------------------------------------------
738 : * INTEGER type with a specified width
739 : *------------------------------------------------*/
740 8 : nStatus = AddFieldNative(papszToken[0], TABFInteger, atoi(papszToken[2]));
741 : }
742 : }
743 26 : else if (numTok >= 2 && EQUAL(papszToken[1], "smallint"))
744 : {
745 0 : if (numTok == 2)
746 : {
747 : /*-------------------------------------------------
748 : * SMALLINT type without a specified width
749 : *------------------------------------------------*/
750 0 : nStatus = AddFieldNative(papszToken[0], TABFSmallInt);
751 : }
752 0 : else if (numTok > 2)
753 : {
754 : /*-------------------------------------------------
755 : * SMALLINT type with a specified width
756 : *------------------------------------------------*/
757 0 : nStatus = AddFieldNative(papszToken[0], TABFSmallInt, atoi(papszToken[2]));
758 : }
759 : }
760 28 : else if (numTok >= 4 && EQUAL(papszToken[1], "decimal"))
761 : {
762 : /*-------------------------------------------------
763 : * DECIMAL type
764 : *------------------------------------------------*/
765 2 : nStatus = AddFieldNative(papszToken[0], TABFDecimal,
766 4 : atoi(papszToken[2]), atoi(papszToken[3]));
767 : }
768 48 : else if (numTok >= 2 && EQUAL(papszToken[1], "float"))
769 : {
770 : /*-------------------------------------------------
771 : * FLOAT type
772 : *------------------------------------------------*/
773 24 : nStatus = AddFieldNative(papszToken[0], TABFFloat);
774 : }
775 0 : else if (numTok >= 2 && EQUAL(papszToken[1], "date"))
776 : {
777 : /*-------------------------------------------------
778 : * DATE type (returned as a string: "DD/MM/YYYY" or "YYYYMMDD")
779 : *------------------------------------------------*/
780 0 : nStatus = AddFieldNative(papszToken[0], TABFDate);
781 : }
782 0 : else if (numTok >= 2 && EQUAL(papszToken[1], "time"))
783 : {
784 : /*-------------------------------------------------
785 : * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm")
786 : *------------------------------------------------*/
787 0 : nStatus = AddFieldNative(papszToken[0], TABFTime);
788 : }
789 0 : else if (numTok >= 2 && EQUAL(papszToken[1], "datetime"))
790 : {
791 : /*-------------------------------------------------
792 : * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS",
793 : * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm")
794 : *------------------------------------------------*/
795 0 : nStatus = AddFieldNative(papszToken[0], TABFDateTime);
796 : }
797 0 : else if (numTok >= 2 && EQUAL(papszToken[1], "logical"))
798 : {
799 : /*-------------------------------------------------
800 : * LOGICAL type (value "T" or "F")
801 : *------------------------------------------------*/
802 0 : nStatus = AddFieldNative(papszToken[0], TABFLogical);
803 : }
804 : else
805 0 : nStatus = -1; // Unrecognized field type or line corrupt
806 :
807 102 : CSLDestroy(papszToken);
808 102 : papszToken = NULL;
809 :
810 102 : if (nStatus != 0)
811 : {
812 : CPLError(CE_Failure, CPLE_FileIO,
813 0 : "Failed to parse field definition in file %s", m_pszFname);
814 0 : return -1;
815 : }
816 :
817 102 : return 0;
818 : }
819 :
820 : /************************************************************************/
821 : /* GetFeatureCount() */
822 : /************************************************************************/
823 :
824 0 : int MIFFile::GetFeatureCount (int bForce)
825 : {
826 :
827 0 : if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
828 0 : return OGRLayer::GetFeatureCount( bForce );
829 : else
830 : {
831 0 : if (bForce == TRUE)
832 0 : PreParseFile();
833 :
834 0 : if (m_bPreParsed)
835 0 : return m_nFeatureCount;
836 : else
837 0 : return -1;
838 : }
839 : }
840 :
841 : /************************************************************************/
842 : /* ResetReading() */
843 : /************************************************************************/
844 :
845 32 : void MIFFile::ResetReading()
846 :
847 : {
848 : const char *pszLine;
849 :
850 32 : m_poMIFFile->Rewind();
851 :
852 32 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
853 224 : if (EQUALN(pszLine,"DATA",4))
854 32 : break;
855 :
856 192 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
857 : {
858 160 : if (m_poMIFFile->IsValidFeature(pszLine))
859 32 : break;
860 : }
861 :
862 32 : m_poMIDFile->Rewind();
863 32 : m_poMIDFile->GetLine();
864 :
865 : // We're positioned on first feature. Feature Ids start at 1.
866 32 : if (m_poCurFeature)
867 : {
868 2 : delete m_poCurFeature;
869 2 : m_poCurFeature = NULL;
870 : }
871 :
872 32 : m_nCurFeatureId = 0;
873 32 : m_nPreloadedId = 1;
874 32 : }
875 :
876 : /************************************************************************/
877 : /* PreParseFile() */
878 : /************************************************************************/
879 :
880 0 : void MIFFile::PreParseFile()
881 : {
882 0 : char **papszToken = NULL;
883 : const char *pszLine;
884 :
885 0 : GBool bPLine = FALSE;
886 0 : GBool bText = FALSE;
887 :
888 0 : if (m_bPreParsed == TRUE)
889 0 : return;
890 :
891 0 : m_poMIFFile->Rewind();
892 :
893 0 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
894 0 : if (EQUALN(pszLine,"DATA",4))
895 0 : break;
896 :
897 0 : m_nPoints = m_nLines = m_nRegions = m_nTexts = 0;
898 :
899 0 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
900 : {
901 0 : if (m_poMIFFile->IsValidFeature(pszLine))
902 : {
903 0 : bPLine = FALSE;
904 0 : bText = FALSE;
905 0 : m_nFeatureCount++;
906 : }
907 :
908 0 : CSLDestroy(papszToken);
909 0 : papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS);
910 :
911 0 : if (EQUALN(pszLine,"POINT",5))
912 : {
913 0 : m_nPoints++;
914 0 : if (CSLCount(papszToken) == 3)
915 : {
916 0 : UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[1])),
917 0 : m_poMIFFile->GetYTrans(atof(papszToken[2])));
918 : }
919 :
920 : }
921 0 : else if (EQUALN(pszLine,"LINE",4) ||
922 : EQUALN(pszLine,"RECT",4) ||
923 : EQUALN(pszLine,"ROUNDRECT",9) ||
924 : EQUALN(pszLine,"ARC",3) ||
925 : EQUALN(pszLine,"ELLIPSE",7))
926 : {
927 0 : if (CSLCount(papszToken) == 5)
928 : {
929 0 : m_nLines++;
930 0 : UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[1])),
931 0 : m_poMIFFile->GetYTrans(atof(papszToken[2])));
932 0 : UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[3])),
933 0 : m_poMIFFile->GetYTrans(atof(papszToken[4])));
934 : }
935 : }
936 0 : else if (EQUALN(pszLine,"REGION",6) )
937 : {
938 0 : m_nRegions++;
939 0 : bPLine = TRUE;
940 : }
941 0 : else if( EQUALN(pszLine,"PLINE",5))
942 : {
943 0 : m_nLines++;
944 0 : bPLine = TRUE;
945 : }
946 0 : else if (EQUALN(pszLine,"TEXT",4))
947 : {
948 0 : m_nTexts++;
949 0 : bText = TRUE;
950 : }
951 0 : else if (bPLine == TRUE)
952 : {
953 0 : if (CSLCount(papszToken) == 2 &&
954 0 : strchr("-.0123456789", papszToken[0][0]) != NULL)
955 : {
956 0 : UpdateExtents( m_poMIFFile->GetXTrans(atof(papszToken[0])),
957 0 : m_poMIFFile->GetYTrans(atof(papszToken[1])));
958 : }
959 : }
960 0 : else if (bText == TRUE)
961 : {
962 0 : if (CSLCount(papszToken) == 4 &&
963 0 : strchr("-.0123456789", papszToken[0][0]) != NULL)
964 : {
965 0 : UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[0])),
966 0 : m_poMIFFile->GetYTrans(atof(papszToken[1])));
967 0 : UpdateExtents(m_poMIFFile->GetXTrans(atof(papszToken[2])),
968 0 : m_poMIFFile->GetYTrans(atof(papszToken[3])));
969 : }
970 : }
971 :
972 : }
973 :
974 0 : CSLDestroy(papszToken);
975 :
976 0 : m_poMIFFile->Rewind();
977 :
978 0 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
979 0 : if (EQUALN(pszLine,"DATA",4))
980 0 : break;
981 :
982 0 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
983 : {
984 0 : if (m_poMIFFile->IsValidFeature(pszLine))
985 0 : break;
986 : }
987 :
988 0 : m_poMIDFile->Rewind();
989 0 : m_poMIDFile->GetLine();
990 :
991 0 : m_bPreParsed = TRUE;
992 :
993 : }
994 :
995 : /**********************************************************************
996 : * MIFFile::WriteMIFHeader()
997 : *
998 : * Generate the .MIF header.
999 : *
1000 : * Returns 0 on success, -1 on error.
1001 : **********************************************************************/
1002 16 : int MIFFile::WriteMIFHeader()
1003 : {
1004 : int iField;
1005 : GBool bFound;
1006 :
1007 16 : if (m_eAccessMode != TABWrite)
1008 : {
1009 : CPLError(CE_Failure, CPLE_NotSupported,
1010 0 : "WriteMIFHeader() can be used only with Write access.");
1011 0 : return -1;
1012 : }
1013 :
1014 16 : if (m_poDefn==NULL || m_poDefn->GetFieldCount() == 0)
1015 : {
1016 : CPLError(CE_Failure, CPLE_NotSupported,
1017 : "File %s must contain at least 1 attribute field.",
1018 0 : m_pszFname);
1019 0 : return -1;
1020 : }
1021 :
1022 : /*-----------------------------------------------------------------
1023 : * Start writing header.
1024 : *----------------------------------------------------------------*/
1025 16 : m_bHeaderWrote = TRUE;
1026 16 : m_poMIFFile->WriteLine("Version %d\n", m_nVersion);
1027 16 : m_poMIFFile->WriteLine("Charset \"%s\"\n", m_pszCharset);
1028 :
1029 : // Delimiter is not required if you use \t as delimiter
1030 16 : if ( !EQUAL(m_pszDelimiter, "\t") )
1031 16 : m_poMIFFile->WriteLine("Delimiter \"%s\"\n", m_pszDelimiter);
1032 :
1033 16 : bFound = FALSE;
1034 50 : for(iField=0; iField<m_poDefn->GetFieldCount(); iField++)
1035 : {
1036 34 : if (m_pabFieldUnique[iField])
1037 : {
1038 0 : if (!bFound)
1039 0 : m_poMIFFile->WriteLine("Unique %d", iField+1);
1040 : else
1041 0 : m_poMIFFile->WriteLine(",%d", iField+1);
1042 0 : bFound = TRUE;
1043 : }
1044 : }
1045 16 : if (bFound)
1046 0 : m_poMIFFile->WriteLine("\n");
1047 :
1048 16 : bFound = FALSE;
1049 50 : for(iField=0; iField<m_poDefn->GetFieldCount(); iField++)
1050 : {
1051 34 : if (m_pabFieldIndexed[iField])
1052 : {
1053 0 : if (!bFound)
1054 0 : m_poMIFFile->WriteLine("Index %d", iField+1);
1055 : else
1056 0 : m_poMIFFile->WriteLine(",%d", iField+1);
1057 0 : bFound = TRUE;
1058 : }
1059 : }
1060 16 : if (bFound)
1061 0 : m_poMIFFile->WriteLine("\n");
1062 :
1063 16 : if (m_pszCoordSys && m_bBoundsSet)
1064 : {
1065 : m_poMIFFile->WriteLine("CoordSys %s "
1066 : "Bounds (%.15g, %.15g) (%.15g, %.15g)\n",
1067 : m_pszCoordSys,
1068 0 : m_dXMin, m_dYMin, m_dXMax, m_dYMax);
1069 : }
1070 16 : else if (m_pszCoordSys)
1071 : {
1072 0 : m_poMIFFile->WriteLine("CoordSys %s\n",m_pszCoordSys);
1073 : }
1074 :
1075 : /*-----------------------------------------------------------------
1076 : * Column definitions
1077 : *----------------------------------------------------------------*/
1078 16 : CPLAssert(m_paeFieldType);
1079 :
1080 16 : m_poMIFFile->WriteLine("Columns %d\n", m_poDefn->GetFieldCount());
1081 :
1082 50 : for(iField=0; iField<m_poDefn->GetFieldCount(); iField++)
1083 : {
1084 : OGRFieldDefn *poFieldDefn;
1085 34 : poFieldDefn = m_poDefn->GetFieldDefn(iField);
1086 :
1087 34 : switch(m_paeFieldType[iField])
1088 : {
1089 : case TABFInteger:
1090 8 : if (poFieldDefn->GetWidth() == 0)
1091 : m_poMIFFile->WriteLine(" %s Integer\n",
1092 0 : poFieldDefn->GetNameRef());
1093 : else
1094 : m_poMIFFile->WriteLine(" %s Integer(%d)\n",
1095 : poFieldDefn->GetNameRef(),
1096 8 : poFieldDefn->GetWidth());
1097 8 : break;
1098 : case TABFSmallInt:
1099 : m_poMIFFile->WriteLine(" %s SmallInt\n",
1100 0 : poFieldDefn->GetNameRef());
1101 0 : break;
1102 : case TABFFloat:
1103 : m_poMIFFile->WriteLine(" %s Float\n",
1104 6 : poFieldDefn->GetNameRef());
1105 6 : break;
1106 : case TABFDecimal:
1107 : m_poMIFFile->WriteLine(" %s Decimal(%d,%d)\n",
1108 : poFieldDefn->GetNameRef(),
1109 : poFieldDefn->GetWidth(),
1110 2 : poFieldDefn->GetPrecision());
1111 2 : break;
1112 : case TABFLogical:
1113 : m_poMIFFile->WriteLine(" %s Logical\n",
1114 0 : poFieldDefn->GetNameRef());
1115 0 : break;
1116 : case TABFDate:
1117 : m_poMIFFile->WriteLine(" %s Date\n",
1118 0 : poFieldDefn->GetNameRef());
1119 0 : break;
1120 : case TABFTime:
1121 : m_poMIFFile->WriteLine(" %s Time\n",
1122 0 : poFieldDefn->GetNameRef());
1123 0 : break;
1124 : case TABFDateTime:
1125 : m_poMIFFile->WriteLine(" %s DateTime\n",
1126 0 : poFieldDefn->GetNameRef());
1127 0 : break;
1128 : case TABFChar:
1129 : default:
1130 : m_poMIFFile->WriteLine(" %s Char(%d)\n",
1131 : poFieldDefn->GetNameRef(),
1132 18 : poFieldDefn->GetWidth());
1133 : }
1134 : }
1135 :
1136 : /*-----------------------------------------------------------------
1137 : * Ready to write objects
1138 : *----------------------------------------------------------------*/
1139 16 : m_poMIFFile->WriteLine("Data\n\n");
1140 :
1141 16 : return 0;
1142 : }
1143 :
1144 : /**********************************************************************
1145 : * MIFFile::Close()
1146 : *
1147 : * Close current file, and release all memory used.
1148 : *
1149 : * Returns 0 on success, -1 on error.
1150 : **********************************************************************/
1151 40 : int MIFFile::Close()
1152 : {
1153 : /* flush .mif header if not already written */
1154 40 : if ( m_poDefn != NULL && m_bHeaderWrote == FALSE
1155 : && m_eAccessMode != TABRead )
1156 : {
1157 2 : WriteMIFHeader();
1158 : }
1159 :
1160 40 : if (m_poMIDFile)
1161 : {
1162 40 : m_poMIDFile->Close();
1163 40 : delete m_poMIDFile;
1164 40 : m_poMIDFile = NULL;
1165 : }
1166 :
1167 40 : if (m_poMIFFile)
1168 : {
1169 40 : m_poMIFFile->Close();
1170 40 : delete m_poMIFFile;
1171 40 : m_poMIFFile = NULL;
1172 : }
1173 :
1174 40 : if (m_poCurFeature)
1175 : {
1176 0 : delete m_poCurFeature;
1177 0 : m_poCurFeature = NULL;
1178 : }
1179 :
1180 : /*-----------------------------------------------------------------
1181 : * Note: we have to check the reference count before deleting
1182 : * m_poSpatialRef and m_poDefn
1183 : *----------------------------------------------------------------*/
1184 40 : if (m_poDefn && m_poDefn->Dereference() == 0)
1185 30 : delete m_poDefn;
1186 40 : m_poDefn = NULL;
1187 :
1188 40 : if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
1189 0 : delete m_poSpatialRef;
1190 40 : m_poSpatialRef = NULL;
1191 :
1192 40 : CPLFree(m_pszCoordSys);
1193 40 : m_pszCoordSys = NULL;
1194 :
1195 40 : CPLFree(m_pszDelimiter);
1196 40 : m_pszDelimiter = NULL;
1197 :
1198 40 : CPLFree(m_pszFname);
1199 40 : m_pszFname = NULL;
1200 :
1201 40 : m_nVersion = 0;
1202 :
1203 40 : CPLFree(m_pszCharset);
1204 40 : m_pszCharset = NULL;
1205 :
1206 40 : CPLFree(m_pabFieldIndexed);
1207 40 : m_pabFieldIndexed = NULL;
1208 40 : CPLFree(m_pabFieldUnique);
1209 40 : m_pabFieldUnique = NULL;
1210 :
1211 40 : CPLFree( m_pszIndex );
1212 40 : m_pszIndex = NULL;
1213 :
1214 40 : CPLFree(m_paeFieldType);
1215 40 : m_paeFieldType = NULL;
1216 :
1217 40 : m_nCurFeatureId = 0;
1218 40 : m_nPreloadedId = 0;
1219 40 : m_nFeatureCount =0;
1220 :
1221 40 : m_bBoundsSet = FALSE;
1222 :
1223 40 : return 0;
1224 : }
1225 :
1226 : /**********************************************************************
1227 : * MIFFile::GetNextFeatureId()
1228 : *
1229 : * Returns feature id that follows nPrevId, or -1 if it is the
1230 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
1231 : **********************************************************************/
1232 100 : int MIFFile::GetNextFeatureId(int nPrevId)
1233 : {
1234 100 : if (m_eAccessMode != TABRead)
1235 : {
1236 : CPLError(CE_Failure, CPLE_NotSupported,
1237 0 : "GetNextFeatureId() can be used only with Read access.");
1238 0 : return -1;
1239 : }
1240 :
1241 100 : if (nPrevId <= 0 && m_poMIFFile->GetLastLine() != NULL)
1242 24 : return 1; // Feature Ids start at 1
1243 76 : else if (nPrevId > 0 && m_poMIFFile->GetLastLine() != NULL)
1244 64 : return nPrevId + 1;
1245 : else
1246 12 : return -1;
1247 : }
1248 :
1249 : /**********************************************************************
1250 : * MIFFile::GotoFeature()
1251 : *
1252 : * Private method to move MIF and MID pointers ready to read specified
1253 : * feature. Note that Feature Ids start at 1.
1254 : *
1255 : * Returns 0 on success, -1 on error (likely request for invalid feature id)
1256 : **********************************************************************/
1257 88 : int MIFFile::GotoFeature(int nFeatureId)
1258 : {
1259 :
1260 88 : if (nFeatureId < 1)
1261 0 : return -1;
1262 :
1263 88 : if (nFeatureId == m_nPreloadedId) // CorrectPosition
1264 : {
1265 78 : return 0;
1266 : }
1267 : else
1268 : {
1269 10 : if (nFeatureId < m_nPreloadedId || m_nCurFeatureId == 0)
1270 10 : ResetReading();
1271 :
1272 20 : while(m_nPreloadedId < nFeatureId)
1273 : {
1274 0 : if (NextFeature() == FALSE)
1275 0 : return -1;
1276 : }
1277 :
1278 10 : CPLAssert(m_nPreloadedId == nFeatureId);
1279 :
1280 10 : return 0;
1281 : }
1282 : }
1283 :
1284 : /**********************************************************************
1285 : * MIFFile::NextFeature()
1286 : **********************************************************************/
1287 :
1288 0 : GBool MIFFile::NextFeature()
1289 : {
1290 : const char *pszLine;
1291 0 : while ((pszLine = m_poMIFFile->GetLine()) != NULL)
1292 : {
1293 0 : if (m_poMIFFile->IsValidFeature(pszLine))
1294 : {
1295 0 : m_poMIDFile->GetLine();
1296 0 : m_nPreloadedId++;
1297 0 : return TRUE;
1298 : }
1299 : }
1300 0 : return FALSE;
1301 : }
1302 :
1303 : /**********************************************************************
1304 : * MIFFile::GetFeatureRef()
1305 : *
1306 : * Fill and return a TABFeature object for the specified feature id.
1307 : *
1308 : * The retruned pointer is a reference to an object owned and maintained
1309 : * by this MIFFile object. It should not be altered or freed by the
1310 : * caller and its contents is guaranteed to be valid only until the next
1311 : * call to GetFeatureRef() or Close().
1312 : *
1313 : * Returns NULL if the specified feature id does not exist of if an
1314 : * error happened. In any case, CPLError() will have been called to
1315 : * report the reason of the failure.
1316 : **********************************************************************/
1317 88 : TABFeature *MIFFile::GetFeatureRef(int nFeatureId)
1318 : {
1319 : const char *pszLine;
1320 :
1321 88 : if (m_eAccessMode != TABRead)
1322 : {
1323 : CPLError(CE_Failure, CPLE_NotSupported,
1324 0 : "GetFeatureRef() can be used only with Read access.");
1325 0 : return NULL;
1326 : }
1327 :
1328 : /*-----------------------------------------------------------------
1329 : * Make sure file is opened and Validate feature id by positioning
1330 : * the read pointers for the .MAP and .DAT files to this feature id.
1331 : *----------------------------------------------------------------*/
1332 88 : if (m_poMIDFile == NULL)
1333 : {
1334 : CPLError(CE_Failure, CPLE_IllegalArg,
1335 0 : "GetFeatureRef() failed: file is not opened!");
1336 0 : return NULL;
1337 : }
1338 :
1339 88 : if (GotoFeature(nFeatureId)!= 0 )
1340 : {
1341 : CPLError(CE_Failure, CPLE_IllegalArg,
1342 : "GetFeatureRef() failed: invalid feature id %d",
1343 0 : nFeatureId);
1344 0 : return NULL;
1345 : }
1346 :
1347 :
1348 : /*-----------------------------------------------------------------
1349 : * Create new feature object of the right type
1350 : *----------------------------------------------------------------*/
1351 88 : if ((pszLine = m_poMIFFile->GetLastLine()) != NULL)
1352 : {
1353 : // Delete previous feature... we'll start we a clean one.
1354 88 : if (m_poCurFeature)
1355 10 : delete m_poCurFeature;
1356 88 : m_poCurFeature = NULL;
1357 :
1358 88 : m_nCurFeatureId = m_nPreloadedId;
1359 :
1360 88 : if (EQUALN(pszLine,"NONE",4))
1361 : {
1362 24 : m_poCurFeature = new TABFeature(m_poDefn);
1363 : }
1364 64 : else if (EQUALN(pszLine,"POINT",5))
1365 : {
1366 : // Special case, we need to know two lines to decide the type
1367 : char **papszToken;
1368 8 : papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS);
1369 :
1370 8 : if (CSLCount(papszToken) !=3)
1371 : {
1372 0 : CSLDestroy(papszToken);
1373 : CPLError(CE_Failure, CPLE_NotSupported,
1374 : "GetFeatureRef() failed: invalid point line: '%s'",
1375 0 : pszLine);
1376 0 : return NULL;
1377 : }
1378 :
1379 8 : m_poMIFFile->SaveLine(pszLine);
1380 :
1381 8 : if ((pszLine = m_poMIFFile->GetLine()) != NULL)
1382 : {
1383 8 : CSLDestroy(papszToken);
1384 : papszToken = CSLTokenizeStringComplex(pszLine," ,()\t",
1385 8 : TRUE,FALSE);
1386 8 : if (CSLCount(papszToken)> 0 &&EQUALN(papszToken[0],"SYMBOL",6))
1387 : {
1388 8 : switch (CSLCount(papszToken))
1389 : {
1390 : case 4:
1391 8 : m_poCurFeature = new TABPoint(m_poDefn);
1392 8 : break;
1393 : case 7:
1394 0 : m_poCurFeature = new TABFontPoint(m_poDefn);
1395 0 : break;
1396 : case 5:
1397 0 : m_poCurFeature = new TABCustomPoint(m_poDefn);
1398 0 : break;
1399 : default:
1400 0 : CSLDestroy(papszToken);
1401 : CPLError(CE_Failure, CPLE_NotSupported,
1402 : "GetFeatureRef() failed: invalid symbol "
1403 0 : "line: '%s'", pszLine);
1404 0 : return NULL;
1405 : break;
1406 : }
1407 :
1408 : }
1409 : }
1410 8 : CSLDestroy(papszToken);
1411 :
1412 8 : if (m_poCurFeature == NULL)
1413 : {
1414 : // No symbol clause... default to TABPoint
1415 0 : m_poCurFeature = new TABPoint(m_poDefn);
1416 : }
1417 : }
1418 56 : else if (EQUALN(pszLine,"LINE",4) ||
1419 : EQUALN(pszLine,"PLINE",5))
1420 : {
1421 0 : m_poCurFeature = new TABPolyline(m_poDefn);
1422 : }
1423 56 : else if (EQUALN(pszLine,"REGION",6))
1424 : {
1425 56 : m_poCurFeature = new TABRegion(m_poDefn);
1426 : }
1427 0 : else if (EQUALN(pszLine,"ARC",3))
1428 : {
1429 0 : m_poCurFeature = new TABArc(m_poDefn);
1430 : }
1431 0 : else if (EQUALN(pszLine,"TEXT",4))
1432 : {
1433 0 : m_poCurFeature = new TABText(m_poDefn);
1434 : }
1435 0 : else if (EQUALN(pszLine,"RECT",4) ||
1436 : EQUALN(pszLine,"ROUNDRECT",9))
1437 : {
1438 0 : m_poCurFeature = new TABRectangle(m_poDefn);
1439 : }
1440 0 : else if (EQUALN(pszLine,"ELLIPSE",7))
1441 : {
1442 0 : m_poCurFeature = new TABEllipse(m_poDefn);
1443 : }
1444 0 : else if (EQUALN(pszLine,"MULTIPOINT",10))
1445 : {
1446 0 : m_poCurFeature = new TABMultiPoint(m_poDefn);
1447 : }
1448 0 : else if (EQUALN(pszLine,"COLLECTION",10))
1449 : {
1450 0 : m_poCurFeature = new TABCollection(m_poDefn);
1451 : }
1452 : else
1453 : {
1454 0 : if (!EQUAL(pszLine,""))
1455 : CPLError(CE_Failure, CPLE_NotSupported,
1456 : "Error during reading, unknown type %s.",
1457 0 : pszLine);
1458 :
1459 : //m_poCurFeature = new TABDebugFeature(m_poDefn);
1460 0 : return NULL;
1461 : }
1462 : }
1463 :
1464 88 : CPLAssert(m_poCurFeature);
1465 88 : if (m_poCurFeature == NULL)
1466 0 : return NULL;
1467 :
1468 : /*-----------------------------------------------------------------
1469 : * Read fields from the .DAT file
1470 : * GetRecordBlock() has already been called above...
1471 : *----------------------------------------------------------------*/
1472 88 : if (m_poCurFeature->ReadRecordFromMIDFile(m_poMIDFile) != 0)
1473 : {
1474 : CPLError(CE_Failure, CPLE_NotSupported,
1475 0 : "Error during reading Record.");
1476 :
1477 0 : delete m_poCurFeature;
1478 0 : m_poCurFeature = NULL;
1479 0 : return NULL;
1480 : }
1481 :
1482 : /*-----------------------------------------------------------------
1483 : * Read geometry from the .MAP file
1484 : * MoveToObjId() has already been called above...
1485 : *----------------------------------------------------------------*/
1486 88 : if (m_poCurFeature->ReadGeometryFromMIFFile(m_poMIFFile) != 0)
1487 : {
1488 : CPLError(CE_Failure, CPLE_NotSupported,
1489 0 : "Error during reading Geometry.");
1490 :
1491 0 : delete m_poCurFeature;
1492 0 : m_poCurFeature = NULL;
1493 0 : return NULL;
1494 : }
1495 :
1496 : /* If the feature geometry is Text, and the value is empty(""), transform
1497 : it to a geometry none */
1498 88 : if (m_poCurFeature->GetFeatureClass() == TABFCText)
1499 : {
1500 : TABFeature *poTmpFeature;
1501 0 : TABText *poTextFeature = (TABText*)m_poCurFeature;
1502 0 : if (strlen(poTextFeature->GetTextString()) == 0)
1503 : {
1504 0 : poTmpFeature = new TABFeature(m_poDefn);
1505 0 : for( int i = 0; i < m_poDefn->GetFieldCount(); i++ )
1506 : {
1507 0 : poTmpFeature->SetField( i, m_poCurFeature->GetRawFieldRef( i ) );
1508 : }
1509 0 : delete m_poCurFeature;
1510 0 : m_poCurFeature = poTmpFeature;
1511 : }
1512 : }
1513 :
1514 : /*---------------------------------------------------------------------
1515 : * The act of reading the geometry causes the first line of the
1516 : * next object to be preloaded. Set the preloaded id appropriately.
1517 : *--------------------------------------------------------------------- */
1518 88 : if( m_poMIFFile->GetLastLine() != NULL )
1519 64 : m_nPreloadedId++;
1520 : else
1521 24 : m_nPreloadedId = 0;
1522 :
1523 : /* Update the Current Feature ID */
1524 88 : m_poCurFeature->SetFID(m_nCurFeatureId);
1525 :
1526 88 : return m_poCurFeature;
1527 : }
1528 :
1529 : /**********************************************************************
1530 : * MIFFile::CreateFeature()
1531 : *
1532 : * Write a new feature to this dataset. The passed in feature is updated
1533 : * with the new feature id.
1534 : *
1535 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
1536 : * error happened in which case, CPLError() will have been called to
1537 : * report the reason of the failure.
1538 : **********************************************************************/
1539 42 : OGRErr MIFFile::CreateFeature(TABFeature *poFeature)
1540 : {
1541 42 : int nFeatureId = -1;
1542 :
1543 42 : if (m_eAccessMode != TABWrite)
1544 : {
1545 : CPLError(CE_Failure, CPLE_NotSupported,
1546 0 : "CreateFeature() can be used only with Write access.");
1547 0 : return OGRERR_UNSUPPORTED_OPERATION;
1548 : }
1549 :
1550 : /*-----------------------------------------------------------------
1551 : * Make sure file is opened and establish new feature id.
1552 : *----------------------------------------------------------------*/
1553 42 : if (m_poMIDFile == NULL)
1554 : {
1555 : CPLError(CE_Failure, CPLE_IllegalArg,
1556 0 : "CreateFeature() failed: file is not opened!");
1557 0 : return OGRERR_FAILURE;
1558 : }
1559 :
1560 42 : if (m_bHeaderWrote == FALSE)
1561 : {
1562 : /*-------------------------------------------------------------
1563 : * OK, this is the first feature in the dataset... make sure the
1564 : * .MID schema has been initialized.
1565 : *------------------------------------------------------------*/
1566 14 : if (m_poDefn == NULL)
1567 0 : SetFeatureDefn(poFeature->GetDefnRef(), NULL);
1568 :
1569 14 : WriteMIFHeader();
1570 14 : nFeatureId = 1;
1571 : }
1572 : else
1573 : {
1574 28 : nFeatureId = ++ m_nWriteFeatureId;
1575 : }
1576 :
1577 :
1578 : /*-----------------------------------------------------------------
1579 : * Write geometry to the .Mif file
1580 : *----------------------------------------------------------------*/
1581 84 : if (m_poMIFFile == NULL ||
1582 42 : poFeature->WriteGeometryToMIFFile(m_poMIFFile) != 0)
1583 : {
1584 : CPLError(CE_Failure, CPLE_FileIO,
1585 : "Failed writing geometry for feature id %d in %s",
1586 0 : nFeatureId, m_pszFname);
1587 0 : return OGRERR_FAILURE;
1588 : }
1589 :
1590 84 : if (m_poMIDFile == NULL ||
1591 42 : poFeature->WriteRecordToMIDFile(m_poMIDFile) != 0 )
1592 : {
1593 : CPLError(CE_Failure, CPLE_FileIO,
1594 : "Failed writing attributes for feature id %d in %s",
1595 0 : nFeatureId, m_pszFname);
1596 0 : return OGRERR_FAILURE;
1597 : }
1598 :
1599 42 : poFeature->SetFID(nFeatureId);
1600 :
1601 42 : return OGRERR_NONE;
1602 : }
1603 :
1604 :
1605 :
1606 : /**********************************************************************
1607 : * MIFFile::GetLayerDefn()
1608 : *
1609 : * Returns a reference to the OGRFeatureDefn that will be used to create
1610 : * features in this dataset.
1611 : *
1612 : * Returns a reference to an object that is maintained by this MIFFile
1613 : * object (and thus should not be modified or freed by the caller) or
1614 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
1615 : * opened yet)
1616 : **********************************************************************/
1617 324 : OGRFeatureDefn *MIFFile::GetLayerDefn()
1618 : {
1619 324 : return m_poDefn;
1620 : }
1621 :
1622 : /**********************************************************************
1623 : * MIFFile::SetFeatureDefn()
1624 : *
1625 : * Pass a reference to the OGRFeatureDefn that will be used to create
1626 : * features in this dataset. This function should be called after
1627 : * creating a new dataset, but before writing the first feature.
1628 : * All features that will be written to this dataset must share this same
1629 : * OGRFeatureDefn.
1630 : *
1631 : * This function will use poFeatureDefn to create a local copy that
1632 : * will be used to build the .MID file, etc.
1633 : *
1634 : * Returns 0 on success, -1 on error.
1635 : **********************************************************************/
1636 0 : int MIFFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
1637 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
1638 : {
1639 : int numFields;
1640 0 : int nStatus = 0;
1641 :
1642 : /*-----------------------------------------------------------------
1643 : * Check that call happens at the right time in dataset's life.
1644 : *----------------------------------------------------------------*/
1645 0 : if ( m_eAccessMode == TABWrite && m_bHeaderWrote )
1646 : {
1647 : CPLError(CE_Failure, CPLE_AssertionFailed,
1648 : "SetFeatureDefn() must be called after opening a new "
1649 0 : "dataset, but before writing the first feature to it.");
1650 0 : return -1;
1651 : }
1652 :
1653 : /*-----------------------------------------------------------------
1654 : * Delete current feature defn if there is already one.
1655 : * AddFieldNative() will take care of creating a new one for us.
1656 : *----------------------------------------------------------------*/
1657 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1658 0 : delete m_poDefn;
1659 0 : m_poDefn = NULL;
1660 :
1661 : /*-----------------------------------------------------------------
1662 : * Copy field information
1663 : *----------------------------------------------------------------*/
1664 0 : numFields = poFeatureDefn->GetFieldCount();
1665 :
1666 0 : for(int iField=0; iField<numFields; iField++)
1667 : {
1668 : TABFieldType eMapInfoType;
1669 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1670 :
1671 0 : if (paeMapInfoNativeFieldTypes)
1672 : {
1673 0 : eMapInfoType = paeMapInfoNativeFieldTypes[iField];
1674 : }
1675 : else
1676 : {
1677 : /*---------------------------------------------------------
1678 : * Map OGRFieldTypes to MapInfo native types
1679 : *--------------------------------------------------------*/
1680 0 : switch(poFieldDefn->GetType())
1681 : {
1682 : case OFTInteger:
1683 0 : eMapInfoType = TABFInteger;
1684 0 : break;
1685 : case OFTReal:
1686 0 : eMapInfoType = TABFFloat;
1687 0 : break;
1688 : case OFTDateTime:
1689 0 : eMapInfoType = TABFDateTime;
1690 0 : break;
1691 : case OFTDate:
1692 0 : eMapInfoType = TABFDate;
1693 0 : break;
1694 : case OFTTime:
1695 0 : eMapInfoType = TABFTime;
1696 0 : break;
1697 : case OFTString:
1698 : default:
1699 0 : eMapInfoType = TABFChar;
1700 : }
1701 : }
1702 :
1703 : nStatus = AddFieldNative(poFieldDefn->GetNameRef(), eMapInfoType,
1704 : poFieldDefn->GetWidth(),
1705 0 : poFieldDefn->GetPrecision(), FALSE, FALSE);
1706 : }
1707 :
1708 0 : return nStatus;
1709 : }
1710 :
1711 : /**********************************************************************
1712 : * MIFFile::AddFieldNative()
1713 : *
1714 : * Create a new field using a native mapinfo data type... this is an
1715 : * alternative to defining fields through the OGR interface.
1716 : * This function should be called after creating a new dataset, but before
1717 : * writing the first feature.
1718 : *
1719 : * This function will build/update the OGRFeatureDefn that will have to be
1720 : * used when writing features to this dataset.
1721 : *
1722 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1723 : *
1724 : * Returns 0 on success, -1 on error.
1725 : **********************************************************************/
1726 136 : int MIFFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1727 : int nWidth /*=0*/, int nPrecision /*=0*/,
1728 : GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK )
1729 : {
1730 : OGRFieldDefn *poFieldDefn;
1731 136 : char *pszCleanName = NULL;
1732 136 : int nStatus = 0;
1733 : char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/
1734 136 : int nRenameNum = 1;
1735 :
1736 : /*-----------------------------------------------------------------
1737 : * Check that call happens at the right time in dataset's life.
1738 : *----------------------------------------------------------------*/
1739 136 : if ( m_eAccessMode == TABWrite && m_bHeaderWrote )
1740 : {
1741 : CPLError(CE_Failure, CPLE_AssertionFailed,
1742 : "AddFieldNative() must be called after opening a new "
1743 0 : "dataset, but before writing the first feature to it.");
1744 0 : return -1;
1745 : }
1746 :
1747 : /*-----------------------------------------------------------------
1748 : * Validate field width... must be <= 254
1749 : *----------------------------------------------------------------*/
1750 136 : if (nWidth > 254)
1751 : {
1752 : CPLError(CE_Warning, CPLE_IllegalArg,
1753 : "Invalid size (%d) for field '%s'. "
1754 0 : "Size must be 254 or less.", nWidth, pszName);
1755 0 : nWidth = 254;
1756 : }
1757 :
1758 : /*-----------------------------------------------------------------
1759 : * Map fields with width=0 (variable length in OGR) to a valid default
1760 : *----------------------------------------------------------------*/
1761 136 : if (eMapInfoType == TABFDecimal && nWidth == 0)
1762 0 : nWidth=20;
1763 136 : else if (nWidth == 0)
1764 44 : nWidth=254; /* char fields */
1765 :
1766 : /*-----------------------------------------------------------------
1767 : * Create new OGRFeatureDefn if not done yet...
1768 : *----------------------------------------------------------------*/
1769 136 : if (m_poDefn == NULL)
1770 : {
1771 0 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
1772 0 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
1773 0 : CPLFree(pszFeatureClassName);
1774 : // Ref count defaults to 0... set it to 1
1775 0 : m_poDefn->Reference();
1776 : }
1777 :
1778 : /*-----------------------------------------------------------------
1779 : * Make sure field name is valid... check for special chars, etc.
1780 : * (pszCleanName will have to be freed.)
1781 : *----------------------------------------------------------------*/
1782 136 : pszCleanName = TABCleanFieldName(pszName);
1783 :
1784 136 : if( !bApproxOK &&
1785 : ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 ||
1786 : !EQUAL(pszName, pszCleanName) ) )
1787 : {
1788 : CPLError( CE_Failure, CPLE_NotSupported,
1789 : "Failed to add field named '%s'",
1790 0 : pszName );
1791 : }
1792 :
1793 136 : strncpy(szNewFieldName, pszCleanName, 31);
1794 136 : szNewFieldName[31] = '\0';
1795 :
1796 272 : while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10)
1797 0 : sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ );
1798 :
1799 272 : while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100)
1800 0 : sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ );
1801 :
1802 136 : if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0)
1803 : {
1804 : CPLError( CE_Failure, CPLE_NotSupported,
1805 : "Too many field names like '%s' when truncated to 31 letters "
1806 0 : "for MapInfo format.", pszCleanName );
1807 : }
1808 :
1809 136 : if( !EQUAL(pszCleanName,szNewFieldName) )
1810 : {
1811 : CPLError( CE_Warning, CPLE_NotSupported,
1812 : "Normalized/laundered field name: '%s' to '%s'",
1813 : pszCleanName,
1814 0 : szNewFieldName );
1815 : }
1816 :
1817 :
1818 : /*-----------------------------------------------------------------
1819 : * Map MapInfo native types to OGR types
1820 : *----------------------------------------------------------------*/
1821 136 : poFieldDefn = NULL;
1822 :
1823 136 : switch(eMapInfoType)
1824 : {
1825 : case TABFChar:
1826 : /*-------------------------------------------------
1827 : * CHAR type
1828 : *------------------------------------------------*/
1829 66 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
1830 66 : poFieldDefn->SetWidth(nWidth);
1831 66 : break;
1832 : case TABFInteger:
1833 : /*-------------------------------------------------
1834 : * INTEGER type
1835 : *------------------------------------------------*/
1836 36 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
1837 36 : poFieldDefn->SetWidth(nWidth);
1838 36 : break;
1839 : case TABFSmallInt:
1840 : /*-------------------------------------------------
1841 : * SMALLINT type
1842 : *------------------------------------------------*/
1843 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
1844 0 : poFieldDefn->SetWidth(nWidth);
1845 0 : break;
1846 : case TABFDecimal:
1847 : /*-------------------------------------------------
1848 : * DECIMAL type
1849 : *------------------------------------------------*/
1850 4 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
1851 4 : poFieldDefn->SetWidth(nWidth);
1852 4 : poFieldDefn->SetPrecision(nPrecision);
1853 4 : break;
1854 : case TABFFloat:
1855 : /*-------------------------------------------------
1856 : * FLOAT type
1857 : *------------------------------------------------*/
1858 30 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
1859 30 : break;
1860 : case TABFDate:
1861 : /*-------------------------------------------------
1862 : * DATE type (V450, returned as a string: "DD/MM/YYYY" or "YYYYMMDD")
1863 : *------------------------------------------------*/
1864 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1865 : #ifdef MITAB_USE_OFTDATETIME
1866 0 : OFTDate);
1867 : #else
1868 : OFTString);
1869 : #endif
1870 0 : poFieldDefn->SetWidth(10);
1871 0 : m_nVersion = MAX(m_nVersion, 450);
1872 0 : break;
1873 : case TABFTime:
1874 : /*-------------------------------------------------
1875 : * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm")
1876 : *------------------------------------------------*/
1877 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1878 : #ifdef MITAB_USE_OFTDATETIME
1879 0 : OFTTime);
1880 : #else
1881 : OFTString);
1882 : #endif
1883 0 : poFieldDefn->SetWidth(9);
1884 0 : m_nVersion = MAX(m_nVersion, 900);
1885 0 : break;
1886 : case TABFDateTime:
1887 : /*-------------------------------------------------
1888 : * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS",
1889 : * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm")
1890 : *------------------------------------------------*/
1891 : poFieldDefn = new OGRFieldDefn(szNewFieldName,
1892 : #ifdef MITAB_USE_OFTDATETIME
1893 0 : OFTDateTime);
1894 : #else
1895 : OFTString);
1896 : #endif
1897 0 : poFieldDefn->SetWidth(19);
1898 0 : m_nVersion = MAX(m_nVersion, 900);
1899 0 : break;
1900 : case TABFLogical:
1901 : /*-------------------------------------------------
1902 : * LOGICAL type (value "T" or "F")
1903 : *------------------------------------------------*/
1904 0 : poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
1905 0 : poFieldDefn->SetWidth(1);
1906 0 : break;
1907 : default:
1908 : CPLError(CE_Failure, CPLE_NotSupported,
1909 0 : "Unsupported type for field %s", pszName);
1910 0 : return -1;
1911 : }
1912 :
1913 : /*-----------------------------------------------------
1914 : * Add the FieldDefn to the FeatureDefn
1915 : *----------------------------------------------------*/
1916 136 : m_poDefn->AddFieldDefn(poFieldDefn);
1917 136 : delete poFieldDefn;
1918 :
1919 : /*-----------------------------------------------------------------
1920 : * Keep track of native field type
1921 : *----------------------------------------------------------------*/
1922 : m_paeFieldType = (TABFieldType *)CPLRealloc(m_paeFieldType,
1923 : m_poDefn->GetFieldCount()*
1924 136 : sizeof(TABFieldType));
1925 136 : m_paeFieldType[m_poDefn->GetFieldCount()-1] = eMapInfoType;
1926 :
1927 : /*-----------------------------------------------------------------
1928 : * Extend array of Indexed/Unique flags
1929 : *----------------------------------------------------------------*/
1930 : m_pabFieldIndexed = (GBool *)CPLRealloc(m_pabFieldIndexed,
1931 : m_poDefn->GetFieldCount()*
1932 136 : sizeof(GBool));
1933 : m_pabFieldUnique = (GBool *)CPLRealloc(m_pabFieldUnique,
1934 : m_poDefn->GetFieldCount()*
1935 136 : sizeof(GBool));
1936 136 : m_pabFieldIndexed[m_poDefn->GetFieldCount()-1] = bIndexed;
1937 136 : m_pabFieldUnique[m_poDefn->GetFieldCount()-1] = bUnique;
1938 :
1939 136 : CPLFree(pszCleanName);
1940 136 : return nStatus;
1941 : }
1942 :
1943 :
1944 : /**********************************************************************
1945 : * MIFFile::GetNativeFieldType()
1946 : *
1947 : * Returns the native MapInfo field type for the specified field.
1948 : *
1949 : * Returns TABFUnknown if file is not opened, or if specified field index is
1950 : * invalid.
1951 : **********************************************************************/
1952 0 : TABFieldType MIFFile::GetNativeFieldType(int nFieldId)
1953 : {
1954 0 : if ( m_poDefn==NULL || m_paeFieldType==NULL ||
1955 : nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount())
1956 0 : return TABFUnknown;
1957 :
1958 0 : return m_paeFieldType[nFieldId];
1959 : }
1960 :
1961 : /************************************************************************
1962 : * MIFFile::SetFieldIndexed()
1963 : ************************************************************************/
1964 :
1965 0 : int MIFFile::SetFieldIndexed( int nFieldId )
1966 :
1967 : {
1968 0 : if ( m_poDefn==NULL || m_pabFieldIndexed==NULL ||
1969 : nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount())
1970 0 : return -1;
1971 :
1972 0 : m_pabFieldIndexed[nFieldId] = TRUE;
1973 :
1974 0 : return 0;
1975 : }
1976 :
1977 : /************************************************************************
1978 : * MIFFile::IsFieldIndexed()
1979 : ************************************************************************/
1980 :
1981 0 : GBool MIFFile::IsFieldIndexed( int nFieldId )
1982 :
1983 : {
1984 0 : if ( m_poDefn==NULL || m_pabFieldIndexed==NULL ||
1985 : nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount())
1986 0 : return FALSE;
1987 :
1988 0 : return m_pabFieldIndexed[nFieldId];
1989 : }
1990 :
1991 : /************************************************************************
1992 : * MIFFile::IsFieldUnique()
1993 : ************************************************************************/
1994 :
1995 0 : GBool MIFFile::IsFieldUnique( int nFieldId )
1996 :
1997 : {
1998 0 : if ( m_poDefn==NULL || m_pabFieldUnique==NULL ||
1999 : nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount())
2000 0 : return FALSE;
2001 :
2002 0 : return m_pabFieldUnique[nFieldId];
2003 : }
2004 :
2005 :
2006 : /************************************************************************/
2007 : /* MIFFile::SetSpatialRef() */
2008 : /************************************************************************/
2009 :
2010 0 : int MIFFile::SetSpatialRef( OGRSpatialReference * poSpatialRef )
2011 :
2012 : {
2013 0 : CPLFree( m_pszCoordSys );
2014 :
2015 0 : m_pszCoordSys = MITABSpatialRef2CoordSys( poSpatialRef );
2016 :
2017 0 : return( m_pszCoordSys != NULL );
2018 : }
2019 :
2020 :
2021 : /************************************************************************/
2022 : /* MIFFile::SetMIFCoordSys() */
2023 : /************************************************************************/
2024 :
2025 0 : int MIFFile::SetMIFCoordSys(const char * pszMIFCoordSys)
2026 :
2027 : {
2028 : char **papszFields, *pszCoordSys;
2029 : int iBounds;
2030 :
2031 : // Extract the word 'COORDSYS' if present
2032 0 : if (EQUALN(pszMIFCoordSys,"COORDSYS",8) )
2033 : {
2034 0 : pszCoordSys = CPLStrdup(pszMIFCoordSys + 9);
2035 : }
2036 : else
2037 : {
2038 0 : pszCoordSys = CPLStrdup(pszMIFCoordSys);
2039 : }
2040 :
2041 : // Extract bounds if present
2042 : papszFields = CSLTokenizeStringComplex(pszCoordSys, " ,()\t",
2043 0 : TRUE, FALSE );
2044 0 : iBounds = CSLFindString( papszFields, "Bounds" );
2045 0 : if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields))
2046 : {
2047 0 : m_dXMin = atof(papszFields[++iBounds]);
2048 0 : m_dYMin = atof(papszFields[++iBounds]);
2049 0 : m_dXMax = atof(papszFields[++iBounds]);
2050 0 : m_dYMax = atof(papszFields[++iBounds]);
2051 0 : m_bBoundsSet = TRUE;
2052 :
2053 0 : pszCoordSys[strstr(pszCoordSys, "Bounds") - pszCoordSys] = '\0';
2054 : }
2055 0 : CSLDestroy( papszFields );
2056 :
2057 : // Assign the CoordSys
2058 0 : CPLFree( m_pszCoordSys );
2059 :
2060 0 : m_pszCoordSys = CPLStrdup(pszCoordSys);
2061 0 : CPLFree(pszCoordSys);
2062 :
2063 0 : return( m_pszCoordSys != NULL );
2064 : }
2065 :
2066 : /************************************************************************/
2067 : /* MIFFile::GetSpatialRef() */
2068 : /************************************************************************/
2069 :
2070 2 : OGRSpatialReference *MIFFile::GetSpatialRef()
2071 :
2072 : {
2073 2 : if( m_poSpatialRef == NULL )
2074 2 : m_poSpatialRef = MITABCoordSys2SpatialRef( m_pszCoordSys );
2075 :
2076 2 : return m_poSpatialRef;
2077 : }
2078 :
2079 : /**********************************************************************
2080 : * MIFFile::UpdateExtents()
2081 : *
2082 : * Private Methode used to update the dataset extents
2083 : **********************************************************************/
2084 0 : void MIFFile::UpdateExtents(double dfX, double dfY)
2085 : {
2086 0 : if (m_bExtentsSet == FALSE)
2087 : {
2088 0 : m_bExtentsSet = TRUE;
2089 0 : m_sExtents.MinX = m_sExtents.MaxX = dfX;
2090 0 : m_sExtents.MinY = m_sExtents.MaxY = dfY;
2091 : }
2092 : else
2093 : {
2094 0 : if (dfX < m_sExtents.MinX)
2095 0 : m_sExtents.MinX = dfX;
2096 0 : if (dfX > m_sExtents.MaxX)
2097 0 : m_sExtents.MaxX = dfX;
2098 0 : if (dfY < m_sExtents.MinY)
2099 0 : m_sExtents.MinY = dfY;
2100 0 : if (dfY > m_sExtents.MaxY)
2101 0 : m_sExtents.MaxY = dfY;
2102 : }
2103 0 : }
2104 :
2105 : /**********************************************************************
2106 : * MIFFile::SetBounds()
2107 : *
2108 : * Set projection coordinates bounds of the newly created dataset.
2109 : *
2110 : * This function must be called after creating a new dataset and before any
2111 : * feature can be written to it.
2112 : *
2113 : * Returns 0 on success, -1 on error.
2114 : **********************************************************************/
2115 0 : int MIFFile::SetBounds(double dXMin, double dYMin,
2116 : double dXMax, double dYMax)
2117 : {
2118 0 : if (m_eAccessMode != TABWrite)
2119 : {
2120 : CPLError(CE_Failure, CPLE_NotSupported,
2121 0 : "SetBounds() can be used only with Write access.");
2122 0 : return -1;
2123 : }
2124 :
2125 0 : m_dXMin = dXMin;
2126 0 : m_dXMax = dXMax;
2127 0 : m_dYMin = dYMin;
2128 0 : m_dYMax = dYMax;
2129 0 : m_bBoundsSet = TRUE;
2130 :
2131 0 : return 0;
2132 : }
2133 :
2134 :
2135 : /**********************************************************************
2136 : * MIFFile::GetFeatureCountByType()
2137 : *
2138 : * Return number of features of each type.
2139 : *
2140 : * NOTE: The current implementation always returns -1 for MIF files
2141 : * since this would require scanning the whole file.
2142 : *
2143 : * When properly implemented, the bForce flag will force scanning the
2144 : * whole file by default.
2145 : *
2146 : * Returns 0 on success, or silently returns -1 (with no error) if this
2147 : * information is not available.
2148 : **********************************************************************/
2149 40 : int MIFFile::GetFeatureCountByType(int &numPoints, int &numLines,
2150 : int &numRegions, int &numTexts,
2151 : GBool bForce )
2152 : {
2153 40 : if( m_bPreParsed || bForce )
2154 : {
2155 0 : PreParseFile();
2156 :
2157 0 : numPoints = m_nPoints;
2158 0 : numLines = m_nLines;
2159 0 : numRegions = m_nRegions;
2160 0 : numTexts = m_nTexts;
2161 0 : return 0;
2162 : }
2163 : else
2164 : {
2165 40 : numPoints = numLines = numRegions = numTexts = 0;
2166 40 : return -1;
2167 : }
2168 : }
2169 :
2170 : /**********************************************************************
2171 : * MIFFile::GetBounds()
2172 : *
2173 : * Fetch projection coordinates bounds of a dataset.
2174 : *
2175 : * Pass bForce=FALSE to avoid a scan of the whole file if the bounds
2176 : * are not already available.
2177 : *
2178 : * Returns 0 on success, -1 on error or if bounds are not available and
2179 : * bForce=FALSE.
2180 : **********************************************************************/
2181 0 : int MIFFile::GetBounds(double &dXMin, double &dYMin,
2182 : double &dXMax, double &dYMax,
2183 : GBool bForce /*= TRUE*/ )
2184 : {
2185 :
2186 0 : if (m_bBoundsSet == FALSE && bForce == FALSE)
2187 : {
2188 0 : return -1;
2189 : }
2190 0 : else if (m_bBoundsSet == FALSE)
2191 : {
2192 0 : PreParseFile();
2193 : }
2194 :
2195 0 : if (m_bBoundsSet == FALSE)
2196 : {
2197 0 : return -1;
2198 : }
2199 :
2200 0 : dXMin = m_dXMin;
2201 0 : dXMax = m_dXMax;
2202 0 : dYMin = m_dYMin;
2203 0 : dYMax = m_dYMax;
2204 :
2205 0 : return 0;
2206 : }
2207 :
2208 : /**********************************************************************
2209 : * MIFFile::GetExtent()
2210 : *
2211 : * Fetch extent of the data currently stored in the dataset. We collect
2212 : * this information while preparsing the file ... often already done for
2213 : * other reasons, and if not it is still faster than fully reading all
2214 : * the features just to count them.
2215 : *
2216 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
2217 : **********************************************************************/
2218 0 : OGRErr MIFFile::GetExtent (OGREnvelope *psExtent, int bForce)
2219 : {
2220 0 : if (bForce == TRUE)
2221 0 : PreParseFile();
2222 :
2223 0 : if (m_bPreParsed)
2224 : {
2225 0 : *psExtent = m_sExtents;
2226 0 : return OGRERR_NONE;
2227 : }
2228 : else
2229 0 : return OGRERR_FAILURE;
2230 : }
2231 :
2232 : /************************************************************************/
2233 : /* TestCapability() */
2234 : /************************************************************************/
2235 :
2236 0 : int MIFFile::TestCapability( const char * pszCap )
2237 :
2238 : {
2239 0 : if( EQUAL(pszCap,OLCRandomRead) )
2240 0 : return TRUE;
2241 :
2242 0 : else if( EQUAL(pszCap,OLCSequentialWrite) )
2243 0 : return TRUE;
2244 :
2245 0 : else if( EQUAL(pszCap,OLCSequentialWrite) )
2246 0 : return FALSE;
2247 :
2248 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
2249 0 : return m_bPreParsed;
2250 :
2251 0 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
2252 0 : return FALSE;
2253 :
2254 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
2255 0 : return m_bPreParsed;
2256 :
2257 : else
2258 0 : return FALSE;
2259 : }
|