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