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