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