1 : /******************************************************************************
2 : * $Id: tigerfilebase.cpp 23871 2012-02-02 03:24:07Z warmerdam $
3 : *
4 : * Project: TIGER/Line Translator
5 : * Purpose: Implements TigerBaseFile class, providing common services to all
6 : * the tiger file readers.
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 1999, Frank Warmerdam
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "ogr_tiger.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_error.h"
34 : #include "cpl_string.h"
35 :
36 : CPL_CVSID("$Id: tigerfilebase.cpp 23871 2012-02-02 03:24:07Z warmerdam $");
37 :
38 : /************************************************************************/
39 : /* TigerFileBase() */
40 : /************************************************************************/
41 :
42 180 : TigerFileBase::TigerFileBase( const TigerRecordInfo *psRTInfoIn,
43 180 : const char *m_pszFileCodeIn )
44 :
45 : {
46 180 : pszShortModule = NULL;
47 180 : pszModule = NULL;
48 180 : fpPrimary = NULL;
49 180 : poFeatureDefn = NULL;
50 180 : nFeatures = 0;
51 180 : nVersionCode = 0;
52 180 : nVersion = TIGER_Unknown;
53 :
54 180 : psRTInfo = psRTInfoIn;
55 180 : m_pszFileCode = m_pszFileCodeIn;
56 180 : }
57 :
58 : /************************************************************************/
59 : /* ~TigerFileBase() */
60 : /************************************************************************/
61 :
62 180 : TigerFileBase::~TigerFileBase()
63 :
64 : {
65 180 : CPLFree( pszModule );
66 180 : CPLFree( pszShortModule );
67 :
68 180 : if( poFeatureDefn != NULL )
69 : {
70 180 : poFeatureDefn->Release();
71 180 : poFeatureDefn = NULL;
72 : }
73 :
74 180 : if( fpPrimary != NULL )
75 : {
76 56 : VSIFCloseL( fpPrimary );
77 56 : fpPrimary = NULL;
78 : }
79 180 : }
80 :
81 : /************************************************************************/
82 : /* OpenFile() */
83 : /************************************************************************/
84 :
85 561 : int TigerFileBase::OpenFile( const char * pszModuleToOpen,
86 : const char *pszExtension )
87 :
88 : {
89 : char *pszFilename;
90 :
91 561 : CPLFree( pszModule );
92 561 : pszModule = NULL;
93 561 : CPLFree( pszShortModule );
94 561 : pszShortModule = NULL;
95 :
96 561 : if( fpPrimary != NULL )
97 : {
98 323 : VSIFCloseL( fpPrimary );
99 323 : fpPrimary = NULL;
100 : }
101 :
102 561 : if( pszModuleToOpen == NULL )
103 180 : return TRUE;
104 :
105 381 : pszFilename = poDS->BuildFilename( pszModuleToOpen, pszExtension );
106 :
107 381 : fpPrimary = VSIFOpenL( pszFilename, "rb" );
108 :
109 381 : CPLFree( pszFilename );
110 :
111 381 : if( fpPrimary != NULL )
112 : {
113 363 : pszModule = CPLStrdup(pszModuleToOpen);
114 363 : pszShortModule = CPLStrdup(pszModuleToOpen);
115 4356 : for( int i = 0; pszShortModule[i] != '\0'; i++ )
116 : {
117 3993 : if( pszShortModule[i] == '.' )
118 363 : pszShortModule[i] = '\0';
119 : }
120 :
121 363 : SetupVersion();
122 :
123 363 : return TRUE;
124 : }
125 : else
126 18 : return FALSE;
127 : }
128 :
129 : /************************************************************************/
130 : /* SetupVersion() */
131 : /************************************************************************/
132 :
133 363 : void TigerFileBase::SetupVersion()
134 :
135 : {
136 : char aszRecordHead[6];
137 :
138 363 : VSIFSeekL( fpPrimary, 0, SEEK_SET );
139 363 : VSIFReadL( aszRecordHead, 1, 5, fpPrimary );
140 363 : aszRecordHead[5] = '\0';
141 363 : nVersionCode = atoi(aszRecordHead+1);
142 363 : VSIFSeekL( fpPrimary, 0, SEEK_SET );
143 :
144 363 : nVersion = TigerClassifyVersion( nVersionCode );
145 363 : }
146 :
147 : /************************************************************************/
148 : /* EstablishRecordLength() */
149 : /************************************************************************/
150 :
151 384 : int TigerFileBase::EstablishRecordLength( VSILFILE * fp )
152 :
153 : {
154 : char chCurrent;
155 384 : int nRecLen = 0;
156 :
157 384 : if( fp == NULL || VSIFSeekL( fp, 0, SEEK_SET ) != 0 )
158 0 : return -1;
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* Read through to the end of line. */
162 : /* -------------------------------------------------------------------- */
163 384 : chCurrent = '\0';
164 37252 : while( VSIFReadL( &chCurrent, 1, 1, fp ) == 1
165 : && chCurrent != 10
166 : && chCurrent != 13 )
167 : {
168 36484 : nRecLen++;
169 : }
170 :
171 : /* -------------------------------------------------------------------- */
172 : /* Is the file zero length? */
173 : /* -------------------------------------------------------------------- */
174 384 : if( nRecLen == 0 )
175 : {
176 0 : return -1;
177 : }
178 :
179 384 : nRecLen++; /* for the 10 or 13 we encountered */
180 :
181 : /* -------------------------------------------------------------------- */
182 : /* Read through line terminator characters. We are trying to */
183 : /* handle cases of CR, CR/LF and LF/CR gracefully. */
184 : /* -------------------------------------------------------------------- */
185 1152 : while( VSIFReadL( &chCurrent, 1, 1, fp ) == 1
186 : && (chCurrent == 10 || chCurrent == 13 ) )
187 : {
188 384 : nRecLen++;
189 : }
190 :
191 384 : VSIFSeekL( fp, 0, SEEK_SET );
192 :
193 384 : return nRecLen;
194 : }
195 :
196 : /************************************************************************/
197 : /* EstablishFeatureCount() */
198 : /************************************************************************/
199 :
200 543 : void TigerFileBase::EstablishFeatureCount()
201 :
202 : {
203 543 : if( fpPrimary == NULL )
204 180 : return;
205 :
206 363 : nRecordLength = EstablishRecordLength( fpPrimary );
207 :
208 363 : if( nRecordLength == -1 )
209 : {
210 0 : nRecordLength = 1;
211 0 : nFeatures = 0;
212 0 : return;
213 : }
214 :
215 : /* -------------------------------------------------------------------- */
216 : /* Now we think we know the fixed record length for the file */
217 : /* (including line terminators). Get the total file size, and */
218 : /* divide by this length to get the presumed number of records. */
219 : /* -------------------------------------------------------------------- */
220 : long nFileSize;
221 :
222 363 : VSIFSeekL( fpPrimary, 0, SEEK_END );
223 363 : nFileSize = VSIFTellL( fpPrimary );
224 :
225 363 : if( (nFileSize % nRecordLength) != 0 )
226 : {
227 : CPLError( CE_Warning, CPLE_FileIO,
228 : "TigerFileBase::EstablishFeatureCount(): "
229 : "File length %d doesn't divide by record length %d.\n",
230 0 : (int) nFileSize, (int) nRecordLength );
231 : }
232 :
233 363 : nFeatures = nFileSize / nRecordLength;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetField() */
238 : /************************************************************************/
239 :
240 27870114 : const char* TigerFileBase::GetField( const char * pachRawDataRecord,
241 : int nStartChar, int nEndChar )
242 :
243 : {
244 : char aszField[128];
245 27870114 : int nLength = nEndChar - nStartChar + 1;
246 :
247 27870114 : CPLAssert( nEndChar - nStartChar + 2 < (int) sizeof(aszField) );
248 :
249 27870114 : strncpy( aszField, pachRawDataRecord + nStartChar - 1, nLength );
250 :
251 27870114 : aszField[nLength] = '\0';
252 119255502 : while( nLength > 0 && aszField[nLength-1] == ' ' )
253 63515274 : aszField[--nLength] = '\0';
254 :
255 27870114 : return CPLSPrintf("%s", aszField);
256 : }
257 :
258 : /************************************************************************/
259 : /* SetField() */
260 : /* */
261 : /* Set a field on an OGRFeature from a tiger record, or leave */
262 : /* NULL if the value isn't found. */
263 : /************************************************************************/
264 :
265 19381443 : void TigerFileBase::SetField( OGRFeature *poFeature, const char *pszField,
266 : const char *pachRecord, int nStart, int nEnd )
267 :
268 : {
269 19381443 : const char *pszFieldValue = GetField( pachRecord, nStart, nEnd );
270 :
271 19381443 : if( pszFieldValue[0] == '\0' )
272 9685101 : return;
273 :
274 9696342 : poFeature->SetField( pszField, pszFieldValue );
275 : }
276 :
277 : /************************************************************************/
278 : /* WriteField() */
279 : /* */
280 : /* Write a field into a record buffer with the indicated */
281 : /* formatting, or leave blank if not found. */
282 : /************************************************************************/
283 :
284 1999075 : int TigerFileBase::WriteField( OGRFeature *poFeature, const char *pszField,
285 : char *pachRecord, int nStart, int nEnd,
286 : char chFormat, char chType )
287 :
288 : {
289 1999075 : int iField = poFeature->GetFieldIndex( pszField );
290 : char szValue[512], szFormat[32];
291 :
292 1999075 : CPLAssert( nEnd - nStart + 1 < (int) sizeof(szValue)-1 );
293 :
294 1999075 : if( iField < 0 || !poFeature->IsFieldSet( iField ) )
295 933690 : return FALSE;
296 :
297 1481547 : if( chType == 'N' && chFormat == 'L' )
298 : {
299 416162 : sprintf( szFormat, "%%0%dd", nEnd - nStart + 1 );
300 416162 : sprintf( szValue, szFormat, poFeature->GetFieldAsInteger( iField ) );
301 : }
302 1019006 : else if( chType == 'N' && chFormat == 'R' )
303 : {
304 369783 : sprintf( szFormat, "%%%dd", nEnd - nStart + 1 );
305 369783 : sprintf( szValue, szFormat, poFeature->GetFieldAsInteger( iField ) );
306 : }
307 499399 : else if( chType == 'A' && chFormat == 'L' )
308 : {
309 : strncpy( szValue, poFeature->GetFieldAsString( iField ),
310 219959 : sizeof(szValue) - 1 );
311 219959 : szValue[sizeof(szValue) - 1] = 0;
312 219959 : if( (int) strlen(szValue) < nEnd - nStart + 1 )
313 : memset( szValue + strlen(szValue), ' ',
314 32822 : nEnd - nStart + 1 - strlen(szValue) );
315 : }
316 118962 : else if( chType == 'A' && chFormat == 'R' )
317 : {
318 59481 : sprintf( szFormat, "%%%ds", nEnd - nStart + 1 );
319 59481 : sprintf( szValue, szFormat, poFeature->GetFieldAsString( iField ) );
320 : }
321 : else
322 : {
323 0 : CPLAssert( FALSE );
324 0 : return FALSE;
325 : }
326 :
327 1065385 : strncpy( pachRecord + nStart - 1, szValue, nEnd - nStart + 1 );
328 :
329 1065385 : return TRUE;
330 : }
331 :
332 : /************************************************************************/
333 : /* WritePoint() */
334 : /************************************************************************/
335 :
336 351775 : int TigerFileBase::WritePoint( char *pachRecord, int nStart,
337 : double dfX, double dfY )
338 :
339 : {
340 : char szTemp[20];
341 :
342 440420 : if( dfX == 0.0 && dfY == 0.0 )
343 : {
344 88645 : strncpy( pachRecord + nStart - 1, "+000000000+00000000", 19 );
345 : }
346 : else
347 : {
348 : sprintf( szTemp, "%+10d%+9d",
349 : (int) floor(dfX * 1000000 + 0.5),
350 263130 : (int) floor(dfY * 1000000 + 0.5) );
351 263130 : strncpy( pachRecord + nStart - 1, szTemp, 19 );
352 : }
353 :
354 351775 : return TRUE;
355 : }
356 :
357 : /************************************************************************/
358 : /* WriteRecord() */
359 : /************************************************************************/
360 :
361 160092 : int TigerFileBase::WriteRecord( char *pachRecord, int nRecLen,
362 : const char *pszType, VSILFILE * fp )
363 :
364 : {
365 160092 : if( fp == NULL )
366 124787 : fp = fpPrimary;
367 :
368 160092 : pachRecord[0] = *pszType;
369 :
370 :
371 : /*
372 : * Prior to TIGER_2002, type 5 files lacked the version. So write
373 : * the version in the record iff we're using TIGER_2002 or higher,
374 : * or if this is not type "5"
375 : */
376 160092 : if ( (poDS->GetVersion() >= TIGER_2002) ||
377 : (!EQUAL(pszType, "5")) )
378 : {
379 : char szVersion[5];
380 160092 : sprintf( szVersion, "%04d", poDS->GetVersionCode() );
381 160092 : strncpy( pachRecord + 1, szVersion, 4 );
382 : }
383 :
384 160092 : VSIFWriteL( pachRecord, nRecLen, 1, fp );
385 160092 : VSIFWriteL( (void *) "\r\n", 2, 1, fp );
386 :
387 160092 : return TRUE;
388 : }
389 :
390 : /************************************************************************/
391 : /* SetWriteModule() */
392 : /* */
393 : /* Setup our access to be to the module indicated in the feature. */
394 : /************************************************************************/
395 :
396 124787 : int TigerFileBase::SetWriteModule( const char *pszExtension, int nRecLen,
397 : OGRFeature *poFeature )
398 :
399 : {
400 : /* -------------------------------------------------------------------- */
401 : /* Work out what module we should be writing to. */
402 : /* -------------------------------------------------------------------- */
403 124787 : const char *pszTargetModule = poFeature->GetFieldAsString( "MODULE" );
404 : char szFullModule[30];
405 :
406 : /* TODO/notdef: eventually more logic based on FILE and STATE/COUNTY can
407 : be inserted here. */
408 :
409 124787 : if( pszTargetModule == NULL )
410 0 : return FALSE;
411 :
412 124787 : sprintf( szFullModule, "%s.RT", pszTargetModule );
413 :
414 : /* -------------------------------------------------------------------- */
415 : /* Is this our current module? */
416 : /* -------------------------------------------------------------------- */
417 124787 : if( pszModule != NULL && EQUAL(szFullModule,pszModule) )
418 124771 : return TRUE;
419 :
420 : /* -------------------------------------------------------------------- */
421 : /* Cleanup the previous file, if any. */
422 : /* -------------------------------------------------------------------- */
423 16 : if( fpPrimary != NULL )
424 : {
425 0 : VSIFCloseL( fpPrimary );
426 0 : fpPrimary = NULL;
427 : }
428 :
429 16 : if( pszModule != NULL )
430 : {
431 0 : CPLFree( pszModule );
432 0 : pszModule = NULL;
433 : }
434 :
435 : /* -------------------------------------------------------------------- */
436 : /* Is this a module we have never written to before? If so, we */
437 : /* will try to blow away any existing files in this file set. */
438 : /* -------------------------------------------------------------------- */
439 16 : if( !poDS->CheckModule( szFullModule ) )
440 : {
441 1 : poDS->DeleteModuleFiles( szFullModule );
442 1 : poDS->AddModule( szFullModule );
443 : }
444 :
445 : /* -------------------------------------------------------------------- */
446 : /* Does this file already exist? */
447 : /* -------------------------------------------------------------------- */
448 : char *pszFilename;
449 :
450 16 : pszFilename = poDS->BuildFilename( szFullModule, pszExtension );
451 :
452 16 : fpPrimary = VSIFOpenL( pszFilename, "ab" );
453 16 : CPLFree(pszFilename);
454 16 : if( fpPrimary == NULL )
455 0 : return FALSE;
456 :
457 16 : pszModule = CPLStrdup( szFullModule );
458 :
459 16 : return TRUE;
460 : }
461 :
462 : /************************************************************************/
463 : /* AddFieldDefns() */
464 : /************************************************************************/
465 190 : void TigerFileBase::AddFieldDefns(const TigerRecordInfo *psRTInfo,
466 : OGRFeatureDefn *poFeatureDefn)
467 : {
468 190 : OGRFieldDefn oField("",OFTInteger);
469 : int i, bLFieldHack;
470 :
471 : bLFieldHack =
472 190 : CSLTestBoolean( CPLGetConfigOption( "TIGER_LFIELD_AS_STRING", "NO" ) );
473 :
474 3070 : for (i=0; i<psRTInfo->nFieldCount; ++i) {
475 2880 : if (psRTInfo->pasFields[i].bDefine) {
476 2830 : OGRFieldType eFT = (OGRFieldType)psRTInfo->pasFields[i].OGRtype;
477 :
478 2830 : if( bLFieldHack
479 0 : && psRTInfo->pasFields[i].cFmt == 'L'
480 0 : && psRTInfo->pasFields[i].cType == 'N' )
481 0 : eFT = OFTString;
482 :
483 2830 : oField.Set( psRTInfo->pasFields[i].pszFieldName, eFT,
484 5660 : psRTInfo->pasFields[i].nLen );
485 2830 : poFeatureDefn->AddFieldDefn( &oField );
486 : }
487 190 : }
488 190 : }
489 :
490 : /************************************************************************/
491 : /* SetFields() */
492 : /************************************************************************/
493 :
494 1154138 : void TigerFileBase::SetFields(const TigerRecordInfo *psRTInfo,
495 : OGRFeature *poFeature,
496 : char *achRecord)
497 : {
498 : int i;
499 21854477 : for (i=0; i<psRTInfo->nFieldCount; ++i) {
500 20700339 : if (psRTInfo->pasFields[i].bSet) {
501 : SetField( poFeature,
502 19381443 : psRTInfo->pasFields[i].pszFieldName,
503 : achRecord,
504 19381443 : psRTInfo->pasFields[i].nBeg,
505 58144329 : psRTInfo->pasFields[i].nEnd );
506 : }
507 : }
508 1154138 : }
509 :
510 : /************************************************************************/
511 : /* WriteField() */
512 : /************************************************************************/
513 129231 : void TigerFileBase::WriteFields(const TigerRecordInfo *psRTInfo,
514 : OGRFeature *poFeature,
515 : char *szRecord)
516 : {
517 : int i;
518 2225044 : for (i=0; i<psRTInfo->nFieldCount; ++i) {
519 2095813 : if (psRTInfo->pasFields[i].bWrite) {
520 : WriteField( poFeature,
521 1968214 : psRTInfo->pasFields[i].pszFieldName,
522 : szRecord,
523 1968214 : psRTInfo->pasFields[i].nBeg,
524 1968214 : psRTInfo->pasFields[i].nEnd,
525 1968214 : psRTInfo->pasFields[i].cFmt,
526 9841070 : psRTInfo->pasFields[i].cType );
527 : }
528 : }
529 129231 : }
530 :
531 :
532 :
533 : /************************************************************************/
534 : /* SetModule() */
535 : /************************************************************************/
536 :
537 489 : int TigerFileBase::SetModule( const char * pszModule )
538 :
539 : {
540 489 : if (m_pszFileCode == NULL)
541 0 : return FALSE;
542 :
543 489 : if( !OpenFile( pszModule, m_pszFileCode ) )
544 18 : return FALSE;
545 :
546 471 : EstablishFeatureCount();
547 :
548 471 : return TRUE;
549 : }
550 :
551 : /************************************************************************/
552 : /* GetFeature() */
553 : /************************************************************************/
554 :
555 748461 : OGRFeature *TigerFileBase::GetFeature( int nRecordId )
556 :
557 : {
558 : char achRecord[OGR_TIGER_RECBUF_LEN];
559 :
560 748461 : if (psRTInfo == NULL)
561 0 : return NULL;
562 :
563 748461 : if( nRecordId < 0 || nRecordId >= nFeatures )
564 : {
565 : CPLError( CE_Failure, CPLE_FileIO,
566 : "Request for out-of-range feature %d of %s",
567 0 : nRecordId, pszModule );
568 0 : return NULL;
569 : }
570 :
571 : /* -------------------------------------------------------------------- */
572 : /* Read the raw record data from the file. */
573 : /* -------------------------------------------------------------------- */
574 748461 : if( fpPrimary == NULL )
575 0 : return NULL;
576 :
577 748461 : if( VSIFSeekL( fpPrimary, nRecordId * nRecordLength, SEEK_SET ) != 0 )
578 : {
579 : CPLError( CE_Failure, CPLE_FileIO,
580 : "Failed to seek to %d of %s",
581 0 : nRecordId * nRecordLength, pszModule );
582 0 : return NULL;
583 : }
584 :
585 748461 : if( VSIFReadL( achRecord, psRTInfo->nRecordLength, 1, fpPrimary ) != 1 )
586 : {
587 : CPLError( CE_Failure, CPLE_FileIO,
588 : "Failed to read record %d of %s",
589 0 : nRecordId, pszModule );
590 0 : return NULL;
591 : }
592 :
593 : /* -------------------------------------------------------------------- */
594 : /* Set fields. */
595 : /* -------------------------------------------------------------------- */
596 748461 : OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
597 :
598 748461 : SetFields( psRTInfo, poFeature, achRecord );
599 :
600 748461 : return poFeature;
601 : }
602 :
603 : /************************************************************************/
604 : /* CreateFeature() */
605 : /************************************************************************/
606 :
607 93531 : OGRErr TigerFileBase::CreateFeature( OGRFeature *poFeature )
608 :
609 : {
610 : char szRecord[OGR_TIGER_RECBUF_LEN];
611 :
612 93531 : if (psRTInfo == NULL)
613 0 : return OGRERR_FAILURE;
614 :
615 93531 : if( !SetWriteModule( m_pszFileCode, psRTInfo->nRecordLength+2, poFeature ) )
616 0 : return OGRERR_FAILURE;
617 :
618 93531 : memset( szRecord, ' ', psRTInfo->nRecordLength );
619 :
620 93531 : WriteFields( psRTInfo, poFeature, szRecord );
621 :
622 93531 : WriteRecord( szRecord, psRTInfo->nRecordLength, m_pszFileCode );
623 :
624 93531 : return OGRERR_NONE;
625 : }
|