1 : /******************************************************************************
2 : * $Id: tigerfilebase.cpp 22961 2011-08-20 17:09:59Z rouault $
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 22961 2011-08-20 17:09:59Z rouault $");
37 :
38 : /************************************************************************/
39 : /* TigerFileBase() */
40 : /************************************************************************/
41 :
42 126 : TigerFileBase::TigerFileBase( const TigerRecordInfo *psRTInfoIn,
43 126 : const char *m_pszFileCodeIn )
44 :
45 : {
46 126 : pszShortModule = NULL;
47 126 : pszModule = NULL;
48 126 : fpPrimary = NULL;
49 126 : poFeatureDefn = NULL;
50 126 : nFeatures = 0;
51 126 : nVersionCode = 0;
52 126 : nVersion = TIGER_Unknown;
53 :
54 126 : psRTInfo = psRTInfoIn;
55 126 : m_pszFileCode = m_pszFileCodeIn;
56 126 : }
57 :
58 : /************************************************************************/
59 : /* ~TigerFileBase() */
60 : /************************************************************************/
61 :
62 126 : TigerFileBase::~TigerFileBase()
63 :
64 : {
65 126 : CPLFree( pszModule );
66 126 : CPLFree( pszShortModule );
67 :
68 126 : if( poFeatureDefn != NULL )
69 : {
70 126 : poFeatureDefn->Release();
71 126 : poFeatureDefn = NULL;
72 : }
73 :
74 126 : if( fpPrimary != NULL )
75 : {
76 52 : VSIFClose( fpPrimary );
77 52 : fpPrimary = NULL;
78 : }
79 126 : }
80 :
81 : /************************************************************************/
82 : /* OpenFile() */
83 : /************************************************************************/
84 :
85 417 : int TigerFileBase::OpenFile( const char * pszModuleToOpen,
86 : const char *pszExtension )
87 :
88 : {
89 : char *pszFilename;
90 :
91 417 : CPLFree( pszModule );
92 417 : pszModule = NULL;
93 417 : CPLFree( pszShortModule );
94 417 : pszShortModule = NULL;
95 :
96 417 : if( fpPrimary != NULL )
97 : {
98 243 : VSIFClose( fpPrimary );
99 243 : fpPrimary = NULL;
100 : }
101 :
102 417 : if( pszModuleToOpen == NULL )
103 126 : return TRUE;
104 :
105 291 : pszFilename = poDS->BuildFilename( pszModuleToOpen, pszExtension );
106 :
107 291 : fpPrimary = VSIFOpen( pszFilename, "rb" );
108 :
109 291 : CPLFree( pszFilename );
110 :
111 291 : if( fpPrimary != NULL )
112 : {
113 279 : pszModule = CPLStrdup(pszModuleToOpen);
114 279 : pszShortModule = CPLStrdup(pszModuleToOpen);
115 3348 : for( int i = 0; pszShortModule[i] != '\0'; i++ )
116 : {
117 3069 : if( pszShortModule[i] == '.' )
118 279 : pszShortModule[i] = '\0';
119 : }
120 :
121 279 : SetupVersion();
122 :
123 279 : return TRUE;
124 : }
125 : else
126 12 : return FALSE;
127 : }
128 :
129 : /************************************************************************/
130 : /* SetupVersion() */
131 : /************************************************************************/
132 :
133 279 : void TigerFileBase::SetupVersion()
134 :
135 : {
136 : char aszRecordHead[6];
137 :
138 279 : VSIFSeek( fpPrimary, 0, SEEK_SET );
139 279 : VSIFRead( aszRecordHead, 1, 5, fpPrimary );
140 279 : aszRecordHead[5] = '\0';
141 279 : nVersionCode = atoi(aszRecordHead+1);
142 279 : VSIFSeek( fpPrimary, 0, SEEK_SET );
143 :
144 279 : nVersion = TigerClassifyVersion( nVersionCode );
145 279 : }
146 :
147 : /************************************************************************/
148 : /* EstablishRecordLength() */
149 : /************************************************************************/
150 :
151 295 : int TigerFileBase::EstablishRecordLength( FILE * fp )
152 :
153 : {
154 : char chCurrent;
155 295 : int nRecLen = 0;
156 :
157 295 : if( fp == NULL || VSIFSeek( fp, 0, SEEK_SET ) != 0 )
158 0 : return -1;
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* Read through to the end of line. */
162 : /* -------------------------------------------------------------------- */
163 295 : chCurrent = '\0';
164 28596 : while( VSIFRead( &chCurrent, 1, 1, fp ) == 1
165 : && chCurrent != 10
166 : && chCurrent != 13 )
167 : {
168 28006 : nRecLen++;
169 : }
170 :
171 : /* -------------------------------------------------------------------- */
172 : /* Is the file zero length? */
173 : /* -------------------------------------------------------------------- */
174 295 : if( nRecLen == 0 )
175 : {
176 0 : return -1;
177 : }
178 :
179 295 : 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 885 : while( VSIFRead( &chCurrent, 1, 1, fp ) == 1
186 : && (chCurrent == 10 || chCurrent == 13 ) )
187 : {
188 295 : nRecLen++;
189 : }
190 :
191 295 : VSIFSeek( fp, 0, SEEK_SET );
192 :
193 295 : return nRecLen;
194 : }
195 :
196 : /************************************************************************/
197 : /* EstablishFeatureCount() */
198 : /************************************************************************/
199 :
200 405 : void TigerFileBase::EstablishFeatureCount()
201 :
202 : {
203 405 : if( fpPrimary == NULL )
204 126 : return;
205 :
206 279 : nRecordLength = EstablishRecordLength( fpPrimary );
207 :
208 279 : 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 279 : VSIFSeek( fpPrimary, 0, SEEK_END );
223 279 : nFileSize = VSIFTell( fpPrimary );
224 :
225 279 : 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 279 : nFeatures = nFileSize / nRecordLength;
234 : }
235 :
236 : /************************************************************************/
237 : /* GetField() */
238 : /************************************************************************/
239 :
240 22661081 : const char* TigerFileBase::GetField( const char * pachRawDataRecord,
241 : int nStartChar, int nEndChar )
242 :
243 : {
244 : char aszField[128];
245 22661081 : int nLength = nEndChar - nStartChar + 1;
246 :
247 22661081 : CPLAssert( nEndChar - nStartChar + 2 < (int) sizeof(aszField) );
248 :
249 22661081 : strncpy( aszField, pachRawDataRecord + nStartChar - 1, nLength );
250 :
251 22661081 : aszField[nLength] = '\0';
252 95759004 : while( nLength > 0 && aszField[nLength-1] == ' ' )
253 50436842 : aszField[--nLength] = '\0';
254 :
255 22661081 : 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 15489209 : void TigerFileBase::SetField( OGRFeature *poFeature, const char *pszField,
266 : const char *pachRecord, int nStart, int nEnd )
267 :
268 : {
269 15489209 : const char *pszFieldValue = GetField( pachRecord, nStart, nEnd );
270 :
271 15489209 : if( pszFieldValue[0] == '\0' )
272 7817613 : return;
273 :
274 7671596 : 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, FILE * 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 : VSIFWrite( pachRecord, nRecLen, 1, fp );
385 160092 : VSIFWrite( (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 : VSIFClose( 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 = VSIFOpen( 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 133 : void TigerFileBase::AddFieldDefns(const TigerRecordInfo *psRTInfo,
466 : OGRFeatureDefn *poFeatureDefn)
467 : {
468 133 : OGRFieldDefn oField("",OFTInteger);
469 : int i, bLFieldHack;
470 :
471 : bLFieldHack =
472 133 : CSLTestBoolean( CPLGetConfigOption( "TIGER_LFIELD_AS_STRING", "NO" ) );
473 :
474 2149 : for (i=0; i<psRTInfo->nFieldCount; ++i) {
475 2016 : if (psRTInfo->pasFields[i].bDefine) {
476 1981 : OGRFieldType eFT = (OGRFieldType)psRTInfo->pasFields[i].OGRtype;
477 :
478 1981 : if( bLFieldHack
479 0 : && psRTInfo->pasFields[i].cFmt == 'L'
480 0 : && psRTInfo->pasFields[i].cType == 'N' )
481 0 : eFT = OFTString;
482 :
483 1981 : oField.Set( psRTInfo->pasFields[i].pszFieldName, eFT,
484 3962 : psRTInfo->pasFields[i].nLen );
485 1981 : poFeatureDefn->AddFieldDefn( &oField );
486 : }
487 133 : }
488 133 : }
489 :
490 : /************************************************************************/
491 : /* SetFields() */
492 : /************************************************************************/
493 :
494 895668 : void TigerFileBase::SetFields(const TigerRecordInfo *psRTInfo,
495 : OGRFeature *poFeature,
496 : char *achRecord)
497 : {
498 : int i;
499 17404127 : for (i=0; i<psRTInfo->nFieldCount; ++i) {
500 16508459 : if (psRTInfo->pasFields[i].bSet) {
501 : SetField( poFeature,
502 15489209 : psRTInfo->pasFields[i].pszFieldName,
503 : achRecord,
504 15489209 : psRTInfo->pasFields[i].nBeg,
505 46467627 : psRTInfo->pasFields[i].nEnd );
506 : }
507 : }
508 895668 : }
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 363 : int TigerFileBase::SetModule( const char * pszModule )
538 :
539 : {
540 363 : if (m_pszFileCode == NULL)
541 0 : return FALSE;
542 :
543 363 : if( !OpenFile( pszModule, m_pszFileCode ) )
544 12 : return FALSE;
545 :
546 351 : EstablishFeatureCount();
547 :
548 351 : return TRUE;
549 : }
550 :
551 : /************************************************************************/
552 : /* GetFeature() */
553 : /************************************************************************/
554 :
555 561397 : OGRFeature *TigerFileBase::GetFeature( int nRecordId )
556 :
557 : {
558 : char achRecord[OGR_TIGER_RECBUF_LEN];
559 :
560 561397 : if (psRTInfo == NULL)
561 0 : return NULL;
562 :
563 561397 : 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 561397 : if( fpPrimary == NULL )
575 0 : return NULL;
576 :
577 561397 : if( VSIFSeek( 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 561397 : if( VSIFRead( 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 561397 : OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
597 :
598 561397 : SetFields( psRTInfo, poFeature, achRecord );
599 :
600 561397 : 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 : }
|