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