Line data Source code
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 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_tiger.h"
15 : #include "cpl_conv.h"
16 :
17 : #include <cinttypes>
18 :
19 : static const TigerFieldInfo rt1_2002_fields[] = {
20 : // fieldname fmt type OFTType beg end len bDefine bSet
21 : {"MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0},
22 : {"TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1},
23 : {"SIDE1", 'R', 'N', OFTInteger, 16, 16, 1, 1, 1},
24 : {"SOURCE", 'L', 'A', OFTString, 17, 17, 1, 1, 1},
25 : {"FEDIRP", 'L', 'A', OFTString, 18, 19, 2, 1, 1},
26 : {"FENAME", 'L', 'A', OFTString, 20, 49, 30, 1, 1},
27 : {"FETYPE", 'L', 'A', OFTString, 50, 53, 4, 1, 1},
28 : {"FEDIRS", 'L', 'A', OFTString, 54, 55, 2, 1, 1},
29 : {"CFCC", 'L', 'A', OFTString, 56, 58, 3, 1, 1},
30 : {"FRADDL", 'R', 'A', OFTString, 59, 69, 11, 1, 1},
31 : {"TOADDL", 'R', 'A', OFTString, 70, 80, 11, 1, 1},
32 : {"FRADDR", 'R', 'A', OFTString, 81, 91, 11, 1, 1},
33 : {"TOADDR", 'R', 'A', OFTString, 92, 102, 11, 1, 1},
34 : {"FRIADDL", 'L', 'A', OFTString, 103, 103, 1, 1, 1},
35 : {"TOIADDL", 'L', 'A', OFTString, 104, 104, 1, 1, 1},
36 : {"FRIADDR", 'L', 'A', OFTString, 105, 105, 1, 1, 1},
37 : {"TOIADDR", 'L', 'A', OFTString, 106, 106, 1, 1, 1},
38 : {"ZIPL", 'L', 'N', OFTInteger, 107, 111, 5, 1, 1},
39 : {"ZIPR", 'L', 'N', OFTInteger, 112, 116, 5, 1, 1},
40 : {"AIANHHFPL", 'L', 'N', OFTInteger, 117, 121, 5, 1, 1},
41 : {"AIANHHFPR", 'L', 'N', OFTInteger, 122, 126, 5, 1, 1},
42 : {"AIHHTLIL", 'L', 'A', OFTString, 127, 127, 1, 1, 1},
43 : {"AIHHTLIR", 'L', 'A', OFTString, 128, 128, 1, 1, 1},
44 : {"CENSUS1", 'L', 'A', OFTString, 129, 129, 1, 1, 1},
45 : {"CENSUS2", 'L', 'A', OFTString, 130, 130, 1, 1, 1},
46 : {"STATEL", 'L', 'N', OFTInteger, 131, 132, 2, 1, 1},
47 : {"STATER", 'L', 'N', OFTInteger, 133, 134, 2, 1, 1},
48 : {"COUNTYL", 'L', 'N', OFTInteger, 135, 137, 3, 1, 1},
49 : {"COUNTYR", 'L', 'N', OFTInteger, 138, 140, 3, 1, 1},
50 :
51 : {"COUSUBL", 'L', 'N', OFTInteger, 141, 145, 5, 1, 1},
52 : {"COUSUBR", 'L', 'N', OFTInteger, 146, 150, 5, 1, 1},
53 : {"SUBMCDL", 'L', 'N', OFTInteger, 151, 155, 5, 1, 1},
54 : {"SUBMCDR", 'L', 'N', OFTInteger, 156, 160, 5, 1, 1},
55 : {"PLACEL", 'L', 'N', OFTInteger, 161, 165, 5, 1, 1},
56 : {"PLACER", 'L', 'N', OFTInteger, 166, 170, 5, 1, 1},
57 : {"TRACTL", 'L', 'N', OFTInteger, 171, 176, 6, 1, 1},
58 : {"TRACTR", 'L', 'N', OFTInteger, 177, 182, 6, 1, 1},
59 : {"BLOCKL", 'L', 'N', OFTInteger, 183, 186, 4, 1, 1},
60 : {"BLOCKR", 'L', 'N', OFTInteger, 187, 190, 4, 1, 1}};
61 : static const TigerRecordInfo rt1_2002_info = {
62 : rt1_2002_fields, sizeof(rt1_2002_fields) / sizeof(TigerFieldInfo), 228};
63 :
64 : static const TigerFieldInfo rt1_fields[] = {
65 : // fieldname fmt type OFTType beg end len bDefine bSet
66 : {"MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0},
67 : {"TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1},
68 : {"SIDE1", 'R', 'N', OFTInteger, 16, 16, 1, 1, 1},
69 : {"SOURCE", 'L', 'A', OFTString, 17, 17, 1, 1, 1},
70 : {"FEDIRP", 'L', 'A', OFTString, 18, 19, 2, 1, 1},
71 : {"FENAME", 'L', 'A', OFTString, 20, 49, 30, 1, 1},
72 : {"FETYPE", 'L', 'A', OFTString, 50, 53, 4, 1, 1},
73 : {"FEDIRS", 'L', 'A', OFTString, 54, 55, 2, 1, 1},
74 : {"CFCC", 'L', 'A', OFTString, 56, 58, 3, 1, 1},
75 : {"FRADDL", 'R', 'A', OFTString, 59, 69, 11, 1, 1},
76 : {"TOADDL", 'R', 'A', OFTString, 70, 80, 11, 1, 1},
77 : {"FRADDR", 'R', 'A', OFTString, 81, 91, 11, 1, 1},
78 : {"TOADDR", 'R', 'A', OFTString, 92, 102, 11, 1, 1},
79 : {"FRIADDL", 'L', 'A', OFTInteger, 103, 103, 1, 1, 1},
80 : {"TOIADDL", 'L', 'A', OFTInteger, 104, 104, 1, 1, 1},
81 : {"FRIADDR", 'L', 'A', OFTInteger, 105, 105, 1, 1, 1},
82 : {"TOIADDR", 'L', 'A', OFTInteger, 106, 106, 1, 1, 1},
83 : {"ZIPL", 'L', 'N', OFTInteger, 107, 111, 5, 1, 1},
84 : {"ZIPR", 'L', 'N', OFTInteger, 112, 116, 5, 1, 1},
85 : {"FAIRL", 'L', 'N', OFTInteger, 117, 121, 5, 1, 1},
86 : {"FAIRR", 'L', 'N', OFTInteger, 122, 126, 5, 1, 1},
87 : {"TRUSTL", 'L', 'A', OFTString, 127, 127, 1, 1, 1},
88 : {"TRUSTR", 'L', 'A', OFTString, 128, 128, 1, 1, 1},
89 : {"CENSUS1", 'L', 'A', OFTString, 129, 129, 1, 1, 1},
90 : {"CENSUS2", 'L', 'A', OFTString, 130, 130, 1, 1, 1},
91 : {"STATEL", 'L', 'N', OFTInteger, 131, 132, 2, 1, 1},
92 : {"STATER", 'L', 'N', OFTInteger, 133, 134, 2, 1, 1},
93 : {"COUNTYL", 'L', 'N', OFTInteger, 135, 137, 3, 1, 1},
94 : {"COUNTYR", 'L', 'N', OFTInteger, 138, 140, 3, 1, 1},
95 :
96 : {"FMCDL", 'L', 'N', OFTInteger, 141, 145, 5, 1, 1},
97 : {"FMCDR", 'L', 'N', OFTInteger, 146, 150, 5, 1, 1},
98 : {"FSMCDL", 'L', 'N', OFTInteger, 151, 155, 5, 1, 1},
99 : {"FSMCDR", 'L', 'N', OFTInteger, 156, 160, 5, 1, 1},
100 : {"FPLL", 'L', 'N', OFTInteger, 161, 165, 5, 1, 1},
101 : {"FPLR", 'L', 'N', OFTInteger, 166, 170, 5, 1, 1},
102 : {"CTBNAL", 'L', 'N', OFTInteger, 171, 176, 6, 1, 1},
103 : {"CTBNAR", 'L', 'N', OFTInteger, 177, 182, 6, 1, 1},
104 : {"BLKL", 'L', 'N', OFTString, 183, 186, 4, 1, 1},
105 : {"BLKR", 'L', 'N', OFTString, 187, 190, 4, 1, 1}};
106 : static const TigerRecordInfo rt1_info = {
107 : rt1_fields, sizeof(rt1_fields) / sizeof(TigerFieldInfo), 228};
108 :
109 : static const TigerRecordInfo rt2_info = {
110 : nullptr, // RT2 is handled specially in the code below; the only
111 : 0, // thing from this structure that is used is:
112 : 208 // <--- nRecordLength
113 : };
114 :
115 : static const TigerFieldInfo rt3_2000_Redistricting_fields[] = {
116 : // fieldname fmt type OFTType beg end len bDefine bSet
117 : {"TLID", 'R', 'N', OFTInteger, 6, 15, 10, 0, 0},
118 : {"STATE90L", 'L', 'N', OFTInteger, 16, 17, 2, 1, 1},
119 : {"STATE90R", 'L', 'N', OFTInteger, 18, 19, 2, 1, 1},
120 : {"COUN90L", 'L', 'N', OFTInteger, 20, 22, 3, 1, 1},
121 : {"COUN90R", 'L', 'N', OFTInteger, 23, 25, 3, 1, 1},
122 : {"FMCD90L", 'L', 'N', OFTInteger, 26, 30, 5, 1, 1},
123 : {"FMCD90R", 'L', 'N', OFTInteger, 31, 35, 5, 1, 1},
124 : {"FPL90L", 'L', 'N', OFTInteger, 36, 40, 5, 1, 1},
125 : {"FPL90R", 'L', 'N', OFTInteger, 41, 45, 5, 1, 1},
126 : {"CTBNA90L", 'L', 'N', OFTInteger, 46, 51, 6, 1, 1},
127 : {"CTBNA90R", 'L', 'N', OFTInteger, 52, 57, 6, 1, 1},
128 : {"AIR90L", 'L', 'N', OFTInteger, 58, 61, 4, 1, 1},
129 : {"AIR90R", 'L', 'N', OFTInteger, 62, 65, 4, 1, 1},
130 : {"TRUST90L", 'L', 'A', OFTString, 66, 66, 1, 1, 1},
131 : {"TRUST90R", 'L', 'A', OFTString, 67, 67, 1, 1, 1},
132 : {"BLK90L", 'L', 'A', OFTString, 70, 73, 4, 1, 1},
133 : {"BLK90R", 'L', 'A', OFTString, 74, 77, 4, 1, 1},
134 : {"AIRL", 'L', 'N', OFTInteger, 78, 81, 4, 1, 1},
135 : {"AIRR", 'L', 'N', OFTInteger, 82, 85, 4, 1, 1},
136 :
137 : {"ANRCL", 'L', 'N', OFTInteger, 86, 90, 5, 1, 1},
138 : {"ANRCR", 'L', 'N', OFTInteger, 91, 95, 5, 1, 1},
139 : {"AITSCEL", 'L', 'N', OFTInteger, 96, 98, 3, 1, 1},
140 : {"AITSCER", 'L', 'N', OFTInteger, 99, 101, 3, 1, 1},
141 : {"AITSL", 'L', 'N', OFTInteger, 102, 106, 5, 1, 1},
142 : {"AITSR", 'L', 'N', OFTInteger, 107, 111, 5, 1, 1}};
143 : static const TigerRecordInfo rt3_2000_Redistricting_info = {
144 : rt3_2000_Redistricting_fields,
145 : sizeof(rt3_2000_Redistricting_fields) / sizeof(TigerFieldInfo), 111};
146 :
147 : static const TigerFieldInfo rt3_fields[] = {
148 : // fieldname fmt type OFTType beg end len bDefine bSet
149 : {"TLID", 'R', 'N', OFTInteger, 6, 15, 10, 0, 0},
150 : {"STATE90L", 'L', 'N', OFTInteger, 16, 17, 2, 1, 1},
151 : {"STATE90R", 'L', 'N', OFTInteger, 18, 19, 2, 1, 1},
152 : {"COUN90L", 'L', 'N', OFTInteger, 20, 22, 3, 1, 1},
153 : {"COUN90R", 'L', 'N', OFTInteger, 23, 25, 3, 1, 1},
154 : {"FMCD90L", 'L', 'N', OFTInteger, 26, 30, 5, 1, 1},
155 : {"FMCD90R", 'L', 'N', OFTInteger, 31, 35, 5, 1, 1},
156 : {"FPL90L", 'L', 'N', OFTInteger, 36, 40, 5, 1, 1},
157 : {"FPL90R", 'L', 'N', OFTInteger, 41, 45, 5, 1, 1},
158 : {"CTBNA90L", 'L', 'N', OFTInteger, 46, 51, 6, 1, 1},
159 : {"CTBNA90R", 'L', 'N', OFTInteger, 52, 57, 6, 1, 1},
160 : {"AIR90L", 'L', 'N', OFTInteger, 58, 61, 4, 1, 1},
161 : {"AIR90R", 'L', 'N', OFTInteger, 62, 65, 4, 1, 1},
162 : {"TRUST90L", 'L', 'A', OFTInteger, 66, 66, 1, 1, 1},
163 : {"TRUST90R", 'L', 'A', OFTInteger, 67, 67, 1, 1, 1},
164 : {"BLK90L", 'L', 'A', OFTString, 70, 73, 4, 1, 1},
165 : {"BLK90R", 'L', 'A', OFTString, 74, 77, 4, 1, 1},
166 : {"AIRL", 'L', 'N', OFTInteger, 78, 81, 4, 1, 1},
167 : {"AIRR", 'L', 'N', OFTInteger, 82, 85, 4, 1, 1},
168 :
169 : {"VTDL", 'L', 'A', OFTString, 104, 107, 4, 1, 1},
170 : {"VTDR", 'L', 'A', OFTString, 108, 111, 4, 1, 1}};
171 :
172 : static const TigerRecordInfo rt3_info = {
173 : rt3_fields, sizeof(rt3_fields) / sizeof(TigerFieldInfo), 111};
174 :
175 : /************************************************************************/
176 : /* TigerCompleteChain() */
177 : /************************************************************************/
178 :
179 0 : TigerCompleteChain::TigerCompleteChain(OGRTigerDataSource *poDSIn,
180 0 : const char * /* pszPrototypeModule */)
181 : : fpShape(nullptr), panShapeRecordId(nullptr), fpRT3(nullptr),
182 : bUsingRT3(false), nRT1RecOffset(0), psRT1Info(nullptr),
183 0 : psRT2Info(nullptr), psRT3Info(nullptr)
184 : {
185 0 : poDS = poDSIn;
186 0 : poFeatureDefn = new OGRFeatureDefn("CompleteChain");
187 0 : poFeatureDefn->Reference();
188 0 : poFeatureDefn->SetGeomType(wkbLineString);
189 :
190 0 : if (poDS->GetVersion() >= TIGER_2002)
191 : {
192 0 : psRT1Info = &rt1_2002_info;
193 : // bUsingRT3 = false;
194 : }
195 : else
196 : {
197 0 : psRT1Info = &rt1_info;
198 0 : bUsingRT3 = true;
199 : }
200 :
201 0 : psRT2Info = &rt2_info;
202 :
203 0 : if (poDS->GetVersion() >= TIGER_2000_Redistricting)
204 : {
205 0 : psRT3Info = &rt3_2000_Redistricting_info;
206 : }
207 : else
208 : {
209 0 : psRT3Info = &rt3_info;
210 : }
211 :
212 : /* -------------------------------------------------------------------- */
213 : /* Fields from type 1 record. */
214 : /* -------------------------------------------------------------------- */
215 :
216 0 : AddFieldDefns(psRT1Info, poFeatureDefn);
217 :
218 : /* -------------------------------------------------------------------- */
219 : /* Fields from type 3 record. Eventually we should verify that */
220 : /* a .RT3 file is available before adding these fields. */
221 : /* -------------------------------------------------------------------- */
222 0 : if (bUsingRT3)
223 : {
224 0 : AddFieldDefns(psRT3Info, poFeatureDefn);
225 : }
226 0 : }
227 :
228 : /************************************************************************/
229 : /* ~TigerCompleteChain() */
230 : /************************************************************************/
231 :
232 0 : TigerCompleteChain::~TigerCompleteChain()
233 :
234 : {
235 0 : CPLFree(panShapeRecordId);
236 :
237 0 : if (fpRT3 != nullptr)
238 0 : VSIFCloseL(fpRT3);
239 :
240 0 : if (fpShape != nullptr)
241 0 : VSIFCloseL(fpShape);
242 0 : }
243 :
244 : /************************************************************************/
245 : /* SetModule() */
246 : /************************************************************************/
247 :
248 0 : bool TigerCompleteChain::SetModule(const char *pszModuleIn)
249 :
250 : {
251 0 : if (!OpenFile(pszModuleIn, "1"))
252 0 : return false;
253 :
254 0 : EstablishFeatureCount();
255 :
256 : /* -------------------------------------------------------------------- */
257 : /* Is this a copyright record inserted at the beginning of the */
258 : /* RT1 file by the folks at GDT? If so, setup to ignore the */
259 : /* first record. */
260 : /* -------------------------------------------------------------------- */
261 0 : nRT1RecOffset = 0;
262 0 : if (pszModuleIn)
263 : {
264 : char achHeader[10];
265 :
266 0 : VSIFSeekL(fpPrimary, 0, SEEK_SET);
267 0 : VSIFReadL(achHeader, sizeof(achHeader), 1, fpPrimary);
268 :
269 0 : if (STARTS_WITH_CI(achHeader, "Copyright"))
270 : {
271 0 : nRT1RecOffset = 1;
272 0 : nFeatures--;
273 : }
274 : }
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* Open the RT3 file */
278 : /* -------------------------------------------------------------------- */
279 0 : if (bUsingRT3)
280 : {
281 0 : if (fpRT3 != nullptr)
282 : {
283 0 : VSIFCloseL(fpRT3);
284 0 : fpRT3 = nullptr;
285 : }
286 :
287 0 : if (pszModuleIn)
288 : {
289 0 : char *pszFilename = poDS->BuildFilename(pszModuleIn, "3");
290 :
291 0 : fpRT3 = VSIFOpenL(pszFilename, "rb");
292 :
293 0 : CPLFree(pszFilename);
294 : }
295 : }
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Close the shape point file, if open and free the list of */
299 : /* record ids. */
300 : /* -------------------------------------------------------------------- */
301 0 : if (fpShape != nullptr)
302 : {
303 0 : VSIFCloseL(fpShape);
304 0 : fpShape = nullptr;
305 : }
306 :
307 0 : CPLFree(panShapeRecordId);
308 0 : panShapeRecordId = nullptr;
309 :
310 : /* -------------------------------------------------------------------- */
311 : /* Try to open the RT2 file corresponding to this RT1 file. */
312 : /* -------------------------------------------------------------------- */
313 0 : if (pszModuleIn != nullptr)
314 : {
315 0 : char *pszFilename = poDS->BuildFilename(pszModuleIn, "2");
316 :
317 0 : fpShape = VSIFOpenL(pszFilename, "rb");
318 :
319 0 : if (fpShape == nullptr)
320 : {
321 0 : if (nRT1RecOffset == 0)
322 0 : CPLError(CE_Warning, CPLE_OpenFailed,
323 : "Failed to open %s, intermediate shape arcs will not "
324 : "be available.\n",
325 : pszFilename);
326 : }
327 : else
328 0 : panShapeRecordId =
329 0 : (int *)CPLCalloc(sizeof(int), (size_t)GetFeatureCount());
330 :
331 0 : CPLFree(pszFilename);
332 : }
333 :
334 0 : return true;
335 : }
336 :
337 : /************************************************************************/
338 : /* GetFeature() */
339 : /************************************************************************/
340 :
341 0 : OGRFeature *TigerCompleteChain::GetFeature(int nRecordId)
342 :
343 : {
344 : char achRecord[OGR_TIGER_RECBUF_LEN];
345 :
346 0 : if (nRecordId < 0 || nRecordId >= nFeatures)
347 : {
348 0 : CPLError(CE_Failure, CPLE_FileIO,
349 : "Request for out-of-range feature %d of %s1", nRecordId,
350 : pszModule);
351 0 : return nullptr;
352 : }
353 :
354 : /* -------------------------------------------------------------------- */
355 : /* Read the raw record data from the file. */
356 : /* -------------------------------------------------------------------- */
357 0 : if (fpPrimary == nullptr)
358 0 : return nullptr;
359 :
360 : {
361 0 : const auto nOffset =
362 0 : static_cast<uint64_t>(nRecordId + nRT1RecOffset) * nRecordLength;
363 0 : if (VSIFSeekL(fpPrimary, nOffset, SEEK_SET) != 0)
364 : {
365 0 : CPLError(CE_Failure, CPLE_FileIO,
366 : "Failed to seek to %" PRIu64 " of %s1", nOffset,
367 : pszModule);
368 0 : return nullptr;
369 : }
370 : }
371 :
372 : // Overflow cannot happen since psRTInfo->nRecordLength is unsigned
373 : // char and sizeof(achRecord) == OGR_TIGER_RECBUF_LEN > 255
374 0 : if (VSIFReadL(achRecord, psRT1Info->nRecordLength, 1, fpPrimary) != 1)
375 : {
376 0 : CPLError(CE_Failure, CPLE_FileIO,
377 : "Failed to read %d bytes of record %d of %s1 at offset %d",
378 0 : psRT1Info->nRecordLength, nRecordId, pszModule,
379 0 : (nRecordId + nRT1RecOffset) * nRecordLength);
380 0 : return nullptr;
381 : }
382 :
383 : /* -------------------------------------------------------------------- */
384 : /* Set fields. */
385 : /* -------------------------------------------------------------------- */
386 :
387 0 : auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn);
388 :
389 0 : SetFields(psRT1Info, poFeature.get(), achRecord);
390 :
391 : /* -------------------------------------------------------------------- */
392 : /* Read RT3 record, and apply fields. */
393 : /* -------------------------------------------------------------------- */
394 :
395 0 : if (fpRT3 != nullptr)
396 : {
397 : char achRT3Rec[OGR_TIGER_RECBUF_LEN];
398 0 : int nRT3RecLen =
399 0 : psRT3Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
400 :
401 0 : const auto nOffset = static_cast<uint64_t>(nRecordId) * nRT3RecLen;
402 0 : if (VSIFSeekL(fpRT3, nOffset, SEEK_SET) != 0)
403 : {
404 0 : CPLError(CE_Failure, CPLE_FileIO,
405 : "Failed to seek to %" PRIu64 " of %s3", nOffset,
406 : pszModule);
407 0 : return nullptr;
408 : }
409 :
410 : // Overflow cannot happen since psRTInfo->nRecordLength is unsigned
411 : // char and sizeof(achRecord) == OGR_TIGER_RECBUF_LEN > 255
412 0 : if (VSIFReadL(achRT3Rec, psRT3Info->nRecordLength, 1, fpRT3) != 1)
413 : {
414 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to read record %d of %s3",
415 : nRecordId, pszModule);
416 0 : return nullptr;
417 : }
418 :
419 0 : SetFields(psRT3Info, poFeature.get(), achRT3Rec);
420 : }
421 :
422 : /* -------------------------------------------------------------------- */
423 : /* Set geometry */
424 : /* -------------------------------------------------------------------- */
425 0 : auto poLine = std::make_unique<OGRLineString>();
426 :
427 0 : poLine->setPoint(0, atoi(GetField(achRecord, 191, 200)) / 1000000.0,
428 0 : atoi(GetField(achRecord, 201, 209)) / 1000000.0);
429 :
430 0 : if (!AddShapePoints(poFeature->GetFieldAsInteger("TLID"), nRecordId,
431 : poLine.get(), 0))
432 : {
433 0 : return nullptr;
434 : }
435 :
436 0 : poLine->addPoint(atoi(GetField(achRecord, 210, 219)) / 1000000.0,
437 0 : atoi(GetField(achRecord, 220, 228)) / 1000000.0);
438 :
439 0 : poFeature->SetGeometryDirectly(poLine.release());
440 :
441 0 : return poFeature.release();
442 : }
443 :
444 : /************************************************************************/
445 : /* AddShapePoints() */
446 : /* */
447 : /* Record zero or more shape records associated with this line */
448 : /* and add the points to the passed line geometry. */
449 : /************************************************************************/
450 :
451 0 : bool TigerCompleteChain::AddShapePoints(int nTLID, int nRecordId,
452 : OGRLineString *poLine,
453 : CPL_UNUSED int nSeqNum)
454 : {
455 0 : int nShapeRecId = GetShapeRecordId(nRecordId, nTLID);
456 :
457 : // -2 means an error occurred.
458 0 : if (nShapeRecId == -2)
459 0 : return false;
460 :
461 : // -1 means there are no extra shape vertices, but things worked fine.
462 0 : if (nShapeRecId == -1)
463 0 : return true;
464 :
465 : /* -------------------------------------------------------------------- */
466 : /* Read all the sequential records with the same TLID. */
467 : /* -------------------------------------------------------------------- */
468 : char achShapeRec[OGR_TIGER_RECBUF_LEN];
469 0 : const int nShapeRecLen =
470 0 : psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
471 :
472 0 : for (; true; nShapeRecId++)
473 : {
474 0 : int nBytesRead = 0;
475 :
476 0 : const auto nOffset =
477 0 : static_cast<uint64_t>(nShapeRecId - 1) * nShapeRecLen;
478 0 : if (VSIFSeekL(fpShape, nOffset, SEEK_SET) != 0)
479 : {
480 0 : CPLError(CE_Failure, CPLE_FileIO,
481 : "Failed to seek to %" PRIu64 " of %s2", nOffset,
482 : pszModule);
483 0 : return false;
484 : }
485 :
486 0 : nBytesRead = static_cast<int>(
487 0 : VSIFReadL(achShapeRec, 1, psRT2Info->nRecordLength, fpShape));
488 :
489 : /*
490 : ** Handle case where the last record in the file is full. We will
491 : ** try to read another record but not find it. We require that we
492 : ** have found at least one shape record for this case though.
493 : */
494 0 : if (nBytesRead <= 0 && VSIFEofL(fpShape) && poLine->getNumPoints() > 0)
495 0 : break;
496 :
497 0 : if (nBytesRead != psRT2Info->nRecordLength)
498 : {
499 0 : CPLError(CE_Failure, CPLE_FileIO,
500 : "Failed to read %d bytes of record %d of %s2 at offset %d",
501 0 : psRT2Info->nRecordLength, nShapeRecId, pszModule,
502 0 : (nShapeRecId - 1) * nShapeRecLen);
503 0 : return false;
504 : }
505 :
506 0 : if (atoi(GetField(achShapeRec, 6, 15)) != nTLID)
507 0 : break;
508 :
509 : /* --------------------------------------------------------------------
510 : */
511 : /* Translate the locations into OGRLineString vertices. */
512 : /* --------------------------------------------------------------------
513 : */
514 0 : int iVertex = 0; // Used after for.
515 :
516 0 : for (; iVertex < 10; iVertex++)
517 : {
518 0 : const int iStart = 19 + 19 * iVertex;
519 0 : const int nX = atoi(GetField(achShapeRec, iStart, iStart + 9));
520 : const int nY =
521 0 : atoi(GetField(achShapeRec, iStart + 10, iStart + 18));
522 :
523 0 : if (nX == 0 && nY == 0)
524 0 : break;
525 :
526 0 : poLine->addPoint(nX / 1000000.0, nY / 1000000.0);
527 : }
528 :
529 : /* --------------------------------------------------------------------
530 : */
531 : /* Don't get another record if this one was incomplete. */
532 : /* --------------------------------------------------------------------
533 : */
534 0 : if (iVertex < 10)
535 0 : break;
536 0 : }
537 :
538 0 : return true;
539 : }
540 :
541 : /************************************************************************/
542 : /* GetShapeRecordId() */
543 : /* */
544 : /* Get the record id of the first record of shape points for */
545 : /* the provided TLID (complete chain). */
546 : /************************************************************************/
547 :
548 0 : int TigerCompleteChain::GetShapeRecordId(int nChainId, int nTLID)
549 :
550 : {
551 0 : CPLAssert(nChainId >= 0 && nChainId < GetFeatureCount());
552 :
553 0 : if (fpShape == nullptr || panShapeRecordId == nullptr)
554 0 : return -1;
555 :
556 : /* -------------------------------------------------------------------- */
557 : /* Do we already have the answer? */
558 : /* -------------------------------------------------------------------- */
559 0 : if (panShapeRecordId[nChainId] != 0)
560 0 : return panShapeRecordId[nChainId];
561 :
562 : /* -------------------------------------------------------------------- */
563 : /* If we don't already have this value, then search from the */
564 : /* previous known record. */
565 : /* -------------------------------------------------------------------- */
566 : int iTestChain, nWorkingRecId;
567 :
568 0 : for (iTestChain = nChainId - 1;
569 0 : iTestChain >= 0 && panShapeRecordId[iTestChain] <= 0; iTestChain--)
570 : {
571 : }
572 :
573 0 : if (iTestChain < 0)
574 : {
575 0 : iTestChain = -1;
576 0 : nWorkingRecId = 1;
577 : }
578 : else
579 : {
580 0 : nWorkingRecId = panShapeRecordId[iTestChain] + 1;
581 : }
582 :
583 : /* -------------------------------------------------------------------- */
584 : /* If we have non existent records following (-1's) we can */
585 : /* narrow our search a bit. */
586 : /* -------------------------------------------------------------------- */
587 0 : while (panShapeRecordId[iTestChain + 1] == -1)
588 : {
589 0 : iTestChain++;
590 : }
591 :
592 : /* -------------------------------------------------------------------- */
593 : /* Read records up to the maximum distance that is possibly */
594 : /* required, looking for our target TLID. */
595 : /* -------------------------------------------------------------------- */
596 0 : int nMaxChainToRead = nChainId - iTestChain;
597 0 : int nChainsRead = 0;
598 : char achShapeRec[OGR_TIGER_RECBUF_LEN];
599 0 : int nShapeRecLen =
600 0 : psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
601 :
602 0 : if (nShapeRecLen <= 0)
603 : {
604 0 : return -2;
605 : }
606 :
607 0 : while (nChainsRead < nMaxChainToRead)
608 : {
609 0 : const auto nOffset =
610 0 : static_cast<uint64_t>(nWorkingRecId - 1) * nShapeRecLen;
611 0 : if (VSIFSeekL(fpShape, nOffset, SEEK_SET) != 0)
612 : {
613 0 : CPLError(CE_Failure, CPLE_FileIO,
614 : "Failed to seek to %" PRIu64 " of %s2", nOffset,
615 : pszModule);
616 0 : return -2;
617 : }
618 :
619 0 : if (VSIFReadL(achShapeRec, psRT2Info->nRecordLength, 1, fpShape) != 1)
620 : {
621 0 : if (!VSIFEofL(fpShape))
622 : {
623 0 : CPLError(CE_Failure, CPLE_FileIO,
624 : "Failed to read record %d of %s2", nWorkingRecId - 1,
625 : pszModule);
626 0 : return -2;
627 : }
628 : else
629 0 : return -1;
630 : }
631 :
632 0 : if (atoi(GetField(achShapeRec, 6, 15)) == nTLID)
633 : {
634 0 : panShapeRecordId[nChainId] = nWorkingRecId;
635 :
636 0 : return nWorkingRecId;
637 : }
638 :
639 0 : if (atoi(GetField(achShapeRec, 16, 18)) == 1)
640 : {
641 0 : nChainsRead++;
642 : }
643 :
644 0 : nWorkingRecId++;
645 : }
646 :
647 0 : panShapeRecordId[nChainId] = -1;
648 :
649 0 : return -1;
650 : }
|