1 : /******************************************************************************
2 : * $Id: ogrcsvlayer.cpp 25267 2012-11-29 19:59:17Z rouault $
3 : *
4 : * Project: CSV Translator
5 : * Purpose: Implements OGRCSVLayer class.
6 : * Author: Frank Warmerdam <warmerdam@pobox.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_csv.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_csv.h"
34 : #include "ogr_p.h"
35 :
36 : CPL_CVSID("$Id: ogrcsvlayer.cpp 25267 2012-11-29 19:59:17Z rouault $");
37 :
38 :
39 :
40 : /************************************************************************/
41 : /* CSVSplitLine() */
42 : /* */
43 : /* Tokenize a CSV line into fields in the form of a string */
44 : /* list. This is used instead of the CPLTokenizeString() */
45 : /* because it provides correct CSV escaping and quoting */
46 : /* semantics. */
47 : /************************************************************************/
48 :
49 10974 : static char **CSVSplitLine( const char *pszString, char chDelimiter )
50 :
51 : {
52 10974 : char **papszRetList = NULL;
53 : char *pszToken;
54 : int nTokenMax, nTokenLen;
55 :
56 10974 : pszToken = (char *) CPLCalloc(10,1);
57 10974 : nTokenMax = 10;
58 :
59 55387 : while( pszString != NULL && *pszString != '\0' )
60 : {
61 33439 : int bInString = FALSE;
62 :
63 33439 : nTokenLen = 0;
64 :
65 : /* Try to find the next delimeter, marking end of token */
66 302098 : for( ; *pszString != '\0'; pszString++ )
67 : {
68 :
69 : /* End if this is a delimeter skip it and break. */
70 291239 : if( !bInString && *pszString == chDelimiter )
71 : {
72 22580 : pszString++;
73 22580 : break;
74 : }
75 :
76 268659 : if( *pszString == '"' )
77 : {
78 946 : if( !bInString || pszString[1] != '"' )
79 : {
80 904 : bInString = !bInString;
81 904 : continue;
82 : }
83 : else /* doubled quotes in string resolve to one quote */
84 : {
85 42 : pszString++;
86 : }
87 : }
88 :
89 267755 : if( nTokenLen >= nTokenMax-2 )
90 : {
91 10831 : nTokenMax = nTokenMax * 2 + 10;
92 10831 : pszToken = (char *) CPLRealloc( pszToken, nTokenMax );
93 : }
94 :
95 267755 : pszToken[nTokenLen] = *pszString;
96 267755 : nTokenLen++;
97 : }
98 :
99 33439 : pszToken[nTokenLen] = '\0';
100 33439 : papszRetList = CSLAddString( papszRetList, pszToken );
101 :
102 : /* If the last token is an empty token, then we have to catch
103 : * it now, otherwise we won't reenter the loop and it will be lost.
104 : */
105 33439 : if ( *pszString == '\0' && *(pszString-1) == chDelimiter )
106 : {
107 115 : papszRetList = CSLAddString( papszRetList, "" );
108 : }
109 : }
110 :
111 10974 : if( papszRetList == NULL )
112 0 : papszRetList = (char **) CPLCalloc(sizeof(char *),1);
113 :
114 10974 : CPLFree( pszToken );
115 :
116 10974 : return papszRetList;
117 : }
118 :
119 : /************************************************************************/
120 : /* OGRCSVReadParseLineL() */
121 : /* */
122 : /* Read one line, and return split into fields. The return */
123 : /* result is a stringlist, in the sense of the CSL functions. */
124 : /************************************************************************/
125 :
126 11086 : char **OGRCSVReadParseLineL( VSILFILE * fp, char chDelimiter, int bDontHonourStrings )
127 :
128 : {
129 : const char *pszLine;
130 : char *pszWorkLine;
131 : char **papszReturn;
132 :
133 11086 : pszLine = CPLReadLineL( fp );
134 11086 : if( pszLine == NULL )
135 112 : return( NULL );
136 :
137 : /* Skip BOM */
138 10974 : GByte* pabyData = (GByte*) pszLine;
139 10974 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF)
140 6 : pszLine += 3;
141 :
142 : /* Special fix to read NdfcFacilities.xls that has non-balanced double quotes */
143 10974 : if (chDelimiter == '\t' && bDontHonourStrings)
144 : {
145 0 : return CSLTokenizeStringComplex(pszLine, "\t", FALSE, TRUE);
146 : }
147 :
148 : /* -------------------------------------------------------------------- */
149 : /* If there are no quotes, then this is the simple case. */
150 : /* Parse, and return tokens. */
151 : /* -------------------------------------------------------------------- */
152 10974 : if( strchr(pszLine,'\"') == NULL )
153 10796 : return CSVSplitLine( pszLine, chDelimiter );
154 :
155 : /* -------------------------------------------------------------------- */
156 : /* We must now count the quotes in our working string, and as */
157 : /* long as it is odd, keep adding new lines. */
158 : /* -------------------------------------------------------------------- */
159 178 : pszWorkLine = CPLStrdup( pszLine );
160 :
161 178 : int i = 0, nCount = 0;
162 178 : int nWorkLineLength = strlen(pszWorkLine);
163 :
164 2 : while( TRUE )
165 : {
166 12806 : for( ; pszWorkLine[i] != '\0'; i++ )
167 : {
168 13555 : if( pszWorkLine[i] == '\"'
169 929 : && (i == 0 || pszWorkLine[i-1] != '\\') )
170 988 : nCount++;
171 : }
172 :
173 180 : if( nCount % 2 == 0 )
174 178 : break;
175 :
176 2 : pszLine = CPLReadLineL( fp );
177 2 : if( pszLine == NULL )
178 0 : break;
179 :
180 2 : int nLineLen = strlen(pszLine);
181 :
182 : char* pszWorkLineTmp = (char *)
183 : VSIRealloc(pszWorkLine,
184 2 : nWorkLineLength + nLineLen + 2);
185 2 : if (pszWorkLineTmp == NULL)
186 0 : break;
187 2 : pszWorkLine = pszWorkLineTmp;
188 2 : strcat( pszWorkLine + nWorkLineLength, "\n" ); // This gets lost in CPLReadLine().
189 2 : strcat( pszWorkLine + nWorkLineLength, pszLine );
190 :
191 2 : nWorkLineLength += nLineLen + 1;
192 : }
193 :
194 178 : papszReturn = CSVSplitLine( pszWorkLine, chDelimiter );
195 :
196 178 : CPLFree( pszWorkLine );
197 :
198 178 : return papszReturn;
199 : }
200 :
201 : /************************************************************************/
202 : /* OGRCSVLayer() */
203 : /* */
204 : /* Note that the OGRCSVLayer assumes ownership of the passed */
205 : /* file pointer. */
206 : /************************************************************************/
207 :
208 151 : OGRCSVLayer::OGRCSVLayer( const char *pszLayerNameIn,
209 : VSILFILE * fp, const char *pszFilename, int bNew, int bInWriteMode,
210 : char chDelimiter, const char* pszNfdcGeomField,
211 151 : const char* pszGeonamesGeomFieldPrefix)
212 :
213 : {
214 151 : fpCSV = fp;
215 :
216 151 : iWktGeomReadField = -1;
217 151 : iNfdcLatitudeS = iNfdcLongitudeS = -1;
218 151 : iLatitudeField = iLongitudeField = -1;
219 151 : this->bInWriteMode = bInWriteMode;
220 151 : this->bNew = bNew;
221 151 : this->pszFilename = CPLStrdup(pszFilename);
222 151 : this->chDelimiter = chDelimiter;
223 :
224 151 : bFirstFeatureAppendedDuringSession = TRUE;
225 151 : bUseCRLF = FALSE;
226 151 : bNeedRewindBeforeRead = FALSE;
227 151 : eGeometryFormat = OGR_CSV_GEOM_NONE;
228 :
229 151 : nNextFID = 1;
230 :
231 151 : poFeatureDefn = new OGRFeatureDefn( pszLayerNameIn );
232 151 : poFeatureDefn->Reference();
233 151 : poFeatureDefn->SetGeomType( wkbNone );
234 :
235 151 : bCreateCSVT = FALSE;
236 151 : bDontHonourStrings = FALSE;
237 151 : bWriteBOM = FALSE;
238 :
239 151 : nTotalFeatures = -1;
240 :
241 : /* -------------------------------------------------------------------- */
242 : /* If this is not a new file, read ahead to establish if it is */
243 : /* already in CRLF (DOS) mode, or just a normal unix CR mode. */
244 : /* -------------------------------------------------------------------- */
245 151 : if( !bNew && bInWriteMode )
246 : {
247 48 : int nBytesRead = 0;
248 : char chNewByte;
249 :
250 3037 : while( nBytesRead < 10000 && VSIFReadL( &chNewByte, 1, 1, fpCSV ) == 1 )
251 : {
252 2950 : if( chNewByte == 13 )
253 : {
254 9 : bUseCRLF = TRUE;
255 9 : break;
256 : }
257 2941 : nBytesRead ++;
258 : }
259 48 : VSIRewindL( fpCSV );
260 : }
261 :
262 : /* -------------------------------------------------------------------- */
263 : /* Check if the first record seems to be field definitions or */
264 : /* not. We assume it is field definitions if none of the */
265 : /* values are strictly numeric. */
266 : /* -------------------------------------------------------------------- */
267 151 : char **papszTokens = NULL;
268 151 : int nFieldCount=0, iField;
269 : CPLValueType eType;
270 :
271 151 : if( !bNew )
272 : {
273 133 : const char *pszLine = NULL;
274 : char szDelimiter[2];
275 133 : szDelimiter[0] = chDelimiter; szDelimiter[1] = '\0';
276 :
277 133 : pszLine = CPLReadLineL( fpCSV );
278 133 : if ( pszLine != NULL )
279 : {
280 : /* Detect and remove UTF-8 BOM marker if found (#4623) */
281 145 : if (pszLine[0] == (char)0xEF &&
282 6 : pszLine[1] == (char)0xBB &&
283 6 : pszLine[2] == (char)0xBF)
284 : {
285 6 : pszLine += 3;
286 : }
287 :
288 : /* tokenize the strings and preserve quotes, so we can separate string from numeric */
289 : /* this is only used in the test for bHasFeldNames (bug #4361) */
290 : papszTokens = CSLTokenizeString2( pszLine, szDelimiter,
291 : (CSLT_HONOURSTRINGS |
292 : CSLT_ALLOWEMPTYTOKENS |
293 133 : CSLT_PRESERVEQUOTES) );
294 133 : nFieldCount = CSLCount( papszTokens );
295 133 : bHasFieldNames = TRUE;
296 :
297 589 : for( iField = 0; iField < nFieldCount && bHasFieldNames; iField++ )
298 : {
299 456 : eType = CPLGetValueType(papszTokens[iField]);
300 456 : if ( (eType == CPL_VALUE_INTEGER ||
301 : eType == CPL_VALUE_REAL) ) {
302 : /* we have a numeric field, therefore do not consider the first line as field names */
303 28 : bHasFieldNames = FALSE;
304 : }
305 : }
306 :
307 : /* tokenize without quotes to get the actual values */
308 133 : CSLDestroy( papszTokens );
309 : // papszTokens = OGRCSVReadParseLineL( fpCSV, chDelimiter, FALSE );
310 : papszTokens = CSLTokenizeString2( pszLine, szDelimiter,
311 : (CSLT_HONOURSTRINGS |
312 133 : CSLT_ALLOWEMPTYTOKENS));
313 133 : nFieldCount = CSLCount( papszTokens );
314 : }
315 : }
316 : else
317 18 : bHasFieldNames = FALSE;
318 :
319 151 : if( !bNew && !bHasFieldNames )
320 28 : VSIRewindL( fpCSV );
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Check for geonames.org tables */
324 : /* -------------------------------------------------------------------- */
325 151 : if( !bHasFieldNames && nFieldCount == 19 )
326 : {
327 0 : if (CPLGetValueType(papszTokens[0]) == CPL_VALUE_INTEGER &&
328 0 : CPLGetValueType(papszTokens[4]) == CPL_VALUE_REAL &&
329 0 : CPLGetValueType(papszTokens[5]) == CPL_VALUE_REAL &&
330 0 : CPLAtof(papszTokens[4]) >= -90 && CPLAtof(papszTokens[4]) <= 90 &&
331 0 : CPLAtof(papszTokens[5]) >= -180 && CPLAtof(papszTokens[4]) <= 180)
332 : {
333 0 : bHasFieldNames = TRUE;
334 0 : CSLDestroy(papszTokens);
335 0 : papszTokens = NULL;
336 :
337 : static const struct {
338 : const char* pszName;
339 : OGRFieldType eType;
340 : }
341 : asGeonamesFieldDesc[] =
342 : {
343 : { "GEONAMEID", OFTString },
344 : { "NAME", OFTString },
345 : { "ASCIINAME", OFTString },
346 : { "ALTNAMES", OFTString },
347 : { "LATITUDE", OFTReal },
348 : { "LONGITUDE", OFTReal },
349 : { "FEATCLASS", OFTString },
350 : { "FEATCODE", OFTString },
351 : { "COUNTRY", OFTString },
352 : { "CC2", OFTString },
353 : { "ADMIN1", OFTString },
354 : { "ADMIN2", OFTString },
355 : { "ADMIN3", OFTString },
356 : { "ADMIN4", OFTString },
357 : { "POPULATION", OFTReal },
358 : { "ELEVATION", OFTInteger },
359 : { "GTOPO30", OFTInteger },
360 : { "TIMEZONE", OFTString },
361 : { "MODDATE", OFTString }
362 : };
363 0 : for(iField = 0; iField < nFieldCount; iField++)
364 : {
365 : OGRFieldDefn oFieldDefn(asGeonamesFieldDesc[iField].pszName,
366 0 : asGeonamesFieldDesc[iField].eType);
367 0 : poFeatureDefn->AddFieldDefn(&oFieldDefn);
368 : }
369 :
370 0 : iLatitudeField = 4;
371 0 : iLongitudeField = 5;
372 :
373 0 : nFieldCount = 0;
374 : }
375 : }
376 :
377 :
378 : /* -------------------------------------------------------------------- */
379 : /* Search a csvt file for types */
380 : /* -------------------------------------------------------------------- */
381 151 : char** papszFieldTypes = NULL;
382 151 : if (!bNew) {
383 133 : char* dname = strdup(CPLGetDirname(pszFilename));
384 133 : char* fname = strdup(CPLGetBasename(pszFilename));
385 133 : VSILFILE* fpCSVT = VSIFOpenL(CPLFormFilename(dname, fname, ".csvt"), "r");
386 133 : free(dname);
387 133 : free(fname);
388 133 : if (fpCSVT!=NULL) {
389 35 : VSIRewindL(fpCSVT);
390 35 : papszFieldTypes = OGRCSVReadParseLineL(fpCSVT, ',', FALSE);
391 35 : VSIFCloseL(fpCSVT);
392 : }
393 : }
394 :
395 :
396 : /* -------------------------------------------------------------------- */
397 : /* Build field definitions. */
398 : /* -------------------------------------------------------------------- */
399 659 : for( iField = 0; iField < nFieldCount; iField++ )
400 : {
401 510 : char *pszFieldName = NULL;
402 : char szFieldNameBuffer[100];
403 :
404 510 : if( bHasFieldNames )
405 : {
406 427 : pszFieldName = papszTokens[iField];
407 :
408 : // trim white space.
409 854 : while( *pszFieldName == ' ' )
410 0 : pszFieldName++;
411 :
412 1251 : while( pszFieldName[0] != '\0'
413 397 : && pszFieldName[strlen(pszFieldName)-1] == ' ' )
414 0 : pszFieldName[strlen(pszFieldName)-1] = '\0';
415 :
416 427 : if (*pszFieldName == '\0')
417 30 : pszFieldName = NULL;
418 : }
419 :
420 510 : if (pszFieldName == NULL)
421 : {
422 : /* Re-read single column CSV files that have a trailing comma */
423 : /* in the header line */
424 113 : if( iField == 1 && nFieldCount == 2 && papszTokens[1][0] == '\0' )
425 : {
426 2 : nFieldCount = 1;
427 2 : break;
428 : }
429 111 : pszFieldName = szFieldNameBuffer;
430 111 : sprintf( szFieldNameBuffer, "field_%d", iField+1 );
431 : }
432 :
433 508 : OGRFieldDefn oField(pszFieldName, OFTString);
434 508 : if (papszFieldTypes!=NULL && iField<CSLCount(papszFieldTypes)) {
435 :
436 175 : char* pszLeftParenthesis = strchr(papszFieldTypes[iField], '(');
437 239 : if (pszLeftParenthesis && pszLeftParenthesis != papszFieldTypes[iField] &&
438 64 : pszLeftParenthesis[1] >= '0' && pszLeftParenthesis[1] <= '9')
439 : {
440 32 : int nWidth = 0;
441 32 : int nPrecision = 0;
442 :
443 32 : char* pszDot = strchr(pszLeftParenthesis, '.');
444 32 : if (pszDot) *pszDot = 0;
445 32 : *pszLeftParenthesis = 0;
446 :
447 32 : if (pszLeftParenthesis[-1] == ' ')
448 2 : pszLeftParenthesis[-1] = 0;
449 :
450 32 : nWidth = atoi(pszLeftParenthesis+1);
451 32 : if (pszDot)
452 11 : nPrecision = atoi(pszDot+1);
453 :
454 32 : oField.SetWidth(nWidth);
455 32 : oField.SetPrecision(nPrecision);
456 : }
457 :
458 175 : if (EQUAL(papszFieldTypes[iField], "Integer"))
459 22 : oField.SetType(OFTInteger);
460 153 : else if (EQUAL(papszFieldTypes[iField], "Real"))
461 75 : oField.SetType(OFTReal);
462 78 : else if (EQUAL(papszFieldTypes[iField], "String"))
463 47 : oField.SetType(OFTString);
464 31 : else if (EQUAL(papszFieldTypes[iField], "Date"))
465 10 : oField.SetType(OFTDate);
466 21 : else if (EQUAL(papszFieldTypes[iField], "Time"))
467 10 : oField.SetType(OFTTime);
468 11 : else if (EQUAL(papszFieldTypes[iField], "DateTime"))
469 11 : oField.SetType(OFTDateTime);
470 : else
471 0 : CPLError(CE_Warning, CPLE_NotSupported, "Unknown type : %s", papszFieldTypes[iField]);
472 : }
473 :
474 508 : if( EQUAL(oField.GetNameRef(),"WKT")
475 : && oField.GetType() == OFTString
476 : && iWktGeomReadField == -1 )
477 : {
478 33 : iWktGeomReadField = iField;
479 33 : poFeatureDefn->SetGeomType( wkbUnknown );
480 : }
481 :
482 : /*http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm specific */
483 508 : if ( pszNfdcGeomField != NULL &&
484 : EQUALN(oField.GetNameRef(), pszNfdcGeomField, strlen(pszNfdcGeomField)) &&
485 : EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField), "LatitudeS") )
486 0 : iNfdcLatitudeS = iField;
487 508 : else if ( pszNfdcGeomField != NULL &&
488 : EQUALN(oField.GetNameRef(), pszNfdcGeomField, strlen(pszNfdcGeomField)) &&
489 : EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField), "LongitudeS") )
490 0 : iNfdcLongitudeS = iField;
491 :
492 : /* GNIS specific */
493 508 : else if ( pszGeonamesGeomFieldPrefix != NULL &&
494 : EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix, strlen(pszGeonamesGeomFieldPrefix)) &&
495 : (EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LAT_DEC") ||
496 : EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LATITUDE_DEC") ||
497 : EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LATITUDE")) )
498 : {
499 0 : oField.SetType(OFTReal);
500 0 : iLatitudeField = iField;
501 : }
502 508 : else if ( pszGeonamesGeomFieldPrefix != NULL &&
503 : EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix, strlen(pszGeonamesGeomFieldPrefix)) &&
504 : (EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LONG_DEC") ||
505 : EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LONGITUDE_DEC") ||
506 : EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LONGITUDE")) )
507 : {
508 0 : oField.SetType(OFTReal);
509 0 : iLongitudeField = iField;
510 : }
511 :
512 508 : poFeatureDefn->AddFieldDefn( &oField );
513 :
514 : }
515 :
516 151 : if ( iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1 )
517 : {
518 0 : bDontHonourStrings = TRUE;
519 0 : poFeatureDefn->SetGeomType( wkbPoint );
520 : }
521 151 : else if ( iLatitudeField != -1 && iLongitudeField != -1 )
522 : {
523 0 : poFeatureDefn->SetGeomType( wkbPoint );
524 : }
525 :
526 151 : CSLDestroy( papszTokens );
527 151 : CSLDestroy( papszFieldTypes );
528 151 : }
529 :
530 : /************************************************************************/
531 : /* ~OGRCSVLayer() */
532 : /************************************************************************/
533 :
534 151 : OGRCSVLayer::~OGRCSVLayer()
535 :
536 : {
537 151 : if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
538 : {
539 : CPLDebug( "CSV", "%d features read on layer '%s'.",
540 : (int) m_nFeaturesRead,
541 82 : poFeatureDefn->GetName() );
542 : }
543 :
544 151 : poFeatureDefn->Release();
545 151 : CPLFree(pszFilename);
546 :
547 151 : if (fpCSV)
548 151 : VSIFCloseL( fpCSV );
549 151 : }
550 :
551 : /************************************************************************/
552 : /* ResetReading() */
553 : /************************************************************************/
554 :
555 170 : void OGRCSVLayer::ResetReading()
556 :
557 : {
558 170 : if (fpCSV)
559 170 : VSIRewindL( fpCSV );
560 :
561 170 : if( bHasFieldNames )
562 142 : CSLDestroy( OGRCSVReadParseLineL( fpCSV, chDelimiter, bDontHonourStrings ) );
563 :
564 170 : bNeedRewindBeforeRead = FALSE;
565 :
566 170 : nNextFID = 1;
567 170 : }
568 :
569 : /************************************************************************/
570 : /* GetNextUnfilteredFeature() */
571 : /************************************************************************/
572 :
573 10768 : OGRFeature * OGRCSVLayer::GetNextUnfilteredFeature()
574 :
575 : {
576 10768 : if (fpCSV == NULL)
577 0 : return NULL;
578 :
579 : /* -------------------------------------------------------------------- */
580 : /* Read the CSV record. */
581 : /* -------------------------------------------------------------------- */
582 : char **papszTokens;
583 :
584 0 : while(TRUE)
585 : {
586 10768 : papszTokens = OGRCSVReadParseLineL( fpCSV, chDelimiter, bDontHonourStrings );
587 10768 : if( papszTokens == NULL )
588 110 : return NULL;
589 :
590 10658 : if( papszTokens[0] != NULL )
591 : break;
592 :
593 0 : CSLDestroy(papszTokens);
594 : }
595 :
596 : /* -------------------------------------------------------------------- */
597 : /* Create the OGR feature. */
598 : /* -------------------------------------------------------------------- */
599 : OGRFeature *poFeature;
600 :
601 21316 : poFeature = new OGRFeature( poFeatureDefn );
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Set attributes for any indicated attribute records. */
605 : /* -------------------------------------------------------------------- */
606 : int iAttr;
607 10671 : int nAttrCount = MIN(CSLCount(papszTokens),
608 : poFeatureDefn->GetFieldCount() );
609 : CPLValueType eType;
610 :
611 42929 : for( iAttr = 0; iAttr < nAttrCount; iAttr++)
612 : {
613 32271 : if( iAttr == iWktGeomReadField && papszTokens[iAttr][0] != '\0' )
614 : {
615 38 : char *pszWKT = papszTokens[iAttr];
616 38 : OGRGeometry *poGeom = NULL;
617 :
618 38 : if( OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeom )
619 : == OGRERR_NONE )
620 38 : poFeature->SetGeometryDirectly( poGeom );
621 : }
622 :
623 32271 : OGRFieldType eFieldType = poFeatureDefn->GetFieldDefn(iAttr)->GetType();
624 32502 : if ( eFieldType == OFTReal || eFieldType == OFTInteger )
625 : {
626 231 : if (chDelimiter == ';' && eFieldType == OFTReal)
627 : {
628 0 : char* chComma = strchr(papszTokens[iAttr], ',');
629 0 : if (chComma)
630 0 : *chComma = '.';
631 : }
632 231 : eType = CPLGetValueType(papszTokens[iAttr]);
633 231 : if ( (papszTokens[iAttr][0] != '\0') &&
634 : ( eType == CPL_VALUE_INTEGER ||
635 : eType == CPL_VALUE_REAL ) )
636 : {
637 163 : poFeature->SetField( iAttr, papszTokens[iAttr] );
638 : }
639 : }
640 32040 : else if (eFieldType != OFTString)
641 : {
642 96 : if (papszTokens[iAttr][0] != '\0')
643 57 : poFeature->SetField( iAttr, papszTokens[iAttr] );
644 : }
645 : else
646 31944 : poFeature->SetField( iAttr, papszTokens[iAttr] );
647 :
648 : }
649 :
650 : /* -------------------------------------------------------------------- */
651 : /*http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm specific */
652 : /* -------------------------------------------------------------------- */
653 :
654 10658 : if ( iNfdcLatitudeS != -1 &&
655 : iNfdcLongitudeS != -1 &&
656 : nAttrCount > iNfdcLatitudeS &&
657 : nAttrCount > iNfdcLongitudeS &&
658 0 : papszTokens[iNfdcLongitudeS][0] != 0 &&
659 0 : papszTokens[iNfdcLatitudeS][0] != 0)
660 : {
661 0 : double dfLon = atof(papszTokens[iNfdcLongitudeS]) / 3600;
662 0 : if (strchr(papszTokens[iNfdcLongitudeS], 'W'))
663 0 : dfLon *= -1;
664 0 : double dfLat = atof(papszTokens[iNfdcLatitudeS]) / 3600;
665 0 : if (strchr(papszTokens[iNfdcLatitudeS], 'S'))
666 0 : dfLat *= -1;
667 0 : poFeature->SetGeometryDirectly( new OGRPoint(dfLon, dfLat) );
668 : }
669 :
670 : /* -------------------------------------------------------------------- */
671 : /* GNIS specific */
672 : /* -------------------------------------------------------------------- */
673 10658 : else if ( iLatitudeField != -1 &&
674 : iLongitudeField != -1 &&
675 : nAttrCount > iLatitudeField &&
676 : nAttrCount > iLongitudeField &&
677 0 : papszTokens[iLongitudeField][0] != 0 &&
678 0 : papszTokens[iLatitudeField][0] != 0)
679 : {
680 : /* Some records have dummy 0,0 value */
681 0 : if (papszTokens[iLongitudeField][0] != '0' ||
682 0 : papszTokens[iLongitudeField][1] != '\0' ||
683 0 : papszTokens[iLatitudeField][0] != '0' ||
684 0 : papszTokens[iLatitudeField][1] != '\0')
685 : {
686 0 : double dfLon = atof(papszTokens[iLongitudeField]);
687 0 : double dfLat = atof(papszTokens[iLatitudeField]);
688 0 : poFeature->SetGeometryDirectly( new OGRPoint(dfLon, dfLat) );
689 : }
690 : }
691 :
692 10658 : CSLDestroy( papszTokens );
693 :
694 : /* -------------------------------------------------------------------- */
695 : /* Translate the record id. */
696 : /* -------------------------------------------------------------------- */
697 10658 : poFeature->SetFID( nNextFID++ );
698 :
699 10658 : m_nFeaturesRead++;
700 :
701 10658 : return poFeature;
702 : }
703 :
704 :
705 : /************************************************************************/
706 : /* GetNextFeature() */
707 : /************************************************************************/
708 :
709 10729 : OGRFeature *OGRCSVLayer::GetNextFeature()
710 :
711 : {
712 10729 : OGRFeature *poFeature = NULL;
713 :
714 10729 : if( bNeedRewindBeforeRead )
715 3 : ResetReading();
716 :
717 : /* -------------------------------------------------------------------- */
718 : /* Read features till we find one that satisfies our current */
719 : /* spatial criteria. */
720 : /* -------------------------------------------------------------------- */
721 39 : while( TRUE )
722 : {
723 10768 : poFeature = GetNextUnfilteredFeature();
724 10768 : if( poFeature == NULL )
725 110 : break;
726 :
727 10658 : if( (m_poFilterGeom == NULL
728 : || FilterGeometry( poFeature->GetGeometryRef() ) )
729 : && (m_poAttrQuery == NULL
730 : || m_poAttrQuery->Evaluate( poFeature )) )
731 10619 : break;
732 :
733 39 : delete poFeature;
734 : }
735 :
736 10729 : return poFeature;
737 : }
738 :
739 : /************************************************************************/
740 : /* TestCapability() */
741 : /************************************************************************/
742 :
743 49 : int OGRCSVLayer::TestCapability( const char * pszCap )
744 :
745 : {
746 49 : if( EQUAL(pszCap,OLCSequentialWrite) )
747 2 : return bInWriteMode;
748 47 : else if( EQUAL(pszCap,OLCCreateField) )
749 0 : return bNew && !bHasFieldNames;
750 : else
751 47 : return FALSE;
752 : }
753 :
754 : /************************************************************************/
755 : /* CreateField() */
756 : /************************************************************************/
757 :
758 67 : OGRErr OGRCSVLayer::CreateField( OGRFieldDefn *poNewField, int bApproxOK )
759 :
760 : {
761 : /* -------------------------------------------------------------------- */
762 : /* If we have already written our field names, then we are not */
763 : /* allowed to add new fields. */
764 : /* -------------------------------------------------------------------- */
765 67 : if( bHasFieldNames || !bNew )
766 : {
767 : CPLError( CE_Failure, CPLE_AppDefined,
768 0 : "Unable to create new fields after first feature written.");
769 0 : return OGRERR_FAILURE;
770 : }
771 :
772 : /* -------------------------------------------------------------------- */
773 : /* Does this duplicate an existing field? */
774 : /* -------------------------------------------------------------------- */
775 67 : if( poFeatureDefn->GetFieldIndex( poNewField->GetNameRef() ) != -1 )
776 : {
777 : CPLError( CE_Failure, CPLE_AppDefined,
778 : "Attempt to create field %s, but a field with this name already exists.",
779 0 : poNewField->GetNameRef() );
780 :
781 0 : return OGRERR_FAILURE;
782 : }
783 :
784 : /* -------------------------------------------------------------------- */
785 : /* Is this a legal field type for CSV? For now we only allow */
786 : /* simple integer, real and string fields. */
787 : /* -------------------------------------------------------------------- */
788 67 : switch( poNewField->GetType() )
789 : {
790 : case OFTInteger:
791 : case OFTReal:
792 : case OFTString:
793 : // these types are OK.
794 64 : break;
795 :
796 : default:
797 3 : if( bApproxOK )
798 : {
799 : CPLError( CE_Warning, CPLE_AppDefined,
800 : "Attempt to create field of type %s, but this is not supported\n"
801 : "for .csv files. Just treating as a plain string.",
802 3 : poNewField->GetFieldTypeName( poNewField->GetType() ) );
803 : }
804 : else
805 : {
806 : CPLError( CE_Failure, CPLE_AppDefined,
807 : "Attempt to create field of type %s, but this is not supported\n"
808 : "for .csv files.",
809 0 : poNewField->GetFieldTypeName( poNewField->GetType() ) );
810 0 : return OGRERR_FAILURE;
811 : }
812 : }
813 :
814 : /* -------------------------------------------------------------------- */
815 : /* Seems ok, add to field list. */
816 : /* -------------------------------------------------------------------- */
817 67 : poFeatureDefn->AddFieldDefn( poNewField );
818 :
819 67 : return OGRERR_NONE;
820 : }
821 :
822 : /************************************************************************/
823 : /* CreateFeature() */
824 : /************************************************************************/
825 :
826 43 : OGRErr OGRCSVLayer::CreateFeature( OGRFeature *poNewFeature )
827 :
828 : {
829 : int iField;
830 :
831 43 : if( !bInWriteMode )
832 : {
833 : CPLError( CE_Failure, CPLE_AppDefined,
834 0 : "The CreateFeature() operation is not permitted on a read-only CSV." );
835 0 : return OGRERR_FAILURE;
836 : }
837 :
838 : /* If we need rewind, it means that we have just written a feature before */
839 : /* so there's no point seeking to the end of the file, as we're already */
840 : /* at the end */
841 43 : int bNeedSeekEnd = !bNeedRewindBeforeRead;
842 :
843 43 : bNeedRewindBeforeRead = TRUE;
844 :
845 : /* -------------------------------------------------------------------- */
846 : /* Write field names if we haven't written them yet. */
847 : /* Write .csvt file if needed */
848 : /* -------------------------------------------------------------------- */
849 43 : if( !bHasFieldNames )
850 : {
851 18 : bHasFieldNames = TRUE;
852 18 : bNeedSeekEnd = FALSE;
853 :
854 40 : for(int iFile=0;iFile<((bCreateCSVT) ? 2 : 1);iFile++)
855 : {
856 22 : VSILFILE* fpCSVT = NULL;
857 26 : if (bCreateCSVT && iFile == 0)
858 : {
859 4 : char* pszDirName = CPLStrdup(CPLGetDirname(pszFilename));
860 4 : char* pszBaseName = CPLStrdup(CPLGetBasename(pszFilename));
861 4 : fpCSVT = VSIFOpenL(CPLFormFilename(pszDirName, pszBaseName, ".csvt"), "wb");
862 4 : CPLFree(pszDirName);
863 4 : CPLFree(pszBaseName);
864 : }
865 : else
866 : {
867 19 : if( strncmp(pszFilename, "/vsistdout/", 11) == 0 ||
868 : strncmp(pszFilename, "/vsizip/", 8) == 0 )
869 1 : fpCSV = VSIFOpenL( pszFilename, "wb" );
870 : else
871 17 : fpCSV = VSIFOpenL( pszFilename, "w+b" );
872 :
873 18 : if( fpCSV == NULL )
874 : {
875 : CPLError( CE_Failure, CPLE_OpenFailed,
876 : "Failed to create %s:\n%s",
877 0 : pszFilename, VSIStrerror( errno ) );
878 0 : return OGRERR_FAILURE;
879 : }
880 : }
881 :
882 22 : if (bWriteBOM && fpCSV)
883 : {
884 2 : VSIFWriteL("\xEF\xBB\xBF", 1, 3, fpCSV);
885 : }
886 :
887 22 : if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT)
888 : {
889 4 : if (fpCSV) VSIFPrintfL( fpCSV, "%s", "WKT");
890 4 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "String");
891 4 : if (poFeatureDefn->GetFieldCount() > 0)
892 : {
893 4 : if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter );
894 4 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ",");
895 : }
896 : }
897 18 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
898 : {
899 2 : if (fpCSV) VSIFPrintfL( fpCSV, "X%cY%cZ", chDelimiter, chDelimiter);
900 2 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "Real,Real,Real");
901 2 : if (poFeatureDefn->GetFieldCount() > 0)
902 : {
903 2 : if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter );
904 2 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ",");
905 : }
906 : }
907 16 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY)
908 : {
909 2 : if (fpCSV) VSIFPrintfL( fpCSV, "X%cY", chDelimiter);
910 2 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "Real,Real");
911 2 : if (poFeatureDefn->GetFieldCount() > 0)
912 : {
913 2 : if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter );
914 2 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ",");
915 : }
916 : }
917 14 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_YX)
918 : {
919 2 : if (fpCSV) VSIFPrintfL( fpCSV, "Y%cX", chDelimiter);
920 2 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "Real,Real");
921 2 : if (poFeatureDefn->GetFieldCount() > 0)
922 : {
923 2 : if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter );
924 2 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ",");
925 : }
926 : }
927 :
928 101 : for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
929 : {
930 : char *pszEscaped;
931 :
932 79 : if( iField > 0 )
933 : {
934 57 : if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter );
935 57 : if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ",");
936 : }
937 :
938 : pszEscaped =
939 : CPLEscapeString( poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
940 79 : -1, CPLES_CSV );
941 :
942 79 : if (fpCSV) VSIFPrintfL( fpCSV, "%s", pszEscaped );
943 79 : CPLFree( pszEscaped );
944 :
945 79 : if (fpCSVT)
946 : {
947 12 : switch( poFeatureDefn->GetFieldDefn(iField)->GetType() )
948 : {
949 2 : case OFTInteger: VSIFPrintfL( fpCSVT, "%s", "Integer"); break;
950 2 : case OFTReal: VSIFPrintfL( fpCSVT, "%s", "Real"); break;
951 1 : case OFTDate: VSIFPrintfL( fpCSVT, "%s", "Date"); break;
952 1 : case OFTTime: VSIFPrintfL( fpCSVT, "%s", "Time"); break;
953 1 : case OFTDateTime: VSIFPrintfL( fpCSVT, "%s", "DateTime"); break;
954 5 : default: VSIFPrintfL( fpCSVT, "%s", "String"); break;
955 : }
956 :
957 12 : int nWidth = poFeatureDefn->GetFieldDefn(iField)->GetWidth();
958 12 : int nPrecision = poFeatureDefn->GetFieldDefn(iField)->GetPrecision();
959 12 : if (nWidth != 0)
960 : {
961 3 : if (nPrecision != 0)
962 1 : VSIFPrintfL( fpCSVT, "(%d.%d)", nWidth, nPrecision);
963 : else
964 2 : VSIFPrintfL( fpCSVT, "(%d)", nWidth);
965 : }
966 : }
967 : }
968 :
969 : /* The CSV driver will not recognize single column tables, so add */
970 : /* a fake second blank field */
971 22 : if( poFeatureDefn->GetFieldCount() == 1 )
972 : {
973 11 : if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter );
974 : }
975 :
976 22 : if( bUseCRLF )
977 : {
978 1 : if (fpCSV) VSIFPutcL( 13, fpCSV );
979 1 : if (fpCSVT) VSIFPutcL( 13, fpCSVT );
980 : }
981 22 : if (fpCSV) VSIFPutcL( '\n', fpCSV );
982 22 : if (fpCSVT) VSIFPutcL( '\n', fpCSVT );
983 22 : if (fpCSVT) VSIFCloseL(fpCSVT);
984 : }
985 : }
986 :
987 43 : if (fpCSV == NULL)
988 0 : return OGRERR_FAILURE;
989 :
990 : /* -------------------------------------------------------------------- */
991 : /* Make sure we are at the end of the file. */
992 : /* -------------------------------------------------------------------- */
993 43 : if (bNeedSeekEnd)
994 : {
995 4 : if (bFirstFeatureAppendedDuringSession)
996 : {
997 : /* Add a newline character to the end of the file if necessary */
998 4 : bFirstFeatureAppendedDuringSession = FALSE;
999 4 : VSIFSeekL( fpCSV, 0, SEEK_END );
1000 4 : VSIFSeekL( fpCSV, VSIFTellL(fpCSV) - 1, SEEK_SET);
1001 : char chLast;
1002 4 : VSIFReadL( &chLast, 1, 1, fpCSV );
1003 4 : VSIFSeekL( fpCSV, 0, SEEK_END );
1004 4 : if (chLast != '\n')
1005 : {
1006 0 : if( bUseCRLF )
1007 0 : VSIFPutcL( 13, fpCSV );
1008 0 : VSIFPutcL( '\n', fpCSV );
1009 : }
1010 : }
1011 : else
1012 : {
1013 0 : VSIFSeekL( fpCSV, 0, SEEK_END );
1014 : }
1015 : }
1016 :
1017 : /* -------------------------------------------------------------------- */
1018 : /* Write out the geometry */
1019 : /* -------------------------------------------------------------------- */
1020 43 : if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT)
1021 : {
1022 4 : OGRGeometry *poGeom = poNewFeature->GetGeometryRef();
1023 4 : char* pszWKT = NULL;
1024 4 : if (poGeom && poGeom->exportToWkt(&pszWKT) == OGRERR_NONE)
1025 : {
1026 4 : VSIFPrintfL( fpCSV, "\"%s\"", pszWKT);
1027 : }
1028 : else
1029 : {
1030 0 : VSIFPrintfL( fpCSV, "\"\"");
1031 : }
1032 4 : CPLFree(pszWKT);
1033 4 : if (poFeatureDefn->GetFieldCount() > 0)
1034 4 : VSIFPrintfL( fpCSV, "%c", chDelimiter);
1035 : }
1036 39 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ ||
1037 : eGeometryFormat == OGR_CSV_GEOM_AS_XY ||
1038 : eGeometryFormat == OGR_CSV_GEOM_AS_YX)
1039 : {
1040 4 : OGRGeometry *poGeom = poNewFeature->GetGeometryRef();
1041 4 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1042 : {
1043 3 : OGRPoint* poPoint = (OGRPoint*) poGeom;
1044 : char szBuffer[75];
1045 3 : if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ )
1046 1 : OGRMakeWktCoordinate(szBuffer, poPoint->getX(), poPoint->getY(), poPoint->getZ(), 3);
1047 2 : else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY )
1048 1 : OGRMakeWktCoordinate(szBuffer, poPoint->getX(), poPoint->getY(), 0, 2);
1049 : else
1050 1 : OGRMakeWktCoordinate(szBuffer, poPoint->getY(), poPoint->getX(), 0, 2);
1051 3 : char* pc = szBuffer;
1052 17 : while(*pc != '\0')
1053 : {
1054 11 : if (*pc == ' ')
1055 4 : *pc = chDelimiter;
1056 11 : pc ++;
1057 : }
1058 3 : VSIFPrintfL( fpCSV, "%s", szBuffer );
1059 : }
1060 : else
1061 : {
1062 1 : VSIFPrintfL( fpCSV, "%c", chDelimiter );
1063 1 : if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ)
1064 0 : VSIFPrintfL( fpCSV, "%c", chDelimiter );
1065 : }
1066 4 : if (poFeatureDefn->GetFieldCount() > 0)
1067 4 : VSIFPrintfL( fpCSV, "%c", chDelimiter );
1068 : }
1069 :
1070 : /* -------------------------------------------------------------------- */
1071 : /* Write out all the field values. */
1072 : /* -------------------------------------------------------------------- */
1073 43 : int bNonEmptyLine = FALSE;
1074 439 : for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
1075 : {
1076 : char *pszEscaped;
1077 :
1078 396 : if( iField > 0 )
1079 353 : VSIFPrintfL( fpCSV, "%c", chDelimiter );
1080 :
1081 396 : if (poFeatureDefn->GetFieldDefn(iField)->GetType() == OFTReal)
1082 : {
1083 5 : pszEscaped = CPLStrdup(poNewFeature->GetFieldAsString(iField));
1084 : /* Use point as decimal separator */
1085 5 : char* pszComma = strchr(pszEscaped, ',');
1086 5 : if (pszComma)
1087 0 : *pszComma = '.';
1088 : }
1089 : else
1090 : {
1091 : pszEscaped =
1092 : CPLEscapeString( poNewFeature->GetFieldAsString(iField),
1093 391 : -1, CPLES_CSV );
1094 : }
1095 :
1096 396 : int nLen = (int)strlen(pszEscaped);
1097 396 : bNonEmptyLine |= (nLen != 0);
1098 396 : VSIFWriteL( pszEscaped, 1, nLen, fpCSV );
1099 396 : CPLFree( pszEscaped );
1100 : }
1101 :
1102 43 : if( poFeatureDefn->GetFieldCount() == 1 && !bNonEmptyLine )
1103 1 : VSIFPrintfL( fpCSV, "%c", chDelimiter );
1104 :
1105 43 : if( bUseCRLF )
1106 5 : VSIFPutcL( 13, fpCSV );
1107 43 : VSIFPutcL( '\n', fpCSV );
1108 :
1109 43 : return OGRERR_NONE;
1110 : }
1111 :
1112 : /************************************************************************/
1113 : /* SetCRLF() */
1114 : /************************************************************************/
1115 :
1116 18 : void OGRCSVLayer::SetCRLF( int bNewValue )
1117 :
1118 : {
1119 18 : bUseCRLF = bNewValue;
1120 18 : }
1121 :
1122 : /************************************************************************/
1123 : /* SetWriteGeometry() */
1124 : /************************************************************************/
1125 :
1126 7 : void OGRCSVLayer::SetWriteGeometry(OGRCSVGeometryFormat eGeometryFormat)
1127 : {
1128 7 : this->eGeometryFormat = eGeometryFormat;
1129 7 : }
1130 :
1131 : /************************************************************************/
1132 : /* SetCreateCSVT() */
1133 : /************************************************************************/
1134 :
1135 4 : void OGRCSVLayer::SetCreateCSVT(int bCreateCSVT)
1136 : {
1137 4 : this->bCreateCSVT = bCreateCSVT;
1138 4 : }
1139 :
1140 : /************************************************************************/
1141 : /* SetWriteBOM() */
1142 : /************************************************************************/
1143 :
1144 2 : void OGRCSVLayer::SetWriteBOM(int bWriteBOM)
1145 : {
1146 2 : this->bWriteBOM = bWriteBOM;
1147 2 : }
1148 :
1149 : /************************************************************************/
1150 : /* GetFeatureCount() */
1151 : /************************************************************************/
1152 :
1153 5 : int OGRCSVLayer::GetFeatureCount( int bForce )
1154 : {
1155 5 : if (bInWriteMode || m_poFilterGeom != NULL || m_poAttrQuery != NULL)
1156 3 : return OGRLayer::GetFeatureCount(bForce);
1157 :
1158 2 : if (nTotalFeatures >= 0)
1159 0 : return nTotalFeatures;
1160 :
1161 2 : if (fpCSV == NULL)
1162 0 : return 0;
1163 :
1164 2 : ResetReading();
1165 :
1166 : char **papszTokens;
1167 2 : nTotalFeatures = 0;
1168 6 : while(TRUE)
1169 : {
1170 8 : papszTokens = OGRCSVReadParseLineL( fpCSV, chDelimiter, bDontHonourStrings );
1171 8 : if( papszTokens == NULL )
1172 : break;
1173 :
1174 6 : if( papszTokens[0] != NULL )
1175 6 : nTotalFeatures ++;
1176 :
1177 6 : CSLDestroy(papszTokens);
1178 : }
1179 :
1180 2 : ResetReading();
1181 :
1182 2 : return nTotalFeatures;
1183 : }
|