1 /******************************************************************************
2  * $Id: tigercompletechain.cpp 28382 2015-01-30 15:29:41Z rouault $
3  *
4  * Project:  TIGER/Line Translator
5  * Purpose:  Implements TigerCompleteChain, providing access to RT1 and
6  *           related files.
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 
34 CPL_CVSID("$Id: tigercompletechain.cpp 28382 2015-01-30 15:29:41Z rouault $");
35 
36 static const TigerFieldInfo rt1_2002_fields[] = {
37   // fieldname    fmt  type OFTType      beg  end  len  bDefine bSet bWrite
38   { "MODULE",     ' ', ' ', OFTString,     0,   0,   8,       1,   0,     0 },
39   { "TLID",       'R', 'N', OFTInteger,    6,  15,  10,       1,   1,     1 },
40   { "SIDE1",      'R', 'N', OFTInteger,   16,  16,   1,       1,   1,     1 },
41   { "SOURCE",     'L', 'A', OFTString,    17,  17,   1,       1,   1,     1 },
42   { "FEDIRP",     'L', 'A', OFTString,    18,  19,   2,       1,   1,     1 },
43   { "FENAME",     'L', 'A', OFTString,    20,  49,  30,       1,   1,     1 },
44   { "FETYPE",     'L', 'A', OFTString,    50,  53,   4,       1,   1,     1 },
45   { "FEDIRS",     'L', 'A', OFTString,    54,  55,   2,       1,   1,     1 },
46   { "CFCC",       'L', 'A', OFTString,    56,  58,   3,       1,   1,     1 },
47   { "FRADDL",     'R', 'A', OFTString,    59,  69,  11,       1,   1,     1 },
48   { "TOADDL",     'R', 'A', OFTString,    70,  80,  11,       1,   1,     1 },
49   { "FRADDR",     'R', 'A', OFTString,    81,  91,  11,       1,   1,     1 },
50   { "TOADDR",     'R', 'A', OFTString,    92, 102,  11,       1,   1,     1 },
51   { "FRIADDL",    'L', 'A', OFTString,   103, 103,   1,       1,   1,     1 },
52   { "TOIADDL",    'L', 'A', OFTString,   104, 104,   1,       1,   1,     1 },
53   { "FRIADDR",    'L', 'A', OFTString,   105, 105,   1,       1,   1,     1 },
54   { "TOIADDR",    'L', 'A', OFTString,   106, 106,   1,       1,   1,     1 },
55   { "ZIPL",       'L', 'N', OFTInteger,  107, 111,   5,       1,   1,     1 },
56   { "ZIPR",       'L', 'N', OFTInteger,  112, 116,   5,       1,   1,     1 },
57   { "AIANHHFPL",  'L', 'N', OFTInteger,  117, 121,   5,       1,   1,     1 },
58   { "AIANHHFPR",  'L', 'N', OFTInteger,  122, 126,   5,       1,   1,     1 },
59   { "AIHHTLIL",   'L', 'A', OFTString,   127, 127,   1,       1,   1,     1 },
60   { "AIHHTLIR",   'L', 'A', OFTString,   128, 128,   1,       1,   1,     1 },
61   { "CENSUS1",    'L', 'A', OFTString,   129, 129,   1,       1,   1,     1 },
62   { "CENSUS2",    'L', 'A', OFTString,   130, 130,   1,       1,   1,     1 },
63   { "STATEL",     'L', 'N', OFTInteger,  131, 132,   2,       1,   1,     1 },
64   { "STATER",     'L', 'N', OFTInteger,  133, 134,   2,       1,   1,     1 },
65   { "COUNTYL",    'L', 'N', OFTInteger,  135, 137,   3,       1,   1,     1 },
66   { "COUNTYR",    'L', 'N', OFTInteger,  138, 140,   3,       1,   1,     1 },
67 
68   { "COUSUBL",    'L', 'N', OFTInteger,  141, 145,   5,       1,   1,     1 },
69   { "COUSUBR",    'L', 'N', OFTInteger,  146, 150,   5,       1,   1,     1 },
70   { "SUBMCDL",    'L', 'N', OFTInteger,  151, 155,   5,       1,   1,     1 },
71   { "SUBMCDR",    'L', 'N', OFTInteger,  156, 160,   5,       1,   1,     1 },
72   { "PLACEL",     'L', 'N', OFTInteger,  161, 165,   5,       1,   1,     1 },
73   { "PLACER",     'L', 'N', OFTInteger,  166, 170,   5,       1,   1,     1 },
74   { "TRACTL",     'L', 'N', OFTInteger,  171, 176,   6,       1,   1,     1 },
75   { "TRACTR",     'L', 'N', OFTInteger,  177, 182,   6,       1,   1,     1 },
76   { "BLOCKL",     'L', 'N', OFTInteger,  183, 186,   4,       1,   1,     1 },
77   { "BLOCKR",     'L', 'N', OFTInteger,  187, 190,   4,       1,   1,     1 }
78 };
79 static const TigerRecordInfo rt1_2002_info =
80   {
81     rt1_2002_fields,
82     sizeof(rt1_2002_fields) / sizeof(TigerFieldInfo),
83     228
84   };
85 
86 static const TigerFieldInfo rt1_fields[] = {
87   // fieldname    fmt  type OFTType      beg  end   len  bDefine bSet bWrite
88   { "MODULE",     ' ', ' ', OFTString,     0,   0,   8,       1,   0,     0 },
89   { "TLID",       'R', 'N', OFTInteger,    6,  15,  10,       1,   1,     1 },
90   { "SIDE1",      'R', 'N', OFTInteger,   16,  16,   1,       1,   1,     1 },
91   { "SOURCE",     'L', 'A', OFTString,    17,  17,   1,       1,   1,     1 },
92   { "FEDIRP",     'L', 'A', OFTString,    18,  19,   2,       1,   1,     1 },
93   { "FENAME",     'L', 'A', OFTString,    20,  49,  30,       1,   1,     1 },
94   { "FETYPE",     'L', 'A', OFTString,    50,  53,   4,       1,   1,     1 },
95   { "FEDIRS",     'L', 'A', OFTString,    54,  55,   2,       1,   1,     1 },
96   { "CFCC",       'L', 'A', OFTString,    56,  58,   3,       1,   1,     1 },
97   { "FRADDL",     'R', 'A', OFTString,    59,  69,  11,       1,   1,     1 },
98   { "TOADDL",     'R', 'A', OFTString,    70,  80,  11,       1,   1,     1 },
99   { "FRADDR",     'R', 'A', OFTString,    81,  91,  11,       1,   1,     1 },
100   { "TOADDR",     'R', 'A', OFTString,    92, 102,  11,       1,   1,     1 },
101   { "FRIADDL",    'L', 'A', OFTInteger,  103, 103,   1,       1,   1,     1 },
102   { "TOIADDL",    'L', 'A', OFTInteger,  104, 104,   1,       1,   1,     1 },
103   { "FRIADDR",    'L', 'A', OFTInteger,  105, 105,   1,       1,   1,     1 },
104   { "TOIADDR",    'L', 'A', OFTInteger,  106, 106,   1,       1,   1,     1 },
105   { "ZIPL",       'L', 'N', OFTInteger,  107, 111,   5,       1,   1,     1 },
106   { "ZIPR",       'L', 'N', OFTInteger,  112, 116,   5,       1,   1,     1 },
107   { "FAIRL",      'L', 'N', OFTInteger,  117, 121,   5,       1,   1,     1 },
108   { "FAIRR",      'L', 'N', OFTInteger,  122, 126,   5,       1,   1,     1 },
109   { "TRUSTL",     'L', 'A', OFTString,   127, 127,   1,       1,   1,     1 },
110   { "TRUSTR",     'L', 'A', OFTString,   128, 128,   1,       1,   1,     1 },
111   { "CENSUS1",    'L', 'A', OFTString,   129, 129,   1,       1,   1,     1 },
112   { "CENSUS2",    'L', 'A', OFTString,   130, 130,   1,       1,   1,     1 },
113   { "STATEL",     'L', 'N', OFTInteger,  131, 132,   2,       1,   1,     1 },
114   { "STATER",     'L', 'N', OFTInteger,  133, 134,   2,       1,   1,     1 },
115   { "COUNTYL",    'L', 'N', OFTInteger,  135, 137,   3,       1,   1,     1 },
116   { "COUNTYR",    'L', 'N', OFTInteger,  138, 140,   3,       1,   1,     1 },
117 
118   { "FMCDL",      'L', 'N', OFTInteger,  141, 145,   5,       1,   1,     1 },
119   { "FMCDR",      'L', 'N', OFTInteger,  146, 150,   5,       1,   1,     1 },
120   { "FSMCDL",     'L', 'N', OFTInteger,  151, 155,   5,       1,   1,     1 },
121   { "FSMCDR",     'L', 'N', OFTInteger,  156, 160,   5,       1,   1,     1 },
122   { "FPLL",       'L', 'N', OFTInteger,  161, 165,   5,       1,   1,     1 },
123   { "FPLR",       'L', 'N', OFTInteger,  166, 170,   5,       1,   1,     1 },
124   { "CTBNAL",     'L', 'N', OFTInteger,  171, 176,   6,       1,   1,     1 },
125   { "CTBNAR",     'L', 'N', OFTInteger,  177, 182,   6,       1,   1,     1 },
126   { "BLKL",       'L', 'N', OFTString,   183, 186,   4,       1,   1,     1 },
127   { "BLKR",       'L', 'N', OFTString,   187, 190,   4,       1,   1,     1 }
128 };
129 static const TigerRecordInfo rt1_info =
130   {
131     rt1_fields,
132     sizeof(rt1_fields) / sizeof(TigerFieldInfo),
133     228
134   };
135 
136 static const TigerRecordInfo rt2_info =
137   {
138     NULL,       // RT2 is handled specially in the code below; the only
139     0,          // thing from this structure that is used is:
140     208         // <--- nRecordLength
141   };
142 
143 
144 static const TigerFieldInfo rt3_2000_Redistricting_fields[] = {
145   // fieldname    fmt  type OFTType       beg  end  len  bDefine bSet bWrite
146   { "TLID",       'R', 'N', OFTInteger,     6,  15,  10,       0,   0,     1 },
147   { "STATE90L",   'L', 'N', OFTInteger,    16,  17,   2,       1,   1,     1 },
148   { "STATE90R",   'L', 'N', OFTInteger,    18,  19,   2,       1,   1,     1 },
149   { "COUN90L",    'L', 'N', OFTInteger,    20,  22,   3,       1,   1,     1 },
150   { "COUN90R",    'L', 'N', OFTInteger,    23,  25,   3,       1,   1,     1 },
151   { "FMCD90L",    'L', 'N', OFTInteger,    26,  30,   5,       1,   1,     1 },
152   { "FMCD90R",    'L', 'N', OFTInteger,    31,  35,   5,       1,   1,     1 },
153   { "FPL90L",     'L', 'N', OFTInteger,    36,  40,   5,       1,   1,     1 },
154   { "FPL90R",     'L', 'N', OFTInteger,    41,  45,   5,       1,   1,     1 },
155   { "CTBNA90L",   'L', 'N', OFTInteger,    46,  51,   6,       1,   1,     1 },
156   { "CTBNA90R",   'L', 'N', OFTInteger,    52,  57,   6,       1,   1,     1 },
157   { "AIR90L",     'L', 'N', OFTInteger,    58,  61,   4,       1,   1,     1 },
158   { "AIR90R",     'L', 'N', OFTInteger,    62,  65,   4,       1,   1,     1 },
159   { "TRUST90L",   'L', 'A', OFTString,     66,  66,   1,       1,   1,     1 },
160   { "TRUST90R",   'L', 'A', OFTString,     67,  67,   1,       1,   1,     1 },
161   { "BLK90L",     'L', 'A', OFTString,     70,  73,   4,       1,   1,     1 },
162   { "BLK90R",     'L', 'A', OFTString,     74,  77,   4,       1,   1,     1 },
163   { "AIRL",       'L', 'N', OFTInteger,    78,  81,   4,       1,   1,     1 },
164   { "AIRR",       'L', 'N', OFTInteger,    82,  85,   4,       1,   1,     1 },
165 
166   { "ANRCL",      'L', 'N', OFTInteger,    86,  90,   5,       1,   1,     1 },
167   { "ANRCR",      'L', 'N', OFTInteger,    91,  95,   5,       1,   1,     1 },
168   { "AITSCEL",    'L', 'N', OFTInteger,    96,  98,   3,       1,   1,     1 },
169   { "AITSCER",    'L', 'N', OFTInteger,    99, 101,   3,       1,   1,     1 },
170   { "AITSL",      'L', 'N', OFTInteger,   102, 106,   5 ,      1,   1,     1 },
171   { "AITSR",      'L', 'N', OFTInteger,   107, 111,   5,       1,   1,     1 }
172 };
173 static const TigerRecordInfo rt3_2000_Redistricting_info  =
174   {
175     rt3_2000_Redistricting_fields,
176     sizeof(rt3_2000_Redistricting_fields) / sizeof(TigerFieldInfo),
177     111
178   };
179 
180 static const TigerFieldInfo rt3_fields[] = {
181   // fieldname    fmt  type OFTType       beg  end  len  bDefine bSet bWrite
182   { "TLID",       'R', 'N', OFTInteger,     6,  15,  10,       0,   0,     1 },
183   { "STATE90L",   'L', 'N', OFTInteger,    16,  17,   2,       1,   1,     1 },
184   { "STATE90R",   'L', 'N', OFTInteger,    18,  19,   2,       1,   1,     1 },
185   { "COUN90L",    'L', 'N', OFTInteger,    20,  22,   3,       1,   1,     1 },
186   { "COUN90R",    'L', 'N', OFTInteger,    23,  25,   3,       1,   1,     1 },
187   { "FMCD90L",    'L', 'N', OFTInteger,    26,  30,   5,       1,   1,     1 },
188   { "FMCD90R",    'L', 'N', OFTInteger,    31,  35,   5,       1,   1,     1 },
189   { "FPL90L",     'L', 'N', OFTInteger,    36,  40,   5,       1,   1,     1 },
190   { "FPL90R",     'L', 'N', OFTInteger,    41,  45,   5,       1,   1,     1 },
191   { "CTBNA90L",   'L', 'N', OFTInteger,    46,  51,   6,       1,   1,     1 },
192   { "CTBNA90R",   'L', 'N', OFTInteger,    52,  57,   6,       1,   1,     1 },
193   { "AIR90L",     'L', 'N', OFTInteger,    58,  61,   4,       1,   1,     1 },
194   { "AIR90R",     'L', 'N', OFTInteger,    62,  65,   4,       1,   1,     1 },
195   { "TRUST90L",   'L', 'A', OFTInteger,    66,  66,   1,       1,   1,     1 },
196   { "TRUST90R",   'L', 'A', OFTInteger,    67,  67,   1,       1,   1,     1 },
197   { "BLK90L",     'L', 'A', OFTString,     70,  73,   4,       1,   1,     1 },
198   { "BLK90R",     'L', 'A', OFTString,     74,  77,   4,       1,   1,     1 },
199   { "AIRL",       'L', 'N', OFTInteger,    78,  81,   4,       1,   1,     1 },
200   { "AIRR",       'L', 'N', OFTInteger,    82,  85,   4,       1,   1,     1 },
201 
202   { "VTDL",       'L', 'A', OFTString,    104, 107,   4,       1,   1,     1 },
203   { "VTDR",       'L', 'A', OFTString,    108, 111,   4,       1,   1,     1 }
204 
205 };
206 static const TigerRecordInfo rt3_info =
207   {
208     rt3_fields,
209     sizeof(rt3_fields) / sizeof(TigerFieldInfo),
210     111
211   };
212 
213 /************************************************************************/
214 /*                         TigerCompleteChain()                         */
215 /************************************************************************/
216 
TigerCompleteChain(OGRTigerDataSource * poDSIn,CPL_UNUSED const char * pszPrototypeModule)217 TigerCompleteChain::TigerCompleteChain( OGRTigerDataSource * poDSIn,
218                                         CPL_UNUSED const char * pszPrototypeModule )
219 {
220     poDS = poDSIn;
221     poFeatureDefn = new OGRFeatureDefn( "CompleteChain" );
222     poFeatureDefn->Reference();
223     poFeatureDefn->SetGeomType( wkbLineString );
224 
225 
226     if (poDS->GetVersion() >= TIGER_2002) {
227       psRT1Info = &rt1_2002_info;
228       bUsingRT3 = FALSE;
229     } else {
230       psRT1Info = &rt1_info;
231       bUsingRT3 = TRUE;
232     }
233 
234     psRT2Info = &rt2_info;
235 
236     nRT1RecOffset = 0;
237 
238     if (poDS->GetVersion() >= TIGER_2000_Redistricting) {
239       psRT3Info = &rt3_2000_Redistricting_info;
240     } else {
241       psRT3Info = &rt3_info;
242     }
243 
244     fpRT3 = NULL;
245 
246     panShapeRecordId = NULL;
247     fpShape = NULL;
248 
249     /* -------------------------------------------------------------------- */
250     /*      Fields from type 1 record.                                      */
251     /* -------------------------------------------------------------------- */
252 
253     AddFieldDefns( psRT1Info, poFeatureDefn );
254 
255     /* -------------------------------------------------------------------- */
256     /*      Fields from type 3 record.  Eventually we should verify that    */
257     /*      a .RT3 file is available before adding these fields.            */
258     /* -------------------------------------------------------------------- */
259     if( bUsingRT3 ) {
260       AddFieldDefns( psRT3Info, poFeatureDefn );
261     }
262 }
263 
264 /************************************************************************/
265 /*                        ~TigerCompleteChain()                         */
266 /************************************************************************/
267 
~TigerCompleteChain()268 TigerCompleteChain::~TigerCompleteChain()
269 
270 {
271     CPLFree( panShapeRecordId );
272 
273     if( fpRT3 != NULL )
274         VSIFCloseL( fpRT3 );
275 
276     if( fpShape != NULL )
277         VSIFCloseL( fpShape );
278 }
279 
280 /************************************************************************/
281 /*                             SetModule()                              */
282 /************************************************************************/
283 
SetModule(const char * pszModule)284 int TigerCompleteChain::SetModule( const char * pszModule )
285 
286 {
287     if( !OpenFile( pszModule, "1" ) )
288         return FALSE;
289 
290     EstablishFeatureCount();
291 
292 /* -------------------------------------------------------------------- */
293 /*      Is this a copyright record inserted at the beginning of the     */
294 /*      RT1 file by the folks at GDT?  If so, setup to ignore the       */
295 /*      first record.                                                   */
296 /* -------------------------------------------------------------------- */
297     nRT1RecOffset = 0;
298     if( pszModule )
299     {
300         char achHeader[10];
301 
302         VSIFSeekL( fpPrimary, 0, SEEK_SET );
303         VSIFReadL( achHeader, sizeof(achHeader), 1, fpPrimary );
304 
305         if( EQUALN(achHeader,"Copyright",8) )
306         {
307             nRT1RecOffset = 1;
308             nFeatures--;
309         }
310     }
311 
312 /* -------------------------------------------------------------------- */
313 /*      Open the RT3 file                                               */
314 /* -------------------------------------------------------------------- */
315     if( bUsingRT3 )
316     {
317         if( fpRT3 != NULL )
318         {
319             VSIFCloseL( fpRT3 );
320             fpRT3 = NULL;
321         }
322 
323         if( pszModule )
324         {
325             char        *pszFilename;
326 
327             pszFilename = poDS->BuildFilename( pszModule, "3" );
328 
329             fpRT3 = VSIFOpenL( pszFilename, "rb" );
330 
331             CPLFree( pszFilename );
332         }
333     }
334 
335 /* -------------------------------------------------------------------- */
336 /*      Close the shape point file, if open and free the list of        */
337 /*      record ids.                                                     */
338 /* -------------------------------------------------------------------- */
339     if( fpShape != NULL )
340     {
341         VSIFCloseL( fpShape );
342         fpShape = NULL;
343     }
344 
345     CPLFree( panShapeRecordId );
346     panShapeRecordId = NULL;
347 
348 /* -------------------------------------------------------------------- */
349 /*      Try to open the RT2 file corresponding to this RT1 file.        */
350 /* -------------------------------------------------------------------- */
351     if( pszModule != NULL )
352     {
353         char    *pszFilename;
354 
355         pszFilename = poDS->BuildFilename( pszModule, "2" );
356 
357         fpShape = VSIFOpenL( pszFilename, "rb" );
358 
359         if( fpShape == NULL )
360         {
361             if( nRT1RecOffset == 0 )
362                 CPLError( CE_Warning, CPLE_OpenFailed,
363                           "Failed to open %s, intermediate shape arcs will not be available.\n",
364                           pszFilename );
365         }
366         else
367             panShapeRecordId = (int *)CPLCalloc(sizeof(int),(size_t)GetFeatureCount());
368 
369         CPLFree( pszFilename );
370     }
371 
372     return TRUE;
373 }
374 
375 /************************************************************************/
376 /*                             GetFeature()                             */
377 /************************************************************************/
378 
GetFeature(int nRecordId)379 OGRFeature *TigerCompleteChain::GetFeature( int nRecordId )
380 
381 {
382     char        achRecord[OGR_TIGER_RECBUF_LEN];
383 
384     if( nRecordId < 0 || nRecordId >= nFeatures )
385     {
386         CPLError( CE_Failure, CPLE_FileIO,
387                   "Request for out-of-range feature %d of %s1",
388                   nRecordId, pszModule );
389         return NULL;
390     }
391 
392 /* -------------------------------------------------------------------- */
393 /*      Read the raw record data from the file.                         */
394 /* -------------------------------------------------------------------- */
395     if( fpPrimary == NULL )
396         return NULL;
397 
398     if( VSIFSeekL( fpPrimary, (nRecordId+nRT1RecOffset) * nRecordLength,
399                   SEEK_SET ) != 0 )
400     {
401         CPLError( CE_Failure, CPLE_FileIO,
402                   "Failed to seek to %d of %s1",
403                   nRecordId * nRecordLength, pszModule );
404         return NULL;
405     }
406 
407     if( VSIFReadL( achRecord, psRT1Info->nRecordLength, 1, fpPrimary ) != 1 )
408     {
409         CPLError( CE_Failure, CPLE_FileIO,
410                   "Failed to read %d bytes of record %d of %s1 at offset %d",
411                   psRT1Info->nRecordLength, nRecordId, pszModule,
412                   (nRecordId+nRT1RecOffset) * nRecordLength );
413         return NULL;
414     }
415 
416     /* -------------------------------------------------------------------- */
417     /*      Set fields.                                                     */
418     /* -------------------------------------------------------------------- */
419 
420     OGRFeature  *poFeature = new OGRFeature( poFeatureDefn );
421 
422     SetFields( psRT1Info, poFeature, achRecord );
423 
424     /* -------------------------------------------------------------------- */
425     /*      Read RT3 record, and apply fields.                              */
426     /* -------------------------------------------------------------------- */
427 
428     if( fpRT3 != NULL )
429     {
430         char    achRT3Rec[OGR_TIGER_RECBUF_LEN];
431         int     nRT3RecLen = psRT3Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
432 
433         if( VSIFSeekL( fpRT3, nRecordId * nRT3RecLen, SEEK_SET ) != 0 )
434         {
435             CPLError( CE_Failure, CPLE_FileIO,
436                       "Failed to seek to %d of %s3",
437                       nRecordId * nRT3RecLen, pszModule );
438             return NULL;
439         }
440 
441         if( VSIFReadL( achRT3Rec, psRT3Info->nRecordLength, 1, fpRT3 ) != 1 )
442         {
443             CPLError( CE_Failure, CPLE_FileIO,
444                       "Failed to read record %d of %s3",
445                       nRecordId, pszModule );
446             return NULL;
447         }
448 
449         SetFields( psRT3Info, poFeature, achRT3Rec );
450 
451     }
452 
453 /* -------------------------------------------------------------------- */
454 /*      Set geometry                                                    */
455 /* -------------------------------------------------------------------- */
456     OGRLineString       *poLine = new OGRLineString();
457 
458     poLine->setPoint(0,
459                      atoi(GetField(achRecord, 191, 200)) / 1000000.0,
460                      atoi(GetField(achRecord, 201, 209)) / 1000000.0 );
461 
462     if( !AddShapePoints( poFeature->GetFieldAsInteger("TLID"), nRecordId,
463                          poLine, 0 ) )
464     {
465         delete poFeature;
466         return NULL;
467     }
468 
469     poLine->addPoint(atoi(GetField(achRecord, 210, 219)) / 1000000.0,
470                      atoi(GetField(achRecord, 220, 228)) / 1000000.0 );
471 
472     poFeature->SetGeometryDirectly( poLine );
473 
474     return poFeature;
475 }
476 
477 /************************************************************************/
478 /*                           AddShapePoints()                           */
479 /*                                                                      */
480 /*      Record zero or more shape records associated with this line     */
481 /*      and add the points to the passed line geometry.                 */
482 /************************************************************************/
483 
AddShapePoints(int nTLID,int nRecordId,OGRLineString * poLine,CPL_UNUSED int nSeqNum)484 int TigerCompleteChain::AddShapePoints( int nTLID, int nRecordId,
485                                         OGRLineString * poLine,
486                                         CPL_UNUSED int nSeqNum )
487 {
488     int         nShapeRecId;
489 
490     nShapeRecId = GetShapeRecordId( nRecordId, nTLID );
491 
492     // -2 means an error occured.
493     if( nShapeRecId == -2 )
494         return FALSE;
495 
496     // -1 means there are no extra shape vertices, but things worked fine.
497     if( nShapeRecId == -1 )
498         return TRUE;
499 
500 /* -------------------------------------------------------------------- */
501 /*      Read all the sequential records with the same TLID.             */
502 /* -------------------------------------------------------------------- */
503     char        achShapeRec[OGR_TIGER_RECBUF_LEN];
504     int         nShapeRecLen = psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
505 
506     for( ; TRUE; nShapeRecId++ )
507     {
508         int  nBytesRead = 0;
509 
510         if( VSIFSeekL( fpShape, (nShapeRecId-1) * nShapeRecLen,
511                       SEEK_SET ) != 0 )
512         {
513             CPLError( CE_Failure, CPLE_FileIO,
514                       "Failed to seek to %d of %s2",
515                       (nShapeRecId-1) * nShapeRecLen, pszModule );
516             return FALSE;
517         }
518 
519         nBytesRead = VSIFReadL( achShapeRec, 1, psRT2Info->nRecordLength,
520                                 fpShape );
521 
522         /*
523         ** Handle case where the last record in the file is full.  We will
524         ** try to read another record but not find it.  We require that we
525         ** have found at least one shape record for this case though.
526         */
527         if( nBytesRead <= 0 && VSIFEofL( fpShape )
528             && poLine->getNumPoints() > 0 )
529             break;
530 
531         if( nBytesRead != psRT2Info->nRecordLength )
532         {
533             CPLError( CE_Failure, CPLE_FileIO,
534                       "Failed to read %d bytes of record %d of %s2 at offset %d",
535                       psRT2Info->nRecordLength, nShapeRecId, pszModule,
536                       (nShapeRecId-1) * nShapeRecLen );
537             return FALSE;
538         }
539 
540         if( atoi(GetField(achShapeRec,6,15)) != nTLID )
541             break;
542 
543 /* -------------------------------------------------------------------- */
544 /*      Translate the locations into OGRLineString vertices.            */
545 /* -------------------------------------------------------------------- */
546         int     iVertex;
547 
548         for( iVertex = 0; iVertex < 10; iVertex++ )
549         {
550             int         iStart = 19 + 19*iVertex;
551             int         nX = atoi(GetField(achShapeRec,iStart,iStart+9));
552             int         nY = atoi(GetField(achShapeRec,iStart+10,iStart+18));
553 
554             if( nX == 0 && nY == 0 )
555                 break;
556 
557             poLine->addPoint( nX / 1000000.0, nY / 1000000.0 );
558         }
559 
560 /* -------------------------------------------------------------------- */
561 /*      Don't get another record if this one was incomplete.            */
562 /* -------------------------------------------------------------------- */
563         if( iVertex < 10 )
564             break;
565     }
566 
567     return TRUE;
568 }
569 
570 /************************************************************************/
571 /*                          GetShapeRecordId()                          */
572 /*                                                                      */
573 /*      Get the record id of the first record of shape points for       */
574 /*      the provided TLID (complete chain).                             */
575 /************************************************************************/
576 
GetShapeRecordId(int nChainId,int nTLID)577 int TigerCompleteChain::GetShapeRecordId( int nChainId, int nTLID )
578 
579 {
580     CPLAssert( nChainId >= 0 && nChainId < GetFeatureCount() );
581 
582     if( fpShape == NULL || panShapeRecordId == NULL )
583         return -1;
584 
585 /* -------------------------------------------------------------------- */
586 /*      Do we already have the answer?                                  */
587 /* -------------------------------------------------------------------- */
588     if( panShapeRecordId[nChainId] != 0 )
589         return panShapeRecordId[nChainId];
590 
591 /* -------------------------------------------------------------------- */
592 /*      If we don't already have this value, then search from the       */
593 /*      previous known record.                                          */
594 /* -------------------------------------------------------------------- */
595     int iTestChain, nWorkingRecId;
596 
597     for( iTestChain = nChainId-1;
598          iTestChain >= 0 && panShapeRecordId[iTestChain] <= 0;
599          iTestChain-- ) {}
600 
601     if( iTestChain < 0 )
602     {
603         iTestChain = -1;
604         nWorkingRecId = 1;
605     }
606     else
607     {
608         nWorkingRecId = panShapeRecordId[iTestChain]+1;
609     }
610 
611 /* -------------------------------------------------------------------- */
612 /*      If we have non existent records following (-1's) we can         */
613 /*      narrow our search a bit.                                        */
614 /* -------------------------------------------------------------------- */
615     while( panShapeRecordId[iTestChain+1] == -1 )
616     {
617         iTestChain++;
618     }
619 
620 /* -------------------------------------------------------------------- */
621 /*      Read records up to the maximum distance that is possibly        */
622 /*      required, looking for our target TLID.                          */
623 /* -------------------------------------------------------------------- */
624     int         nMaxChainToRead = nChainId - iTestChain;
625     int         nChainsRead = 0;
626     char        achShapeRec[OGR_TIGER_RECBUF_LEN];
627     int         nShapeRecLen = psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
628 
629     while( nChainsRead < nMaxChainToRead )
630     {
631         if( VSIFSeekL( fpShape, (nWorkingRecId-1) * nShapeRecLen,
632                       SEEK_SET ) != 0 )
633         {
634             CPLError( CE_Failure, CPLE_FileIO,
635                       "Failed to seek to %d of %s2",
636                       (nWorkingRecId-1) * nShapeRecLen, pszModule );
637             return -2;
638         }
639 
640         if( VSIFReadL( achShapeRec, psRT2Info->nRecordLength, 1, fpShape ) != 1 )
641         {
642             if( !VSIFEofL( fpShape ) )
643             {
644                 CPLError( CE_Failure, CPLE_FileIO,
645                           "Failed to read record %d of %s2",
646                           nWorkingRecId-1, pszModule );
647                 return -2;
648             }
649             else
650                 return -1;
651         }
652 
653         if( atoi(GetField(achShapeRec,6,15)) == nTLID )
654         {
655             panShapeRecordId[nChainId] = nWorkingRecId;
656 
657             return nWorkingRecId;
658         }
659 
660         if( atoi(GetField(achShapeRec,16,18)) == 1 )
661         {
662             nChainsRead++;
663         }
664 
665         nWorkingRecId++;
666     }
667 
668     panShapeRecordId[nChainId] = -1;
669 
670     return -1;
671 }
672 
673 /************************************************************************/
674 /*                           SetWriteModule()                           */
675 /************************************************************************/
SetWriteModule(const char * pszFileCode,int nRecLen,OGRFeature * poFeature)676 int TigerCompleteChain::SetWriteModule( const char *pszFileCode, int nRecLen,
677                                         OGRFeature *poFeature )
678 
679 {
680     int bSuccess;
681 
682     bSuccess = TigerFileBase::SetWriteModule( pszFileCode, nRecLen, poFeature);
683     if( !bSuccess )
684         return bSuccess;
685 
686 /* -------------------------------------------------------------------- */
687 /*      Open the RT3 file                                               */
688 /* -------------------------------------------------------------------- */
689     if( bUsingRT3 )
690     {
691         if( fpRT3 != NULL )
692         {
693             VSIFCloseL( fpRT3 );
694             fpRT3 = NULL;
695         }
696 
697         if( pszModule )
698         {
699             char        *pszFilename;
700 
701             pszFilename = poDS->BuildFilename( pszModule, "3" );
702 
703             fpRT3 = VSIFOpenL( pszFilename, "ab" );
704 
705             CPLFree( pszFilename );
706         }
707     }
708 
709 /* -------------------------------------------------------------------- */
710 /*      Close the shape point file, if open and free the list of        */
711 /*      record ids.                                                     */
712 /* -------------------------------------------------------------------- */
713     if( fpShape != NULL )
714     {
715         VSIFCloseL( fpShape );
716         fpShape = NULL;
717     }
718 
719     if( pszModule )
720     {
721         char        *pszFilename;
722 
723         pszFilename = poDS->BuildFilename( pszModule, "2" );
724 
725         fpShape = VSIFOpenL( pszFilename, "ab" );
726 
727         CPLFree( pszFilename );
728     }
729 
730     return TRUE;
731 }
732 
733 /************************************************************************/
734 /*                           CreateFeature()                            */
735 /************************************************************************/
736 
CreateFeature(OGRFeature * poFeature)737 OGRErr TigerCompleteChain::CreateFeature( OGRFeature *poFeature )
738 
739 {
740     char        szRecord[OGR_TIGER_RECBUF_LEN];
741     OGRLineString *poLine = (OGRLineString *) poFeature->GetGeometryRef();
742 
743     if( poLine == NULL
744         || (poLine->getGeometryType() != wkbLineString
745             && poLine->getGeometryType() != wkbLineString25D) )
746         return OGRERR_FAILURE;
747 
748     /* -------------------------------------------------------------------- */
749     /*      Write basic data record ("RT1")                                 */
750     /* -------------------------------------------------------------------- */
751     if( !SetWriteModule( "1", psRT1Info->nRecordLength+2, poFeature ) )
752         return OGRERR_FAILURE;
753     memset( szRecord, ' ', psRT1Info->nRecordLength );
754     WriteFields( psRT1Info, poFeature, szRecord );
755     WritePoint( szRecord, 191, poLine->getX(0), poLine->getY(0) );
756     WritePoint( szRecord, 210,
757                 poLine->getX(poLine->getNumPoints()-1),
758                 poLine->getY(poLine->getNumPoints()-1) );
759     WriteRecord( szRecord, psRT1Info->nRecordLength, "1" );
760 
761     /* -------------------------------------------------------------------- */
762     /*      Write geographic entity codes (RT3)                             */
763     /* -------------------------------------------------------------------- */
764     if (bUsingRT3) {
765       memset( szRecord, ' ', psRT3Info->nRecordLength );
766       WriteFields( psRT3Info, poFeature, szRecord );
767       WriteRecord( szRecord, psRT3Info->nRecordLength, "3", fpRT3 );
768     }
769 
770     /* -------------------------------------------------------------------- */
771     /*      Write shapes sections (RT2)                                     */
772     /* -------------------------------------------------------------------- */
773     if( poLine->getNumPoints() > 2 )
774     {
775         int     nPoints = poLine->getNumPoints();
776         int     iPoint, nRTSQ = 1;
777 
778         for( iPoint = 1; iPoint < nPoints-1; )
779         {
780             int         i;
781             char        szTemp[5];
782 
783             memset( szRecord, ' ', psRT2Info->nRecordLength );
784 
785             WriteField( poFeature, "TLID", szRecord, 6, 15, 'R', 'N' );
786 
787             sprintf( szTemp, "%3d", nRTSQ );
788             strncpy( ((char *)szRecord) + 15, szTemp, 4 );
789 
790             for( i = 0; i < 10; i++ )
791             {
792                 if( iPoint < nPoints-1 )
793                     WritePoint( szRecord, 19+19*i,
794                                 poLine->getX(iPoint), poLine->getY(iPoint) );
795                 else
796                     WritePoint( szRecord, 19+19*i, 0.0, 0.0 );
797 
798                 iPoint++;
799             }
800 
801             WriteRecord( szRecord, psRT2Info->nRecordLength, "2", fpShape );
802 
803             nRTSQ++;
804         }
805     }
806 
807     return OGRERR_NONE;
808 }
809