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